diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h --- a/clang/include/clang/ExtractAPI/API.h +++ b/clang/include/clang/ExtractAPI/API.h @@ -157,6 +157,7 @@ /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.) enum RecordKind { RK_Unknown, + RK_Namespace, RK_GlobalFunction, RK_GlobalFunctionTemplate, RK_GlobalFunctionTemplateSpecialization, @@ -267,6 +268,20 @@ virtual ~APIRecord() = 0; }; +struct NamespaceRecord : APIRecord { + NamespaceRecord(StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, LinkageInfo Linkage, + const DocComment &Comment, DeclarationFragments Declaration, + DeclarationFragments SubHeading, bool IsFromSystemHeader) + : APIRecord(RK_Namespace, USR, Name, Loc, std::move(Availabilities), + Linkage, Comment, Declaration, SubHeading, + IsFromSystemHeader) {} + + static bool classof(const APIRecord *Record) { + return Record->getKind() == RK_Namespace; + } +}; + /// This holds information associated with global functions. struct GlobalFunctionRecord : APIRecord { FunctionSignature Signature; @@ -900,15 +915,17 @@ SmallVector> Fields; SmallVector> Methods; SmallVector Bases; + AccessControl Access; CXXClassRecord(StringRef USR, StringRef Name, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, RecordKind Kind, - bool IsFromSystemHeader) + AccessControl Access, bool IsFromSystemHeader) : APIRecord(Kind, USR, Name, Loc, std::move(Availabilities), LinkageInfo::none(), Comment, Declaration, SubHeading, - IsFromSystemHeader) {} + IsFromSystemHeader), + Access(Access) {} static bool classof(const APIRecord *Record) { return (Record->getKind() == RK_CXXClass); @@ -925,9 +942,9 @@ AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, Template Template, - bool IsFromSystemHeader) + AccessControl Access, bool IsFromSystemHeader) : CXXClassRecord(USR, Name, Loc, std::move(Availabilities), Comment, - Declaration, SubHeading, RK_ClassTemplate, + Declaration, SubHeading, RK_ClassTemplate, Access, IsFromSystemHeader), Templ(Template) {} @@ -937,16 +954,14 @@ }; struct ClassTemplateSpecializationRecord : CXXClassRecord { - ClassTemplateSpecializationRecord(StringRef USR, StringRef Name, - PresumedLoc Loc, - AvailabilitySet Availabilities, - const DocComment &Comment, - DeclarationFragments Declaration, - DeclarationFragments SubHeading, - bool IsFromSystemHeader) + ClassTemplateSpecializationRecord( + StringRef USR, StringRef Name, PresumedLoc Loc, + AvailabilitySet Availabilities, const DocComment &Comment, + DeclarationFragments Declaration, DeclarationFragments SubHeading, + AccessControl Access, bool IsFromSystemHeader) : CXXClassRecord(USR, Name, Loc, std::move(Availabilities), Comment, Declaration, SubHeading, RK_ClassTemplateSpecialization, - IsFromSystemHeader) {} + Access, IsFromSystemHeader) {} static bool classof(const APIRecord *Record) { return Record->getKind() == RK_ClassTemplateSpecialization; @@ -959,10 +974,10 @@ StringRef USR, StringRef Name, PresumedLoc Loc, AvailabilitySet Availabilities, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - Template Template, bool IsFromSystemHeader) + Template Template, AccessControl Access, bool IsFromSystemHeader) : CXXClassRecord(USR, Name, Loc, std::move(Availabilities), Comment, Declaration, SubHeading, RK_ClassTemplateSpecialization, - IsFromSystemHeader), + Access, IsFromSystemHeader), Templ(Template) {} static bool classof(const APIRecord *Record) { @@ -1138,6 +1153,13 @@ : public std::true_type {}; template <> struct has_access : public std::true_type {}; +template <> struct has_access : public std::true_type {}; +template <> struct has_access : public std::true_type {}; +template <> +struct has_access : public std::true_type {}; +template <> +struct has_access + : public std::true_type {}; template struct has_template : public std::false_type {}; template <> struct has_template : public std::true_type {}; @@ -1160,6 +1182,13 @@ /// APISet holds the set of API records collected from given inputs. class APISet { public: + NamespaceRecord *addNamespace(APIRecord *Parent, StringRef Name, + StringRef USR, PresumedLoc Loc, + AvailabilitySet Availability, + LinkageInfo Linkage, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + bool IsFromSystemHeaderg); /// Create and add a global variable record into the API set. /// /// Note: the caller is responsible for keeping the StringRef \p Name and @@ -1277,31 +1306,33 @@ DeclarationFragments Declaration, DeclarationFragments SubHeading, AccessControl Access, Template Template, bool IsFromSystemHeader); - CXXClassRecord * - addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc, - AvailabilitySet Availability, const DocComment &Comment, - DeclarationFragments Declaration, DeclarationFragments SubHeading, - APIRecord::RecordKind Kind, bool IsFromSystemHeader); + CXXClassRecord *addCXXClass(APIRecord *Parent, StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availability, + const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, + APIRecord::RecordKind Kind, AccessControl Access, + bool IsFromSystemHeader); ClassTemplateRecord * - addClassTemplate(StringRef Name, StringRef USR, PresumedLoc Loc, - AvailabilitySet Availability, const DocComment &Comment, - DeclarationFragments Declaration, + addClassTemplate(APIRecord *Parent, StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availability, + const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, Template Template, - bool IsFromSystemHeader); + AccessControl Access, bool IsFromSystemHeader); ClassTemplateSpecializationRecord *addClassTemplateSpecialization( - StringRef Name, StringRef USR, PresumedLoc Loc, + APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - bool IsFromSystemHeader); + AccessControl Access, bool IsFromSystemHeader); ClassTemplatePartialSpecializationRecord * addClassTemplatePartialSpecialization( - StringRef Name, StringRef USR, PresumedLoc Loc, + APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - Template Template, bool IsFromSystemHeader); + Template Template, AccessControl Access, bool IsFromSystemHeader); GlobalVariableTemplateSpecializationRecord * addGlobalVariableTemplateSpecialization( @@ -1476,6 +1507,7 @@ /// Get the language used by the APIs. Language getLanguage() const { return Lang; } + const RecordMap &getNamespaces() const { return Namespaces; } const RecordMap &getGlobalFunctions() const { return GlobalFunctions; } @@ -1589,6 +1621,7 @@ const Language Lang; llvm::DenseMap USRBasedLookupTable; + RecordMap Namespaces; RecordMap GlobalFunctions; RecordMap GlobalFunctionTemplates; RecordMap diff --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h --- a/clang/include/clang/ExtractAPI/DeclarationFragments.h +++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h @@ -257,17 +257,19 @@ static AccessControl getAccessControl(const Decl *Decl) { switch (Decl->getAccess()) { case AS_public: + case AS_none: 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"); } + static DeclarationFragments + getFragmentsForNamespace(const NamespaceDecl *Decl); + /// Build DeclarationFragments for a variable declaration VarDecl. static DeclarationFragments getFragmentsForVar(const VarDecl *); @@ -334,8 +336,8 @@ static DeclarationFragments getFragmentsForFunctionTemplate(const FunctionTemplateDecl *Decl); - static DeclarationFragments getFragmentsForFunctionTemplateSpecialization( - const FunctionDecl *Decl); + static DeclarationFragments + getFragmentsForFunctionTemplateSpecialization(const FunctionDecl *Decl); /// Build DeclarationFragments for an Objective-C category declaration /// ObjCCategoryDecl. diff --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h --- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h +++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h @@ -74,6 +74,10 @@ bool WalkUpFromFunctionTemplateDecl(const FunctionTemplateDecl *Decl); + bool WalkUpFromNamespaceDecl(const NamespaceDecl *Decl); + + bool VisitNamespaceDecl(const NamespaceDecl *Decl); + bool VisitRecordDecl(const RecordDecl *Decl); bool VisitCXXRecordDecl(const CXXRecordDecl *Decl); @@ -187,6 +191,17 @@ } return Bases; } + + APIRecord *determineParentRecord(const DeclContext *Context) { + SmallString<128> ParentUSR; + if (Context->getDeclKind() == Decl::TranslationUnit) + return nullptr; + + index::generateUSRForDecl(dyn_cast(Context), ParentUSR); + + APIRecord *Parent = API.findRecordForUSR(ParentUSR); + return Parent; + } }; template @@ -447,6 +462,44 @@ return true; } +template +bool ExtractAPIVisitorBase::WalkUpFromNamespaceDecl( + const NamespaceDecl *Decl) { + getDerivedExtractAPIVisitor().VisitNamespaceDecl(Decl); + return true; +} + +template +bool ExtractAPIVisitorBase::VisitNamespaceDecl( + const NamespaceDecl *Decl) { + + if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) + return true; + if (Decl->isAnonymousNamespace()) + return true; + StringRef Name = Decl->getName(); + StringRef USR = API.recordUSR(Decl); + LinkageInfo Linkage = Decl->getLinkageAndVisibility(); + PresumedLoc Loc = + Context.getSourceManager().getPresumedLoc(Decl->getLocation()); + DocComment Comment; + if (auto *RawComment = + getDerivedExtractAPIVisitor().fetchRawCommentForDecl(Decl)) + Comment = RawComment->getFormattedLines(Context.getSourceManager(), + Context.getDiagnostics()); + + // Build declaration fragments and sub-heading for the struct. + DeclarationFragments Declaration = + DeclarationFragmentsBuilder::getFragmentsForNamespace(Decl); + DeclarationFragments SubHeading = + DeclarationFragmentsBuilder::getSubHeading(Decl); + APIRecord *Parent = determineParentRecord(Decl->getDeclContext()); + API.addNamespace(Parent, Name, USR, Loc, AvailabilitySet(Decl), Linkage, + Comment, Declaration, SubHeading, isInSystemHeader(Decl)); + + return true; +} + template bool ExtractAPIVisitorBase::VisitRecordDecl(const RecordDecl *Decl) { if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl)) @@ -512,7 +565,9 @@ Kind = APIRecord::RecordKind::RK_Struct; else Kind = APIRecord::RecordKind::RK_CXXClass; + auto Access = DeclarationFragmentsBuilder::getAccessControl(Decl); + APIRecord *Parent = determineParentRecord(Decl->getDeclContext()); CXXClassRecord *CXXClassRecord; if (Decl->getDescribedClassTemplate()) { // Inject template fragments before class fragments. @@ -521,12 +576,13 @@ DeclarationFragmentsBuilder::getFragmentsForRedeclarableTemplate( Decl->getDescribedClassTemplate())); CXXClassRecord = API.addClassTemplate( - Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading, - Template(Decl->getDescribedClassTemplate()), isInSystemHeader(Decl)); + Parent, Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, + SubHeading, Template(Decl->getDescribedClassTemplate()), Access, + isInSystemHeader(Decl)); } else - CXXClassRecord = - API.addCXXClass(Name, USR, Loc, AvailabilitySet(Decl), Comment, - Declaration, SubHeading, Kind, isInSystemHeader(Decl)); + CXXClassRecord = API.addCXXClass( + Parent, Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, + SubHeading, Kind, Access, isInSystemHeader(Decl)); CXXClassRecord->Bases = getBases(Decl); @@ -708,8 +764,10 @@ DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); + APIRecord *Parent = determineParentRecord(Decl->getDeclContext()); auto *ClassTemplateSpecializationRecord = API.addClassTemplateSpecialization( - Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, SubHeading, + Parent, Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, + SubHeading, DeclarationFragmentsBuilder::getAccessControl(Decl), isInSystemHeader(Decl)); ClassTemplateSpecializationRecord->Bases = getBases(Decl); @@ -738,10 +796,13 @@ DeclarationFragments SubHeading = DeclarationFragmentsBuilder::getSubHeading(Decl); + APIRecord *Parent = determineParentRecord(Decl->getDeclContext()); auto *ClassTemplatePartialSpecRecord = API.addClassTemplatePartialSpecialization( - Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, - SubHeading, Template(Decl), isInSystemHeader(Decl)); + Parent, Name, USR, Loc, AvailabilitySet(Decl), Comment, Declaration, + SubHeading, Template(Decl), + DeclarationFragmentsBuilder::getAccessControl(Decl), + isInSystemHeader(Decl)); ClassTemplatePartialSpecRecord->Bases = getBases(Decl); diff --git a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h --- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h +++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h @@ -23,6 +23,8 @@ template class APISetVisitor { public: void traverseAPISet() { + getDerived()->traverseNamespaces(); + getDerived()->traverseGlobalVariableRecords(); getDerived()->traverseGlobalFunctionRecords(); @@ -74,6 +76,11 @@ getDerived()->traverseTypedefRecords(); } + void traverseNamespaces() { + for (const auto &Namespace : API.getNamespaces()) + getDerived()->visitNamespaceRecord(*Namespace.second); + } + void traverseGlobalFunctionRecords() { for (const auto &GlobalFunction : API.getGlobalFunctions()) getDerived()->visitGlobalFunctionRecord(*GlobalFunction.second); @@ -213,6 +220,8 @@ getDerived()->visitTypedefRecord(*Typedef.second); } + void visitNamespaceRecord(const NamespaceRecord &Record){}; + /// Visit a global function record. void visitGlobalFunctionRecord(const GlobalFunctionRecord &Record){}; diff --git a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h --- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h +++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h @@ -152,6 +152,8 @@ SymbolGraphSerializerOption Options; public: + void visitNamespaceRecord(const NamespaceRecord &Record); + /// Visit a global function record. void visitGlobalFunctionRecord(const GlobalFunctionRecord &Record); diff --git a/clang/lib/ExtractAPI/API.cpp b/clang/lib/ExtractAPI/API.cpp --- a/clang/lib/ExtractAPI/API.cpp +++ b/clang/lib/ExtractAPI/API.cpp @@ -44,6 +44,22 @@ } // namespace +NamespaceRecord * +APISet::addNamespace(APIRecord *Parent, StringRef Name, StringRef USR, + PresumedLoc Loc, AvailabilitySet Availability, + LinkageInfo Linkage, const DocComment &Comment, + DeclarationFragments Declaration, + DeclarationFragments SubHeading, bool IsFromSystemHeader) { + auto *Record = addTopLevelRecord( + USRBasedLookupTable, Namespaces, USR, Name, Loc, std::move(Availability), + Linkage, Comment, Declaration, SubHeading, IsFromSystemHeader); + + if (Parent) + Record->ParentInformation = APIRecord::HierarchyInformation( + Parent->USR, Parent->Name, Parent->getKind(), Parent); + return Record; +} + GlobalVariableRecord * APISet::addGlobalVar(StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availabilities, LinkageInfo Linkage, @@ -200,47 +216,65 @@ } CXXClassRecord * -APISet::addCXXClass(StringRef Name, StringRef USR, PresumedLoc Loc, - AvailabilitySet Availabilities, const DocComment &Comment, - DeclarationFragments Declaration, +APISet::addCXXClass(APIRecord *Parent, 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); + AccessControl Access, bool IsFromSystemHeader) { + auto *Record = + addTopLevelRecord(USRBasedLookupTable, CXXClasses, USR, Name, Loc, + std::move(Availabilities), Comment, Declaration, + SubHeading, Kind, Access, IsFromSystemHeader); + if (Parent) + Record->ParentInformation = APIRecord::HierarchyInformation( + Parent->USR, Parent->Name, Parent->getKind(), Parent); + return Record; } ClassTemplateRecord *APISet::addClassTemplate( - StringRef Name, StringRef USR, PresumedLoc Loc, + APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - Template Template, bool IsFromSystemHeader) { - - return addTopLevelRecord(USRBasedLookupTable, ClassTemplates, USR, Name, Loc, - std::move(Availability), Comment, Declaration, - SubHeading, Template, IsFromSystemHeader); + Template Template, AccessControl Access, bool IsFromSystemHeader) { + auto *Record = + addTopLevelRecord(USRBasedLookupTable, ClassTemplates, USR, Name, Loc, + std::move(Availability), Comment, Declaration, + SubHeading, Template, Access, IsFromSystemHeader); + if (Parent) + Record->ParentInformation = APIRecord::HierarchyInformation( + Parent->USR, Parent->Name, Parent->getKind(), Parent); + return Record; } ClassTemplateSpecializationRecord *APISet::addClassTemplateSpecialization( - StringRef Name, StringRef USR, PresumedLoc Loc, + APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - bool IsFromSystemHeader) { - return addTopLevelRecord(USRBasedLookupTable, ClassTemplateSpecializations, - USR, Name, Loc, std::move(Availability), Comment, - Declaration, SubHeading, IsFromSystemHeader); + AccessControl Access, bool IsFromSystemHeader) { + auto *Record = + addTopLevelRecord(USRBasedLookupTable, ClassTemplateSpecializations, USR, + Name, Loc, std::move(Availability), Comment, + Declaration, SubHeading, Access, IsFromSystemHeader); + if (Parent) + Record->ParentInformation = APIRecord::HierarchyInformation( + Parent->USR, Parent->Name, Parent->getKind(), Parent); + return Record; } ClassTemplatePartialSpecializationRecord * APISet::addClassTemplatePartialSpecialization( - StringRef Name, StringRef USR, PresumedLoc Loc, + APIRecord *Parent, StringRef Name, StringRef USR, PresumedLoc Loc, AvailabilitySet Availability, const DocComment &Comment, DeclarationFragments Declaration, DeclarationFragments SubHeading, - Template Template, bool IsFromSystemHeader) { - return addTopLevelRecord(USRBasedLookupTable, - ClassTemplatePartialSpecializations, USR, Name, Loc, - std::move(Availability), Comment, Declaration, - SubHeading, Template, IsFromSystemHeader); + Template Template, AccessControl Access, bool IsFromSystemHeader) { + auto *Record = addTopLevelRecord( + USRBasedLookupTable, ClassTemplatePartialSpecializations, USR, Name, Loc, + std::move(Availability), Comment, Declaration, SubHeading, Template, + Access, IsFromSystemHeader); + if (Parent) + Record->ParentInformation = APIRecord::HierarchyInformation( + Parent->USR, Parent->Name, Parent->getKind(), Parent); + return Record; } GlobalVariableTemplateSpecializationRecord * diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp --- a/clang/lib/ExtractAPI/DeclarationFragments.cpp +++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp @@ -423,6 +423,16 @@ return QualsFragments.appendSpace().append(std::move(TypeFragments)); } +DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForNamespace( + const NamespaceDecl *Decl) { + DeclarationFragments Fragments; + Fragments.append("namespace", DeclarationFragments::FragmentKind::Keyword); + if (!Decl->isAnonymousNamespace()) + Fragments.appendSpace().append( + Decl->getName(), DeclarationFragments::FragmentKind::Identifier); + return Fragments.append(";", DeclarationFragments::FragmentKind::Text); +} + DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForVar(const VarDecl *Var) { DeclarationFragments Fragments; diff --git a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp --- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp +++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp @@ -351,6 +351,10 @@ case APIRecord::RK_Unknown: llvm_unreachable("Records should have an explicit kind"); break; + case APIRecord::RK_Namespace: + Kind["identifier"] = AddLangPrefix("namespace"); + Kind["displayName"] = "Namespace"; + break; case APIRecord::RK_GlobalFunction: Kind["identifier"] = AddLangPrefix("func"); Kind["displayName"] = "Function"; @@ -821,6 +825,17 @@ Relationships.emplace_back(std::move(Relationship)); } +void SymbolGraphSerializer::visitNamespaceRecord( + const NamespaceRecord &Record) { + auto Namespace = serializeAPIRecord(Record); + if (!Namespace) + return; + Symbols.emplace_back(std::move(*Namespace)); + if (!Record.ParentInformation.empty()) + serializeRelationship(RelationshipKind::MemberOf, Record, + Record.ParentInformation.ParentRecord); +} + void SymbolGraphSerializer::visitGlobalFunctionRecord( const GlobalFunctionRecord &Record) { auto Obj = serializeAPIRecord(Record); @@ -874,6 +889,9 @@ Symbols.emplace_back(std::move(*Class)); for (const auto Base : Record.Bases) serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); + if (!Record.ParentInformation.empty()) + serializeRelationship(RelationshipKind::MemberOf, Record, + Record.ParentInformation.ParentRecord); } void SymbolGraphSerializer::visitClassTemplateRecord( @@ -885,6 +903,9 @@ Symbols.emplace_back(std::move(*Class)); for (const auto Base : Record.Bases) serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); + if (!Record.ParentInformation.empty()) + serializeRelationship(RelationshipKind::MemberOf, Record, + Record.ParentInformation.ParentRecord); } void SymbolGraphSerializer::visitClassTemplateSpecializationRecord( @@ -897,6 +918,9 @@ for (const auto Base : Record.Bases) serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); + if (!Record.ParentInformation.empty()) + serializeRelationship(RelationshipKind::MemberOf, Record, + Record.ParentInformation.ParentRecord); } void SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord( @@ -909,6 +933,9 @@ for (const auto Base : Record.Bases) serializeRelationship(RelationshipKind::InheritsFrom, Record, Base); + if (!Record.ParentInformation.empty()) + serializeRelationship(RelationshipKind::MemberOf, Record, + Record.ParentInformation.ParentRecord); } void SymbolGraphSerializer::visitCXXInstanceMethodRecord( diff --git a/clang/test/ExtractAPI/namespace.cpp b/clang/test/ExtractAPI/namespace.cpp new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/namespace.cpp @@ -0,0 +1,164 @@ +// 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_cc1 -std=c++20 -extract-api -triple arm64-apple-macosx \ +// RUN: -x c++-header %t/input.h -o %t/output.json -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 +namespace Foo { + class 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": "memberOf", + "source": "c:@N@Foo@S@Bar", + "target": "c:@N@Foo", + "targetFallback": "Foo" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "namespace" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@N@Foo" + }, + "kind": { + "displayName": "Namespace", + "identifier": "c++.namespace" + }, + "location": { + "position": { + "character": 11, + "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:@N@Foo@S@Bar" + }, + "kind": { + "displayName": "Class", + "identifier": "c++.class" + }, + "location": { + "position": { + "character": 9, + "line": 2 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Bar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Bar" + } + ], + "title": "Bar" + }, + "pathComponents": [ + "Foo", + "Bar" + ] + } + ] +} diff --git a/clang/test/ExtractAPI/nested_namespaces.cpp b/clang/test/ExtractAPI/nested_namespaces.cpp new file mode 100644 --- /dev/null +++ b/clang/test/ExtractAPI/nested_namespaces.cpp @@ -0,0 +1,164 @@ +// 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_cc1 -std=c++20 -extract-api -triple arm64-apple-macosx \ +// RUN: -x c++-header %t/input.h -o %t/output.json -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 +namespace Foo { + namespace 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": "memberOf", + "source": "c:@N@Foo@N@Bar", + "target": "c:@N@Foo", + "targetFallback": "Foo" + } + ], + "symbols": [ + { + "accessLevel": "public", + "declarationFragments": [ + { + "kind": "keyword", + "spelling": "namespace" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Foo" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@N@Foo" + }, + "kind": { + "displayName": "Namespace", + "identifier": "c++.namespace" + }, + "location": { + "position": { + "character": 11, + "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": "namespace" + }, + { + "kind": "text", + "spelling": " " + }, + { + "kind": "identifier", + "spelling": "Bar" + }, + { + "kind": "text", + "spelling": ";" + } + ], + "identifier": { + "interfaceLanguage": "c++", + "precise": "c:@N@Foo@N@Bar" + }, + "kind": { + "displayName": "Namespace", + "identifier": "c++.namespace" + }, + "location": { + "position": { + "character": 13, + "line": 2 + }, + "uri": "file://INPUT_DIR/input.h" + }, + "names": { + "navigator": [ + { + "kind": "identifier", + "spelling": "Bar" + } + ], + "subHeading": [ + { + "kind": "identifier", + "spelling": "Bar" + } + ], + "title": "Bar" + }, + "pathComponents": [ + "Foo", + "Bar" + ] + } + ] +}