diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h --- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h +++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h @@ -14,23 +14,25 @@ #ifndef LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H #define LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H +#include "llvm/ADT/FunctionExtras.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ParentMapContext.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/SourceManager.h" #include "clang/ExtractAPI/API.h" -#include "llvm/ADT/FunctionExtras.h" +#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h" +#include namespace clang { namespace extractapi { +namespace impl { -/// The RecursiveASTVisitor to traverse symbol declarations and collect API -/// information. -class ExtractAPIVisitor : public RecursiveASTVisitor { +template +class ExtractAPIVisitorBase : public RecursiveASTVisitor { public: - ExtractAPIVisitor(ASTContext &Context, - llvm::unique_function LocationChecker, - APISet &API) - : Context(Context), API(API), - LocationChecker(std::move(LocationChecker)) {} + ExtractAPIVisitorBase(ASTContext &Context, APISet &API) + : Context(Context), API(API) {} const APISet &getAPI() const { return API; } @@ -50,7 +52,11 @@ bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl); -private: + bool shouldDeclBeIncluded(const Decl *Decl) const; + + const RawComment *fetchRawCommentForDecl(const Decl *Decl) const; + +protected: /// Collect API information for the enum constants and associate with the /// parent enum. void recordEnumConstants(EnumRecord *EnumRecord, @@ -77,9 +83,554 @@ void recordObjCProtocols(ObjCContainerRecord *Container, ObjCInterfaceDecl::protocol_range Protocols); + ASTContext &Context; APISet &API; - llvm::unique_function LocationChecker; + + StringRef getTypedefName(const TagDecl *Decl) { + if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl()) + return TypedefDecl->getName(); + + return {}; + } + + bool isInSystemHeader(const Decl *D) { + return Context.getSourceManager().isInSystemHeader(D->getLocation()); + } + +private: + Derived &getConcrete() { return *static_cast(this); } +}; + +template +bool ExtractAPIVisitorBase::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 (!getConcrete().shouldDeclBeIncluded(Decl)) + 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 = getConcrete().fetchRawCommentForDecl(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(Decl)); + return true; +} + +template +bool ExtractAPIVisitorBase::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.template 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 (!getConcrete().shouldDeclBeIncluded(Decl)) + 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 = getConcrete().fetchRawCommentForDecl(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(Decl)); + return true; +} + +template +bool ExtractAPIVisitorBase::VisitEnumDecl(const EnumDecl *Decl) { + if (!getConcrete().shouldDeclBeIncluded(Decl)) + 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 = getConcrete().fetchRawCommentForDecl(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(Decl)); + + // Now collect information about the enumerators in this enum. + getConcrete().recordEnumConstants(EnumRecord, Decl->enumerators()); + + return true; +} + +template +bool ExtractAPIVisitorBase::VisitRecordDecl(const RecordDecl *Decl) { + // Skip C++ structs/classes/unions + // TODO: support C++ records + if (isa(Decl)) + return true; + + if (!getConcrete().shouldDeclBeIncluded(Decl)) + 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 = getConcrete().fetchRawCommentForDecl(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(Decl)); + + // Now collect information about the fields in this struct. + getConcrete().recordStructFields(StructRecord, Decl->fields()); + + return true; +} + +template +bool ExtractAPIVisitorBase::VisitObjCInterfaceDecl( + const ObjCInterfaceDecl *Decl) { + if (!getConcrete().shouldDeclBeIncluded(Decl)) + 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 = getConcrete().fetchRawCommentForDecl(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(Decl)); + + // Record all methods (selectors). This doesn't include automatically + // synthesized property methods. + getConcrete().recordObjCMethods(ObjCInterfaceRecord, Decl->methods()); + getConcrete().recordObjCProperties(ObjCInterfaceRecord, Decl->properties()); + getConcrete().recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars()); + getConcrete().recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols()); + + return true; +} + +template +bool ExtractAPIVisitorBase::VisitObjCProtocolDecl( + const ObjCProtocolDecl *Decl) { + if (!getConcrete().shouldDeclBeIncluded(Decl)) + 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 = getConcrete().fetchRawCommentForDecl(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(Decl)); + + getConcrete().recordObjCMethods(ObjCProtocolRecord, Decl->methods()); + getConcrete().recordObjCProperties(ObjCProtocolRecord, Decl->properties()); + getConcrete().recordObjCProtocols(ObjCProtocolRecord, Decl->protocols()); + + return true; +} + +template +bool ExtractAPIVisitorBase::VisitTypedefNameDecl( + const TypedefNameDecl *Decl) { + // Skip ObjC Type Parameter for now. + if (isa(Decl)) + return true; + + if (!Decl->isDefinedOutsideFunctionOrMethod()) + return true; + + if (!getConcrete().shouldDeclBeIncluded(Decl)) + return true; + + PresumedLoc Loc = + Context.getSourceManager().getPresumedLoc(Decl->getLocation()); + StringRef Name = Decl->getName(); + StringRef USR = API.recordUSR(Decl); + DocComment Comment; + if (auto *RawComment = getConcrete().fetchRawCommentForDecl(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(Decl)); + + return true; +} + +template +bool ExtractAPIVisitorBase::VisitObjCCategoryDecl( + const ObjCCategoryDecl *Decl) { + if (!getConcrete().shouldDeclBeIncluded(Decl)) + return true; + + StringRef Name = Decl->getName(); + StringRef USR = API.recordUSR(Decl); + PresumedLoc Loc = + Context.getSourceManager().getPresumedLoc(Decl->getLocation()); + DocComment Comment; + if (auto *RawComment = getConcrete().fetchRawCommentForDecl(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(Decl)); + + getConcrete().recordObjCMethods(ObjCCategoryRecord, Decl->methods()); + getConcrete().recordObjCProperties(ObjCCategoryRecord, Decl->properties()); + getConcrete().recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars()); + getConcrete().recordObjCProtocols(ObjCCategoryRecord, Decl->protocols()); + + return true; +} + +/// Collect API information for the enum constants and associate with the +/// parent enum. +template +void ExtractAPIVisitorBase::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 = getConcrete().fetchRawCommentForDecl(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(Constant)); + } +} + +/// Collect API information for the struct fields and associate with the +/// parent struct. +template +void ExtractAPIVisitorBase::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 = getConcrete().fetchRawCommentForDecl(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(Field)); + } +} + +/// Collect API information for the Objective-C methods and associate with the +/// parent container. +template +void ExtractAPIVisitorBase::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 = getConcrete().fetchRawCommentForDecl(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(Method)); + } +} + +template +void ExtractAPIVisitorBase::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 = getConcrete().fetchRawCommentForDecl(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(Property)); + } +} + +template +void ExtractAPIVisitorBase::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 = getConcrete().fetchRawCommentForDecl(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(Ivar)); + } +} + +template +void ExtractAPIVisitorBase::recordObjCProtocols( + ObjCContainerRecord *Container, + ObjCInterfaceDecl::protocol_range Protocols) { + for (const auto *Protocol : Protocols) + Container->Protocols.emplace_back(Protocol->getName(), + API.recordUSR(Protocol)); +} + +} // namespace impl + +/// The RecursiveASTVisitor to traverse symbol declarations and collect API +/// information. +template +class ExtractAPIVisitor + : public impl::ExtractAPIVisitorBase, ExtractAPIVisitor<>, Derived>> { + using Base = impl::ExtractAPIVisitorBase, ExtractAPIVisitor<>, Derived>>; + +public: + ExtractAPIVisitor(ASTContext &Context, APISet &API) : Base(Context, API) {} + + bool shouldDeclBeIncluded(const Decl *D) const { return true; } + const RawComment *fetchRawCommentForDecl(const Decl *D) const { + return this->Context.getRawCommentForDeclNoCache(D); + } }; } // namespace extractapi diff --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h b/clang/include/clang/ExtractAPI/TypedefUnderlyingTypeResolver.h rename from clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h rename to clang/include/clang/ExtractAPI/TypedefUnderlyingTypeResolver.h diff --git a/clang/lib/ExtractAPI/CMakeLists.txt b/clang/lib/ExtractAPI/CMakeLists.txt --- a/clang/lib/ExtractAPI/CMakeLists.txt +++ b/clang/lib/ExtractAPI/CMakeLists.txt @@ -8,7 +8,6 @@ APIIgnoresList.cpp AvailabilityInfo.cpp ExtractAPIConsumer.cpp - ExtractAPIVisitor.cpp DeclarationFragments.cpp Serialization/SerializerBase.cpp Serialization/SymbolGraphSerializer.cpp diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/ExtractAPI/DeclarationFragments.h" -#include "TypedefUnderlyingTypeResolver.h" +#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h" #include "clang/Index/USRGeneration.h" #include "llvm/ADT/StringSwitch.h" diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp --- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -12,8 +12,10 @@ /// //===----------------------------------------------------------------------===// +#include "clang/AST/ASTConcept.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DeclObjC.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" @@ -33,6 +35,7 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" @@ -164,7 +167,7 @@ struct LocationFileChecker { bool operator()(SourceLocation Loc) { - // If the loc refers to a macro expansion we need to first get the file + // If the loc refersSourceLocationxpansion we need to first get the file // location of the expansion. auto &SM = CI.getSourceManager(); auto FileLoc = SM.getFileLoc(Loc); @@ -219,11 +222,34 @@ llvm::DenseSet ExternalFileEntries; }; +struct BatchExtractAPIVisitor : ExtractAPIVisitor { + bool shouldDeclBeIncluded(const Decl *D) const { + bool ShouldBeIncluded = true; + // Check that we have the definition for redeclarable types. + if (auto *TD = llvm::dyn_cast(D)) + ShouldBeIncluded = TD->isThisDeclarationADefinition(); + else if (auto *Interface = llvm::dyn_cast(D)) + ShouldBeIncluded = Interface->isThisDeclarationADefinition(); + else if (auto *Protocol = llvm::dyn_cast(D)) + ShouldBeIncluded = Protocol->isThisDeclarationADefinition(); + + ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation()); + return ShouldBeIncluded; + } + + BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context, + APISet &API) + : ExtractAPIVisitor(Context, API), LCF(LCF) {} + +private: + LocationFileChecker &LCF; +}; + class ExtractAPIConsumer : public ASTConsumer { public: ExtractAPIConsumer(ASTContext &Context, std::unique_ptr LCF, APISet &API) - : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {} + : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {} void HandleTranslationUnit(ASTContext &Context) override { // Use ExtractAPIVisitor to traverse symbol declarations in the context. @@ -231,7 +257,7 @@ } private: - ExtractAPIVisitor Visitor; + BatchExtractAPIVisitor Visitor; std::unique_ptr LCF; }; diff --git a/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp b/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp deleted file mode 100644 --- a/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp +++ /dev/null @@ -1,560 +0,0 @@ -//===- 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)); -} diff --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp --- a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp +++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp @@ -11,7 +11,7 @@ /// //===----------------------------------------------------------------------===// -#include "TypedefUnderlyingTypeResolver.h" +#include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h" #include "clang/Index/USRGeneration.h" using namespace clang; diff --git a/clang/test/Index/extract-api-cursor.m b/clang/test/Index/extract-api-cursor.m --- a/clang/test/Index/extract-api-cursor.m +++ b/clang/test/Index/extract-api-cursor.m @@ -1,3 +1,5 @@ +// Test is line- and column-sensitive. Run lines are below + /// Foo docs struct Foo { /// Bar docs @@ -25,91 +27,94 @@ - (void)derivedMethodWithValue:(id)value; @end -/// This won't show up in docs because we can't serialize it -@interface Derived () -/// Derived method in category docs, won't show up either. -- (void)derivedMethodInCategory; +@implementation Derived +- (void)derivedMethodWithValue:(id)value { + int a = 5; +} @end -// RUN: c-index-test -single-symbol-sgfs local %s | FileCheck %s - -// Checking for Foo -// CHECK: "parentContexts":[] -// CHECK-SAME: "relatedSymbols":[] -// CHECK-SAME: "relationships":[] -// CHECK-SAME: "text":"Foo docs" -// CHECK-SAME: "kind":{"displayName":"Structure","identifier":"objective-c.struct"} -// CHECK-SAME: "title":"Foo" - -// Checking for bar -// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S@Foo"}] -// CHECK-SAME: "relatedSymbols":[] -// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:@S@Foo@FI@bar","target":"c:@S@Foo" -// CHECK-SAME: "text":"Bar docs" -// CHECK-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"} -// CHECK-SAME: "title":"bar" - -// Checking for Base -// CHECK-NEXT: "parentContexts":[] -// CHECK-SAME: "relatedSymbols":[] -// CHECK-SAME: "relationships":[] -// CHECK-SAME: "text":"Base docs" -// CHECK-SAME: "kind":{"displayName":"Class","identifier":"objective-c.class"} -// CHECK-SAME: "title":"Base" - -// Checking for baseProperty -// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}] -// CHECK-SAME: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c" -// CHECK-SAME: "isSystem":false -// CHECK-SAME: "usr":"c:@S@Foo"}] -// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(py)baseProperty","target":"c:objc(cs)Base" -// CHECK-SAME: "text":"Base property docs" -// CHECK-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"} -// CHECK-SAME: "title":"baseProperty" - -// Checking for baseMethodWithArg -// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}] -// CHECK-SAME: "relatedSymbols":[] -// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(im)baseMethodWithArg:","target":"c:objc(cs)Base" -// CHECK-SAME: "text":"Base method docs" -// CHECK-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"} -// CHECK-SAME: "title":"baseMethodWithArg:" - -// Checking for Protocol -// CHECK-NEXT: "parentContexts":[] -// CHECK-SAME: "relatedSymbols":[] -// CHECK-SAME: "relationships":[] -// CHECK-SAME: "text":"Protocol docs" -// CHECK-SAME: "kind":{"displayName":"Protocol","identifier":"objective-c.protocol"} -// CHECK-SAME: "title":"Protocol" - -// Checking for protocolProperty -// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"}] -// CHECK-SAME: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c" -// CHECK-SAME: "isSystem":false -// CHECK-SAME: "usr":"c:@S@Foo"}] -// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(pl)Protocol(py)protocolProperty","target":"c:objc(pl)Protocol" -// CHECK-SAME: "text":"Protocol property docs" -// CHECK-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"} -// CHECK-SAME: "title":"protocolProperty" - -// Checking for Derived -// CHECK-NEXT: "parentContexts":[] -// CHECK-SAME: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c" -// CHECK-SAME: "isSystem":false -// CHECK-SAME: "usr":"c:objc(cs)Base"}] -// CHECK-SAME: "relationships":[{"kind":"inheritsFrom","source":"c:objc(cs)Derived","target":"c:objc(cs)Base" -// CHECK-SAME: "text":"Derived docs" -// CHECK-SAME: "kind":{"displayName":"Class","identifier":"objective-c.class"} -// CHECK-SAME: "title":"Derived" - -// Checking for derivedMethodWithValue -// CHECK-NEXT: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}] -// CHECK-SAME: "relatedSymbols":[] -// CHECK-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived" -// CHECK-SAME: "text":"Derived method docs" -// CHECK-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"} -// CHECK-SAME: "title":"derivedMethodWithValue:" - -// CHECK-NOT: This won't show up in docs because we can't serialize it -// CHECK-NOT: Derived method in category docs, won't show up either. +// RUN: c-index-test -single-symbol-sgf-at=%s:4:9 local %s | FileCheck -check-prefix=CHECK-FOO %s +// CHECK-FOO: "parentContexts":[] +// CHECK-FOO: "relatedSymbols":[] +// CHECK-FOO: "relationships":[] +// CHECK-FOO: "text":"Foo docs" +// CHECK-FOO: "kind":{"displayName":"Structure","identifier":"objective-c.struct"} +// CHECK-FOO: "title":"Foo" + +// RUN: c-index-test -single-symbol-sgf-at=%s:6:9 local %s | FileCheck -check-prefix=CHECK-BAR %s +// CHECK-BAR: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S@Foo"}] +// CHECK-BAR: "relatedSymbols":[] +// CHECK-BAR: "relationships":[{"kind":"memberOf","source":"c:@S@Foo@FI@bar","target":"c:@S@Foo" +// CHECK-BAR: "text":"Bar docs" +// CHECK-BAR: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"} +// CHECK-BAR: "title":"bar" + +// RUN: c-index-test -single-symbol-sgf-at=%s:10:11 local %s | FileCheck -check-prefix=CHECK-BASE %s +// CHECK-BASE: "parentContexts":[] +// CHECK-BASE: "relatedSymbols":[] +// CHECK-BASE: "relationships":[] +// CHECK-BASE: "text":"Base docs" +// CHECK-BASE: "kind":{"displayName":"Class","identifier":"objective-c.class"} +// CHECK-BASE: "title":"Base" + +// RUN: c-index-test -single-symbol-sgf-at=%s:12:25 local %s | FileCheck -check-prefix=CHECK-BASE-PROP %s +// CHECK-BASE-PROP: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}] +// CHECK-BASE-PROP: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c" +// CHECK-BASE-PROP: "isSystem":false +// CHECK-BASE-PROP: "usr":"c:@S@Foo"}] +// CHECK-BASE-PROP: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(py)baseProperty","target":"c:objc(cs)Base" +// CHECK-BASE-PROP: "text":"Base property docs" +// CHECK-BASE-PROP: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"} +// CHECK-BASE-PROP: "title":"baseProperty" + +// RUN: c-index-test -single-symbol-sgf-at=%s:15:9 local %s | FileCheck -check-prefix=CHECK-BASE-METHOD %s +// CHECK-BASE-METHOD: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}] +// CHECK-BASE-METHOD: "relatedSymbols":[] +// CHECK-BASE-METHOD: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(im)baseMethodWithArg:","target":"c:objc(cs)Base" +// CHECK-BASE-METHOD: "text":"Base method docs" +// CHECK-BASE-METHOD: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"} +// CHECK-BASE-METHOD: "title":"baseMethodWithArg:" + +// RUN: c-index-test -single-symbol-sgf-at=%s:19:11 local %s | FileCheck -check-prefix=CHECK-PROTOCOL %s +// CHECK-PROTOCOL: "parentContexts":[] +// CHECK-PROTOCOL: "relatedSymbols":[] +// CHECK-PROTOCOL: "relationships":[] +// CHECK-PROTOCOL: "text":"Protocol docs" +// CHECK-PROTOCOL: "kind":{"displayName":"Protocol","identifier":"objective-c.protocol"} +// CHECK-PROTOCOL: "title":"Protocol" + +// RUN: c-index-test -single-symbol-sgf-at=%s:21:27 local %s | FileCheck -check-prefix=CHECK-PROTOCOL-PROP %s +// CHECK-PROTOCOL-PROP: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"}] +// CHECK-PROTOCOL-PROP: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c" +// CHECK-PROTOCOL-PROP: "isSystem":false +// CHECK-PROTOCOL-PROP: "usr":"c:@S@Foo"}] +// CHECK-PROTOCOL-PROP: "relationships":[{"kind":"memberOf","source":"c:objc(pl)Protocol(py)protocolProperty","target":"c:objc(pl)Protocol" +// CHECK-PROTOCOL-PROP: "text":"Protocol property docs" +// CHECK-PROTOCOL-PROP: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"} +// CHECK-PROTOCOL-PROP: "title":"protocolProperty" + +// RUN: c-index-test -single-symbol-sgf-at=%s:25:15 local %s | FileCheck -check-prefix=CHECK-DERIVED %s +// CHECK-DERIVED: "parentContexts":[] +// CHECK-DERIVED: "relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c" +// CHECK-DERIVED: "isSystem":false +// CHECK-DERIVED: "usr":"c:objc(cs)Base"}] +// CHECK-DERIVED: "relationships":[{"kind":"inheritsFrom","source":"c:objc(cs)Derived","target":"c:objc(cs)Base" +// CHECK-DERIVED: "text":"Derived docs" +// CHECK-DERIVED: "kind":{"displayName":"Class","identifier":"objective-c.class"} +// CHECK-DERIVED: "title":"Derived" + +// RUN: c-index-test -single-symbol-sgf-at=%s:27:11 local %s | FileCheck -check-prefix=CHECK-DERIVED-METHOD %s +// CHECK-DERIVED-METHOD: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}] +// CHECK-DERIVED-METHOD: "relatedSymbols":[] +// CHECK-DERIVED-METHOD: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived" +// CHECK-DERIVED-METHOD: "text":"Derived method docs" +// CHECK-DERIVED-METHOD: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"} +// CHECK-DERIVED-METHOD: "title":"derivedMethodWithValue:" + +// RUN: c-index-test -single-symbol-sgf-at=%s:31:11 local %s | FileCheck -check-prefix=CHECK-DERIVED-METHOD-IMPL %s +// CHECK-DERIVED-METHOD-IMPL: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}] +// CHECK-DERIVED-METHOD-IMPL: "relatedSymbols":[] +// CHECK-DERIVED-METHOD-IMPL: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived" +// CHECK-DERIVED-METHOD-IMPL: "text":"Derived method docs" +// CHECK-DERIVED-METHOD-IMPL: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"} +// CHECK-DERIVED-METHOD-IMPL: "title":"derivedMethodWithValue:" diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -3,6 +3,7 @@ #include "clang-c/BuildSystem.h" #include "clang-c/CXCompilationDatabase.h" #include "clang-c/CXErrorCode.h" +#include "clang-c/CXSourceLocation.h" #include "clang-c/CXString.h" #include "clang-c/Documentation.h" #include "clang-c/Index.h" @@ -4881,6 +4882,21 @@ return result; } +static void inspect_single_symbol_sgf_cursor(CXCursor Cursor) { + CXSourceLocation CursorLoc; + CXString SGFData; + const char *SGF; + unsigned line, column; + CursorLoc = clang_getCursorLocation(Cursor); + clang_getSpellingLocation(CursorLoc, 0, &line, &column, 0); + printf("%d:%d: ", line, column); + + SGFData = clang_getSymbolGraphForCursor(Cursor); + SGF = clang_getCString(SGFData); + if (SGF) + printf("%s\n", SGF); +} + /******************************************************************************/ /* Command line processing. */ /******************************************************************************/ @@ -4940,6 +4956,7 @@ " c-index-test -print-usr-file \n"); fprintf(stderr, " c-index-test -single-symbol-sgfs {*}\n" + " c-index-test -single-symbol-sgf-at= {*}\n" " c-index-test -single-symbol-sgf-for= {}*\n"); fprintf(stderr, " c-index-test -write-pch \n" @@ -5076,6 +5093,9 @@ else if (argc > 3 && strcmp(argv[1], "-single-symbol-sgfs") == 0) return perform_test_load_source(argc - 3, argv + 3, argv[2], PrintSingleSymbolSGFs, NULL); + else if (argc > 2 && strstr(argv[1], "-single-symbol-sgf-at=") == argv[1]) + return inspect_cursor_at( + argc, argv, "-single-symbol-sgf-at=", inspect_single_symbol_sgf_cursor); else if (argc > 2 && strstr(argv[1], "-single-symbol-sgf-for=") == argv[1]) return perform_test_single_symbol_sgf(argv[1], argc - 2, argv + 2); diff --git a/clang/tools/libclang/CXExtractAPI.cpp b/clang/tools/libclang/CXExtractAPI.cpp --- a/clang/tools/libclang/CXExtractAPI.cpp +++ b/clang/tools/libclang/CXExtractAPI.cpp @@ -18,6 +18,7 @@ #include "clang-c/Index.h" #include "clang-c/Platform.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" #include "clang/Basic/TargetInfo.h" #include "clang/ExtractAPI/API.h" #include "clang/ExtractAPI/ExtractAPIVisitor.h" @@ -34,13 +35,73 @@ using namespace clang; using namespace clang::extractapi; +namespace { +struct LibClangExtractAPIVisitor + : ExtractAPIVisitor { + using Base = ExtractAPIVisitor; + + LibClangExtractAPIVisitor(ASTContext &Context, APISet &API) + : ExtractAPIVisitor(Context, API) {} + + const RawComment *fetchRawCommentForDecl(const Decl *D) const { + return Context.getRawCommentForAnyRedecl(D); + } + + // We need to visit implementations as well to ensure that when a user clicks + // on a method defined only within the implementation that we can still + // provide a symbol graph for it. + bool VisitObjCImplementationDecl(const ObjCImplementationDecl *Decl) { + if (!shouldDeclBeIncluded(Decl)) + return true; + + const ObjCInterfaceDecl *Interface = Decl->getClassInterface(); + StringRef Name = Interface->getName(); + StringRef USR = API.recordUSR(Decl); + PresumedLoc Loc = + Context.getSourceManager().getPresumedLoc(Decl->getLocation()); + LinkageInfo Linkage = Decl->getLinkageAndVisibility(); + DocComment Comment; + if (auto *RawComment = fetchRawCommentForDecl(Interface)) + Comment = RawComment->getFormattedLines(Context.getSourceManager(), + Context.getDiagnostics()); + + // Build declaration fragments and sub-heading by generating them for the + // interface. + DeclarationFragments Declaration = + DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Interface); + 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(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()); + + return true; + } +}; +} // namespace + DEFINE_SIMPLE_CONVERSION_FUNCTIONS(APISet, CXAPISet) -static void WalkupFromMostDerivedType(ExtractAPIVisitor &Visitor, Decl *D); +static void WalkupFromMostDerivedType(LibClangExtractAPIVisitor &Visitor, + Decl *D); template static bool WalkupParentContext(DeclContext *Parent, - ExtractAPIVisitor &Visitor) { + LibClangExtractAPIVisitor &Visitor) { if (auto *D = dyn_cast(Parent)) { WalkupFromMostDerivedType(Visitor, D); return true; @@ -48,7 +109,8 @@ return false; } -static void WalkupFromMostDerivedType(ExtractAPIVisitor &Visitor, Decl *D) { +static void WalkupFromMostDerivedType(LibClangExtractAPIVisitor &Visitor, + Decl *D) { switch (D->getKind()) { #define ABSTRACT_DECL(DECL) #define DECL(CLASS, BASE) \ @@ -84,8 +146,7 @@ auto Lang = Unit->getInputKind().getLanguage(); APISet *API = new APISet(Ctx.getTargetInfo().getTriple(), Lang, Unit->getMainFileName().str()); - ExtractAPIVisitor Visitor( - Ctx, [](SourceLocation Loc) { return true; }, *API); + LibClangExtractAPIVisitor Visitor(Ctx, *API); for (auto It = Unit->top_level_begin(); It != Unit->top_level_end(); ++It) { Visitor.TraverseDecl(*It); @@ -107,45 +168,50 @@ } CXString clang_getSymbolGraphForCursor(CXCursor cursor) { + cursor = clang_getCursorReferenced(cursor); CXCursorKind Kind = clang_getCursorKind(cursor); - if (clang_isDeclaration(Kind)) { - const Decl *D = cxcursor::getCursorDecl(cursor); + if (!clang_isDeclaration(Kind)) + return cxstring::createNull(); - if (!D) - return cxstring::createNull(); + const Decl *D = cxcursor::getCursorDecl(cursor); - CXTranslationUnit TU = cxcursor::getCursorTU(cursor); - if (!TU) - return cxstring::createNull(); + if (!D) + return cxstring::createNull(); - ASTUnit *Unit = cxtu::getASTUnit(TU); + CXTranslationUnit TU = cxcursor::getCursorTU(cursor); + if (!TU) + return cxstring::createNull(); - auto &Ctx = Unit->getASTContext(); - auto Lang = Unit->getInputKind().getLanguage(); - APISet API(Ctx.getTargetInfo().getTriple(), Lang, - Unit->getMainFileName().str()); - ExtractAPIVisitor Visitor( - Ctx, [](SourceLocation Loc) { return true; }, API); + ASTUnit *Unit = cxtu::getASTUnit(TU); - SmallString<128> USR; - if (index::generateUSRForDecl(D, USR)) - return cxstring::createNull(); + auto &Ctx = Unit->getASTContext(); - WalkupFromMostDerivedType(Visitor, const_cast(D)); - auto *Record = API.findRecordForUSR(USR); + auto Lang = Unit->getInputKind().getLanguage(); + APISet API(Ctx.getTargetInfo().getTriple(), Lang, + Unit->getMainFileName().str()); + LibClangExtractAPIVisitor Visitor(Ctx, API); - if (!Record) - return cxstring::createNull(); + const Decl *CanonicalDecl = D->getCanonicalDecl(); + CanonicalDecl = CanonicalDecl ? CanonicalDecl : D; - for (const auto &Fragment : Record->Declaration.getFragments()) { - if (Fragment.Declaration) - WalkupFromMostDerivedType(Visitor, - const_cast(Fragment.Declaration)); - } + SmallString<128> USR; + if (index::generateUSRForDecl(CanonicalDecl, USR)) + return cxstring::createNull(); - if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(USR, API)) - return GenerateCXStringFromSymbolGraphData(std::move(*SGF)); + WalkupFromMostDerivedType(Visitor, const_cast(CanonicalDecl)); + auto *Record = API.findRecordForUSR(USR); + + if (!Record) + return cxstring::createNull(); + + for (const auto &Fragment : Record->Declaration.getFragments()) { + if (Fragment.Declaration) + WalkupFromMostDerivedType(Visitor, + const_cast(Fragment.Declaration)); } + if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(USR, API)) + return GenerateCXStringFromSymbolGraphData(std::move(*SGF)); + return cxstring::createNull(); }