[clang] 08f034f - [clang][ExtractAPI] Add support for namespaces

Erick Velez via cfe-commits cfe-commits at lists.llvm.org
Tue Aug 22 09:57:00 PDT 2023


Author: Erick Velez
Date: 2023-08-22T09:56:34-07:00
New Revision: 08f034f952fa67fc379df4caee2904de466a69f9

URL: https://github.com/llvm/llvm-project/commit/08f034f952fa67fc379df4caee2904de466a69f9
DIFF: https://github.com/llvm/llvm-project/commit/08f034f952fa67fc379df4caee2904de466a69f9.diff

LOG: [clang][ExtractAPI] Add support for namespaces

Serialize namespaces, nested namespaces, and class relationships inside them.

Depends on D157076

Reviewed By: dang

Differential Revision: https://reviews.llvm.org/D158239

Added: 
    clang/test/ExtractAPI/namespace.cpp
    clang/test/ExtractAPI/nested_namespaces.cpp

Modified: 
    clang/include/clang/ExtractAPI/API.h
    clang/include/clang/ExtractAPI/DeclarationFragments.h
    clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
    clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
    clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
    clang/lib/ExtractAPI/API.cpp
    clang/lib/ExtractAPI/DeclarationFragments.cpp
    clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index 7b2d28f738e260..b4c0e0ad39cdf2 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -157,6 +157,7 @@ struct APIRecord {
   /// Discriminator for LLVM-style RTTI (dyn_cast<> et al.)
   enum RecordKind {
     RK_Unknown,
+    RK_Namespace,
     RK_GlobalFunction,
     RK_GlobalFunctionTemplate,
     RK_GlobalFunctionTemplateSpecialization,
@@ -271,6 +272,20 @@ struct APIRecord {
   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;
@@ -904,15 +919,17 @@ struct CXXClassRecord : APIRecord {
   SmallVector<std::unique_ptr<CXXFieldRecord>> Fields;
   SmallVector<std::unique_ptr<CXXMethodRecord>> Methods;
   SmallVector<SymbolReference> 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);
@@ -929,9 +946,9 @@ struct ClassTemplateRecord : CXXClassRecord {
                       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) {}
 
@@ -941,16 +958,14 @@ struct ClassTemplateRecord : CXXClassRecord {
 };
 
 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;
@@ -963,10 +978,10 @@ struct ClassTemplatePartialSpecializationRecord : CXXClassRecord {
       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 @@ struct has_access<CXXMethodTemplateSpecializationRecord>
     : public std::true_type {};
 template <>
 struct has_access<CXXFieldTemplateRecord> : public std::true_type {};
+template <> struct has_access<CXXClassRecord> : public std::true_type {};
+template <> struct has_access<ClassTemplateRecord> : public std::true_type {};
+template <>
+struct has_access<ClassTemplateSpecializationRecord> : public std::true_type {};
+template <>
+struct has_access<ClassTemplatePartialSpecializationRecord>
+    : public std::true_type {};
 
 template <typename RecordTy> struct has_template : public std::false_type {};
 template <> struct has_template<ClassTemplateRecord> : public std::true_type {};
@@ -1167,6 +1189,13 @@ struct has_function_signature<GlobalFunctionTemplateSpecializationRecord>
 /// 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
@@ -1284,31 +1313,33 @@ class APISet {
       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(
@@ -1483,6 +1514,7 @@ class APISet {
   /// Get the language used by the APIs.
   Language getLanguage() const { return Lang; }
 
+  const RecordMap<NamespaceRecord> &getNamespaces() const { return Namespaces; }
   const RecordMap<GlobalFunctionRecord> &getGlobalFunctions() const {
     return GlobalFunctions;
   }
@@ -1596,6 +1628,7 @@ class APISet {
   const Language Lang;
 
   llvm::DenseMap<StringRef, APIRecord *> USRBasedLookupTable;
+  RecordMap<NamespaceRecord> Namespaces;
   RecordMap<GlobalFunctionRecord> GlobalFunctions;
   RecordMap<GlobalFunctionTemplateRecord> GlobalFunctionTemplates;
   RecordMap<GlobalFunctionTemplateSpecializationRecord>

diff  --git a/clang/include/clang/ExtractAPI/DeclarationFragments.h b/clang/include/clang/ExtractAPI/DeclarationFragments.h
index b72ce667c4d55a..3c05f43e829c60 100644
--- a/clang/include/clang/ExtractAPI/DeclarationFragments.h
+++ b/clang/include/clang/ExtractAPI/DeclarationFragments.h
@@ -257,17 +257,19 @@ class DeclarationFragmentsBuilder {
   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 *);
 

diff  --git a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
index 38239b09dd484f..17c2ab6fd20501 100644
--- a/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
+++ b/clang/include/clang/ExtractAPI/ExtractAPIVisitor.h
@@ -74,6 +74,10 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
 
   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 @@ class ExtractAPIVisitorBase : public RecursiveASTVisitor<Derived> {
     }
     return Bases;
   }
+
+  APIRecord *determineParentRecord(const DeclContext *Context) {
+    SmallString<128> ParentUSR;
+    if (Context->getDeclKind() == Decl::TranslationUnit)
+      return nullptr;
+
+    index::generateUSRForDecl(dyn_cast<Decl>(Context), ParentUSR);
+
+    APIRecord *Parent = API.findRecordForUSR(ParentUSR);
+    return Parent;
+  }
 };
 
 template <typename T>
@@ -447,6 +462,44 @@ bool ExtractAPIVisitorBase<Derived>::WalkUpFromFunctionTemplateDecl(
   return true;
 }
 
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::WalkUpFromNamespaceDecl(
+    const NamespaceDecl *Decl) {
+  getDerivedExtractAPIVisitor().VisitNamespaceDecl(Decl);
+  return true;
+}
+
+template <typename Derived>
+bool ExtractAPIVisitorBase<Derived>::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 <typename Derived>
 bool ExtractAPIVisitorBase<Derived>::VisitRecordDecl(const RecordDecl *Decl) {
   if (!getDerivedExtractAPIVisitor().shouldDeclBeIncluded(Decl))
@@ -512,7 +565,9 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
     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 @@ bool ExtractAPIVisitorBase<Derived>::VisitCXXRecordDecl(
         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 @@ bool ExtractAPIVisitorBase<Derived>::VisitClassTemplateSpecializationDecl(
   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 @@ bool ExtractAPIVisitorBase<Derived>::
   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
index 269333245c8363..a188400a74d558 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SerializerBase.h
@@ -23,6 +23,8 @@ namespace extractapi {
 template <typename Derived> class APISetVisitor {
 public:
   void traverseAPISet() {
+    getDerived()->traverseNamespaces();
+
     getDerived()->traverseGlobalVariableRecords();
 
     getDerived()->traverseGlobalFunctionRecords();
@@ -76,6 +78,11 @@ template <typename Derived> class APISetVisitor {
     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);
@@ -220,6 +227,8 @@ template <typename Derived> class APISetVisitor {
       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
index 05cc33a0cb8c08..a9b714dc484659 100644
--- a/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
+++ b/clang/include/clang/ExtractAPI/Serialization/SymbolGraphSerializer.h
@@ -159,6 +159,8 @@ class SymbolGraphSerializer : public APISetVisitor<SymbolGraphSerializer> {
   llvm::StringSet<> visitedCategories;
 
 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
index 9e4cfe6dbab308..2973a31345c9b2 100644
--- a/clang/lib/ExtractAPI/API.cpp
+++ b/clang/lib/ExtractAPI/API.cpp
@@ -44,6 +44,22 @@ RecordTy *addTopLevelRecord(DenseMap<StringRef, APIRecord *> &USRLookupTable,
 
 } // 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 @@ CXXFieldTemplateRecord *APISet::addCXXFieldTemplate(
 }
 
 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
index 36b0d1f07f1ba6..f1fff6bf513df6 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -423,6 +423,16 @@ DeclarationFragments DeclarationFragmentsBuilder::getFragmentsForType(
   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
index e1b5f652764f11..229bf04c77fae4 100644
--- a/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
+++ b/clang/lib/ExtractAPI/Serialization/SymbolGraphSerializer.cpp
@@ -357,6 +357,10 @@ Object serializeSymbolKind(APIRecord::RecordKind RK, Language Lang) {
   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";
@@ -834,6 +838,17 @@ void SymbolGraphSerializer::serializeRelationship(RelationshipKind Kind,
   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);
@@ -887,6 +902,9 @@ void SymbolGraphSerializer::visitCXXClassRecord(const CXXClassRecord &Record) {
   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(
@@ -898,6 +916,9 @@ void SymbolGraphSerializer::visitClassTemplateRecord(
   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(
@@ -910,6 +931,9 @@ void SymbolGraphSerializer::visitClassTemplateSpecializationRecord(
 
   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(
@@ -922,6 +946,9 @@ void SymbolGraphSerializer::visitClassTemplatePartialSpecializationRecord(
 
   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
index 00000000000000..2346093db7d5fd
--- /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 at 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 at Foo@S at Bar",
+      "target": "c:@N at 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 at 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 at Foo@S at 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
index 00000000000000..2e562fa1e2e2e0
--- /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 at 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 at Foo@N at Bar",
+      "target": "c:@N at 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 at 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 at Foo@N at 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"
+      ]
+    }
+  ]
+}


        


More information about the cfe-commits mailing list