diff --git a/clang/include/clang-c/Documentation.h b/clang/include/clang-c/Documentation.h index 5bece2cb6758f..e04c50a0e68b6 100644 --- a/clang/include/clang-c/Documentation.h +++ b/clang/include/clang-c/Documentation.h @@ -15,6 +15,7 @@ #ifndef LLVM_CLANG_C_DOCUMENTATION_H #define LLVM_CLANG_C_DOCUMENTATION_H +#include "clang-c/CXErrorCode.h" #include "clang-c/ExternC.h" #include "clang-c/Index.h" @@ -545,6 +546,69 @@ CINDEX_LINKAGE CXString clang_FullComment_getAsHTML(CXComment Comment); */ CINDEX_LINKAGE CXString clang_FullComment_getAsXML(CXComment Comment); +/** + * CXAPISet is an opaque type that represents a data structure containing all + * the API information for a given translation unit. This can be used for a + * single symbol symbol graph for a given symbol. + */ +typedef struct CXAPISetImpl *CXAPISet; + +/** + * Traverses the translation unit to create a \c CXAPISet. + * + * \param tu is the \c CXTranslationUnit to build the \c CXAPISet for. + * + * \param out_api is a pointer to the output of this function. It is needs to be + * disposed of by calling clang_disposeAPISet. + * + * \returns Error code indicating success or failure of the APISet creation. + */ +CINDEX_LINKAGE enum CXErrorCode clang_createAPISet(CXTranslationUnit tu, + CXAPISet *out_api); + +/** + * Dispose of an APISet. + * + * The provided \c CXAPISet can not be used after this function is called. + */ +CINDEX_LINKAGE void clang_disposeAPISet(CXAPISet api); + +/** + * Generate a single symbol symbol graph for the given USR. Returns a null + * string if the associated symbol can not be found in the provided \c CXAPISet. + * + * The output contains the symbol graph as well as some additional information + * about related symbols. + * + * \param usr is a string containing the USR of the symbol to generate the + * symbol graph for. + * + * \param api the \c CXAPISet to look for the symbol in. + * + * \returns a string containing the serialized symbol graph representation for + * the symbol being queried or a null string if it can not be found in the + * APISet. + */ +CINDEX_LINKAGE CXString clang_getSymbolGraphForUSR(const char *usr, + CXAPISet api); + +/** + * Generate a single symbol symbol graph for the declaration at the given + * cursor. Returns a null string if the AST node for the cursor isn't a + * declaration. + * + * The output contains the symbol graph as well as some additional information + * about related symbols. + * + * \param cursor the declaration for which to generate the single symbol symbol + * graph. + * + * \returns a string containing the serialized symbol graph representation for + * the symbol being queried or a null string if it can not be found in the + * APISet. + */ +CINDEX_LINKAGE CXString clang_getSymbolGraphForCursor(CXCursor cursor); + /** * @} */ diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h index b77d76d500df6..f3c1cce3fe403 100644 --- a/clang/include/clang/ExtractAPI/API.h +++ b/clang/include/clang/ExtractAPI/API.h @@ -55,6 +55,51 @@ using DocComment = std::vector; // defined in API.cpp /// The base representation of an API record. Holds common symbol information. struct APIRecord { + /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.) + enum RecordKind { + RK_Unknown, + RK_GlobalFunction, + RK_GlobalVariable, + RK_EnumConstant, + RK_Enum, + RK_StructField, + RK_Struct, + RK_ObjCInstanceProperty, + RK_ObjCClassProperty, + RK_ObjCIvar, + RK_ObjCClassMethod, + RK_ObjCInstanceMethod, + RK_ObjCInterface, + RK_ObjCCategory, + RK_ObjCProtocol, + RK_MacroDefinition, + RK_Typedef, + }; + + /// Stores information about the context of the declaration of this API. + /// This is roughly analogous to the DeclContext hierarchy for an AST Node. + struct HierarchyInformation { + /// The USR of the parent API. + StringRef ParentUSR; + /// The name of the parent API. + StringRef ParentName; + /// The record kind of the parent API. + RecordKind ParentKind = RK_Unknown; + /// A pointer to the parent APIRecord if known. + APIRecord *ParentRecord = nullptr; + + HierarchyInformation() = default; + HierarchyInformation(StringRef ParentUSR, StringRef ParentName, + RecordKind Kind, APIRecord *ParentRecord = nullptr) + : ParentUSR(ParentUSR), ParentName(ParentName), ParentKind(Kind), + ParentRecord(ParentRecord) {} + + bool empty() const { + return ParentUSR.empty() && ParentName.empty() && + ParentKind == RK_Unknown && ParentRecord == nullptr; + } + }; + StringRef USR; StringRef Name; PresumedLoc Location; @@ -75,23 +120,11 @@ struct APIRecord { /// Objective-C class/instance methods). DeclarationFragments SubHeading; - /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.) - enum RecordKind { - RK_GlobalFunction, - RK_GlobalVariable, - RK_EnumConstant, - RK_Enum, - RK_StructField, - RK_Struct, - RK_ObjCProperty, - RK_ObjCIvar, - RK_ObjCMethod, - RK_ObjCInterface, - RK_ObjCCategory, - RK_ObjCProtocol, - RK_MacroDefinition, - RK_Typedef, - }; + /// Information about the parent record of this record. + HierarchyInformation ParentInformation; + + /// Whether the symbol was defined in a system header. + bool IsFromSystemHeader; private: const RecordKind Kind; @@ -104,11 +137,12 @@ struct APIRecord { APIRecord(RecordKind Kind, StringRef USR, StringRef Name, PresumedLoc Location, AvailabilitySet Availabilities, LinkageInfo Linkage, const DocComment &Comment, - DeclarationFragments Declaration, DeclarationFragments SubHeading) + DeclarationFragments Declaration, DeclarationFragments SubHeading, + bool IsFromSystemHeader) : USR(USR), Name(Name), Location(Location), Availabilities(std::move(Availabilities)), Linkage(Linkage), Comment(Comment), Declaration(Declaration), SubHeading(SubHeading), - Kind(Kind) {} + IsFromSystemHeader(IsFromSystemHeader), Kind(Kind) {} // Pure virtual destructor to make APIRecord abstract virtual ~APIRecord() = 0; @@ -123,9 +157,10 @@ struct GlobalFunctionRecord : APIRecord { const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - FunctionSignature Signature) + FunctionSignature Signature, bool IsFromSystemHeader) : APIRecord(RK_GlobalFunction, USR, Name, Loc, std::move(Availabilities), - Linkage, Comment, Declaration, SubHeading), + Linkage, Comment, Declaration, SubHeading, + IsFromSystemHeader), Signature(Signature) {} static bool classof(const APIRecord *Record) { @@ -142,9 +177,10 @@ struct GlobalVariableRecord : APIRecord { AvailabilitySet Availabilities, LinkageInfo Linkage, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) + DeclarationFragments SubHeading, bool IsFromSystemHeader) : APIRecord(RK_GlobalVariable, USR, Name, Loc, std::move(Availabilities), - Linkage, Comment, Declaration, SubHeading) {} + Linkage, Comment, Declaration, SubHeading, + IsFromSystemHeader) {} static bool classof(const APIRecord *Record) { return Record->getKind() == RK_GlobalVariable; @@ -159,9 +195,10 @@ struct EnumConstantRecord : APIRecord { EnumConstantRecord(StringRef USR, StringRef Name, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) + DeclarationFragments SubHeading, bool IsFromSystemHeader) : APIRecord(RK_EnumConstant, USR, Name, Loc, std::move(Availabilities), - LinkageInfo::none(), Comment, Declaration, SubHeading) {} + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader) {} static bool classof(const APIRecord *Record) { return Record->getKind() == RK_EnumConstant; @@ -177,9 +214,11 @@ struct EnumRecord : APIRecord { EnumRecord(StringRef USR, StringRef Name, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, - DeclarationFragments Declaration, DeclarationFragments SubHeading) + DeclarationFragments Declaration, DeclarationFragments SubHeading, + bool IsFromSystemHeader) : APIRecord(RK_Enum, USR, Name, Loc, std::move(Availabilities), - LinkageInfo::none(), Comment, Declaration, SubHeading) {} + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader) {} static bool classof(const APIRecord *Record) { return Record->getKind() == RK_Enum; @@ -194,9 +233,10 @@ struct StructFieldRecord : APIRecord { StructFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) + DeclarationFragments SubHeading, bool IsFromSystemHeader) : APIRecord(RK_StructField, USR, Name, Loc, std::move(Availabilities), - LinkageInfo::none(), Comment, Declaration, SubHeading) {} + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader) {} static bool classof(const APIRecord *Record) { return Record->getKind() == RK_StructField; @@ -213,9 +253,10 @@ struct StructRecord : APIRecord { StructRecord(StringRef USR, StringRef Name, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) + DeclarationFragments SubHeading, bool IsFromSystemHeader) : APIRecord(RK_Struct, USR, Name, Loc, std::move(Availabilities), - LinkageInfo::none(), Comment, Declaration, SubHeading) {} + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader) {} static bool classof(const APIRecord *Record) { return Record->getKind() == RK_Struct; @@ -231,7 +272,6 @@ struct ObjCPropertyRecord : APIRecord { enum AttributeKind : unsigned { NoAttr = 0, ReadOnly = 1, - Class = 1 << 1, Dynamic = 1 << 2, }; @@ -240,23 +280,63 @@ struct ObjCPropertyRecord : APIRecord { StringRef SetterName; bool IsOptional; - ObjCPropertyRecord(StringRef USR, StringRef Name, PresumedLoc Loc, - AvailabilitySet Availabilities, const DocComment &Comment, + ObjCPropertyRecord(RecordKind Kind, StringRef USR, StringRef Name, + PresumedLoc Loc, AvailabilitySet Availabilities, + const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, AttributeKind Attributes, StringRef GetterName, StringRef SetterName, - bool IsOptional) - : APIRecord(RK_ObjCProperty, USR, Name, Loc, std::move(Availabilities), - LinkageInfo::none(), Comment, Declaration, SubHeading), + bool IsOptional, bool IsFromSystemHeader) + : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader), Attributes(Attributes), GetterName(GetterName), SetterName(SetterName), IsOptional(IsOptional) {} bool isReadOnly() const { return Attributes & ReadOnly; } bool isDynamic() const { return Attributes & Dynamic; } - bool isClassProperty() const { return Attributes & Class; } + + virtual ~ObjCPropertyRecord() = 0; +}; + +struct ObjCInstancePropertyRecord : ObjCPropertyRecord { + ObjCInstancePropertyRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + AttributeKind Attributes, StringRef GetterName, + StringRef SetterName, bool IsOptional, + bool IsFromSystemHeader) + : ObjCPropertyRecord(RK_ObjCInstanceProperty, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Attributes, GetterName, SetterName, + IsOptional, IsFromSystemHeader) {} static bool classof(const APIRecord *Record) { - return Record->getKind() == RK_ObjCProperty; + return Record->getKind() == RK_ObjCInstanceProperty; + } + +private: + virtual void anchor(); +}; + +struct ObjCClassPropertyRecord : ObjCPropertyRecord { + ObjCClassPropertyRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + AttributeKind Attributes, StringRef GetterName, + StringRef SetterName, bool IsOptional, + bool IsFromSystemHeader) + : ObjCPropertyRecord(RK_ObjCClassProperty, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Attributes, GetterName, SetterName, + IsOptional, IsFromSystemHeader) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_ObjCClassProperty; } private: @@ -273,9 +353,10 @@ struct ObjCInstanceVariableRecord : APIRecord { const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - AccessControl Access) + AccessControl Access, bool IsFromSystemHeader) : APIRecord(RK_ObjCIvar, USR, Name, Loc, std::move(Availabilities), - LinkageInfo::none(), Comment, Declaration, SubHeading), + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader), Access(Access) {} static bool classof(const APIRecord *Record) { @@ -289,19 +370,53 @@ struct ObjCInstanceVariableRecord : APIRecord { /// This holds information associated with Objective-C methods. struct ObjCMethodRecord : APIRecord { FunctionSignature Signature; - bool IsInstanceMethod; - ObjCMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc, - AvailabilitySet Availabilities, const DocComment &Comment, - DeclarationFragments Declaration, + ObjCMethodRecord() = delete; + + ObjCMethodRecord(RecordKind Kind, StringRef USR, StringRef Name, + PresumedLoc Loc, AvailabilitySet Availabilities, + const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, FunctionSignature Signature, - bool IsInstanceMethod) - : APIRecord(RK_ObjCMethod, USR, Name, Loc, std::move(Availabilities), - LinkageInfo::none(), Comment, Declaration, SubHeading), - Signature(Signature), IsInstanceMethod(IsInstanceMethod) {} + bool IsFromSystemHeader) + : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader), + Signature(Signature) {} + + virtual ~ObjCMethodRecord() = 0; +}; +struct ObjCInstanceMethodRecord : ObjCMethodRecord { + ObjCInstanceMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + FunctionSignature Signature, bool IsFromSystemHeader) + : ObjCMethodRecord(RK_ObjCInstanceMethod, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Signature, IsFromSystemHeader) {} static bool classof(const APIRecord *Record) { - return Record->getKind() == RK_ObjCMethod; + return Record->getKind() == RK_ObjCInstanceMethod; + } + +private: + virtual void anchor(); +}; + +struct ObjCClassMethodRecord : ObjCMethodRecord { + ObjCClassMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + FunctionSignature Signature, bool IsFromSystemHeader) + : ObjCMethodRecord(RK_ObjCClassMethod, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Signature, IsFromSystemHeader) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_ObjCClassMethod; } private: @@ -343,9 +458,9 @@ struct ObjCContainerRecord : APIRecord { PresumedLoc Loc, AvailabilitySet Availabilities, LinkageInfo Linkage, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) + DeclarationFragments SubHeading, bool IsFromSystemHeader) : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), Linkage, - Comment, Declaration, SubHeading) {} + Comment, Declaration, SubHeading, IsFromSystemHeader) {} virtual ~ObjCContainerRecord() = 0; }; @@ -357,10 +472,12 @@ struct ObjCCategoryRecord : ObjCContainerRecord { ObjCCategoryRecord(StringRef USR, StringRef Name, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading, SymbolReference Interface) + DeclarationFragments SubHeading, SymbolReference Interface, + bool IsFromSystemHeader) : ObjCContainerRecord(RK_ObjCCategory, USR, Name, Loc, std::move(Availabilities), LinkageInfo::none(), - Comment, Declaration, SubHeading), + Comment, Declaration, SubHeading, + IsFromSystemHeader), Interface(Interface) {} static bool classof(const APIRecord *Record) { @@ -382,10 +499,10 @@ struct ObjCInterfaceRecord : ObjCContainerRecord { const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - SymbolReference SuperClass) + SymbolReference SuperClass, bool IsFromSystemHeader) : ObjCContainerRecord(RK_ObjCInterface, USR, Name, Loc, std::move(Availabilities), Linkage, Comment, - Declaration, SubHeading), + Declaration, SubHeading, IsFromSystemHeader), SuperClass(SuperClass) {} static bool classof(const APIRecord *Record) { @@ -401,10 +518,11 @@ struct ObjCProtocolRecord : ObjCContainerRecord { ObjCProtocolRecord(StringRef USR, StringRef Name, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) + DeclarationFragments SubHeading, bool IsFromSystemHeader) : ObjCContainerRecord(RK_ObjCProtocol, USR, Name, Loc, std::move(Availabilities), LinkageInfo::none(), - Comment, Declaration, SubHeading) {} + Comment, Declaration, SubHeading, + IsFromSystemHeader) {} static bool classof(const APIRecord *Record) { return Record->getKind() == RK_ObjCProtocol; @@ -418,9 +536,11 @@ struct ObjCProtocolRecord : ObjCContainerRecord { struct MacroDefinitionRecord : APIRecord { MacroDefinitionRecord(StringRef USR, StringRef Name, PresumedLoc Loc, DeclarationFragments Declaration, - DeclarationFragments SubHeading) + DeclarationFragments SubHeading, + bool IsFromSystemHeader) : APIRecord(RK_MacroDefinition, USR, Name, Loc, AvailabilitySet(), - LinkageInfo(), {}, Declaration, SubHeading) {} + LinkageInfo(), {}, Declaration, SubHeading, + IsFromSystemHeader) {} static bool classof(const APIRecord *Record) { return Record->getKind() == RK_MacroDefinition; @@ -441,9 +561,11 @@ struct TypedefRecord : APIRecord { TypedefRecord(StringRef USR, StringRef Name, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading, SymbolReference UnderlyingType) + DeclarationFragments SubHeading, SymbolReference UnderlyingType, + bool IsFromSystemHeader) : APIRecord(RK_Typedef, USR, Name, Loc, std::move(Availabilities), - LinkageInfo(), Comment, Declaration, SubHeading), + LinkageInfo(), Comment, Declaration, SubHeading, + IsFromSystemHeader), UnderlyingType(UnderlyingType) {} static bool classof(const APIRecord *Record) { @@ -464,6 +586,11 @@ template <> struct has_function_signature : public std::true_type {}; template <> struct has_function_signature : public std::true_type {}; +template <> +struct has_function_signature + : public std::true_type {}; +template <> +struct has_function_signature : public std::true_type {}; /// APISet holds the set of API records collected from given inputs. class APISet { @@ -478,7 +605,7 @@ class APISet { addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, LinkageInfo Linkage, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading); + DeclarationFragments SubHeadin, bool IsFromSystemHeaderg); /// Create and add a function record into the API set. /// @@ -491,7 +618,7 @@ class APISet { AvailabilitySet Availability, LinkageInfo Linkage, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - FunctionSignature Signature); + FunctionSignature Signature, bool IsFromSystemHeader); /// Create and add an enum constant record into the API set. /// @@ -499,12 +626,11 @@ class APISet { /// \p USR alive. APISet::copyString provides a way to copy strings into /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method /// to generate the USR for \c D and keep it alive in APISet. - EnumConstantRecord *addEnumConstant(EnumRecord *Enum, StringRef Name, - StringRef USR, PresumedLoc Loc, - AvailabilitySet Availability, - const DocComment &Comment, - DeclarationFragments Declaration, - DeclarationFragments SubHeading); + EnumConstantRecord * + addEnumConstant(EnumRecord *Enum, StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availability, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading, bool IsFromSystemHeader); /// Create and add an enum record into the API set. /// @@ -515,7 +641,7 @@ class APISet { EnumRecord *addEnum(StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading); + DeclarationFragments SubHeading, bool IsFromSystemHeader); /// Create and add a struct field record into the API set. /// @@ -523,12 +649,11 @@ class APISet { /// \p USR alive. APISet::copyString provides a way to copy strings into /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method /// to generate the USR for \c D and keep it alive in APISet. - StructFieldRecord *addStructField(StructRecord *Struct, StringRef Name, - StringRef USR, PresumedLoc Loc, - AvailabilitySet Availability, - const DocComment &Comment, - DeclarationFragments Declaration, - DeclarationFragments SubHeading); + StructFieldRecord * + addStructField(StructRecord *Struct, StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availability, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading, bool IsFromSystemHeader); /// Create and add a struct record into the API set. /// @@ -540,7 +665,8 @@ class APISet { AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading); + DeclarationFragments SubHeading, + bool IsFromSystemHeader); /// Create and add an Objective-C category record into the API set. /// @@ -552,7 +678,8 @@ class APISet { addObjCCategory(StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading, SymbolReference Interface); + DeclarationFragments SubHeading, SymbolReference Interface, + bool IsFromSystemHeader); /// Create and add an Objective-C interface record into the API set. /// @@ -564,7 +691,8 @@ class APISet { addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, LinkageInfo Linkage, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading, SymbolReference SuperClass); + DeclarationFragments SubHeading, SymbolReference SuperClass, + bool IsFromSystemHeader); /// Create and add an Objective-C method record into the API set. /// @@ -577,7 +705,7 @@ class APISet { PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, FunctionSignature Signature, - bool IsInstanceMethod); + bool IsInstanceMethod, bool IsFromSystemHeader); /// Create and add an Objective-C property record into the API set. /// @@ -591,7 +719,8 @@ class APISet { const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, ObjCPropertyRecord::AttributeKind Attributes, - StringRef GetterName, StringRef SetterName, bool IsOptional); + StringRef GetterName, StringRef SetterName, bool IsOptional, + bool IsInstanceProperty, bool IsFromSystemHeader); /// Create and add an Objective-C instance variable record into the API set. /// @@ -603,7 +732,8 @@ class APISet { ObjCContainerRecord *Container, StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - ObjCInstanceVariableRecord::AccessControl Access); + ObjCInstanceVariableRecord::AccessControl Access, + bool IsFromSystemHeader); /// Create and add an Objective-C protocol record into the API set. /// @@ -611,12 +741,11 @@ class APISet { /// \p USR alive. APISet::copyString provides a way to copy strings into /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method /// to generate the USR for \c D and keep it alive in APISet. - ObjCProtocolRecord *addObjCProtocol(StringRef Name, StringRef USR, - PresumedLoc Loc, - AvailabilitySet Availability, - const DocComment &Comment, - DeclarationFragments Declaration, - DeclarationFragments SubHeading); + ObjCProtocolRecord * + addObjCProtocol(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, bool IsFromSystemHeader); /// Create a macro definition record into the API set. /// @@ -628,7 +757,8 @@ class APISet { MacroDefinitionRecord *addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc, DeclarationFragments Declaration, - DeclarationFragments SubHeading); + DeclarationFragments SubHeading, + bool IsFromSystemHeader); /// Create a typedef record into the API set. /// @@ -636,12 +766,11 @@ class APISet { /// \p USR alive. APISet::copyString provides a way to copy strings into /// APISet itself, and APISet::recordUSR(const Decl *D) is a helper method /// to generate the USR for \c D and keep it alive in APISet. - TypedefRecord *addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc, - AvailabilitySet Availability, - const DocComment &Comment, - DeclarationFragments Declaration, - DeclarationFragments SubHeading, - SymbolReference UnderlyingType); + TypedefRecord * + addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + SymbolReference UnderlyingType, bool IsFromSystemHeader); /// A mapping type to store a set of APIRecord%s with the USR as the key. template &getMacros() const { return Macros; } const RecordMap &getTypedefs() const { return Typedefs; } + /// Finds the APIRecord for a given USR. + /// + /// \returns a pointer to the APIRecord associated with that USR or nullptr. + APIRecord *findRecordForUSR(StringRef USR) const; + /// Generate and store the USR of declaration \p D. /// /// Note: The USR string is stored in and owned by Allocator. @@ -695,8 +829,9 @@ class APISet { /// \returns a StringRef of the copied string in APISet::Allocator. StringRef copyString(StringRef String); - APISet(const llvm::Triple &Target, Language Lang) - : Target(Target), Lang(Lang) {} + APISet(const llvm::Triple &Target, Language Lang, + const std::string &ProductName) + : Target(Target), Lang(Lang), ProductName(ProductName) {} private: /// BumpPtrAllocator to store generated/copied strings. @@ -707,6 +842,7 @@ class APISet { const llvm::Triple Target; const Language Lang; + llvm::DenseMap USRBasedLookupTable; RecordMap GlobalFunctions; RecordMap GlobalVariables; RecordMap Enums; @@ -716,6 +852,9 @@ class APISet { RecordMap ObjCProtocols; RecordMap Macros; RecordMap Typedefs; + +public: + const std::string ProductName; }; } // namespace extractapi diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h index 55a6768eed9b9..a5db4d23e8b55 100644 --- a/clang/include/clang/ExtractAPI/DeclarationFragments.h +++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h @@ -87,9 +87,14 @@ class DeclarationFragments { /// The USR of the fragment symbol, if applicable. std::string PreciseIdentifier; - Fragment(StringRef Spelling, FragmentKind Kind, StringRef PreciseIdentifier) - : Spelling(Spelling), Kind(Kind), PreciseIdentifier(PreciseIdentifier) { - } + /// The associated declaration, if applicable. This is not intended to be + /// used outside of libclang. + const Decl *Declaration; + + Fragment(StringRef Spelling, FragmentKind Kind, StringRef PreciseIdentifier, + const Decl *Declaration) + : Spelling(Spelling), Kind(Kind), PreciseIdentifier(PreciseIdentifier), + Declaration(Declaration) {} }; const std::vector &getFragments() const { return Fragments; } @@ -99,14 +104,15 @@ class DeclarationFragments { /// \returns a reference to the DeclarationFragments object itself after /// appending to chain up consecutive appends. DeclarationFragments &append(StringRef Spelling, FragmentKind Kind, - StringRef PreciseIdentifier = "") { + StringRef PreciseIdentifier = "", + const Decl *Declaration = nullptr) { if (Kind == FragmentKind::Text && !Fragments.empty() && Fragments.back().Kind == FragmentKind::Text) { // If appending a text fragment, and the last fragment is also text, // merge into the last fragment. Fragments.back().Spelling.append(Spelling.data(), Spelling.size()); } else { - Fragments.emplace_back(Spelling, Kind, PreciseIdentifier); + Fragments.emplace_back(Spelling, Kind, PreciseIdentifier, Declaration); } return *this; } diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h new file mode 100644 index 0000000000000..f6546fb4776a6 --- /dev/null +++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h @@ -0,0 +1,88 @@ +//===- ExtractAPI/ExtractAPIVisitor.h ---------------------------*- 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 defines the ExtractAPVisitor AST visitation interface. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H +#define LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H + +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/ExtractAPI/API.h" +#include "llvm/ADT/FunctionExtras.h" + +namespace clang { +namespace extractapi { + +/// The RecursiveASTVisitor to traverse symbol declarations and collect API +/// information. +class ExtractAPIVisitor : public RecursiveASTVisitor { +public: + ExtractAPIVisitor(ASTContext &Context, + llvm::unique_function LocationChecker, + APISet &API) + : Context(Context), API(API), + LocationChecker(std::move(LocationChecker)) {} + + const APISet &getAPI() const { return API; } + + bool VisitVarDecl(const VarDecl *Decl); + + bool VisitFunctionDecl(const FunctionDecl *Decl); + + bool VisitEnumDecl(const EnumDecl *Decl); + + bool VisitRecordDecl(const RecordDecl *Decl); + + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl); + + bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl); + + bool VisitTypedefNameDecl(const TypedefNameDecl *Decl); + + bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl); + +private: + /// Collect API information for the enum constants and associate with the + /// parent enum. + void recordEnumConstants(EnumRecord *EnumRecord, + const EnumDecl::enumerator_range Constants); + + /// Collect API information for the struct fields and associate with the + /// parent struct. + void recordStructFields(StructRecord *StructRecord, + const RecordDecl::field_range Fields); + + /// Collect API information for the Objective-C methods and associate with the + /// parent container. + void recordObjCMethods(ObjCContainerRecord *Container, + const ObjCContainerDecl::method_range Methods); + + void recordObjCProperties(ObjCContainerRecord *Container, + const ObjCContainerDecl::prop_range Properties); + + void recordObjCInstanceVariables( + ObjCContainerRecord *Container, + const llvm::iterator_range< + DeclContext::specific_decl_iterator> + Ivars); + + void recordObjCProtocols(ObjCContainerRecord *Container, + ObjCInterfaceDecl::protocol_range Protocols); + ASTContext &Context; + APISet &API; + llvm::unique_function LocationChecker; +}; + +} // namespace extractapi +} // namespace clang + +#endif // LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h index 0510dacb06495..d8aa826e3f4f6 100644 --- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h +++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h @@ -36,11 +36,6 @@ class APISerializer { protected: const APISet &API; - /// The product name of API. - /// - /// Note: This should be used for populating metadata about the API. - StringRef ProductName; - /// The list of symbols to ignore. /// /// Note: This should be consulted before emitting a symbol. @@ -56,11 +51,9 @@ class APISerializer { APISerializer &operator=(APISerializer &&) = delete; protected: - APISerializer(const APISet &API, StringRef ProductName, - const APIIgnoresList &IgnoresList, + APISerializer(const APISet &API, const APIIgnoresList &IgnoresList, APISerializerOption Options = {}) - : API(API), ProductName(ProductName), IgnoresList(IgnoresList), - Options(Options) {} + : API(API), IgnoresList(IgnoresList), Options(Options) {} virtual ~APISerializer() = default; }; diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h index e20ca85fc8d6e..9bf6083fe901e 100644 --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -47,24 +47,9 @@ class SymbolGraphSerializer : public APISerializer { /// The Symbol Graph format version used by this serializer. static const VersionTuple FormatVersion; - using PathComponentStack = llvm::SmallVector; - /// The current path component stack. - /// - /// Note: this is used to serialize the ``pathComponents`` field of symbols in - /// the Symbol Graph. - PathComponentStack PathComponents; - - /// A helper type to manage PathComponents correctly using RAII. - struct PathComponentGuard { - PathComponentGuard(PathComponentStack &PC, StringRef Component) : PC(PC) { - PC.emplace_back(Component); - } - - ~PathComponentGuard() { PC.pop_back(); } - - private: - PathComponentStack &PC; - }; + /// Indicates whether child symbols should be serialized. This is mainly + /// useful for \c serializeSingleSymbolSGF. + bool ShouldRecurse; public: /// Serialize the APIs in \c APISet in the Symbol Graph format. @@ -77,6 +62,14 @@ class SymbolGraphSerializer : public APISerializer { /// write out the serialized JSON object to \p os. void serialize(raw_ostream &os) override; + /// Serialize a single symbol SGF. This is primarily used for libclang. + /// + /// \returns an optional JSON Object representing the payload that libclang + /// expects for providing symbol information for a single symbol. If this is + /// not a known symbol returns \c None. + static Optional serializeSingleSymbolSGF(StringRef USR, + const APISet &API); + /// The kind of a relationship between two symbols. enum RelationshipKind { /// The source symbol is a member of the target symbol. @@ -96,6 +89,9 @@ class SymbolGraphSerializer : public APISerializer { static StringRef getRelationshipString(RelationshipKind Kind); private: + /// Just serialize the currently recorded objects in Symbol Graph format. + Object serializeCurrentGraph(); + /// Synthesize the metadata section of the Symbol Graph format. /// /// The metadata section describes information about the Symbol Graph itself, @@ -160,18 +156,14 @@ class SymbolGraphSerializer : public APISerializer { /// Serialize a typedef record. void serializeTypedefRecord(const TypedefRecord &Record); - /// Push a component to the current path components stack. - /// - /// \param Component The component to push onto the path components stack. - /// \return A PathComponentGuard responsible for removing the latest - /// component from the stack on scope exit. - [[nodiscard]] PathComponentGuard makePathComponentGuard(StringRef Component); + void serializeSingleRecord(const APIRecord *Record); public: - SymbolGraphSerializer(const APISet &API, StringRef ProductName, - const APIIgnoresList &IgnoresList, - APISerializerOption Options = {}) - : APISerializer(API, ProductName, IgnoresList, Options) {} + SymbolGraphSerializer(const APISet &API, const APIIgnoresList &IgnoresList, + APISerializerOption Options = {}, + bool ShouldRecurse = true) + : APISerializer(API, IgnoresList, Options), ShouldRecurse(ShouldRecurse) { + } }; } // namespace extractapi diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp index 8ab03a833e3c2..553b7bbe710f8 100644 --- a/clang/lib/ExtractAPI/API.cpp +++ b/clang/lib/ExtractAPI/API.cpp @@ -27,7 +27,8 @@ using namespace llvm; namespace { template -RecordTy *addTopLevelRecord(APISet::RecordMap &RecordMap, +RecordTy *addTopLevelRecord(DenseMap &USRLookupTable, + APISet::RecordMap &RecordMap, StringRef USR, CtorArgsTy &&...CtorArgs) { auto Result = RecordMap.insert({USR, nullptr}); @@ -36,7 +37,9 @@ RecordTy *addTopLevelRecord(APISet::RecordMap &RecordMap, Result.first->second = std::make_unique(USR, std::forward(CtorArgs)...); - return Result.first->second.get(); + auto *Record = Result.first->second.get(); + USRLookupTable.insert({USR, Record}); + return Record; } } // namespace @@ -45,20 +48,22 @@ GlobalVariableRecord * APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availabilities, LinkageInfo Linkage, const DocComment &Comment, DeclarationFragments Fragments, - DeclarationFragments SubHeading) { - return addTopLevelRecord(GlobalVariables, USR, Name, Loc, + DeclarationFragments SubHeading, bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, GlobalVariables, USR, Name, Loc, std::move(Availabilities), Linkage, Comment, - Fragments, SubHeading); + Fragments, SubHeading, IsFromSystemHeader); } GlobalFunctionRecord *APISet::addGlobalFunction( StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availabilities, LinkageInfo Linkage, const DocComment &Comment, DeclarationFragments Fragments, - DeclarationFragments SubHeading, FunctionSignature Signature) { - return addTopLevelRecord(GlobalFunctions, USR, Name, Loc, + DeclarationFragments SubHeading, FunctionSignature Signature, + bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, GlobalFunctions, USR, Name, Loc, std::move(Availabilities), Linkage, Comment, - Fragments, SubHeading, Signature); + Fragments, SubHeading, Signature, + IsFromSystemHeader); } EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name, @@ -66,10 +71,14 @@ EnumConstantRecord *APISet::addEnumConstant(EnumRecord *Enum, StringRef Name, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) { + DeclarationFragments SubHeading, + bool IsFromSystemHeader) { auto Record = std::make_unique( USR, Name, Loc, std::move(Availabilities), Comment, Declaration, - SubHeading); + SubHeading, IsFromSystemHeader); + Record->ParentInformation = APIRecord::HierarchyInformation( + Enum->USR, Enum->Name, Enum->getKind(), Enum); + USRBasedLookupTable.insert({USR, Record.get()}); return Enum->Constants.emplace_back(std::move(Record)).get(); } @@ -77,9 +86,11 @@ EnumRecord *APISet::addEnum(StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) { - return addTopLevelRecord(Enums, USR, Name, Loc, std::move(Availabilities), - Comment, Declaration, SubHeading); + DeclarationFragments SubHeading, + bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, Enums, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, IsFromSystemHeader); } StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name, @@ -87,10 +98,14 @@ StructFieldRecord *APISet::addStructField(StructRecord *Struct, StringRef Name, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) { + DeclarationFragments SubHeading, + bool IsFromSystemHeader) { auto Record = std::make_unique( USR, Name, Loc, std::move(Availabilities), Comment, Declaration, - SubHeading); + SubHeading, IsFromSystemHeader); + Record->ParentInformation = APIRecord::HierarchyInformation( + Struct->USR, Struct->Name, Struct->getKind(), Struct); + USRBasedLookupTable.insert({USR, Record.get()}); return Struct->Fields.emplace_back(std::move(Record)).get(); } @@ -98,22 +113,23 @@ StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) { - return addTopLevelRecord(Structs, USR, Name, Loc, std::move(Availabilities), - Comment, Declaration, SubHeading); + DeclarationFragments SubHeading, + bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, Structs, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, IsFromSystemHeader); } -ObjCCategoryRecord *APISet::addObjCCategory(StringRef Name, StringRef USR, - PresumedLoc Loc, - AvailabilitySet Availabilities, - const DocComment &Comment, - DeclarationFragments Declaration, - DeclarationFragments SubHeading, - SymbolReference Interface) { +ObjCCategoryRecord *APISet::addObjCCategory( + StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availabilities, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + SymbolReference Interface, bool IsFromSystemHeader) { // Create the category record. - auto *Record = addTopLevelRecord(ObjCCategories, USR, Name, Loc, - std::move(Availabilities), Comment, - Declaration, SubHeading, Interface); + auto *Record = + addTopLevelRecord(USRBasedLookupTable, ObjCCategories, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Interface, IsFromSystemHeader); // If this category is extending a known interface, associate it with the // ObjCInterfaceRecord. @@ -124,24 +140,38 @@ ObjCCategoryRecord *APISet::addObjCCategory(StringRef Name, StringRef USR, return Record; } -ObjCInterfaceRecord *APISet::addObjCInterface( - StringRef Name, StringRef USR, PresumedLoc Loc, - AvailabilitySet Availabilities, LinkageInfo Linkage, - const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading, SymbolReference SuperClass) { - return addTopLevelRecord(ObjCInterfaces, USR, Name, Loc, +ObjCInterfaceRecord * +APISet::addObjCInterface(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availabilities, LinkageInfo Linkage, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + SymbolReference SuperClass, bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, ObjCInterfaces, USR, Name, Loc, std::move(Availabilities), Linkage, Comment, - Declaration, SubHeading, SuperClass); + Declaration, SubHeading, SuperClass, + IsFromSystemHeader); } ObjCMethodRecord *APISet::addObjCMethod( ObjCContainerRecord *Container, StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - FunctionSignature Signature, bool IsInstanceMethod) { - auto Record = std::make_unique( - USR, Name, Loc, std::move(Availabilities), Comment, Declaration, - SubHeading, Signature, IsInstanceMethod); + FunctionSignature Signature, bool IsInstanceMethod, + bool IsFromSystemHeader) { + std::unique_ptr Record; + if (IsInstanceMethod) + Record = std::make_unique( + USR, Name, Loc, std::move(Availabilities), Comment, Declaration, + SubHeading, Signature, IsFromSystemHeader); + else + Record = std::make_unique( + USR, Name, Loc, std::move(Availabilities), Comment, Declaration, + SubHeading, Signature, IsFromSystemHeader); + + Record->ParentInformation = APIRecord::HierarchyInformation( + Container->USR, Container->Name, Container->getKind(), Container); + USRBasedLookupTable.insert({USR, Record.get()}); return Container->Methods.emplace_back(std::move(Record)).get(); } @@ -150,10 +180,22 @@ ObjCPropertyRecord *APISet::addObjCProperty( PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, ObjCPropertyRecord::AttributeKind Attributes, StringRef GetterName, - StringRef SetterName, bool IsOptional) { - auto Record = std::make_unique( - USR, Name, Loc, std::move(Availabilities), Comment, Declaration, - SubHeading, Attributes, GetterName, SetterName, IsOptional); + StringRef SetterName, bool IsOptional, bool IsInstanceProperty, + bool IsFromSystemHeader) { + std::unique_ptr Record; + if (IsInstanceProperty) + Record = std::make_unique( + USR, Name, Loc, std::move(Availabilities), Comment, Declaration, + SubHeading, Attributes, GetterName, SetterName, IsOptional, + IsFromSystemHeader); + else + Record = std::make_unique( + USR, Name, Loc, std::move(Availabilities), Comment, Declaration, + SubHeading, Attributes, GetterName, SetterName, IsOptional, + IsFromSystemHeader); + Record->ParentInformation = APIRecord::HierarchyInformation( + Container->USR, Container->Name, Container->getKind(), Container); + USRBasedLookupTable.insert({USR, Record.get()}); return Container->Properties.emplace_back(std::move(Record)).get(); } @@ -161,10 +203,13 @@ ObjCInstanceVariableRecord *APISet::addObjCInstanceVariable( ObjCContainerRecord *Container, StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - ObjCInstanceVariableRecord::AccessControl Access) { + ObjCInstanceVariableRecord::AccessControl Access, bool IsFromSystemHeader) { auto Record = std::make_unique( USR, Name, Loc, std::move(Availabilities), Comment, Declaration, - SubHeading, Access); + SubHeading, Access, IsFromSystemHeader); + Record->ParentInformation = APIRecord::HierarchyInformation( + Container->USR, Container->Name, Container->getKind(), Container); + USRBasedLookupTable.insert({USR, Record.get()}); return Container->Ivars.emplace_back(std::move(Record)).get(); } @@ -173,28 +218,41 @@ ObjCProtocolRecord *APISet::addObjCProtocol(StringRef Name, StringRef USR, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, - DeclarationFragments SubHeading) { - return addTopLevelRecord(ObjCProtocols, USR, Name, Loc, + DeclarationFragments SubHeading, + bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, ObjCProtocols, USR, Name, Loc, std::move(Availabilities), Comment, Declaration, - SubHeading); + SubHeading, IsFromSystemHeader); } MacroDefinitionRecord * APISet::addMacroDefinition(StringRef Name, StringRef USR, PresumedLoc Loc, DeclarationFragments Declaration, - DeclarationFragments SubHeading) { - return addTopLevelRecord(Macros, USR, Name, Loc, Declaration, SubHeading); + DeclarationFragments SubHeading, + bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, Macros, USR, Name, Loc, + Declaration, SubHeading, IsFromSystemHeader); } -TypedefRecord *APISet::addTypedef(StringRef Name, StringRef USR, - PresumedLoc Loc, - AvailabilitySet Availabilities, - const DocComment &Comment, - DeclarationFragments Declaration, - DeclarationFragments SubHeading, - SymbolReference UnderlyingType) { - return addTopLevelRecord(Typedefs, USR, Name, Loc, std::move(Availabilities), - Comment, Declaration, SubHeading, UnderlyingType); +TypedefRecord * +APISet::addTypedef(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availabilities, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + SymbolReference UnderlyingType, bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, Typedefs, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, UnderlyingType, IsFromSystemHeader); +} + +APIRecord *APISet::findRecordForUSR(StringRef USR) const { + if (USR.empty()) + return nullptr; + + auto It = USRBasedLookupTable.find(USR); + if (It != USRBasedLookupTable.end()) + return It->second; + return nullptr; } StringRef APISet::recordUSR(const Decl *D) { @@ -224,8 +282,9 @@ StringRef APISet::copyString(StringRef String) { } APIRecord::~APIRecord() {} - ObjCContainerRecord::~ObjCContainerRecord() {} +ObjCMethodRecord::~ObjCMethodRecord() {} +ObjCPropertyRecord::~ObjCPropertyRecord() {} void GlobalFunctionRecord::anchor() {} void GlobalVariableRecord::anchor() {} @@ -233,9 +292,11 @@ void EnumConstantRecord::anchor() {} void EnumRecord::anchor() {} void StructFieldRecord::anchor() {} void StructRecord::anchor() {} -void ObjCPropertyRecord::anchor() {} +void ObjCInstancePropertyRecord::anchor() {} +void ObjCClassPropertyRecord::anchor() {} void ObjCInstanceVariableRecord::anchor() {} -void ObjCMethodRecord::anchor() {} +void ObjCInstanceMethodRecord::anchor() {} +void ObjCClassMethodRecord::anchor() {} void ObjCCategoryRecord::anchor() {} void ObjCInterfaceRecord::anchor() {} void ObjCProtocolRecord::anchor() {} diff --git a/clang/lib/ExtractAPI/CMakeLists.txt b/clang/lib/ExtractAPI/CMakeLists.txt index e7de57d2984cc..d73cfdec98feb 100644 --- a/clang/lib/ExtractAPI/CMakeLists.txt +++ b/clang/lib/ExtractAPI/CMakeLists.txt @@ -7,6 +7,7 @@ add_clang_library(clangExtractAPI 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 index 71117bc4b68b6..12c91c582aa98 100644 --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -109,7 +109,7 @@ DeclarationFragmentsBuilder::getFragmentsForNNS(const NestedNameSpecifier *NNS, SmallString<128> USR; index::generateUSRForDecl(NS, USR); Fragments.append(NS->getName(), - DeclarationFragments::FragmentKind::Identifier, USR); + DeclarationFragments::FragmentKind::Identifier, USR, NS); break; } @@ -118,7 +118,8 @@ DeclarationFragmentsBuilder::getFragmentsForNNS(const NestedNameSpecifier *NNS, SmallString<128> USR; index::generateUSRForDecl(Alias, USR); Fragments.append(Alias->getName(), - DeclarationFragments::FragmentKind::Identifier, USR); + DeclarationFragments::FragmentKind::Identifier, USR, + Alias); break; } @@ -255,11 +256,11 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType( // direct reference to the typedef instead of the wrapped type. if (const TypedefType *TypedefTy = dyn_cast(T)) { const TypedefNameDecl *Decl = TypedefTy->getDecl(); - std::string USR = - TypedefUnderlyingTypeResolver(Context).getUSRForType(QualType(T, 0)); - return Fragments.append(Decl->getName(), - DeclarationFragments::FragmentKind::TypeIdentifier, - USR); + TypedefUnderlyingTypeResolver TypedefResolver(Context); + std::string USR = TypedefResolver.getUSRForType(QualType(T, 0)); + return Fragments.append( + Decl->getName(), DeclarationFragments::FragmentKind::TypeIdentifier, + USR, TypedefResolver.getUnderlyingTypeDecl(QualType(T, 0))); } // If the base type is a TagType (struct/interface/union/class/enum), let's @@ -273,7 +274,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType( clang::index::generateUSRForDecl(Decl, TagUSR); return Fragments.append(Decl->getName(), DeclarationFragments::FragmentKind::TypeIdentifier, - TagUSR); + TagUSR, Decl); } // If the base type is an ObjCInterfaceType, use the underlying @@ -284,7 +285,7 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType( index::generateUSRForDecl(Decl, USR); return Fragments.append(Decl->getName(), DeclarationFragments::FragmentKind::TypeIdentifier, - USR); + USR, Decl); } // Default fragment builder for other kinds of types (BuiltinType etc.) @@ -530,13 +531,15 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCCategory( const ObjCCategoryDecl *Category) { DeclarationFragments Fragments; + auto *Interface = Category->getClassInterface(); SmallString<128> InterfaceUSR; - index::generateUSRForDecl(Category->getClassInterface(), InterfaceUSR); + index::generateUSRForDecl(Interface, InterfaceUSR); Fragments.append("@interface", DeclarationFragments::FragmentKind::Keyword) .appendSpace() .append(Category->getClassInterface()->getName(), - DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR) + DeclarationFragments::FragmentKind::TypeIdentifier, InterfaceUSR, + Interface) .append(" (", DeclarationFragments::FragmentKind::Text) .append(Category->getName(), DeclarationFragments::FragmentKind::Identifier) @@ -560,7 +563,8 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCInterface( index::generateUSRForDecl(SuperClass, SuperUSR); Fragments.append(" : ", DeclarationFragments::FragmentKind::Text) .append(SuperClass->getName(), - DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR); + DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR, + SuperClass); } return Fragments; @@ -719,7 +723,8 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForObjCProtocol( SmallString<128> USR; index::generateUSRForDecl(*It, USR); Fragments.append((*It)->getName(), - DeclarationFragments::FragmentKind::TypeIdentifier, USR); + DeclarationFragments::FragmentKind::TypeIdentifier, USR, + *It); } Fragments.append(">", DeclarationFragments::FragmentKind::Text); } diff --git a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp index 5f185d0bd6aa2..a274585cc9ae2 100644 --- a/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp +++ b/clang/lib/ExtractAPI/ExtractAPIConsumer.cpp @@ -7,27 +7,20 @@ //===----------------------------------------------------------------------===// /// /// \file -/// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to -/// collect API information. +/// This file implements the ExtractAPIAction, and ASTConsumer to collect API +/// information. /// //===----------------------------------------------------------------------===// -#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/AST/RecursiveASTVisitor.h" #include "clang/Basic/DiagnosticFrontend.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/ExtractAPI/API.h" #include "clang/ExtractAPI/APIIgnoresList.h" -#include "clang/ExtractAPI/AvailabilityInfo.h" -#include "clang/ExtractAPI/DeclarationFragments.h" +#include "clang/ExtractAPI/ExtractAPIVisitor.h" #include "clang/ExtractAPI/FrontendActions.h" #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" #include "clang/Frontend/ASTConsumers.h" @@ -55,16 +48,9 @@ using namespace extractapi; namespace { -StringRef getTypedefName(const TagDecl *Decl) { - if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl()) - return TypedefDecl->getName(); - - return {}; -} - -std::optional getRelativeIncludeName(const CompilerInstance &CI, - StringRef File, - bool *IsQuoted = nullptr) { +Optional getRelativeIncludeName(const CompilerInstance &CI, + StringRef File, + bool *IsQuoted = nullptr) { assert(CI.hasFileManager() && "CompilerInstance does not have a FileNamager!"); @@ -177,7 +163,7 @@ std::optional getRelativeIncludeName(const CompilerInstance &CI, } struct LocationFileChecker { - bool isLocationInKnownFile(SourceLocation Loc) { + bool operator()(SourceLocation Loc) { // If the loc refers to a macro expansion we need to first get the file // location of the expansion. auto &SM = CI.getSourceManager(); @@ -233,516 +219,6 @@ struct LocationFileChecker { llvm::DenseSet ExternalFileEntries; }; -/// The RecursiveASTVisitor to traverse symbol declarations and collect API -/// information. -class ExtractAPIVisitor : public RecursiveASTVisitor { -public: - ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API) - : Context(Context), API(API), LCF(LCF) {} - - const APISet &getAPI() const { return API; } - - bool 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 (!LCF.isLocationInKnownFile(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); - return true; - } - - bool 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 (!LCF.isLocationInKnownFile(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); - return true; - } - - bool VisitEnumDecl(const EnumDecl *Decl) { - if (!Decl->isComplete()) - return true; - - // Skip forward declaration. - if (!Decl->isThisDeclarationADefinition()) - return true; - - if (!LCF.isLocationInKnownFile(Decl->getLocation())) - return true; - - // Collect symbol information. - std::string NameString = Decl->getQualifiedNameAsString(); - StringRef Name(NameString); - if (Name.empty()) - Name = getTypedefName(Decl); - - 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); - - // Now collect information about the enumerators in this enum. - recordEnumConstants(EnumRecord, Decl->enumerators()); - - return true; - } - - bool VisitRecordDecl(const RecordDecl *Decl) { - if (!Decl->isCompleteDefinition()) - return true; - - // Skip C++ structs/classes/unions - // TODO: support C++ records - if (isa(Decl)) - return true; - - if (!LCF.isLocationInKnownFile(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); - - // Now collect information about the fields in this struct. - recordStructFields(StructRecord, Decl->fields()); - - return true; - } - - bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) { - // Skip forward declaration for classes (@class) - if (!Decl->isThisDeclarationADefinition()) - return true; - - if (!LCF.isLocationInKnownFile(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); - - // 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 VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) { - // Skip forward declaration for protocols (@protocol). - if (!Decl->isThisDeclarationADefinition()) - return true; - - if (!LCF.isLocationInKnownFile(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); - - recordObjCMethods(ObjCProtocolRecord, Decl->methods()); - recordObjCProperties(ObjCProtocolRecord, Decl->properties()); - recordObjCProtocols(ObjCProtocolRecord, Decl->protocols()); - - return true; - } - - bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) { - // Skip ObjC Type Parameter for now. - if (isa(Decl)) - return true; - - if (!Decl->isDefinedOutsideFunctionOrMethod()) - return true; - - if (!LCF.isLocationInKnownFile(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); - - return true; - } - - bool 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); - - recordObjCMethods(ObjCCategoryRecord, Decl->methods()); - recordObjCProperties(ObjCCategoryRecord, Decl->properties()); - recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars()); - recordObjCProtocols(ObjCCategoryRecord, Decl->protocols()); - - return true; - } - -private: - /// Collect API information for the enum constants and associate with the - /// parent enum. - void 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); - } - } - - /// Collect API information for the struct fields and associate with the - /// parent struct. - void 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); - } - } - - /// Collect API information for the Objective-C methods and associate with the - /// parent container. - void 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()); - } - } - - void 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; - if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class) - Attributes |= ObjCPropertyRecord::Class; - - API.addObjCProperty( - Container, Name, USR, Loc, AvailabilitySet(Property), Comment, - Declaration, SubHeading, - static_cast(Attributes), - GetterName, SetterName, Property->isOptional()); - } - } - - void 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); - } - } - - void recordObjCProtocols(ObjCContainerRecord *Container, - ObjCInterfaceDecl::protocol_range Protocols) { - for (const auto *Protocol : Protocols) - Container->Protocols.emplace_back(Protocol->getName(), - API.recordUSR(Protocol)); - } - - ASTContext &Context; - APISet &API; - LocationFileChecker &LCF; -}; - class ExtractAPIConsumer : public ASTConsumer { public: ExtractAPIConsumer(ASTContext &Context, @@ -803,7 +279,7 @@ class MacroCallback : public PPCallbacks { if (PM.MD->getMacroInfo()->isUsedForHeaderGuard()) continue; - if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation())) + if (!LCF(PM.MacroNameToken.getLocation())) continue; StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName(); @@ -814,7 +290,8 @@ class MacroCallback : public PPCallbacks { API.addMacroDefinition( Name, USR, Loc, DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD), - DeclarationFragmentsBuilder::getSubHeadingForMacro(Name)); + DeclarationFragmentsBuilder::getSubHeadingForMacro(Name), + SM.isInSystemHeader(PM.MacroNameToken.getLocation())); } PendingMacros.clear(); @@ -844,13 +321,13 @@ ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { if (!OS) return nullptr; - ProductName = CI.getFrontendOpts().ProductName; + auto ProductName = CI.getFrontendOpts().ProductName; // Now that we have enough information about the language options and the // target triple, let's create the APISet before anyone uses it. API = std::make_unique( CI.getTarget().getTriple(), - CI.getFrontendOpts().Inputs.back().getKind().getLanguage()); + CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName); auto LCF = std::make_unique(CI, KnownInputFiles); @@ -942,7 +419,7 @@ void ExtractAPIAction::EndSourceFileAction() { // Setup a SymbolGraphSerializer to write out collected API information in // the Symbol Graph format. // FIXME: Make the kind of APISerializer configurable. - SymbolGraphSerializer SGSerializer(*API, ProductName, IgnoresList); + SymbolGraphSerializer SGSerializer(*API, IgnoresList); SGSerializer.serialize(*OS); OS.reset(); } diff --git a/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp b/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp new file mode 100644 index 0000000000000..38d95aaa6d5eb --- /dev/null +++ b/clang/lib/ExtractAPI/ExtractAPIVisitor.cpp @@ -0,0 +1,554 @@ +//===- 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" + +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; + + // Collect symbol information. + std::string NameString = Decl->getQualifiedNameAsString(); + StringRef Name(NameString); + if (Name.empty()) + Name = getTypedefName(Decl); + + 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/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp index cea4d91a84d50..9122d491b873c 100644 --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -12,10 +12,17 @@ //===----------------------------------------------------------------------===// #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/Version.h" #include "clang/ExtractAPI/API.h" #include "clang/ExtractAPI/APIIgnoresList.h" #include "clang/ExtractAPI/DeclarationFragments.h" +#include "clang/ExtractAPI/Serialization/SerializerBase.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Path.h" #include "llvm/Support/VersionTuple.h" @@ -323,18 +330,16 @@ Object serializeNames(const APIRecord &Record) { return Names; } -/// Serialize the symbol kind information. -/// -/// The Symbol Graph symbol kind property contains a shorthand \c identifier -/// which is prefixed by the source language name, useful for tooling to parse -/// the kind, and a \c displayName for rendering human-readable names. -Object serializeSymbolKind(const APIRecord &Record, Language Lang) { +Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) { auto AddLangPrefix = [&Lang](StringRef S) -> std::string { return (getLanguageName(Lang) + "." + S).str(); }; Object Kind; - switch (Record.getKind()) { + switch (RK) { + case APIRecord::RK_Unknown: + llvm_unreachable("Records should have an explicit kind"); + break; case APIRecord::RK_GlobalFunction: Kind["identifier"] = AddLangPrefix("func"); Kind["displayName"] = "Function"; @@ -363,23 +368,21 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) { Kind["identifier"] = AddLangPrefix("ivar"); Kind["displayName"] = "Instance Variable"; break; - case APIRecord::RK_ObjCMethod: - if (cast(&Record)->IsInstanceMethod) { - Kind["identifier"] = AddLangPrefix("method"); - Kind["displayName"] = "Instance Method"; - } else { - Kind["identifier"] = AddLangPrefix("type.method"); - Kind["displayName"] = "Type Method"; - } + case APIRecord::RK_ObjCInstanceMethod: + Kind["identifier"] = AddLangPrefix("method"); + Kind["displayName"] = "Instance Method"; break; - case APIRecord::RK_ObjCProperty: - if (cast(&Record)->isClassProperty()) { - Kind["identifier"] = AddLangPrefix("type.property"); - Kind["displayName"] = "Type Property"; - } else { - Kind["identifier"] = AddLangPrefix("property"); - Kind["displayName"] = "Instance Property"; - } + case APIRecord::RK_ObjCClassMethod: + Kind["identifier"] = AddLangPrefix("type.method"); + Kind["displayName"] = "Type Method"; + break; + case APIRecord::RK_ObjCInstanceProperty: + Kind["identifier"] = AddLangPrefix("property"); + Kind["displayName"] = "Instance Property"; + break; + case APIRecord::RK_ObjCClassProperty: + Kind["identifier"] = AddLangPrefix("type.property"); + Kind["displayName"] = "Type Property"; break; case APIRecord::RK_ObjCInterface: Kind["identifier"] = AddLangPrefix("class"); @@ -407,6 +410,15 @@ Object serializeSymbolKind(const APIRecord &Record, Language Lang) { return Kind; } +/// Serialize the symbol kind information. +/// +/// The Symbol Graph symbol kind property contains a shorthand \c identifier +/// which is prefixed by the source language name, useful for tooling to parse +/// the kind, and a \c displayName for rendering human-readable names. +Object serializeSymbolKind(const APIRecord &Record, Language Lang) { + return serializeSymbolKind(Record.getKind(), Lang); +} + template Optional serializeFunctionSignatureMixinImpl(const RecordTy &Record, std::true_type) { @@ -456,6 +468,78 @@ void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) { Record, has_function_signature())); } +struct PathComponent { + StringRef USR; + StringRef Name; + APIRecord::RecordKind Kind; + + PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind) + : USR(USR), Name(Name), Kind(Kind) {} +}; + +template +bool generatePathComponents( + const RecordTy &Record, const APISet &API, + function_ref ComponentTransformer) { + SmallVector ReverseComponenents; + ReverseComponenents.emplace_back(Record.USR, Record.Name, Record.getKind()); + const auto *CurrentParent = &Record.ParentInformation; + while (CurrentParent && !CurrentParent->empty()) { + PathComponent CurrentParentComponent(CurrentParent->ParentUSR, + CurrentParent->ParentName, + CurrentParent->ParentKind); + + auto *ParentRecord = CurrentParent->ParentRecord; + // Slow path if we don't have a direct reference to the ParentRecord + if (!ParentRecord) + ParentRecord = API.findRecordForUSR(CurrentParent->ParentUSR); + + // If the parent is a category then we need to pretend this belongs to the + // associated interface. + if (auto *CategoryRecord = + dyn_cast_or_null(ParentRecord)) { + ParentRecord = API.findRecordForUSR(CategoryRecord->Interface.USR); + CurrentParentComponent = PathComponent(CategoryRecord->Interface.USR, + CategoryRecord->Interface.Name, + APIRecord::RK_ObjCInterface); + } + + // The parent record doesn't exist which means the symbol shouldn't be + // treated as part of the current product. + if (!ParentRecord) + return true; + + ReverseComponenents.push_back(std::move(CurrentParentComponent)); + CurrentParent = &ParentRecord->ParentInformation; + } + + for (const auto &PC : reverse(ReverseComponenents)) + ComponentTransformer(PC); + + return false; +} +Object serializeParentContext(const PathComponent &PC, Language Lang) { + Object ParentContextElem; + ParentContextElem["usr"] = PC.USR; + ParentContextElem["name"] = PC.Name; + ParentContextElem["kind"] = serializeSymbolKind(PC.Kind, Lang)["identifier"]; + return ParentContextElem; +} + +template +Array generateParentContexts(const RecordTy &Record, const APISet &API, + Language Lang) { + Array ParentContexts; + if (generatePathComponents( + Record, API, [Lang, &ParentContexts](const PathComponent &PC) { + ParentContexts.push_back(serializeParentContext(PC, Lang)); + })) + ParentContexts.clear(); + ParentContexts.pop_back(); + + return ParentContexts; +} + } // namespace void SymbolGraphSerializer::anchor() {} @@ -475,7 +559,7 @@ Object SymbolGraphSerializer::serializeModule() const { Object Module; // The user is expected to always pass `--product-name=` on the command line // to populate this field. - Module["name"] = ProductName; + Module["name"] = API.ProductName; serializeObject(Module, "platform", serializePlatform(API.getTarget())); return Module; } @@ -519,7 +603,16 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const { // TODO: Once we keep track of symbol access information serialize it // correctly here. Obj["accessLevel"] = "public"; - serializeArray(Obj, "pathComponents", Array(PathComponents)); + SmallVector PathComponentsNames; + // If this returns true it indicates that we couldn't find a symbol in the + // hierarchy. + if (generatePathComponents(Record, API, + [&PathComponentsNames](const PathComponent &PC) { + PathComponentsNames.push_back(PC.Name); + })) + return {}; + + serializeArray(Obj, "pathComponents", Array(PathComponentsNames)); serializeFunctionSignatureMixin(Obj, Record); @@ -530,8 +623,10 @@ template void SymbolGraphSerializer::serializeMembers( const APIRecord &Record, const SmallVector> &Members) { + // Members should not be serialized if we aren't recursing. + if (!ShouldRecurse) + return; for (const auto &Member : Members) { - auto MemberPathComponentGuard = makePathComponentGuard(Member->Name); auto MemberRecord = serializeAPIRecord(*Member); if (!MemberRecord) continue; @@ -567,8 +662,6 @@ void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind, void SymbolGraphSerializer::serializeGlobalFunctionRecord( const GlobalFunctionRecord &Record) { - auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name); - auto Obj = serializeAPIRecord(Record); if (!Obj) return; @@ -578,8 +671,6 @@ void SymbolGraphSerializer::serializeGlobalFunctionRecord( void SymbolGraphSerializer::serializeGlobalVariableRecord( const GlobalVariableRecord &Record) { - auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name); - auto Obj = serializeAPIRecord(Record); if (!Obj) return; @@ -588,7 +679,6 @@ void SymbolGraphSerializer::serializeGlobalVariableRecord( } void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) { - auto EnumPathComponentGuard = makePathComponentGuard(Record.Name); auto Enum = serializeAPIRecord(Record); if (!Enum) return; @@ -598,7 +688,6 @@ void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) { } void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) { - auto StructPathComponentGuard = makePathComponentGuard(Record.Name); auto Struct = serializeAPIRecord(Record); if (!Struct) return; @@ -609,7 +698,6 @@ void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) { void SymbolGraphSerializer::serializeObjCContainerRecord( const ObjCContainerRecord &Record) { - auto ObjCContainerPathComponentGuard = makePathComponentGuard(Record.Name); auto ObjCContainer = serializeAPIRecord(Record); if (!ObjCContainer) return; @@ -647,7 +735,6 @@ void SymbolGraphSerializer::serializeObjCContainerRecord( void SymbolGraphSerializer::serializeMacroDefinitionRecord( const MacroDefinitionRecord &Record) { - auto MacroPathComponentGuard = makePathComponentGuard(Record.Name); auto Macro = serializeAPIRecord(Record); if (!Macro) @@ -656,6 +743,46 @@ void SymbolGraphSerializer::serializeMacroDefinitionRecord( Symbols.emplace_back(std::move(*Macro)); } +void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) { + switch (Record->getKind()) { + case APIRecord::RK_Unknown: + llvm_unreachable("Records should have a known kind!"); + case APIRecord::RK_GlobalFunction: + serializeGlobalFunctionRecord(*cast(Record)); + break; + case APIRecord::RK_GlobalVariable: + serializeGlobalVariableRecord(*cast(Record)); + break; + case APIRecord::RK_Enum: + serializeEnumRecord(*cast(Record)); + break; + case APIRecord::RK_Struct: + serializeStructRecord(*cast(Record)); + break; + case APIRecord::RK_ObjCInterface: + serializeObjCContainerRecord(*cast(Record)); + break; + case APIRecord::RK_ObjCProtocol: + serializeObjCContainerRecord(*cast(Record)); + break; + case APIRecord::RK_MacroDefinition: + serializeMacroDefinitionRecord(*cast(Record)); + break; + case APIRecord::RK_Typedef: + serializeTypedefRecord(*cast(Record)); + break; + default: + if (auto Obj = serializeAPIRecord(*Record)) { + Symbols.emplace_back(std::move(*Obj)); + auto &ParentInformation = Record->ParentInformation; + if (!ParentInformation.empty()) + serializeRelationship(RelationshipKind::MemberOf, *Record, + *ParentInformation.ParentRecord); + } + break; + } +} + void SymbolGraphSerializer::serializeTypedefRecord( const TypedefRecord &Record) { // Typedefs of anonymous types have their entries unified with the underlying @@ -667,7 +794,6 @@ void SymbolGraphSerializer::serializeTypedefRecord( if (ShouldDrop) return; - auto TypedefPathComponentGuard = makePathComponentGuard(Record.Name); auto Typedef = serializeAPIRecord(Record); if (!Typedef) return; @@ -677,16 +803,7 @@ void SymbolGraphSerializer::serializeTypedefRecord( Symbols.emplace_back(std::move(*Typedef)); } -SymbolGraphSerializer::PathComponentGuard -SymbolGraphSerializer::makePathComponentGuard(StringRef Component) { - return PathComponentGuard(PathComponents, Component); -} - Object SymbolGraphSerializer::serialize() { - Object Root; - serializeObject(Root, "metadata", serializeMetadata()); - serializeObject(Root, "module", serializeModule()); - // Serialize global variables in the API set. for (const auto &GlobalVar : API.getGlobalVariables()) serializeGlobalVariableRecord(*GlobalVar.second); @@ -716,6 +833,14 @@ Object SymbolGraphSerializer::serialize() { for (const auto &Typedef : API.getTypedefs()) serializeTypedefRecord(*Typedef.second); + return serializeCurrentGraph(); +} + +Object SymbolGraphSerializer::serializeCurrentGraph() { + Object Root; + serializeObject(Root, "metadata", serializeMetadata()); + serializeObject(Root, "module", serializeModule()); + Root["symbols"] = std::move(Symbols); Root["relationships"] = std::move(Relationships); @@ -729,3 +854,53 @@ void SymbolGraphSerializer::serialize(raw_ostream &os) { else os << formatv("{0:2}", Value(std::move(root))) << "\n"; } + +Optional +SymbolGraphSerializer::serializeSingleSymbolSGF(StringRef USR, + const APISet &API) { + APIRecord *Record = API.findRecordForUSR(USR); + if (!Record) + return {}; + + Object Root; + APIIgnoresList EmptyIgnores; + SymbolGraphSerializer Serializer(API, EmptyIgnores, + /*Options.Compact*/ {true}, + /*ShouldRecurse*/ false); + Serializer.serializeSingleRecord(Record); + serializeObject(Root, "symbolGraph", Serializer.serializeCurrentGraph()); + + Language Lang = API.getLanguage(); + serializeArray(Root, "parentContexts", + generateParentContexts(*Record, API, Lang)); + + Array RelatedSymbols; + + for (const auto &Fragment : Record->Declaration.getFragments()) { + // If we don't have a USR there isn't much we can do. + if (Fragment.PreciseIdentifier.empty()) + continue; + + APIRecord *RelatedRecord = API.findRecordForUSR(Fragment.PreciseIdentifier); + + // If we can't find the record let's skip. + if (!RelatedRecord) + continue; + + Object RelatedSymbol; + RelatedSymbol["usr"] = RelatedRecord->USR; + RelatedSymbol["declarationLanguage"] = getLanguageName(Lang); + // TODO: once we record this properly let's serialize it right. + RelatedSymbol["accessLevel"] = "public"; + RelatedSymbol["filePath"] = RelatedRecord->Location.getFilename(); + RelatedSymbol["moduleName"] = API.ProductName; + RelatedSymbol["isSystem"] = RelatedRecord->IsFromSystemHeader; + + serializeArray(RelatedSymbol, "parentContexts", + generateParentContexts(*RelatedRecord, API, Lang)); + RelatedSymbols.push_back(std::move(RelatedSymbol)); + } + + serializeArray(Root, "relatedSymbols", RelatedSymbols); + return Root; +} diff --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp index 9c165e693e0e7..3da2424ea7263 100644 --- a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp +++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.cpp @@ -17,9 +17,8 @@ using namespace clang; using namespace extractapi; -namespace { - -const NamedDecl *getUnderlyingTypeDecl(QualType Type) { +const NamedDecl * +TypedefUnderlyingTypeResolver::getUnderlyingTypeDecl(QualType Type) const { const NamedDecl *TypeDecl = nullptr; const TypedefType *TypedefTy = Type->getAs(); @@ -44,8 +43,6 @@ const NamedDecl *getUnderlyingTypeDecl(QualType Type) { return TypeDecl; } -} // namespace - SymbolReference TypedefUnderlyingTypeResolver::getSymbolReferenceForType(QualType Type, APISet &API) const { diff --git a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h index 0096ff2359148..54aa11c354c0b 100644 --- a/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h +++ b/clang/lib/ExtractAPI/TypedefUnderlyingTypeResolver.h @@ -26,6 +26,8 @@ namespace clang { namespace extractapi { struct TypedefUnderlyingTypeResolver { + /// Gets the underlying type declaration. + const NamedDecl *getUnderlyingTypeDecl(QualType Type) const; /// Get a SymbolReference for the given type. SymbolReference getSymbolReferenceForType(QualType Type, APISet &API) const; diff --git a/clang/test/Index/extract-api-cursor.m b/clang/test/Index/extract-api-cursor.m new file mode 100644 index 0000000000000..a462c115826c1 --- /dev/null +++ b/clang/test/Index/extract-api-cursor.m @@ -0,0 +1,106 @@ +/// Foo docs +struct Foo { + /// Bar docs + int bar; +}; + +/// Base docs +@interface Base +/// Base property docs +@property struct Foo baseProperty; + +/// Base method docs +- (void)baseMethodWithArg:(int)arg; +@end + +/// Protocol docs +@protocol Protocol +/// Protocol property docs +@property struct Foo protocolProperty; +@end + +/// Derived docs +@interface Derived: Base +/// Derived method docs +- (void)derivedMethodWithValue:(id)value; +@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:" diff --git a/clang/test/Index/extract-api-usr.m b/clang/test/Index/extract-api-usr.m new file mode 100644 index 0000000000000..12bfb0a676e66 --- /dev/null +++ b/clang/test/Index/extract-api-usr.m @@ -0,0 +1,115 @@ +/// Foo docs +struct Foo { + /// Bar docs + int bar; +}; + +/// Base docs +@interface Base +/// Base property docs +@property struct Foo baseProperty; + +/// Base method docs +- (void)baseMethodWithArg:(int)arg; +@end + +/// Protocol docs +@protocol Protocol +/// Protocol property docs +@property struct Foo protocolProperty; +@end + +/// Derived docs +@interface Derived: Base +/// Derived method docs +- (void)derivedMethodWithValue:(id)value; +@end + + +// Checking for Foo +// RUN: c-index-test "-single-symbol-sgf-for=c:@S@Foo" %s | FileCheck -check-prefix=CHECK-FOO %s +// CHECK-FOO: "parentContexts":[] +// CHECK-FOO-SAME: "relatedSymbols":[] +// CHECK-FOO-SAME: "relationships":[] +// CHECK-FOO-SAME: "text":"Foo docs" +// CHECK-FOO-SAME: "kind":{"displayName":"Structure","identifier":"objective-c.struct"} +// CHECK-FOO-SAME: "title":"Foo" + + +// Checking for bar +// RUN: c-index-test "-single-symbol-sgf-for=c:@S@Foo@FI@bar" %s | FileCheck -check-prefix=CHECK-BAR %s +// CHECK-BAR: "parentContexts":[{"kind":"objective-c.struct","name":"Foo","usr":"c:@S@Foo"}] +// CHECK-BAR-SAME: "relatedSymbols":[] +// CHECK-BAR-SAME: "relationships":[{"kind":"memberOf","source":"c:@S@Foo@FI@bar","target":"c:@S@Foo" +// CHECK-BAR-SAME: "text":"Bar docs" +// CHECK-BAR-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"} +// CHECK-BAR-SAME: "title":"bar" + +// Checking for Base +// RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Base" %s | FileCheck -check-prefix=CHECK-BASE %s +// CHECK-BASE: "parentContexts":[] +// CHECK-BASE-SAME: "relatedSymbols":[] +// CHECK-BASE-SAME: "relationships":[] +// CHECK-BASE-SAME: "text":"Base docs" +// CHECK-BASE-SAME: "kind":{"displayName":"Class","identifier":"objective-c.class"} +// CHECK-BASE-SAME: "title":"Base" + +// Checking for baseProperty +// RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Base(py)baseProperty" %s | FileCheck -check-prefix=CHECK-BASEPROP %s +// CHECK-BASEPROP: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}] +// CHECK-BASEPROP-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c" +// CHECK-BASEPROP-SAME: "isSystem":false +// CHECK-BASEPROP-SAME: "usr":"c:@S@Foo"}] +// CHECK-BASEPROP-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(py)baseProperty","target":"c:objc(cs)Base" +// CHECK-BASEPROP-SAME: "text":"Base property docs" +// CHECK-BASEPROP-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"} +// CHECK-BASEPROP-SAME: "title":"baseProperty" + +// Checking for baseMethodWithArg +// RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Base(im)baseMethodWithArg:" %s | FileCheck -check-prefix=CHECK-BASEMETHOD %s +// CHECK-BASEMETHOD: "parentContexts":[{"kind":"objective-c.class","name":"Base","usr":"c:objc(cs)Base"}] +// CHECK-BASEMETHOD-SAME:"relatedSymbols":[] +// CHECK-BASEMETHOD-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Base(im)baseMethodWithArg:","target":"c:objc(cs)Base" +// CHECK-BASEMETHOD-SAME: "text":"Base method docs" +// CHECK-BASEMETHOD-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"} +// CHECK-BASEMETHOD-SAME: "title":"baseMethodWithArg:" + +// Checking for Protocol +// RUN: c-index-test "-single-symbol-sgf-for=c:objc(pl)Protocol" %s | FileCheck -check-prefix=CHECK-PROT %s +// CHECK-PROT: "parentContexts":[] +// CHECK-PROT-SAME: "relatedSymbols":[] +// CHECK-PROT-SAME: "relationships":[] +// CHECK-PROT-SAME: "text":"Protocol docs" +// CHECK-PROT-SAME: "kind":{"displayName":"Protocol","identifier":"objective-c.protocol"} +// CHECK-PROT-SAME: "title":"Protocol" + +// Checking for protocolProperty +// RUN: c-index-test "-single-symbol-sgf-for=c:objc(pl)Protocol(py)protocolProperty" %s | FileCheck -check-prefix=CHECK-PROTPROP %s +// CHECK-PROTPROP: "parentContexts":[{"kind":"objective-c.protocol","name":"Protocol","usr":"c:objc(pl)Protocol"}] +// CHECK-PROTPROP-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c" +// CHECK-PROTPROP-SAME: "isSystem":false +// CHECK-PROTPROP-SAME: "usr":"c:@S@Foo"}] +// CHECK-PROTPROP-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(pl)Protocol(py)protocolProperty","target":"c:objc(pl)Protocol" +// CHECK-PROTPROP-SAME: "text":"Protocol property docs" +// CHECK-PROTPROP-SAME: "kind":{"displayName":"Instance Property","identifier":"objective-c.property"} +// CHECK-PROTPROP-SAME: "title":"protocolProperty" + +// Checking for Derived +// RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Derived" %s | FileCheck -check-prefix=CHECK-DERIVED %s +// CHECK-DERIVED: "parentContexts":[] +// CHECK-DERIVED-SAME:"relatedSymbols":[{"accessLevel":"public","declarationLanguage":"objective-c" +// CHECK-DERIVED-SAME: "isSystem":false +// CHECK-DERIVED-SAME: "usr":"c:objc(cs)Base"}] +// CHECK-DERIVED-SAME: "relationships":[{"kind":"inheritsFrom","source":"c:objc(cs)Derived","target":"c:objc(cs)Base" +// CHECK-DERIVED-SAME: "text":"Derived docs" +// CHECK-DERIVED-SAME: "kind":{"displayName":"Class","identifier":"objective-c.class"} +// CHECK-DERIVED-SAME: "title":"Derived" + +// Checking for derivedMethodWithValue +// RUN: c-index-test "-single-symbol-sgf-for=c:objc(cs)Derived(im)derivedMethodWithValue:" %s | FileCheck -check-prefix=CHECK-DERIVEDMETHOD %s +// CHECK-DERIVEDMETHOD: "parentContexts":[{"kind":"objective-c.class","name":"Derived","usr":"c:objc(cs)Derived"}] +// CHECK-DERIVEDMETHOD-SAME:"relatedSymbols":[] +// CHECK-DERIVEDMETHOD-SAME: "relationships":[{"kind":"memberOf","source":"c:objc(cs)Derived(im)derivedMethodWithValue:","target":"c:objc(cs)Derived" +// CHECK-DERIVEDMETHOD-SAME: "text":"Derived method docs" +// CHECK-DERIVEDMETHOD-SAME: "kind":{"displayName":"Instance Method","identifier":"objective-c.method"} +// CHECK-DERIVEDMETHOD-SAME: "title":"derivedMethodWithValue:" diff --git a/clang/tools/c-index-test/c-index-test.c b/clang/tools/c-index-test/c-index-test.c index ce49b8ed53bc5..cc425fc51efee 100644 --- a/clang/tools/c-index-test/c-index-test.c +++ b/clang/tools/c-index-test/c-index-test.c @@ -1,15 +1,17 @@ /* c-index-test.c */ -#include "clang/Config/config.h" -#include "clang-c/Index.h" -#include "clang-c/CXCompilationDatabase.h" #include "clang-c/BuildSystem.h" +#include "clang-c/CXCompilationDatabase.h" +#include "clang-c/CXErrorCode.h" +#include "clang-c/CXString.h" #include "clang-c/Documentation.h" +#include "clang-c/Index.h" +#include "clang/Config/config.h" +#include #include -#include #include +#include #include -#include #ifdef CLANG_HAVE_LIBXML #include @@ -1840,6 +1842,18 @@ static enum CXChildVisitResult PrintManglings(CXCursor cursor, CXCursor p, return CXChildVisit_Recurse; } +static enum CXChildVisitResult +PrintSingleSymbolSGFs(CXCursor cursor, CXCursor parent, CXClientData data) { + CXString SGFData = clang_getSymbolGraphForCursor(cursor); + const char *SGF = clang_getCString(SGFData); + if (SGF) + printf("%s\n", SGF); + + clang_disposeString(SGFData); + + return CXChildVisit_Recurse; +} + /******************************************************************************/ /* Bitwidth testing. */ /******************************************************************************/ @@ -4791,6 +4805,64 @@ static int perform_print_build_session_timestamp(void) { return 0; } +static int perform_test_single_symbol_sgf(const char *input, int argc, + const char *argv[]) { + CXIndex Idx; + CXTranslationUnit TU; + CXAPISet API; + struct CXUnsavedFile *unsaved_files = 0; + int num_unsaved_files = 0; + enum CXErrorCode Err; + int result = 0; + const char *InvocationPath; + CXString SGF; + const char *usr; + + usr = input + strlen("-single-symbol-sgf-for="); + + Idx = clang_createIndex(/* excludeDeclsFromPCH */ 1, + /* displayDiagnostics=*/0); + InvocationPath = getenv("CINDEXTEST_INVOCATION_EMISSION_PATH"); + if (InvocationPath) + clang_CXIndex_setInvocationEmissionPathOption(Idx, InvocationPath); + + if (parse_remapped_files(argc, argv, 0, &unsaved_files, &num_unsaved_files)) { + result = -1; + goto dispose_index; + } + + Err = clang_parseTranslationUnit2( + Idx, 0, argv + num_unsaved_files, argc - num_unsaved_files, unsaved_files, + num_unsaved_files, getDefaultParsingOptions(), &TU); + if (Err != CXError_Success) { + fprintf(stderr, "Unable to load translation unit!\n"); + describeLibclangFailure(Err); + result = 1; + goto free_remapped_files; + } + + Err = clang_createAPISet(TU, &API); + if (Err != CXError_Success) { + fprintf(stderr, + "Unable to create API Set for API information extraction!\n"); + result = 2; + goto dispose_tu; + } + + SGF = clang_getSymbolGraphForUSR(usr, API); + printf("%s", clang_getCString(SGF)); + + clang_disposeString(SGF); + clang_disposeAPISet(API); +dispose_tu: + clang_disposeTranslationUnit(TU); +free_remapped_files: + free_remapped_files(unsaved_files, num_unsaved_files); +dispose_index: + clang_disposeIndex(Idx); + return result; +} + /******************************************************************************/ /* Command line processing. */ /******************************************************************************/ @@ -4848,6 +4920,9 @@ static void print_usage(void) { " c-index-test -test-print-type-declaration {}*\n" " c-index-test -print-usr [ {}]*\n" " c-index-test -print-usr-file \n"); + fprintf(stderr, + " c-index-test -single-symbol-sgfs {*}\n" + " c-index-test -single-symbol-sgf-for= {}*\n"); fprintf(stderr, " c-index-test -write-pch \n" " c-index-test -compilation-db [lookup ] database\n"); @@ -4980,6 +5055,11 @@ int cindextest_main(int argc, const char **argv) { return perform_test_compilation_db(argv[argc-1], argc - 3, argv + 2); else if (argc == 2 && strcmp(argv[1], "-print-build-session-timestamp") == 0) return perform_print_build_session_timestamp(); + 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-for=") == argv[1]) + return perform_test_single_symbol_sgf(argv[1], argc - 2, argv + 2); print_usage(); return 1; diff --git a/clang/tools/libclang/CMakeLists.txt b/clang/tools/libclang/CMakeLists.txt index 3bfa78a28856b..a0cc07aa4d7c1 100644 --- a/clang/tools/libclang/CMakeLists.txt +++ b/clang/tools/libclang/CMakeLists.txt @@ -32,6 +32,7 @@ set(SOURCES CIndexer.cpp CXComment.cpp CXCursor.cpp + CXExtractAPI.cpp CXIndexDataConsumer.cpp CXCompilationDatabase.cpp CXLoadedDiagnostic.cpp @@ -60,6 +61,7 @@ set(LIBS clangAST clangBasic clangDriver + clangExtractAPI clangFrontend clangIndex clangLex diff --git a/clang/tools/libclang/CXExtractAPI.cpp b/clang/tools/libclang/CXExtractAPI.cpp new file mode 100644 index 0000000000000..787334ab1bbb2 --- /dev/null +++ b/clang/tools/libclang/CXExtractAPI.cpp @@ -0,0 +1,151 @@ +//===- CXExtractAPI.cpp - libclang APIs for manipulating CXAPISet ---------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines all libclang APIs related to manipulation CXAPISet +// +//===----------------------------------------------------------------------===// + +#include "CXCursor.h" +#include "CXString.h" +#include "CXTranslationUnit.h" +#include "clang-c/CXErrorCode.h" +#include "clang-c/Documentation.h" +#include "clang-c/Index.h" +#include "clang-c/Platform.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/ExtractAPI/API.h" +#include "clang/ExtractAPI/ExtractAPIVisitor.h" +#include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Index/USRGeneration.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/CBindingWrapping.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::extractapi; + +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(APISet, CXAPISet) + +static void WalkupFromMostDerivedType(ExtractAPIVisitor &Visitor, Decl *D); + +template +static bool WalkupParentContext(DeclContext *Parent, + ExtractAPIVisitor &Visitor) { + if (auto *D = dyn_cast(Parent)) { + WalkupFromMostDerivedType(Visitor, D); + return true; + } + return false; +} + +static void WalkupFromMostDerivedType(ExtractAPIVisitor &Visitor, Decl *D) { + switch (D->getKind()) { +#define ABSTRACT_DECL(DECL) +#define DECL(CLASS, BASE) \ + case Decl::CLASS: \ + Visitor.WalkUpFrom##CLASS##Decl(static_cast(D)); \ + break; +#include "clang/AST/DeclNodes.inc" + } + + for (auto *Parent = D->getDeclContext(); Parent != nullptr; + Parent = Parent->getParent()) { + if (WalkupParentContext(Parent, Visitor)) + return; + if (WalkupParentContext(Parent, Visitor)) + return; + } +} + +static CXString GenerateCXStringFromSymbolGraphData(llvm::json::Object Obj) { + llvm::SmallString<0> BackingString; + llvm::raw_svector_ostream OS(BackingString); + OS << Value(std::move(Obj)); + return cxstring::createDup(BackingString.str()); +} + +enum CXErrorCode clang_createAPISet(CXTranslationUnit tu, CXAPISet *out_api) { + if (cxtu::isNotUsableTU(tu) || !out_api) + return CXError_InvalidArguments; + + ASTUnit *Unit = cxtu::getASTUnit(tu); + + auto &Ctx = Unit->getASTContext(); + auto Lang = Unit->getInputKind().getLanguage(); + APISet *API = new APISet(Ctx.getTargetInfo().getTriple(), Lang, + Unit->getMainFileName().str()); + ExtractAPIVisitor Visitor( + Ctx, [](SourceLocation Loc) { return true; }, *API); + + for (auto It = Unit->top_level_begin(); It != Unit->top_level_end(); ++It) { + Visitor.TraverseDecl(*It); + } + + *out_api = wrap(API); + return CXError_Success; +} + +void clang_disposeAPISet(CXAPISet api) { delete unwrap(api); } + +CXString clang_getSymbolGraphForUSR(const char *usr, CXAPISet api) { + auto *API = unwrap(api); + + if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(usr, *API)) + return GenerateCXStringFromSymbolGraphData(std::move(*SGF)); + + return cxstring::createNull(); +} + +CXString clang_getSymbolGraphForCursor(CXCursor cursor) { + CXCursorKind Kind = clang_getCursorKind(cursor); + if (clang_isDeclaration(Kind)) { + const Decl *D = cxcursor::getCursorDecl(cursor); + + if (!D) + return cxstring::createNull(); + + CXTranslationUnit TU = cxcursor::getCursorTU(cursor); + if (!TU) + return cxstring::createNull(); + + ASTUnit *Unit = cxtu::getASTUnit(TU); + + 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); + + SmallString<128> USR; + if (index::generateUSRForDecl(D, USR)) + return cxstring::createNull(); + + WalkupFromMostDerivedType(Visitor, const_cast(D)); + 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(); +} diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map index b4bf700dc1bee..e98187071852b 100644 --- a/clang/tools/libclang/libclang.map +++ b/clang/tools/libclang/libclang.map @@ -412,6 +412,10 @@ LLVM_16 { clang_CXXMethod_isDeleted; clang_CXXMethod_isCopyAssignmentOperator; clang_CXXMethod_isMoveAssignmentOperator; + clang_createAPISet; + clang_disposeAPISet; + clang_getSymbolGraphForCursor; + clang_getSymbolGraphForUSR; }; # Example of how to add a new symbol version entry. If you do add a new symbol