diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h index a18879f39645f..437ac953b7409 100644 --- a/clang/include/clang/ExtractAPI/API.h +++ b/clang/include/clang/ExtractAPI/API.h @@ -22,6 +22,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/RawCommentList.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/Specifiers.h" #include "clang/ExtractAPI/AvailabilityInfo.h" #include "clang/ExtractAPI/DeclarationFragments.h" #include "llvm/ADT/MapVector.h" @@ -64,6 +65,14 @@ struct APIRecord { RK_Enum, RK_StructField, RK_Struct, + RK_Union, + RK_StaticField, + RK_CXXField, + RK_CXXClass, + RK_CXXStaticMethod, + RK_CXXInstanceMethod, + RK_CXXConstructorMethod, + RK_CXXDestructorMethod, RK_ObjCInstanceProperty, RK_ObjCClassProperty, RK_ObjCIvar, @@ -266,6 +275,132 @@ struct StructRecord : APIRecord { virtual void anchor(); }; +struct CXXFieldRecord : APIRecord { + AccessControl Access; + + CXXFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, AccessControl Access, + bool IsFromSystemHeader) + : APIRecord(RK_CXXField, USR, Name, Loc, std::move(Availabilities), + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader), + Access(Access) {} + + CXXFieldRecord(RecordKind Kind, StringRef USR, StringRef Name, + PresumedLoc Loc, AvailabilitySet Availabilities, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading, AccessControl Access, + bool IsFromSystemHeader) + : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader), + Access(Access) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_CXXField; + } + +private: + virtual void anchor(); +}; + +struct CXXMethodRecord : APIRecord { + FunctionSignature Signature; + AccessControl Access; + + CXXMethodRecord() = delete; + + CXXMethodRecord(RecordKind Kind, StringRef USR, StringRef Name, + PresumedLoc Loc, AvailabilitySet Availabilities, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading, FunctionSignature Signature, + AccessControl Access, bool IsFromSystemHeader) + : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader), + Signature(Signature), Access(Access) {} + + virtual ~CXXMethodRecord() = 0; +}; + +struct CXXConstructorRecord : CXXMethodRecord { + CXXConstructorRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + FunctionSignature Signature, AccessControl Access, + bool IsFromSystemHeader) + : CXXMethodRecord(RK_CXXConstructorMethod, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Signature, Access, IsFromSystemHeader) {} + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_CXXConstructorMethod; + } + +private: + virtual void anchor(); +}; + +struct CXXDestructorRecord : CXXMethodRecord { + CXXDestructorRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + FunctionSignature Signature, AccessControl Access, + bool IsFromSystemHeader) + : CXXMethodRecord(RK_CXXDestructorMethod, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Signature, Access, IsFromSystemHeader) {} + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_CXXDestructorMethod; + } + +private: + virtual void anchor(); +}; + +struct CXXStaticMethodRecord : CXXMethodRecord { + CXXStaticMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + FunctionSignature Signature, AccessControl Access, + bool IsFromSystemHeader) + : CXXMethodRecord(RK_CXXStaticMethod, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Signature, Access, IsFromSystemHeader) {} + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_CXXStaticMethod; + } + +private: + virtual void anchor(); +}; + +struct CXXInstanceMethodRecord : CXXMethodRecord { + CXXInstanceMethodRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + FunctionSignature Signature, AccessControl Access, + bool IsFromSystemHeader) + : CXXMethodRecord(RK_CXXInstanceMethod, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Signature, Access, IsFromSystemHeader) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_CXXInstanceMethod; + } + +private: + virtual void anchor(); +}; + /// This holds information associated with Objective-C properties. struct ObjCPropertyRecord : APIRecord { /// The attributes associated with an Objective-C property. @@ -444,6 +579,24 @@ struct SymbolReference { bool empty() const { return Name.empty() && USR.empty() && Source.empty(); } }; +struct StaticFieldRecord : CXXFieldRecord { + SymbolReference Context; + + StaticFieldRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, LinkageInfo Linkage, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading, SymbolReference Context, + AccessControl Access, bool IsFromSystemHeader) + : CXXFieldRecord(RK_StaticField, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Access, IsFromSystemHeader), + Context(Context) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_StaticField; + } +}; + /// The base representation of an Objective-C container record. Holds common /// information associated with Objective-C containers. struct ObjCContainerRecord : APIRecord { @@ -465,6 +618,28 @@ struct ObjCContainerRecord : APIRecord { virtual ~ObjCContainerRecord() = 0; }; +struct CXXClassRecord : APIRecord { + SmallVector> Fields; + SmallVector> Methods; + SmallVector Bases; + + CXXClassRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, RecordKind Kind, + bool IsFromSystemHeader) + : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), + LinkageInfo::none(), Comment, Declaration, SubHeading, + IsFromSystemHeader) {} + + static bool classof(const APIRecord *Record) { + return (Record->getKind() == RK_CXXClass); + } + +private: + virtual void anchor(); +}; + /// This holds information associated with Objective-C categories. struct ObjCCategoryRecord : ObjCContainerRecord { SymbolReference Interface; @@ -591,6 +766,12 @@ struct has_function_signature : public std::true_type {}; template <> struct has_function_signature : public std::true_type {}; +template <> +struct has_function_signature : public std::true_type {}; + +template struct has_access : public std::false_type {}; +template <> struct has_access : public std::true_type {}; +template <> struct has_access : public std::true_type {}; /// APISet holds the set of API records collected from given inputs. class APISet { @@ -668,6 +849,41 @@ class APISet { DeclarationFragments SubHeading, bool IsFromSystemHeader); + StaticFieldRecord * + addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availabilities, LinkageInfo Linkage, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading, SymbolReference Context, + AccessControl Access, bool IsFromSystemHeaderg); + + CXXFieldRecord *addCXXField(CXXClassRecord *CXXClass, StringRef Name, + StringRef USR, PresumedLoc Loc, + AvailabilitySet Availabilities, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + AccessControl Access, bool IsFromSystemHeader); + + CXXClassRecord * + addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + APIRecord::RecordKind Kind, bool IsFromSystemHeader); + + CXXMethodRecord * + addCXXMethod(CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availability, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading, FunctionSignature Signature, + bool IsStatic, AccessControl Access, bool IsFromSystemHeader); + + CXXMethodRecord *addCXXSpecialMethod( + CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + FunctionSignature Signature, bool IsConstructor, AccessControl Access, + bool IsFromSystemHeader); + /// Create and add an Objective-C category record into the API set. /// /// Note: the caller is responsible for keeping the StringRef \p Name and @@ -790,8 +1006,12 @@ class APISet { const RecordMap &getGlobalVariables() const { return GlobalVariables; } + const RecordMap &getStaticFields() const { + return StaticFields; + } const RecordMap &getEnums() const { return Enums; } const RecordMap &getStructs() const { return Structs; } + const RecordMap &getCXXClasses() const { return CXXClasses; } const RecordMap &getObjCCategories() const { return ObjCCategories; } @@ -845,8 +1065,10 @@ class APISet { llvm::DenseMap USRBasedLookupTable; RecordMap GlobalFunctions; RecordMap GlobalVariables; + RecordMap StaticFields; RecordMap Enums; RecordMap Structs; + RecordMap CXXClasses; RecordMap ObjCCategories; RecordMap ObjCInterfaces; RecordMap ObjCProtocols; diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h index 82f0c42ab8aa0..dc8a02e8811ae 100644 --- a/clang/include/clang/ExtractAPI/DeclarationFragments.h +++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h @@ -173,10 +173,27 @@ class DeclarationFragments { /// Get the corresponding FragmentKind from string \p S. static FragmentKind parseFragmentKindFromString(StringRef S); + static DeclarationFragments + getExceptionSpecificationString(ExceptionSpecificationType ExceptionSpec); + + static DeclarationFragments getStructureTypeFragment(const RecordDecl *Decl); + private: std::vector Fragments; }; +class AccessControl { +public: + AccessControl(std::string Access) : Access(Access) {} + + const std::string &getAccess() const { return Access; } + + bool empty() const { return Access.empty(); } + +private: + std::string Access; +}; + /// Store function signature information with DeclarationFragments of the /// return type and parameters. class FunctionSignature { @@ -219,6 +236,29 @@ class FunctionSignature { /// A factory class to build DeclarationFragments for different kinds of Decl. class DeclarationFragmentsBuilder { public: + /// Build FunctionSignature for a function-like declaration \c FunctionT like + /// FunctionDecl, ObjCMethodDecl, or CXXMethodDecl. + /// + /// The logic and implementation of building a signature for a FunctionDecl, + /// CXXMethodDecl, and ObjCMethodDecl are exactly the same, but they do not + /// share a common base. This template helps reuse the code. + template + static FunctionSignature getFunctionSignature(const FunctionT *Function); + + static AccessControl getAccessControl(const Decl *Decl) { + switch (Decl->getAccess()) { + case AS_public: + return AccessControl("public"); + case AS_private: + return AccessControl("private"); + case AS_protected: + return AccessControl("protected"); + case AS_none: + return AccessControl("none"); + } + llvm_unreachable("Unhandled access control"); + } + /// Build DeclarationFragments for a variable declaration VarDecl. static DeclarationFragments getFragmentsForVar(const VarDecl *); @@ -239,6 +279,19 @@ class DeclarationFragmentsBuilder { /// Build DeclarationFragments for a struct record declaration RecordDecl. static DeclarationFragments getFragmentsForStruct(const RecordDecl *); + static DeclarationFragments getFragmentsForCXXClass(const CXXRecordDecl *); + + static DeclarationFragments + getFragmentsForSpecialCXXMethod(const CXXMethodDecl *); + + static DeclarationFragments getFragmentsForCXXMethod(const CXXMethodDecl *); + + static DeclarationFragments + getFragmentsForConversionFunction(const CXXConversionDecl *); + + static DeclarationFragments + getFragmentsForOverloadedOperator(const CXXMethodDecl *); + /// Build DeclarationFragments for an Objective-C category declaration /// ObjCCategoryDecl. static DeclarationFragments @@ -283,15 +336,6 @@ class DeclarationFragmentsBuilder { /// Build a sub-heading for macro \p Name. static DeclarationFragments getSubHeadingForMacro(StringRef Name); - /// Build FunctionSignature for a function-like declaration \c FunctionT like - /// FunctionDecl or ObjCMethodDecl. - /// - /// The logic and implementation of building a signature for a FunctionDecl - /// and an ObjCMethodDecl are exactly the same, but they do not share a common - /// base. This template helps reuse the code. - template - static FunctionSignature getFunctionSignature(const FunctionT *); - private: DeclarationFragmentsBuilder() = delete; @@ -316,6 +360,24 @@ class DeclarationFragmentsBuilder { static DeclarationFragments getFragmentsForParam(const ParmVarDecl *); }; +template +FunctionSignature +DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) { + FunctionSignature Signature; + + DeclarationFragments ReturnType, After; + ReturnType + .append(getFragmentsForType(Function->getReturnType(), + Function->getASTContext(), After)) + .append(std::move(After)); + Signature.setReturnType(ReturnType); + + for (const auto *Param : Function->parameters()) + Signature.addParameter(Param->getName(), getFragmentsForParam(Param)); + + return Signature; +} + } // namespace extractapi } // namespace clang diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h index f0882afb5a61b..2eed2d119e6ae 100644 --- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h +++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h @@ -14,6 +14,10 @@ #ifndef LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H #define LLVM_CLANG_EXTRACTAPI_EXTRACT_API_VISITOR_H +#include "clang/AST/DeclCXX.h" +#include "clang/Basic/OperatorKinds.h" +#include "clang/Basic/Specifiers.h" +#include "clang/ExtractAPI/DeclarationFragments.h" #include "llvm/ADT/FunctionExtras.h" #include "clang/AST/ASTContext.h" @@ -44,8 +48,14 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor { bool VisitEnumDecl(const EnumDecl *Decl); + bool WalkUpFromRecordDecl(const RecordDecl *Decl); + + bool WalkUpFromCXXRecordDecl(const CXXRecordDecl *Decl); + bool VisitRecordDecl(const RecordDecl *Decl); + bool VisitCXXRecordDecl(const CXXRecordDecl *Decl); + bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl); bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl); @@ -69,6 +79,20 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor { void recordStructFields(StructRecord *StructRecord, const RecordDecl::field_range Fields); + /// Collect API information for the class fields and associate with the parent + /// struct + void recordCXXFields(CXXClassRecord *CXXClassRecord, + const RecordDecl::field_range Fields); + + void recordCXXMethods(CXXClassRecord *CXXClassRecord, + const CXXRecordDecl::method_range Methods); + + void recordConversionMethod(CXXClassRecord *CXXClassRecord, + const CXXMethodDecl *SpecialCXXMethod); + + void recordSpecialCXXMethod(CXXClassRecord *CXXClassRecord, + const CXXMethodDecl *SpecialCXXMethod); + /// Collect API information for the Objective-C methods and associate with the /// parent container. void recordObjCMethods(ObjCContainerRecord *Container, @@ -131,8 +155,9 @@ bool ExtractAPIVisitorBase::VisitVarDecl(const VarDecl *Decl) { if (isa(Decl)) return true; - // Skip non-global variables in records (struct/union/class). - if (Decl->getDeclContext()->isRecord()) + // Skip non-global variables in records (struct/union/class) but not static + // members. + if (Decl->getDeclContext()->isRecord() && !Decl->isStaticDataMember()) return true; // Skip local variables inside function or method. @@ -165,9 +190,19 @@ bool ExtractAPIVisitorBase::VisitVarDecl(const VarDecl *Decl) { DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); - // Add the global variable record to the API set. - API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, - Declaration, SubHeading, isInSystemHeader(Decl)); + if (Decl->isStaticDataMember()) { + SymbolReference Context; + auto Record = dyn_cast(Decl->getDeclContext()); + Context.Name = Record->getName(); + Context.USR = API.recordUSR(Record); + auto Access = DeclarationFragmentsBuilder::getAccessControl(Decl); + API.addStaticField(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, + Declaration, SubHeading, Context, Access, + isInSystemHeader(Decl)); + } else + // Add the global variable record to the API set. + API.addGlobalVar(Name, USR, Loc, AvailabilitySet(Decl), Linkage, Comment, + Declaration, SubHeading, isInSystemHeader(Decl)); return true; } @@ -280,15 +315,23 @@ bool ExtractAPIVisitorBase::VisitEnumDecl(const EnumDecl *Decl) { } template -bool ExtractAPIVisitorBase::VisitRecordDecl(const RecordDecl *Decl) { - // Skip C++ structs/classes/unions - // TODO: support C++ records - if (isa(Decl)) - return true; +bool ExtractAPIVisitorBase::WalkUpFromRecordDecl( + const RecordDecl *Decl) { + getDerivedExtractAPIVisitor().VisitRecordDecl(Decl); + return true; +} + +template +bool ExtractAPIVisitorBase::WalkUpFromCXXRecordDecl( + const CXXRecordDecl *Decl) { + getDerivedExtractAPIVisitor().VisitCXXRecordDecl(Decl); + return true; +} +template +bool ExtractAPIVisitorBase::VisitRecordDecl(const RecordDecl *Decl) { if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) return true; - // Collect symbol information. StringRef Name = Decl->getName(); if (Name.empty()) @@ -322,6 +365,57 @@ bool ExtractAPIVisitorBase::VisitRecordDecl(const RecordDecl *Decl) { return true; } +template +bool ExtractAPIVisitorBase::VisitCXXRecordDecl( + const CXXRecordDecl *Decl) { + if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) + return true; + + StringRef Name = Decl->getName(); + StringRef USR = API.recordUSR(Decl); + PresumedLoc Loc = + Context.getSourceManager().getPresumedLoc(Decl->getLocation()); + DocComment Comment; + if (auto *RawComment = + getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl)) + Comment = RawComment->getFormattedLines(Context.getSourceManager(), + Context.getDiagnostics()); + DeclarationFragments Declaration = + DeclarationFragmentsBuilder::getFragmentsForCXXClass(Decl); + DeclarationFragments SubHeading = + DeclarationFragmentsBuilder::getSubHeading(Decl); + + APIRecord::RecordKind Kind; + if (Decl->isUnion()) + Kind = APIRecord::RecordKind::RK_Union; + else if (Decl->isStruct()) + Kind = APIRecord::RecordKind::RK_Struct; + else + Kind = APIRecord::RecordKind::RK_CXXClass; + + CXXClassRecord *CXXClassRecord = + API.addCXXClass(Name, USR, Loc, AvailabilitySet(Decl), Comment, + Declaration, SubHeading, Kind, isInSystemHeader(Decl)); + + // FIXME: store AccessSpecifier given by inheritance + for (const auto BaseSpecifier : Decl->bases()) { + // skip classes not inherited as public + if (BaseSpecifier.getAccessSpecifier() != AccessSpecifier::AS_public) + continue; + SymbolReference BaseClass; + CXXRecordDecl *BaseClassDecl = + BaseSpecifier.getType().getTypePtr()->getAsCXXRecordDecl(); + BaseClass.Name = BaseClassDecl->getName(); + BaseClass.USR = API.recordUSR(BaseClassDecl); + CXXClassRecord->Bases.emplace_back(BaseClass); + } + + getDerivedExtractAPIVisitor().recordCXXFields(CXXClassRecord, Decl->fields()); + getDerivedExtractAPIVisitor().recordCXXMethods(CXXClassRecord, + Decl->methods()); + return true; +} + template bool ExtractAPIVisitorBase::VisitObjCInterfaceDecl( const ObjCInterfaceDecl *Decl) { @@ -558,6 +652,154 @@ void ExtractAPIVisitorBase::recordStructFields( } } +template +void ExtractAPIVisitorBase::recordCXXFields( + CXXClassRecord *CXXClassRecord, 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()); + Context.getSourceManager().getPresumedLoc(Field->getLocation()); + DocComment Comment; + if (auto *RawComment = + getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Field)) + Comment = RawComment->getFormattedLines(Context.getSourceManager(), + Context.getDiagnostics()); + + // Build declaration fragments and sub-heading for the struct field. + DeclarationFragments Declaration = + DeclarationFragmentsBuilder::getFragmentsForField(Field); + DeclarationFragments SubHeading = + DeclarationFragmentsBuilder::getSubHeading(Field); + AccessControl Access = DeclarationFragmentsBuilder::getAccessControl(Field); + + API.addCXXField(CXXClassRecord, Name, USR, Loc, AvailabilitySet(Field), + Comment, Declaration, SubHeading, Access, + isInSystemHeader(Field)); + } +} + +/// Collect API information for constructors and destructors and associate with +/// the parent class. +template +void ExtractAPIVisitorBase::recordSpecialCXXMethod( + CXXClassRecord *CXXClassRecord, const CXXMethodDecl *CXXSpecialMethod) { + StringRef Name; + bool isConstructor = false; + if (isa(CXXSpecialMethod)) { + isConstructor = true; + Name = CXXClassRecord->Name; + } else if (isa(CXXSpecialMethod)) { + Name = API.copyString(CXXSpecialMethod->getNameAsString()); + } + + StringRef USR = API.recordUSR(CXXSpecialMethod); + PresumedLoc Loc = Context.getSourceManager().getPresumedLoc( + CXXSpecialMethod->getLocation()); + DocComment Comment; + if (auto *RawComment = getDerivedExtractAPIVisitor().fetchRawCommentForDecl( + CXXSpecialMethod)) + Comment = RawComment->getFormattedLines(Context.getSourceManager(), + Context.getDiagnostics()); + + // Build declaration fragments, sub-heading, and signature for the method. + DeclarationFragments Declaration = + DeclarationFragmentsBuilder::getFragmentsForSpecialCXXMethod( + CXXSpecialMethod); + DeclarationFragments SubHeading = + DeclarationFragmentsBuilder::getSubHeading(CXXSpecialMethod); + FunctionSignature Signature = + DeclarationFragmentsBuilder::getFunctionSignature(CXXSpecialMethod); + AccessControl Access = + DeclarationFragmentsBuilder::getAccessControl(CXXSpecialMethod); + + API.addCXXSpecialMethod(CXXClassRecord, Name, USR, Loc, + AvailabilitySet(CXXSpecialMethod), Comment, + Declaration, SubHeading, Signature, isConstructor, + Access, isInSystemHeader(CXXSpecialMethod)); +} + +template +void ExtractAPIVisitorBase::recordConversionMethod( + CXXClassRecord *CXXClassRecord, const CXXMethodDecl *SpecialCXXMethod) { + StringRef Name = API.copyString(SpecialCXXMethod->getNameAsString()); + StringRef USR = API.recordUSR(SpecialCXXMethod); + PresumedLoc Loc = Context.getSourceManager().getPresumedLoc( + SpecialCXXMethod->getLocation()); + DocComment Comment; + if (auto *RawComment = getDerivedExtractAPIVisitor().fetchRawCommentForDecl( + SpecialCXXMethod)) + Comment = RawComment->getFormattedLines(Context.getSourceManager(), + Context.getDiagnostics()); + + // Build declaration fragments, sub-heading, and signature for the method. + DeclarationFragments Declaration = + DeclarationFragmentsBuilder::getFragmentsForConversionFunction( + cast(SpecialCXXMethod)); + DeclarationFragments SubHeading = + DeclarationFragmentsBuilder::getSubHeading(SpecialCXXMethod); + FunctionSignature Signature = + DeclarationFragmentsBuilder::getFunctionSignature(SpecialCXXMethod); + AccessControl Access = + DeclarationFragmentsBuilder::getAccessControl(SpecialCXXMethod); + + API.addCXXMethod(CXXClassRecord, Name, USR, Loc, + AvailabilitySet(SpecialCXXMethod), Comment, Declaration, + SubHeading, Signature, SpecialCXXMethod->isStatic(), Access, + isInSystemHeader(SpecialCXXMethod)); +} + +template +void ExtractAPIVisitorBase::recordCXXMethods( + CXXClassRecord *CXXClassRecord, const CXXRecordDecl::method_range Methods) { + for (const auto *Method : Methods) { + if (isa(Method) || isa(Method)) { + recordSpecialCXXMethod(CXXClassRecord, Method); + continue; + } + + if (isa(Method)) { + recordConversionMethod(CXXClassRecord, Method); + continue; + } + + StringRef Name; + DeclarationFragments Declaration; + if (Method->isOverloadedOperator()) { + Name = API.copyString(Method->getNameAsString()); + Declaration = + DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator( + Method); + } else { + Name = API.copyString(Method->getNameAsString()); + Declaration = + DeclarationFragmentsBuilder::getFragmentsForCXXMethod(Method); + } + StringRef USR = API.recordUSR(Method); + PresumedLoc Loc = + Context.getSourceManager().getPresumedLoc(Method->getLocation()); + DocComment Comment; + if (auto *RawComment = + getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Method)) + Comment = RawComment->getFormattedLines(Context.getSourceManager(), + Context.getDiagnostics()); + + // Build declaration fragments, sub-heading, and signature for the method. + DeclarationFragments SubHeading = + DeclarationFragmentsBuilder::getSubHeading(Method); + FunctionSignature Signature = + DeclarationFragmentsBuilder::getFunctionSignature(Method); + AccessControl Access = + DeclarationFragmentsBuilder::getAccessControl(Method); + + API.addCXXMethod(CXXClassRecord, Name, USR, Loc, AvailabilitySet(Method), + Comment, Declaration, SubHeading, Signature, + Method->isStatic(), Access, isInSystemHeader(Method)); + } +} + /// Collect API information for the Objective-C methods and associate with the /// parent container. template diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h index 006e92be29555..afb84c20ddefe 100644 --- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h +++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h @@ -29,6 +29,10 @@ template class APISetVisitor { getDerived()->traverseEnumRecords(); + getDerived()->traverseStaticFieldRecords(); + + getDerived()->traverseCXXClassRecords(); + getDerived()->traverseStructRecords(); getDerived()->traverseObjCInterfaces(); @@ -60,6 +64,16 @@ template class APISetVisitor { getDerived()->visitStructRecord(*Struct.second); } + void traverseStaticFieldRecords() { + for (const auto &StaticField : API.getStaticFields()) + getDerived()->visitStaticFieldRecord(*StaticField.second); + } + + void traverseCXXClassRecords() { + for (const auto &Class : API.getCXXClasses()) + getDerived()->visitCXXClassRecord(*Class.second); + } + void traverseObjCInterfaces() { for (const auto &Interface : API.getObjCInterfaces()) getDerived()->visitObjCContainerRecord(*Interface.second); @@ -92,6 +106,10 @@ template class APISetVisitor { /// Visit a struct record. void visitStructRecord(const StructRecord &Record){}; + void visitStaticFieldRecord(const StaticFieldRecord &Record){}; + + void visitCXXClassRecord(const CXXClassRecord &Record){}; + /// Visit an Objective-C container record. void visitObjCContainerRecord(const ObjCContainerRecord &Record){}; diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h index e77903f8ba08f..d975e7a61345d 100644 --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -160,6 +160,10 @@ class SymbolGraphSerializer : public APISetVisitor { /// Visit a struct record. void visitStructRecord(const StructRecord &Record); + void visitStaticFieldRecord(const StaticFieldRecord &Record); + + void visitCXXClassRecord(const CXXClassRecord &Record); + /// Visit an Objective-C container record. void visitObjCContainerRecord(const ObjCContainerRecord &Record); diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp index 10e79b37de739..0287532509936 100644 --- a/clang/lib/ExtractAPI/API.cpp +++ b/clang/lib/ExtractAPI/API.cpp @@ -120,6 +120,91 @@ StructRecord *APISet::addStruct(StringRef Name, StringRef USR, PresumedLoc Loc, SubHeading, IsFromSystemHeader); } +StaticFieldRecord * +APISet::addStaticField(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availabilities, LinkageInfo Linkage, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, SymbolReference Context, + AccessControl Access, bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, StaticFields, USR, Name, Loc, + std::move(Availabilities), Linkage, Comment, + Declaration, SubHeading, Context, Access, + IsFromSystemHeader); +} + +CXXFieldRecord * +APISet::addCXXField(CXXClassRecord *CXXClass, StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availabilities, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading, AccessControl Access, + bool IsFromSystemHeader) { + auto Record = std::make_unique( + USR, Name, Loc, std::move(Availabilities), Comment, Declaration, + SubHeading, Access, IsFromSystemHeader); + Record->ParentInformation = APIRecord::HierarchyInformation( + CXXClass->USR, CXXClass->Name, CXXClass->getKind(), CXXClass); + USRBasedLookupTable.insert({USR, Record.get()}); + return CXXClass->Fields.emplace_back(std::move(Record)).get(); +} + +CXXClassRecord * +APISet::addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc, + AvailabilitySet Availabilities, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, APIRecord::RecordKind Kind, + bool IsFromSystemHeader) { + return addTopLevelRecord(USRBasedLookupTable, CXXClasses, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Kind, IsFromSystemHeader); +} + +CXXMethodRecord *APISet::addCXXMethod( + CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + FunctionSignature Signature, bool IsStatic, AccessControl Access, + bool IsFromSystemHeader) { + std::unique_ptr Record; + if (IsStatic) + Record = std::make_unique( + USR, Name, Loc, std::move(Availability), Comment, Declaration, + SubHeading, Signature, Access, IsFromSystemHeader); + else + Record = std::make_unique( + USR, Name, Loc, std::move(Availability), Comment, Declaration, + SubHeading, Signature, Access, IsFromSystemHeader); + + Record->ParentInformation = APIRecord::HierarchyInformation( + CXXClassRecord->USR, CXXClassRecord->Name, CXXClassRecord->getKind(), + CXXClassRecord); + USRBasedLookupTable.insert({USR, Record.get()}); + return CXXClassRecord->Methods.emplace_back(std::move(Record)).get(); +} + +CXXMethodRecord *APISet::addCXXSpecialMethod( + CXXClassRecord *CXXClassRecord, StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + FunctionSignature Signature, bool IsConstructor, AccessControl Access, + bool IsFromSystemHeader) { + std::unique_ptr Record; + if (IsConstructor) + Record = std::make_unique( + USR, Name, Loc, std::move(Availability), Comment, Declaration, + SubHeading, Signature, Access, IsFromSystemHeader); + else + Record = std::make_unique( + USR, Name, Loc, std::move(Availability), Comment, Declaration, + SubHeading, Signature, Access, IsFromSystemHeader); + + Record->ParentInformation = APIRecord::HierarchyInformation( + CXXClassRecord->USR, CXXClassRecord->Name, CXXClassRecord->getKind(), + CXXClassRecord); + USRBasedLookupTable.insert({USR, Record.get()}); + return CXXClassRecord->Methods.emplace_back(std::move(Record)).get(); +} + ObjCCategoryRecord *APISet::addObjCCategory( StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, @@ -282,6 +367,7 @@ APIRecord::~APIRecord() {} ObjCContainerRecord::~ObjCContainerRecord() {} ObjCMethodRecord::~ObjCMethodRecord() {} ObjCPropertyRecord::~ObjCPropertyRecord() {} +CXXMethodRecord::~CXXMethodRecord() {} void GlobalFunctionRecord::anchor() {} void GlobalVariableRecord::anchor() {} @@ -289,6 +375,12 @@ void EnumConstantRecord::anchor() {} void EnumRecord::anchor() {} void StructFieldRecord::anchor() {} void StructRecord::anchor() {} +void CXXFieldRecord::anchor() {} +void CXXClassRecord::anchor() {} +void CXXConstructorRecord::anchor() {} +void CXXDestructorRecord::anchor() {} +void CXXInstanceMethodRecord::anchor() {} +void CXXStaticMethodRecord::anchor() {} void ObjCInstancePropertyRecord::anchor() {} void ObjCClassPropertyRecord::anchor() {} void ObjCInstanceVariableRecord::anchor() {} diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp index 20335768ac30b..ea72835c99531 100644 --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -12,8 +12,12 @@ //===----------------------------------------------------------------------===// #include "clang/ExtractAPI/DeclarationFragments.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Basic/OperatorKinds.h" #include "clang/ExtractAPI/TypedefUnderlyingTypeResolver.h" #include "clang/Index/USRGeneration.h" +#include "clang/Parse/Parser.h" #include "llvm/ADT/StringSwitch.h" using namespace clang::extractapi; @@ -84,6 +88,59 @@ DeclarationFragments::parseFragmentKindFromString(StringRef S) { .Default(DeclarationFragments::FragmentKind::None); } +DeclarationFragments DeclarationFragments::getExceptionSpecificationString( + ExceptionSpecificationType ExceptionSpec) { + DeclarationFragments Fragments; + switch (ExceptionSpec) { + case ExceptionSpecificationType::EST_None: + return Fragments; + case ExceptionSpecificationType::EST_DynamicNone: + return Fragments.append(" ", DeclarationFragments::FragmentKind::Text) + .append("throw", DeclarationFragments::FragmentKind::Keyword) + .append("(", DeclarationFragments::FragmentKind::Text) + .append(")", DeclarationFragments::FragmentKind::Text); + case ExceptionSpecificationType::EST_Dynamic: + // FIXME: throw(int), get types of inner expression + return Fragments; + case ExceptionSpecificationType::EST_BasicNoexcept: + + return Fragments.append(" ", DeclarationFragments::FragmentKind::Text) + .append("noexcept", DeclarationFragments::FragmentKind::Keyword); + case ExceptionSpecificationType::EST_DependentNoexcept: + // FIXME: throw(conditional-expression), get expression + break; + case ExceptionSpecificationType::EST_NoexceptFalse: + return Fragments.append(" ", DeclarationFragments::FragmentKind::Text) + .append("noexcept", DeclarationFragments::FragmentKind::Keyword) + .append("(", DeclarationFragments::FragmentKind::Text) + .append("false", DeclarationFragments::FragmentKind::Keyword) + .append(")", DeclarationFragments::FragmentKind::Text); + case ExceptionSpecificationType::EST_NoexceptTrue: + return Fragments.append(" ", DeclarationFragments::FragmentKind::Text) + .append("noexcept", DeclarationFragments::FragmentKind::Keyword) + .append("(", DeclarationFragments::FragmentKind::Text) + .append("true", DeclarationFragments::FragmentKind::Keyword) + .append(")", DeclarationFragments::FragmentKind::Text); + default: + return Fragments; + } + + llvm_unreachable("Unhandled exception specification"); +} + +DeclarationFragments +DeclarationFragments::getStructureTypeFragment(const RecordDecl *Record) { + DeclarationFragments Fragments; + if (Record->isStruct()) + Fragments.append("struct", DeclarationFragments::FragmentKind::Keyword); + else if (Record->isUnion()) + Fragments.append("union", DeclarationFragments::FragmentKind::Keyword); + else + Fragments.append("class", DeclarationFragments::FragmentKind::Keyword); + + return Fragments; +} + // NNS stores C++ nested name specifiers, which are prefixes to qualified names. // Build declaration fragments for NNS recursively so that we have the USR for // every part in a qualified name, and also leaves the actual underlying type @@ -369,6 +426,9 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType( DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) { DeclarationFragments Fragments; + if (Var->isConstexpr()) + Fragments.append("constexpr ", DeclarationFragments::FragmentKind::Keyword); + StorageClass SC = Var->getStorageClass(); if (SC != SC_None) Fragments @@ -438,7 +498,10 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) { case SC_Register: llvm_unreachable("invalid for functions"); } - // FIXME: Handle C++ function specifiers: constexpr, consteval, explicit, etc. + if (Func->isConsteval()) // if consteval, it is also constexpr + Fragments.append("consteval ", DeclarationFragments::FragmentKind::Keyword); + else if (Func->isConstexpr()) + Fragments.append("constexpr ", DeclarationFragments::FragmentKind::Keyword); // FIXME: Is `after` actually needed here? DeclarationFragments After; @@ -457,6 +520,9 @@ DeclarationFragmentsBuilder::getFragmentsForFunction(const FunctionDecl *Func) { } Fragments.append(")", DeclarationFragments::FragmentKind::Text); + Fragments.append(DeclarationFragments::getExceptionSpecificationString( + Func->getExceptionSpecType())); + // FIXME: Handle exception specifiers: throw, noexcept return Fragments.append(";", DeclarationFragments::FragmentKind::Text); } @@ -493,7 +559,13 @@ DeclarationFragmentsBuilder::getFragmentsForEnum(const EnumDecl *EnumDecl) { DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForField(const FieldDecl *Field) { DeclarationFragments After; - return getFragmentsForType(Field->getType(), Field->getASTContext(), After) + DeclarationFragments Fragments; + if (Field->isMutable()) + Fragments.append("mutable", DeclarationFragments::FragmentKind::Keyword) + .appendSpace(); + return Fragments + .append( + getFragmentsForType(Field->getType(), Field->getASTContext(), After)) .appendSpace() .append(Field->getName(), DeclarationFragments::FragmentKind::Identifier) .append(std::move(After)) @@ -515,6 +587,156 @@ DeclarationFragmentsBuilder::getFragmentsForStruct(const RecordDecl *Record) { return Fragments.append(";", DeclarationFragments::FragmentKind::Text); } +DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXClass( + const CXXRecordDecl *Record) { + if (const auto *TypedefNameDecl = Record->getTypedefNameForAnonDecl()) + return getFragmentsForTypedef(TypedefNameDecl); + + DeclarationFragments Fragments; + Fragments.append(DeclarationFragments::getStructureTypeFragment(Record)); + + if (!Record->getName().empty()) + Fragments.appendSpace().append( + Record->getName(), DeclarationFragments::FragmentKind::Identifier); + + return Fragments.append(";", DeclarationFragments::FragmentKind::Text); +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForSpecialCXXMethod( + const CXXMethodDecl *Method) { + DeclarationFragments Fragments; + StringRef Name; + if (isa(Method)) { + auto *Constructor = dyn_cast(Method); + Name = cast(Constructor->getDeclContext())->getName(); + if (Constructor->isExplicit()) + Fragments.append("explicit", DeclarationFragments::FragmentKind::Keyword) + .appendSpace(); + } else if (isa(Method)) + Name = StringRef(Method->getNameAsString()); + + DeclarationFragments After; + Fragments.append(Name, DeclarationFragments::FragmentKind::Identifier) + .append(std::move(After)); + Fragments.append("(", DeclarationFragments::FragmentKind::Text); + for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) { + if (i) + Fragments.append(", ", DeclarationFragments::FragmentKind::Text); + Fragments.append(getFragmentsForParam(Method->getParamDecl(i))); + } + Fragments.append(")", DeclarationFragments::FragmentKind::Text); + + Fragments.append(DeclarationFragments::getExceptionSpecificationString( + Method->getExceptionSpecType())); + + return Fragments.append(";", DeclarationFragments::FragmentKind::Text); +} + +DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForCXXMethod( + const CXXMethodDecl *Method) { + DeclarationFragments Fragments; + StringRef Name; + Name = Method->getName(); + if (Method->isStatic()) + Fragments.append("static", DeclarationFragments::FragmentKind::Keyword) + .appendSpace(); + if (Method->isConstexpr()) + Fragments.append("constexpr", DeclarationFragments::FragmentKind::Keyword) + .appendSpace(); + if (Method->isVolatile()) + Fragments.append("volatile", DeclarationFragments::FragmentKind::Keyword) + .appendSpace(); + + // Build return type + DeclarationFragments After; + Fragments + .append(getFragmentsForType(Method->getReturnType(), + Method->getASTContext(), After)) + .appendSpace() + .append(Name, DeclarationFragments::FragmentKind::Identifier) + .append(std::move(After)); + Fragments.append("(", DeclarationFragments::FragmentKind::Text); + for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) { + if (i) + Fragments.append(", ", DeclarationFragments::FragmentKind::Text); + Fragments.append(getFragmentsForParam(Method->getParamDecl(i))); + } + Fragments.append(")", DeclarationFragments::FragmentKind::Text); + + if (Method->isConst()) + Fragments.appendSpace().append("const", + DeclarationFragments::FragmentKind::Keyword); + + Fragments.append(DeclarationFragments::getExceptionSpecificationString( + Method->getExceptionSpecType())); + + return Fragments.append(";", DeclarationFragments::FragmentKind::Text); +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForConversionFunction( + const CXXConversionDecl *ConversionFunction) { + DeclarationFragments Fragments; + + if (ConversionFunction->isExplicit()) + Fragments.append("explicit", DeclarationFragments::FragmentKind::Keyword) + .appendSpace(); + + Fragments.append("operator", DeclarationFragments::FragmentKind::Keyword) + .appendSpace(); + + Fragments + .append(ConversionFunction->getConversionType().getAsString(), + DeclarationFragments::FragmentKind::TypeIdentifier) + .append("(", DeclarationFragments::FragmentKind::Text); + for (unsigned i = 0, end = ConversionFunction->getNumParams(); i != end; + ++i) { + if (i) + Fragments.append(", ", DeclarationFragments::FragmentKind::Text); + Fragments.append(getFragmentsForParam(ConversionFunction->getParamDecl(i))); + } + Fragments.append(")", DeclarationFragments::FragmentKind::Text); + + if (ConversionFunction->isConst()) + Fragments.appendSpace().append("const", + DeclarationFragments::FragmentKind::Keyword); + + return Fragments.append(";", DeclarationFragments::FragmentKind::Text); +} + +DeclarationFragments +DeclarationFragmentsBuilder::getFragmentsForOverloadedOperator( + const CXXMethodDecl *Method) { + DeclarationFragments Fragments; + + // Build return type + DeclarationFragments After; + Fragments + .append(getFragmentsForType(Method->getReturnType(), + Method->getASTContext(), After)) + .appendSpace() + .append(Method->getNameAsString(), + DeclarationFragments::FragmentKind::Identifier) + .append(std::move(After)); + Fragments.append("(", DeclarationFragments::FragmentKind::Text); + for (unsigned i = 0, end = Method->getNumParams(); i != end; ++i) { + if (i) + Fragments.append(", ", DeclarationFragments::FragmentKind::Text); + Fragments.append(getFragmentsForParam(Method->getParamDecl(i))); + } + Fragments.append(")", DeclarationFragments::FragmentKind::Text); + + if (Method->isConst()) + Fragments.appendSpace().append("const", + DeclarationFragments::FragmentKind::Keyword); + + Fragments.append(DeclarationFragments::getExceptionSpecificationString( + Method->getExceptionSpecType())); + + return Fragments.append(";", DeclarationFragments::FragmentKind::Text); +} + DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForMacro(StringRef Name, const MacroDirective *MD) { @@ -765,24 +987,6 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForTypedef( return Fragments.append(";", DeclarationFragments::FragmentKind::Text); } -template -FunctionSignature -DeclarationFragmentsBuilder::getFunctionSignature(const FunctionT *Function) { - FunctionSignature Signature; - - DeclarationFragments ReturnType, After; - ReturnType - .append(getFragmentsForType(Function->getReturnType(), - Function->getASTContext(), After)) - .append(std::move(After)); - Signature.setReturnType(ReturnType); - - for (const auto *Param : Function->parameters()) - Signature.addParameter(Param->getName(), getFragmentsForParam(Param)); - - return Signature; -} - // Instantiate template for FunctionDecl. template FunctionSignature DeclarationFragmentsBuilder::getFunctionSignature(const FunctionDecl *); @@ -795,7 +999,18 @@ DeclarationFragmentsBuilder::getFunctionSignature(const ObjCMethodDecl *); DeclarationFragments DeclarationFragmentsBuilder::getSubHeading(const NamedDecl *Decl) { DeclarationFragments Fragments; - if (!Decl->getName().empty()) + if (isa(Decl) || isa(Decl)) + Fragments.append(cast(Decl->getDeclContext())->getName(), + DeclarationFragments::FragmentKind::Identifier); + else if (isa(Decl)) { + Fragments.append( + cast(Decl)->getConversionType().getAsString(), + DeclarationFragments::FragmentKind::Identifier); + } else if (isa(Decl) && + cast(Decl)->isOverloadedOperator()) { + Fragments.append(Decl->getNameAsString(), + DeclarationFragments::FragmentKind::Identifier); + } else if (!Decl->getName().empty()) Fragments.append(Decl->getName(), DeclarationFragments::FragmentKind::Identifier); return Fragments; diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp index 534e9288cc713..c9fbf0d87ac21 100644 --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -38,6 +38,14 @@ void serializeObject(Object &Paren, StringRef Key, std::optional Obj) { Paren[Key] = std::move(*Obj); } +/// Helper function to inject a StringRef \p String into an object \p Paren at +/// position \p Key +void serializeString(Object &Paren, StringRef Key, + std::optional String) { + if (String) + Paren[Key] = std::move(*String); +} + /// Helper function to inject a JSON array \p Array into object \p Paren at /// position \p Key. void serializeArray(Object &Paren, StringRef Key, std::optional Array) { @@ -189,9 +197,10 @@ StringRef getLanguageName(Language Lang) { return "c"; case Language::ObjC: return "objective-c"; + case Language::CXX: + return "c++"; // Unsupported language currently - case Language::CXX: case Language::ObjCXX: case Language::OpenCL: case Language::OpenCLCXX: @@ -366,6 +375,38 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) { Kind["identifier"] = AddLangPrefix("struct"); Kind["displayName"] = "Structure"; break; + case APIRecord::RK_CXXField: + Kind["identifier"] = AddLangPrefix("property"); + Kind["displayName"] = "Instance Property"; + break; + case APIRecord::RK_Union: + Kind["identifier"] = AddLangPrefix("union"); + Kind["displayName"] = "Union"; + break; + case APIRecord::RK_StaticField: + Kind["identifier"] = AddLangPrefix("type.property"); + Kind["displayName"] = "Type Property"; + break; + case APIRecord::RK_CXXClass: + Kind["identifier"] = AddLangPrefix("class"); + Kind["displayName"] = "Class"; + break; + case APIRecord::RK_CXXStaticMethod: + Kind["identifier"] = AddLangPrefix("type.method"); + Kind["displayName"] = "Static Method"; + break; + case APIRecord::RK_CXXInstanceMethod: + Kind["identifier"] = AddLangPrefix("method"); + Kind["displayName"] = "Instance Method"; + break; + case APIRecord::RK_CXXConstructorMethod: + Kind["identifier"] = AddLangPrefix("method"); + Kind["displayName"] = "Constructor"; + break; + case APIRecord::RK_CXXDestructorMethod: + Kind["identifier"] = AddLangPrefix("method"); + Kind["displayName"] = "Destructor"; + break; case APIRecord::RK_ObjCIvar: Kind["identifier"] = AddLangPrefix("ivar"); Kind["displayName"] = "Instance Variable"; @@ -470,6 +511,31 @@ void serializeFunctionSignatureMixin(Object &Paren, const RecordTy &Record) { Record, has_function_signature())); } +template +std::optional serializeAccessMixinImpl(const RecordTy &Record, + std::true_type) { + const auto &AccessControl = Record.Access; + std::string Access; + if (AccessControl.empty()) + return std::nullopt; + Access = AccessControl.getAccess(); + return Access; +} + +template +std::optional serializeAccessMixinImpl(const RecordTy &Record, + std::false_type) { + return std::nullopt; +} + +template +void serializeAccessMixin(Object &Paren, const RecordTy &Record) { + auto accessLevel = serializeAccessMixinImpl(Record, has_access()); + if (!accessLevel.has_value()) + accessLevel = "public"; + serializeString(Paren, "accessLevel", accessLevel); +} + struct PathComponent { StringRef USR; StringRef Name; @@ -543,7 +609,6 @@ Array generateParentContexts(const RecordTy &Record, const APISet &API, return ParentContexts; } - } // namespace /// Defines the format version emitted by SymbolGraphSerializer. @@ -602,9 +667,6 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const { serializeObject(Obj, "docComment", serializeDocComment(Record.Comment)); serializeArray(Obj, "declarationFragments", serializeDeclarationFragments(Record.Declaration)); - // TODO: Once we keep track of symbol access information serialize it - // correctly here. - Obj["accessLevel"] = "public"; SmallVector PathComponentsNames; // If this returns true it indicates that we couldn't find a symbol in the // hierarchy. @@ -617,6 +679,7 @@ SymbolGraphSerializer::serializeAPIRecord(const RecordTy &Record) const { serializeArray(Obj, "pathComponents", Array(PathComponentsNames)); serializeFunctionSignatureMixin(Obj, Record); + serializeAccessMixin(Obj, Record); return Obj; } @@ -698,6 +761,28 @@ void SymbolGraphSerializer::visitStructRecord(const StructRecord &Record) { serializeMembers(Record, Record.Fields); } +void SymbolGraphSerializer::visitStaticFieldRecord( + const StaticFieldRecord &Record) { + auto StaticField = serializeAPIRecord(Record); + if (!StaticField) + return; + Symbols.emplace_back(std::move(*StaticField)); + serializeRelationship(RelationshipKind::MemberOf, Record, Record.Context); +} + +void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) { + auto Class = serializeAPIRecord(Record); + if (!Class) + return; + + Symbols.emplace_back(std::move(*Class)); + serializeMembers(Record, Record.Fields); + serializeMembers(Record, Record.Methods); + + for (const auto Base : Record.Bases) + serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); +} + void SymbolGraphSerializer::visitObjCContainerRecord( const ObjCContainerRecord &Record) { auto ObjCContainer = serializeAPIRecord(Record); @@ -761,6 +846,12 @@ void SymbolGraphSerializer::serializeSingleRecord(const APIRecord *Record) { case APIRecord::RK_Struct: visitStructRecord(*cast(Record)); break; + case APIRecord::RK_StaticField: + visitStaticFieldRecord(*cast(Record)); + break; + case APIRecord::RK_CXXClass: + visitCXXClassRecord(*cast(Record)); + break; case APIRecord::RK_ObjCInterface: visitObjCContainerRecord(*cast(Record)); break; diff --git a/clang/test/ExtractAPI/class.cpp b/clang/test/ExtractAPI/class.cpp new file mode 100644 index 0000000000000..17250cc3b401d --- /dev/null +++ b/clang/test/ExtractAPI/class.cpp @@ -0,0 +1,366 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \ +// RUN: %t/input.h -o %t/output.json -Xclang -verify + +// Generator version is not consistent across test runs, normalize it. +// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \ +// RUN: %t/output.json >> %t/output-normalized.json +// RUN: diff %t/reference.output.json %t/output-normalized.json + +//--- input.h +class Foo { +private: + int a; + mutable int b; + +protected: + int c; + +public: + int d; +}; +/// expected-no-diagnostics + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [ + { + "kind": "memberOf", + "source": "c:@S@Foo@FI@a", + "target": "c:@S@Foo", + "targetFallback": "Foo" + }, + { + "kind": "memberOf", + "source": "c:@S@Foo@FI@b", + "target": "c:@S@Foo", + "targetFallback": "Foo" + }, + { + "kind": "memberOf", + "source": "c:@S@Foo@FI@c", + "target": "c:@S@Foo", + "targetFallback": "Foo" + }, + { + "kind": "memberOf", + "source": "c:@S@Foo@FI@d", + "target": "c:@S@Foo", + "targetFallback": "Foo" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 1 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "Foo" + }, + "pathComponents": [ + "Foo" + ] + }, + { + "accessLevel": "private", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "a" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@FI@a" + }, + "kind": { + "displayName": "Instance Property", + "identifier": "c++.property" + }, + "location": { + "position": { + "character": 7, + "line": 3 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "a" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "a" + } + ], + "title": "a" + }, + "pathComponents": [ + "Foo", + "a" + ] + }, + { + "accessLevel": "private", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "mutable" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "b" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@FI@b" + }, + "kind": { + "displayName": "Instance Property", + "identifier": "c++.property" + }, + "location": { + "position": { + "character": 15, + "line": 4 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "b" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "b" + } + ], + "title": "b" + }, + "pathComponents": [ + "Foo", + "b" + ] + }, + { + "accessLevel": "protected", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "c" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@FI@c" + }, + "kind": { + "displayName": "Instance Property", + "identifier": "c++.property" + }, + "location": { + "position": { + "character": 7, + "line": 7 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "c" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "c" + } + ], + "title": "c" + }, + "pathComponents": [ + "Foo", + "c" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "d" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@FI@d" + }, + "kind": { + "displayName": "Instance Property", + "identifier": "c++.property" + }, + "location": { + "position": { + "character": 7, + "line": 10 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "d" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "d" + } + ], + "title": "d" + }, + "pathComponents": [ + "Foo", + "d" + ] + } + ] +} diff --git a/clang/test/ExtractAPI/constructor_destructor.cpp b/clang/test/ExtractAPI/constructor_destructor.cpp new file mode 100644 index 0000000000000..3c8e9821f0808 --- /dev/null +++ b/clang/test/ExtractAPI/constructor_destructor.cpp @@ -0,0 +1,227 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \ +// RUN: %t/input.h -o %t/output.json -Xclang -verify + +// Generator version is not consistent across test runs, normalize it. +// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \ +// RUN: %t/output.json >> %t/output-normalized.json +// RUN: diff %t/reference.output.json %t/output-normalized.json + +//--- input.h +class Foo { + Foo(); + ~Foo(); +}; +/// expected-no-diagnostics + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [ + { + "kind": "memberOf", + "source": "c:@S@Foo@F@Foo#", + "target": "c:@S@Foo", + "targetFallback": "Foo" + }, + { + "kind": "memberOf", + "source": "c:@S@Foo@F@~Foo#", + "target": "c:@S@Foo", + "targetFallback": "Foo" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 1 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "Foo" + }, + "pathComponents": [ + "Foo" + ] + }, + { + "accessLevel": "private", + "declarationFragments": [ + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": "();" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@F@Foo#" + }, + "kind": { + "displayName": "Constructor", + "identifier": "c++.method" + }, + "location": { + "position": { + "character": 3, + "line": 2 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "Foo" + }, + "pathComponents": [ + "Foo", + "Foo" + ] + }, + { + "accessLevel": "private", + "declarationFragments": [ + { + "kind": "identifier", + "spelling": "~Foo" + }, + { + "kind": "text", + "spelling": "();" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@F@~Foo#" + }, + "kind": { + "displayName": "Destructor", + "identifier": "c++.method" + }, + "location": { + "position": { + "character": 3, + "line": 3 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "~Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "~Foo" + }, + "pathComponents": [ + "Foo", + "~Foo" + ] + } + ] +} diff --git a/clang/test/ExtractAPI/conversions.cpp b/clang/test/ExtractAPI/conversions.cpp new file mode 100644 index 0000000000000..2da1639f447e9 --- /dev/null +++ b/clang/test/ExtractAPI/conversions.cpp @@ -0,0 +1,251 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \ +// RUN: %t/input.h -o %t/output.json -Xclang -verify + +// Generator version is not consistent across test runs, normalize it. +// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \ +// RUN: %t/output.json >> %t/output-normalized.json +// RUN: diff %t/reference.output.json %t/output-normalized.json + +//--- input.h +class Foo { + operator int(); + explicit operator double(); +}; +/// expected-no-diagnostics + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [ + { + "kind": "memberOf", + "source": "c:@S@Foo@F@operator int#", + "target": "c:@S@Foo", + "targetFallback": "Foo" + }, + { + "kind": "memberOf", + "source": "c:@S@Foo@F@operator double#", + "target": "c:@S@Foo", + "targetFallback": "Foo" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 1 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "Foo" + }, + "pathComponents": [ + "Foo" + ] + }, + { + "accessLevel": "private", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "operator" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "spelling": "int" + }, + { + "kind": "text", + "spelling": "();" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@F@operator int#" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "c++.method" + }, + "location": { + "position": { + "character": 3, + "line": 2 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "operator int" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "int" + } + ], + "title": "operator int" + }, + "pathComponents": [ + "Foo", + "operator int" + ] + }, + { + "accessLevel": "private", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "explicit" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "keyword", + "spelling": "operator" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "spelling": "double" + }, + { + "kind": "text", + "spelling": "();" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:d", + "spelling": "double" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@F@operator double#" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "c++.method" + }, + "location": { + "position": { + "character": 12, + "line": 3 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "operator double" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "double" + } + ], + "title": "operator double" + }, + "pathComponents": [ + "Foo", + "operator double" + ] + } + ] +} diff --git a/clang/test/ExtractAPI/function_noexcepts.cpp b/clang/test/ExtractAPI/function_noexcepts.cpp new file mode 100644 index 0000000000000..7d5cc1741b056 --- /dev/null +++ b/clang/test/ExtractAPI/function_noexcepts.cpp @@ -0,0 +1,293 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \ +// RUN: %t/input.h -o %t/output.json -Xclang -verify + +// Generator version is not consistent across test runs, normalize it. +// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \ +// RUN: %t/output.json >> %t/output-normalized.json +// RUN: diff %t/reference.output.json %t/output-normalized.json + +//--- input.h +void getFoo() noexcept; + +void getBar() noexcept(true); + +void getFooBar() noexcept(false); +/// expected-no-diagnostics + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "getFoo" + }, + { + "kind": "text", + "spelling": "()" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "keyword", + "spelling": "noexcept" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@F@getFoo#" + }, + "kind": { + "displayName": "Function", + "identifier": "c++.func" + }, + "location": { + "position": { + "character": 6, + "line": 1 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "getFoo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "getFoo" + } + ], + "title": "getFoo" + }, + "pathComponents": [ + "getFoo" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "getBar" + }, + { + "kind": "text", + "spelling": "()" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "keyword", + "spelling": "noexcept" + }, + { + "kind": "text", + "spelling": "(" + }, + { + "kind": "keyword", + "spelling": "true" + }, + { + "kind": "text", + "spelling": ");" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@F@getBar#" + }, + "kind": { + "displayName": "Function", + "identifier": "c++.func" + }, + "location": { + "position": { + "character": 6, + "line": 3 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "getBar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "getBar" + } + ], + "title": "getBar" + }, + "pathComponents": [ + "getBar" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "getFooBar" + }, + { + "kind": "text", + "spelling": "()" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "keyword", + "spelling": "noexcept" + }, + { + "kind": "text", + "spelling": "(" + }, + { + "kind": "keyword", + "spelling": "false" + }, + { + "kind": "text", + "spelling": ");" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@F@getFooBar#" + }, + "kind": { + "displayName": "Function", + "identifier": "c++.func" + }, + "location": { + "position": { + "character": 6, + "line": 5 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "getFooBar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "getFooBar" + } + ], + "title": "getFooBar" + }, + "pathComponents": [ + "getFooBar" + ] + } + ] +} diff --git a/clang/test/ExtractAPI/methods.cpp b/clang/test/ExtractAPI/methods.cpp new file mode 100644 index 0000000000000..0dea2a39a17ee --- /dev/null +++ b/clang/test/ExtractAPI/methods.cpp @@ -0,0 +1,467 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \ +// RUN: %t/input.h -o %t/output.json -Xclang -verify + +// Generator version is not consistent across test runs, normalize it. +// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \ +// RUN: %t/output.json >> %t/output-normalized.json +// RUN: diff %t/reference.output.json %t/output-normalized.json + +//--- input.h +class Foo { + int getCount(); + + void setLength(int length) noexcept; + +public: + static double getFoo(); + +protected: + constexpr int getBar() const; +}; +/// expected-no-diagnostics + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [ + { + "kind": "memberOf", + "source": "c:@S@Foo@F@getCount#", + "target": "c:@S@Foo", + "targetFallback": "Foo" + }, + { + "kind": "memberOf", + "source": "c:@S@Foo@F@setLength#I#", + "target": "c:@S@Foo", + "targetFallback": "Foo" + }, + { + "kind": "memberOf", + "source": "c:@S@Foo@F@getFoo#S", + "target": "c:@S@Foo", + "targetFallback": "Foo" + }, + { + "kind": "memberOf", + "source": "c:@S@Foo@F@getBar#1", + "target": "c:@S@Foo", + "targetFallback": "Foo" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 1 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "Foo" + }, + "pathComponents": [ + "Foo" + ] + }, + { + "accessLevel": "private", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "getCount" + }, + { + "kind": "text", + "spelling": "();" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@F@getCount#" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "c++.method" + }, + "location": { + "position": { + "character": 7, + "line": 2 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "getCount" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "getCount" + } + ], + "title": "getCount" + }, + "pathComponents": [ + "Foo", + "getCount" + ] + }, + { + "accessLevel": "private", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "setLength" + }, + { + "kind": "text", + "spelling": "(" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "internalParam", + "spelling": "length" + }, + { + "kind": "text", + "spelling": ")" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "keyword", + "spelling": "noexcept" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "functionSignature": { + "parameters": [ + { + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "internalParam", + "spelling": "length" + } + ], + "name": "length" + } + ], + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:v", + "spelling": "void" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@F@setLength#I#" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "c++.method" + }, + "location": { + "position": { + "character": 8, + "line": 4 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "setLength" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "setLength" + } + ], + "title": "setLength" + }, + "pathComponents": [ + "Foo", + "setLength" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "static" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:d", + "spelling": "double" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "getFoo" + }, + { + "kind": "text", + "spelling": "();" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:d", + "spelling": "double" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@F@getFoo#S" + }, + "kind": { + "displayName": "Static Method", + "identifier": "c++.type.method" + }, + "location": { + "position": { + "character": 17, + "line": 7 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "getFoo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "getFoo" + } + ], + "title": "getFoo" + }, + "pathComponents": [ + "Foo", + "getFoo" + ] + }, + { + "accessLevel": "protected", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "constexpr" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "getBar" + }, + { + "kind": "text", + "spelling": "() " + }, + { + "kind": "keyword", + "spelling": "const" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "functionSignature": { + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@F@getBar#1" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "c++.method" + }, + "location": { + "position": { + "character": 17, + "line": 10 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "getBar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "getBar" + } + ], + "title": "getBar" + }, + "pathComponents": [ + "Foo", + "getBar" + ] + } + ] +} diff --git a/clang/test/ExtractAPI/multiple_inheritance.cpp b/clang/test/ExtractAPI/multiple_inheritance.cpp new file mode 100644 index 0000000000000..b9869cd8b9b75 --- /dev/null +++ b/clang/test/ExtractAPI/multiple_inheritance.cpp @@ -0,0 +1,293 @@ + +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \ +// RUN: %t/input.h -o %t/output.json -Xclang -verify + +// Generator version is not consistent across test runs, normalize it. +// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \ +// RUN: %t/output.json >> %t/output-normalized.json +// RUN: diff %t/reference.output.json %t/output-normalized.json + +//--- input.h +class Fizz {}; + +class Foo : public Fizz {}; + +class Bar : public Fizz {}; + +class FooBar : public Foo, public Bar{}; +/// expected-no-diagnostics + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [ + { + "kind": "inheritsFrom", + "source": "c:@S@Foo", + "target": "c:@S@Fizz", + "targetFallback": "Fizz" + }, + { + "kind": "inheritsFrom", + "source": "c:@S@Bar", + "target": "c:@S@Fizz", + "targetFallback": "Fizz" + }, + { + "kind": "inheritsFrom", + "source": "c:@S@FooBar", + "target": "c:@S@Foo", + "targetFallback": "Foo" + }, + { + "kind": "inheritsFrom", + "source": "c:@S@FooBar", + "target": "c:@S@Bar", + "targetFallback": "Bar" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Fizz" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Fizz" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 1 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Fizz" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Fizz" + } + ], + "title": "Fizz" + }, + "pathComponents": [ + "Fizz" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 3 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "Foo" + }, + "pathComponents": [ + "Foo" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Bar" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Bar" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 5 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Bar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Bar" + } + ], + "title": "Bar" + }, + "pathComponents": [ + "Bar" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "FooBar" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@FooBar" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 7 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "FooBar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "FooBar" + } + ], + "title": "FooBar" + }, + "pathComponents": [ + "FooBar" + ] + } + ] +} diff --git a/clang/test/ExtractAPI/operator_overload.cpp b/clang/test/ExtractAPI/operator_overload.cpp new file mode 100644 index 0000000000000..1547a9c2c68f5 --- /dev/null +++ b/clang/test/ExtractAPI/operator_overload.cpp @@ -0,0 +1,210 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \ +// RUN: %t/input.h -o %t/output.json -Xclang -verify + +// Generator version is not consistent across test runs, normalize it. +// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \ +// RUN: %t/output.json >> %t/output-normalized.json +// RUN: diff %t/reference.output.json %t/output-normalized.json + +//--- input.h +class Foo { + int operator+(int x); +}; +/// expected-no-diagnostics + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [ + { + "kind": "memberOf", + "source": "c:@S@Foo@F@operator+#I#", + "target": "c:@S@Foo", + "targetFallback": "Foo" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 1 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "Foo" + }, + "pathComponents": [ + "Foo" + ] + }, + { + "accessLevel": "private", + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "operator+" + }, + { + "kind": "text", + "spelling": "(" + }, + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "internalParam", + "spelling": "x" + }, + { + "kind": "text", + "spelling": ");" + } + ], + "functionSignature": { + "parameters": [ + { + "declarationFragments": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "internalParam", + "spelling": "x" + } + ], + "name": "x" + } + ], + "returns": [ + { + "kind": "typeIdentifier", + "preciseIdentifier": "c:I", + "spelling": "int" + } + ] + }, + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo@F@operator+#I#" + }, + "kind": { + "displayName": "Instance Method", + "identifier": "c++.method" + }, + "location": { + "position": { + "character": 7, + "line": 2 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "operator+" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "operator+" + } + ], + "title": "operator+" + }, + "pathComponents": [ + "Foo", + "operator+" + ] + } + ] +} diff --git a/clang/test/ExtractAPI/simple_inheritance.cpp b/clang/test/ExtractAPI/simple_inheritance.cpp new file mode 100644 index 0000000000000..94465f0333af3 --- /dev/null +++ b/clang/test/ExtractAPI/simple_inheritance.cpp @@ -0,0 +1,162 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: sed -e "s@INPUT_DIR@%{/t:regex_replacement}@g" \ +// RUN: %t/reference.output.json.in >> %t/reference.output.json +// RUN: %clang++ -extract-api -target arm64-apple-macosx -x c++-header \ +// RUN: %t/input.h -o %t/output.json -Xclang -verify + +// Generator version is not consistent across test runs, normalize it. +// RUN: sed -e "s@\"generator\": \".*\"@\"generator\": \"?\"@g" \ +// RUN: %t/output.json >> %t/output-normalized.json +// RUN: diff %t/reference.output.json %t/output-normalized.json + +//--- input.h +class Foo {}; + +class Bar : public Foo {}; +/// expected-no-diagnostics + +//--- reference.output.json.in +{ + "metadata": { + "formatVersion": { + "major": 0, + "minor": 5, + "patch": 3 + }, + "generator": "?" + }, + "module": { + "name": "", + "platform": { + "architecture": "arm64", + "operatingSystem": { + "minimumVersion": { + "major": 11, + "minor": 0, + "patch": 0 + }, + "name": "macosx" + }, + "vendor": "apple" + } + }, + "relationships": [ + { + "kind": "inheritsFrom", + "source": "c:@S@Bar", + "target": "c:@S@Foo", + "targetFallback": "Foo" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Foo" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 1 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Foo" + } + ], + "title": "Foo" + }, + "pathComponents": [ + "Foo" + ] + }, + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "class" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Bar" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@S@Bar" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 7, + "line": 3 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Bar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Bar" + } + ], + "title": "Bar" + }, + "pathComponents": [ + "Bar" + ] + } + ] +}