#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"
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);
+
+/**
* @}
*/
// 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;
/// 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;
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;
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) {
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;
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;
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;
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;
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;
enum AttributeKind : unsigned {
NoAttr = 0,
ReadOnly = 1,
- Class = 1 << 1,
Dynamic = 1 << 2,
};
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:
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) {
/// 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:
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;
};
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) {
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) {
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;
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;
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) {
struct has_function_signature<GlobalFunctionRecord> : public std::true_type {};
template <>
struct has_function_signature<ObjCMethodRecord> : public std::true_type {};
+template <>
+struct has_function_signature<ObjCInstanceMethodRecord>
+ : public std::true_type {};
+template <>
+struct has_function_signature<ObjCClassMethodRecord> : public std::true_type {};
/// APISet holds the set of API records collected from given inputs.
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.
///
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.
///
/// \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.
///
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.
///
/// \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.
///
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.
///
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.
///
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.
///
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.
///
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.
///
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.
///
/// \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.
///
MacroDefinitionRecord *addMacroDefinition(StringRef Name, StringRef USR,
PresumedLoc Loc,
DeclarationFragments Declaration,
- DeclarationFragments SubHeading);
+ DeclarationFragments SubHeading,
+ bool IsFromSystemHeader);
/// Create a typedef record into the API set.
///
/// \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 <typename RecordTy,
const RecordMap<MacroDefinitionRecord> &getMacros() const { return Macros; }
const RecordMap<TypedefRecord> &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.
/// \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.
const llvm::Triple Target;
const Language Lang;
+ llvm::DenseMap<StringRef, APIRecord *> USRBasedLookupTable;
RecordMap<GlobalFunctionRecord> GlobalFunctions;
RecordMap<GlobalVariableRecord> GlobalVariables;
RecordMap<EnumRecord> Enums;
RecordMap<ObjCProtocolRecord> ObjCProtocols;
RecordMap<MacroDefinitionRecord> Macros;
RecordMap<TypedefRecord> Typedefs;
+
+public:
+ const std::string ProductName;
};
} // namespace extractapi
/// 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<Fragment> &getFragments() const { return Fragments; }
/// \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;
}
--- /dev/null
+//===- 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<ExtractAPIVisitor> {
+public:
+ ExtractAPIVisitor(ASTContext &Context,
+ llvm::unique_function<bool(SourceLocation)> 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<ObjCIvarDecl>>
+ Ivars);
+
+ void recordObjCProtocols(ObjCContainerRecord *Container,
+ ObjCInterfaceDecl::protocol_range Protocols);
+ ASTContext &Context;
+ APISet &API;
+ llvm::unique_function<bool(SourceLocation)> LocationChecker;
+};
+
+} // namespace extractapi
+} // namespace clang
+
+#endif // LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H
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.
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;
};
/// The Symbol Graph format version used by this serializer.
static const VersionTuple FormatVersion;
- using PathComponentStack = llvm::SmallVector<llvm::StringRef, 4>;
- /// 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.
/// 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<Object> 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.
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,
/// 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
namespace {
template <typename RecordTy, typename... CtorArgsTy>
-RecordTy *addTopLevelRecord(APISet::RecordMap<RecordTy> &RecordMap,
+RecordTy *addTopLevelRecord(DenseMap<StringRef, APIRecord *> &USRLookupTable,
+ APISet::RecordMap<RecordTy> &RecordMap,
StringRef USR, CtorArgsTy &&...CtorArgs) {
auto Result = RecordMap.insert({USR, nullptr});
Result.first->second =
std::make_unique<RecordTy>(USR, std::forward<CtorArgsTy>(CtorArgs)...);
- return Result.first->second.get();
+ auto *Record = Result.first->second.get();
+ USRLookupTable.insert({USR, Record});
+ return Record;
}
} // namespace
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,
AvailabilitySet Availabilities,
const DocComment &Comment,
DeclarationFragments Declaration,
- DeclarationFragments SubHeading) {
+ DeclarationFragments SubHeading,
+ bool IsFromSystemHeader) {
auto Record = std::make_unique<EnumConstantRecord>(
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();
}
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,
AvailabilitySet Availabilities,
const DocComment &Comment,
DeclarationFragments Declaration,
- DeclarationFragments SubHeading) {
+ DeclarationFragments SubHeading,
+ bool IsFromSystemHeader) {
auto Record = std::make_unique<StructFieldRecord>(
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();
}
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.
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<ObjCMethodRecord>(
- USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
- SubHeading, Signature, IsInstanceMethod);
+ FunctionSignature Signature, bool IsInstanceMethod,
+ bool IsFromSystemHeader) {
+ std::unique_ptr<ObjCMethodRecord> Record;
+ if (IsInstanceMethod)
+ Record = std::make_unique<ObjCInstanceMethodRecord>(
+ USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+ SubHeading, Signature, IsFromSystemHeader);
+ else
+ Record = std::make_unique<ObjCClassMethodRecord>(
+ 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();
}
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<ObjCPropertyRecord>(
- USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
- SubHeading, Attributes, GetterName, SetterName, IsOptional);
+ StringRef SetterName, bool IsOptional, bool IsInstanceProperty,
+ bool IsFromSystemHeader) {
+ std::unique_ptr<ObjCPropertyRecord> Record;
+ if (IsInstanceProperty)
+ Record = std::make_unique<ObjCInstancePropertyRecord>(
+ USR, Name, Loc, std::move(Availabilities), Comment, Declaration,
+ SubHeading, Attributes, GetterName, SetterName, IsOptional,
+ IsFromSystemHeader);
+ else
+ Record = std::make_unique<ObjCClassPropertyRecord>(
+ 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();
}
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<ObjCInstanceVariableRecord>(
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();
}
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) {
}
APIRecord::~APIRecord() {}
-
ObjCContainerRecord::~ObjCContainerRecord() {}
+ObjCMethodRecord::~ObjCMethodRecord() {}
+ObjCPropertyRecord::~ObjCPropertyRecord() {}
void GlobalFunctionRecord::anchor() {}
void GlobalVariableRecord::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() {}
APIIgnoresList.cpp
AvailabilityInfo.cpp
ExtractAPIConsumer.cpp
+ ExtractAPIVisitor.cpp
DeclarationFragments.cpp
Serialization/SerializerBase.cpp
Serialization/SymbolGraphSerializer.cpp
SmallString<128> USR;
index::generateUSRForDecl(NS, USR);
Fragments.append(NS->getName(),
- DeclarationFragments::FragmentKind::Identifier, USR);
+ DeclarationFragments::FragmentKind::Identifier, USR, NS);
break;
}
SmallString<128> USR;
index::generateUSRForDecl(Alias, USR);
Fragments.append(Alias->getName(),
- DeclarationFragments::FragmentKind::Identifier, USR);
+ DeclarationFragments::FragmentKind::Identifier, USR,
+ Alias);
break;
}
// direct reference to the typedef instead of the wrapped type.
if (const TypedefType *TypedefTy = dyn_cast<TypedefType>(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
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
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.)
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)
index::generateUSRForDecl(SuperClass, SuperUSR);
Fragments.append(" : ", DeclarationFragments::FragmentKind::Text)
.append(SuperClass->getName(),
- DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR);
+ DeclarationFragments::FragmentKind::TypeIdentifier, SuperUSR,
+ SuperClass);
}
return Fragments;
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);
}
//===----------------------------------------------------------------------===//
///
/// \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"
namespace {
-StringRef getTypedefName(const TagDecl *Decl) {
- if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
- return TypedefDecl->getName();
-
- return {};
-}
-
-std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
- StringRef File,
- bool *IsQuoted = nullptr) {
+Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
+ StringRef File,
+ bool *IsQuoted = nullptr) {
assert(CI.hasFileManager() &&
"CompilerInstance does not have a FileNamager!");
}
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();
llvm::DenseSet<const FileEntry *> ExternalFileEntries;
};
-/// The RecursiveASTVisitor to traverse symbol declarations and collect API
-/// information.
-class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
-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<ParmVarDecl>(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<CXXMethodDecl>(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<CXXRecordDecl>())
- return true;
- }
-
- // Skip ConstructorDecl and DestructorDecl.
- if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(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<CXXRecordDecl>(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<ObjCTypeParamDecl>(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<ObjCPropertyRecord::AttributeKind>(Attributes),
- GetterName, SetterName, Property->isOptional());
- }
- }
-
- void recordObjCInstanceVariables(
- ObjCContainerRecord *Container,
- const llvm::iterator_range<
- DeclContext::specific_decl_iterator<ObjCIvarDecl>>
- 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,
if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
continue;
- if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
+ if (!LCF(PM.MacroNameToken.getLocation()))
continue;
StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
API.addMacroDefinition(
Name, USR, Loc,
DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
- DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
+ DeclarationFragmentsBuilder::getSubHeadingForMacro(Name),
+ SM.isInSystemHeader(PM.MacroNameToken.getLocation()));
}
PendingMacros.clear();
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<APISet>(
CI.getTarget().getTriple(),
- CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
+ CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName);
auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
// 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();
}
--- /dev/null
+//===- 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 <class DeclTy>
+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<ParmVarDecl>(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<CXXMethodDecl>(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<CXXRecordDecl>())
+ return true;
+ }
+
+ // Skip ConstructorDecl and DestructorDecl.
+ if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(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<CXXRecordDecl>(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<ObjCTypeParamDecl>(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<ObjCPropertyRecord::AttributeKind>(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<ObjCIvarDecl>>
+ 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));
+}
//===----------------------------------------------------------------------===//
#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"
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";
Kind["identifier"] = AddLangPrefix("ivar");
Kind["displayName"] = "Instance Variable";
break;
- case APIRecord::RK_ObjCMethod:
- if (cast<ObjCMethodRecord>(&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<ObjCPropertyRecord>(&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");
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 <typename RecordTy>
Optional<Object> serializeFunctionSignatureMixinImpl(const RecordTy &Record,
std::true_type) {
Record, has_function_signature<RecordTy>()));
}
+struct PathComponent {
+ StringRef USR;
+ StringRef Name;
+ APIRecord::RecordKind Kind;
+
+ PathComponent(StringRef USR, StringRef Name, APIRecord::RecordKind Kind)
+ : USR(USR), Name(Name), Kind(Kind) {}
+};
+
+template <typename RecordTy>
+bool generatePathComponents(
+ const RecordTy &Record, const APISet &API,
+ function_ref<void(const PathComponent &)> ComponentTransformer) {
+ SmallVector<PathComponent, 4> 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<ObjCCategoryRecord>(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 <typename RecordTy>
+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() {}
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;
}
// TODO: Once we keep track of symbol access information serialize it
// correctly here.
Obj["accessLevel"] = "public";
- serializeArray(Obj, "pathComponents", Array(PathComponents));
+ SmallVector<StringRef, 4> 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);
void SymbolGraphSerializer::serializeMembers(
const APIRecord &Record,
const SmallVector<std::unique_ptr<MemberTy>> &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;
void SymbolGraphSerializer::serializeGlobalFunctionRecord(
const GlobalFunctionRecord &Record) {
- auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name);
-
auto Obj = serializeAPIRecord(Record);
if (!Obj)
return;
void SymbolGraphSerializer::serializeGlobalVariableRecord(
const GlobalVariableRecord &Record) {
- auto GlobalPathComponentGuard = makePathComponentGuard(Record.Name);
-
auto Obj = serializeAPIRecord(Record);
if (!Obj)
return;
}
void SymbolGraphSerializer::serializeEnumRecord(const EnumRecord &Record) {
- auto EnumPathComponentGuard = makePathComponentGuard(Record.Name);
auto Enum = serializeAPIRecord(Record);
if (!Enum)
return;
}
void SymbolGraphSerializer::serializeStructRecord(const StructRecord &Record) {
- auto StructPathComponentGuard = makePathComponentGuard(Record.Name);
auto Struct = serializeAPIRecord(Record);
if (!Struct)
return;
void SymbolGraphSerializer::serializeObjCContainerRecord(
const ObjCContainerRecord &Record) {
- auto ObjCContainerPathComponentGuard = makePathComponentGuard(Record.Name);
auto ObjCContainer = serializeAPIRecord(Record);
if (!ObjCContainer)
return;
void SymbolGraphSerializer::serializeMacroDefinitionRecord(
const MacroDefinitionRecord &Record) {
- auto MacroPathComponentGuard = makePathComponentGuard(Record.Name);
auto Macro = serializeAPIRecord(Record);
if (!Macro)
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<GlobalFunctionRecord>(Record));
+ break;
+ case APIRecord::RK_GlobalVariable:
+ serializeGlobalVariableRecord(*cast<GlobalVariableRecord>(Record));
+ break;
+ case APIRecord::RK_Enum:
+ serializeEnumRecord(*cast<EnumRecord>(Record));
+ break;
+ case APIRecord::RK_Struct:
+ serializeStructRecord(*cast<StructRecord>(Record));
+ break;
+ case APIRecord::RK_ObjCInterface:
+ serializeObjCContainerRecord(*cast<ObjCInterfaceRecord>(Record));
+ break;
+ case APIRecord::RK_ObjCProtocol:
+ serializeObjCContainerRecord(*cast<ObjCProtocolRecord>(Record));
+ break;
+ case APIRecord::RK_MacroDefinition:
+ serializeMacroDefinitionRecord(*cast<MacroDefinitionRecord>(Record));
+ break;
+ case APIRecord::RK_Typedef:
+ serializeTypedefRecord(*cast<TypedefRecord>(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
if (ShouldDrop)
return;
- auto TypedefPathComponentGuard = makePathComponentGuard(Record.Name);
auto Typedef = serializeAPIRecord(Record);
if (!Typedef)
return;
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);
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);
else
os << formatv("{0:2}", Value(std::move(root))) << "\n";
}
+
+Optional<Object>
+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;
+}
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<TypedefType>();
return TypeDecl;
}
-} // namespace
-
SymbolReference
TypedefUnderlyingTypeResolver::getSymbolReferenceForType(QualType Type,
APISet &API) const {
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;
--- /dev/null
+/// 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<Protocol>)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:"
--- /dev/null
+/// 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<Protocol>)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:"
/* 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 <assert.h>
#include <ctype.h>
-#include <stdlib.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <assert.h>
#ifdef CLANG_HAVE_LIBXML
#include <libxml/parser.h>
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. */
/******************************************************************************/
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. */
/******************************************************************************/
" c-index-test -print-usr [<CursorKind> {<args>}]*\n"
" c-index-test -print-usr-file <file>\n");
fprintf(stderr,
+ " c-index-test -single-symbol-sgfs <symbol filter> {<args>*}\n"
+ " c-index-test -single-symbol-sgf-for=<usr> {<args>}*\n");
+ fprintf(stderr,
" c-index-test -write-pch <file> <compiler arguments>\n"
" c-index-test -compilation-db [lookup <filename>] database\n");
fprintf(stderr,
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;
CIndexer.cpp
CXComment.cpp
CXCursor.cpp
+ CXExtractAPI.cpp
CXIndexDataConsumer.cpp
CXCompilationDatabase.cpp
CXLoadedDiagnostic.cpp
clangAST
clangBasic
clangDriver
+ clangExtractAPI
clangFrontend
clangIndex
clangLex
--- /dev/null
+//===- 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 <typename DeclTy>
+static bool WalkupParentContext(DeclContext *Parent,
+ ExtractAPIVisitor &Visitor) {
+ if (auto *D = dyn_cast<DeclTy>(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<CLASS##Decl *>(D)); \
+ break;
+#include "clang/AST/DeclNodes.inc"
+ }
+
+ for (auto *Parent = D->getDeclContext(); Parent != nullptr;
+ Parent = Parent->getParent()) {
+ if (WalkupParentContext<ObjCContainerDecl>(Parent, Visitor))
+ return;
+ if (WalkupParentContext<TagDecl>(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<Decl *>(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<Decl *>(Fragment.Declaration));
+ }
+
+ if (auto SGF = SymbolGraphSerializer::serializeSingleSymbolSGF(USR, API))
+ return GenerateCXStringFromSymbolGraphData(std::move(*SGF));
+ }
+
+ return cxstring::createNull();
+}
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