//===- ExtractAPI/ExtractAPIVisitor.cpp -------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file implements the ExtractAPIVisitor an ASTVisitor to collect API /// information. /// //===----------------------------------------------------------------------===// #include "clang/ExtractAPI/ExtractAPIVisitor.h" #include "TypedefUnderlyingTypeResolver.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ParentMapContext.h" #include "clang/AST/RawCommentList.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/ExtractAPI/API.h" #include "clang/ExtractAPI/AvailabilityInfo.h" #include "clang/ExtractAPI/DeclarationFragments.h" #include "clang/Frontend/ASTConsumers.h" #include "clang/Frontend/FrontendOptions.h" #include "llvm/Support/raw_ostream.h" using namespace clang; using namespace extractapi; namespace { StringRef getTypedefName(const TagDecl *Decl) { if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl()) return TypedefDecl->getName(); return {}; } template bool isInSystemHeader(const ASTContext &Context, const DeclTy *D) { return Context.getSourceManager().isInSystemHeader(D->getLocation()); } } // namespace bool ExtractAPIVisitor::VisitVarDecl(const VarDecl *Decl) { // skip function parameters. if (isa(Decl)) return true; // Skip non-global variables in records (struct/union/class). if (Decl->getDeclContext()->isRecord()) return true; // Skip local variables inside function or method. if (!Decl->isDefinedOutsideFunctionOrMethod()) return true; // If this is a template but not specialization or instantiation, skip. if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) && Decl->getTemplateSpecializationKind() == TSK_Undeclared) return true; if (!LocationChecker(Decl->getLocation())) return true; // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); LinkageInfo Linkage = Decl->getLinkageAndVisibility(); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the variable. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForVar(Decl); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); // Add the global variable record to the API set. API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, Declaration, SubHeading, isInSystemHeader(Context, Decl)); return true; } bool ExtractAPIVisitor::VisitFunctionDecl(const FunctionDecl *Decl) { if (const auto *Method = dyn_cast(Decl)) { // Skip member function in class templates. if (Method->getParent()->getDescribedClassTemplate() != nullptr) return true; // Skip methods in records. for (auto P : Context.getParents(*Method)) { if (P.get()) return true; } // Skip ConstructorDecl and DestructorDecl. if (isa(Method) || isa(Method)) return true; } // Skip templated functions. switch (Decl->getTemplatedKind()) { case FunctionDecl::TK_NonTemplate: case FunctionDecl::TK_DependentNonTemplate: break; case FunctionDecl::TK_MemberSpecialization: case FunctionDecl::TK_FunctionTemplateSpecialization: if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) { if (!TemplateInfo->isExplicitInstantiationOrSpecialization()) return true; } break; case FunctionDecl::TK_FunctionTemplate: case FunctionDecl::TK_DependentFunctionTemplateSpecialization: return true; } if (!LocationChecker(Decl->getLocation())) return true; // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); LinkageInfo Linkage = Decl->getLinkageAndVisibility(); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments, sub-heading, and signature of the function. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForFunction(Decl); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); FunctionSignature Signature = DeclarationFragmentsBuilder::getFunctionSignature(Decl); // Add the function record to the API set. API.addGlobalFunction(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, Declaration, SubHeading, Signature, isInSystemHeader(Context, Decl)); return true; } bool ExtractAPIVisitor::VisitEnumDecl(const EnumDecl *Decl) { if (!Decl->isComplete()) return true; // Skip forward declaration. if (!Decl->isThisDeclarationADefinition()) return true; if (!LocationChecker(Decl->getLocation())) return true; SmallString<128> QualifiedNameBuffer; // Collect symbol information. StringRef Name = Decl->getName(); if (Name.empty()) Name = getTypedefName(Decl); if (Name.empty()) { llvm::raw_svector_ostream OS(QualifiedNameBuffer); Decl->printQualifiedName(OS); Name = QualifiedNameBuffer.str(); } StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the enum. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForEnum(Decl); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); EnumRecord *EnumRecord = API.addEnum( API.copyString(Name), USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading, isInSystemHeader(Context, Decl)); // Now collect information about the enumerators in this enum. recordEnumConstants(EnumRecord, Decl->enumerators()); return true; } bool ExtractAPIVisitor::VisitRecordDecl(const RecordDecl *Decl) { if (!Decl->isCompleteDefinition()) return true; // Skip C++ structs/classes/unions // TODO: support C++ records if (isa(Decl)) return true; if (!LocationChecker(Decl->getLocation())) return true; // Collect symbol information. StringRef Name = Decl->getName(); if (Name.empty()) Name = getTypedefName(Decl); if (Name.empty()) return true; StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the struct. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForStruct(Decl); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); StructRecord *StructRecord = API.addStruct(Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading, isInSystemHeader(Context, Decl)); // Now collect information about the fields in this struct. recordStructFields(StructRecord, Decl->fields()); return true; } bool ExtractAPIVisitor::VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) { // Skip forward declaration for classes (@class) if (!Decl->isThisDeclarationADefinition()) return true; if (!LocationChecker(Decl->getLocation())) return true; // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); LinkageInfo Linkage = Decl->getLinkageAndVisibility(); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the interface. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); // Collect super class information. SymbolReference SuperClass; if (const auto *SuperClassDecl = Decl->getSuperClass()) { SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString(); SuperClass.USR = API.recordUSR(SuperClassDecl); } ObjCInterfaceRecord *ObjCInterfaceRecord = API.addObjCInterface( Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, Declaration, SubHeading, SuperClass, isInSystemHeader(Context, Decl)); // Record all methods (selectors). This doesn't include automatically // synthesized property methods. recordObjCMethods(ObjCInterfaceRecord, Decl->methods()); recordObjCProperties(ObjCInterfaceRecord, Decl->properties()); recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars()); recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols()); return true; } bool ExtractAPIVisitor::VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) { // Skip forward declaration for protocols (@protocol). if (!Decl->isThisDeclarationADefinition()) return true; if (!LocationChecker(Decl->getLocation())) return true; // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the protocol. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol( Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading, isInSystemHeader(Context, Decl)); recordObjCMethods(ObjCProtocolRecord, Decl->methods()); recordObjCProperties(ObjCProtocolRecord, Decl->properties()); recordObjCProtocols(ObjCProtocolRecord, Decl->protocols()); return true; } bool ExtractAPIVisitor::VisitTypedefNameDecl(const TypedefNameDecl *Decl) { // Skip ObjC Type Parameter for now. if (isa(Decl)) return true; if (!Decl->isDefinedOutsideFunctionOrMethod()) return true; if (!LocationChecker(Decl->getLocation())) return true; PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); QualType Type = Decl->getUnderlyingType(); SymbolReference SymRef = TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type, API); API.addTypedef(Name, USR, Loc, AvailabilitySet(Decl), Comment, DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl), DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef, isInSystemHeader(Context, Decl)); return true; } bool ExtractAPIVisitor::VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) { // Collect symbol information. StringRef Name = Decl->getName(); StringRef USR = API.recordUSR(Decl); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Decl->getLocation()); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the category. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface(); SymbolReference Interface(InterfaceDecl->getName(), API.recordUSR(InterfaceDecl)); ObjCCategoryRecord *ObjCCategoryRecord = API.addObjCCategory( Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading, Interface, isInSystemHeader(Context, Decl)); recordObjCMethods(ObjCCategoryRecord, Decl->methods()); recordObjCProperties(ObjCCategoryRecord, Decl->properties()); recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars()); recordObjCProtocols(ObjCCategoryRecord, Decl->protocols()); return true; } /// Collect API information for the enum constants and associate with the /// parent enum. void ExtractAPIVisitor::recordEnumConstants( EnumRecord *EnumRecord, const EnumDecl::enumerator_range Constants) { for (const auto *Constant : Constants) { // Collect symbol information. StringRef Name = Constant->getName(); StringRef USR = API.recordUSR(Constant); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Constant->getLocation()); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the enum constant. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Constant); API.addEnumConstant(EnumRecord, Name, USR, Loc, AvailabilitySet(Constant), Comment, Declaration, SubHeading, isInSystemHeader(Context, Constant)); } } /// Collect API information for the struct fields and associate with the /// parent struct. void ExtractAPIVisitor::recordStructFields( StructRecord *StructRecord, const RecordDecl::field_range Fields) { for (const auto *Field : Fields) { // Collect symbol information. StringRef Name = Field->getName(); StringRef USR = API.recordUSR(Field); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Field->getLocation()); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the struct field. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForField(Field); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Field); API.addStructField(StructRecord, Name, USR, Loc, AvailabilitySet(Field), Comment, Declaration, SubHeading, isInSystemHeader(Context, Field)); } } /// Collect API information for the Objective-C methods and associate with the /// parent container. void ExtractAPIVisitor::recordObjCMethods( ObjCContainerRecord *Container, const ObjCContainerDecl::method_range Methods) { for (const auto *Method : Methods) { // Don't record selectors for properties. if (Method->isPropertyAccessor()) continue; StringRef Name = API.copyString(Method->getSelector().getAsString()); StringRef USR = API.recordUSR(Method); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Method->getLocation()); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments, sub-heading, and signature for the method. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Method); FunctionSignature Signature = DeclarationFragmentsBuilder::getFunctionSignature(Method); API.addObjCMethod(Container, Name, USR, Loc, AvailabilitySet(Method), Comment, Declaration, SubHeading, Signature, Method->isInstanceMethod(), isInSystemHeader(Context, Method)); } } void ExtractAPIVisitor::recordObjCProperties( ObjCContainerRecord *Container, const ObjCContainerDecl::prop_range Properties) { for (const auto *Property : Properties) { StringRef Name = Property->getName(); StringRef USR = API.recordUSR(Property); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Property->getLocation()); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the property. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Property); StringRef GetterName = API.copyString(Property->getGetterName().getAsString()); StringRef SetterName = API.copyString(Property->getSetterName().getAsString()); // Get the attributes for property. unsigned Attributes = ObjCPropertyRecord::NoAttr; if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_readonly) Attributes |= ObjCPropertyRecord::ReadOnly; API.addObjCProperty( Container, Name, USR, Loc, AvailabilitySet(Property), Comment, Declaration, SubHeading, static_cast(Attributes), GetterName, SetterName, Property->isOptional(), !(Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class), isInSystemHeader(Context, Property)); } } void ExtractAPIVisitor::recordObjCInstanceVariables( ObjCContainerRecord *Container, const llvm::iterator_range< DeclContext::specific_decl_iterator> Ivars) { for (const auto *Ivar : Ivars) { StringRef Name = Ivar->getName(); StringRef USR = API.recordUSR(Ivar); PresumedLoc Loc = Context.getSourceManager().getPresumedLoc(Ivar->getLocation()); DocComment Comment; if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar)) Comment = RawComment->getFormattedLines(Context.getSourceManager(), Context.getDiagnostics()); // Build declaration fragments and sub-heading for the instance variable. DeclarationFragments Declaration = DeclarationFragmentsBuilder::getFragmentsForField(Ivar); DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Ivar); ObjCInstanceVariableRecord::AccessControl Access = Ivar->getCanonicalAccessControl(); API.addObjCInstanceVariable( Container, Name, USR, Loc, AvailabilitySet(Ivar), Comment, Declaration, SubHeading, Access, isInSystemHeader(Context, Ivar)); } } void ExtractAPIVisitor::recordObjCProtocols( ObjCContainerRecord *Container, ObjCInterfaceDecl::protocol_range Protocols) { for (const auto *Protocol : Protocols) Container->Protocols.emplace_back(Protocol->getName(), API.recordUSR(Protocol)); }