https://github.com/evelez7 updated 
https://github.com/llvm/llvm-project/pull/143209

>From 979bfbf20654c7814a607201827ecc9f327e8b74 Mon Sep 17 00:00:00 2001
From: Erick Velez <erickvel...@gmail.com>
Date: Tue, 3 Jun 2025 11:39:48 -0700
Subject: [PATCH] [clang-doc] add namespaces to JSON generator

---
 clang-tools-extra/clang-doc/JSONGenerator.cpp |  36 ++++++
 clang-tools-extra/clang-doc/Serialize.cpp     |   1 +
 .../clang-doc/json/function-specifiers.cpp    |  25 ++++
 .../test/clang-doc/json/namespace.cpp         | 108 ++++++++++++++++++
 .../unittests/clang-doc/JSONGeneratorTest.cpp |  73 ++++++++++++
 5 files changed, 243 insertions(+)
 create mode 100644 
clang-tools-extra/test/clang-doc/json/function-specifiers.cpp
 create mode 100644 clang-tools-extra/test/clang-doc/json/namespace.cpp

diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp 
b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index ef9c147f1a5e2..93c73acb4cb64 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -446,6 +446,41 @@ static void serializeInfo(const RecordInfo &I, 
json::Object &Obj,
   serializeCommonChildren(I.Children, Obj, RepositoryUrl);
 }
 
+static void serializeInfo(const NamespaceInfo &I, json::Object &Obj,
+                          std::optional<StringRef> RepositoryUrl) {
+  serializeCommonAttributes(I, Obj, RepositoryUrl);
+  SmallString<64> BasePath = I.getRelativeFilePath("");
+  Obj["NamespacePath"] = BasePath;
+
+  if (!I.Children.Namespaces.empty()) {
+    json::Value NamespacesArray = Array();
+    auto &NamespacesArrayRef = *NamespacesArray.getAsArray();
+    NamespacesArrayRef.reserve(I.Children.Namespaces.size());
+    for (auto &Namespace : I.Children.Namespaces) {
+      json::Value NamespaceVal = Object();
+      auto &NamespaceObj = *NamespaceVal.getAsObject();
+      serializeReference(Namespace, NamespaceObj, BasePath);
+      NamespacesArrayRef.push_back(NamespaceVal);
+    }
+    Obj["Namespaces"] = NamespacesArray;
+  }
+
+  if (!I.Children.Functions.empty()) {
+    json::Value FunctionsArray = Array();
+    auto &FunctionsArrayRef = *FunctionsArray.getAsArray();
+    FunctionsArrayRef.reserve(I.Children.Functions.size());
+    for (const auto &Function : I.Children.Functions) {
+      json::Value FunctionVal = Object();
+      auto &FunctionObj = *FunctionVal.getAsObject();
+      serializeInfo(Function, FunctionObj, RepositoryUrl);
+      FunctionsArrayRef.push_back(FunctionVal);
+    }
+    Obj["Functions"] = FunctionsArray;
+  }
+
+  serializeCommonChildren(I.Children, Obj, RepositoryUrl);
+}
+
 Error JSONGenerator::generateDocs(
     StringRef RootDir, llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
     const ClangDocContext &CDCtx) {
@@ -488,6 +523,7 @@ Error JSONGenerator::generateDocForInfo(Info *I, 
raw_ostream &OS,
 
   switch (I->IT) {
   case InfoType::IT_namespace:
+    serializeInfo(*static_cast<NamespaceInfo *>(I), Obj, CDCtx.RepositoryUrl);
     break;
   case InfoType::IT_record:
     serializeInfo(*static_cast<RecordInfo *>(I), Obj, CDCtx.RepositoryUrl);
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp 
b/clang-tools-extra/clang-doc/Serialize.cpp
index 462001b3f3027..3cda38115ff7f 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -742,6 +742,7 @@ static void populateFunctionInfo(FunctionInfo &I, const 
FunctionDecl *D,
   I.ReturnType = getTypeInfoForType(D->getReturnType(), LO);
   I.Prototype = getFunctionPrototype(D);
   parseParameters(I, D);
+  I.IsStatic = D->isStatic();
 
   populateTemplateParameters(I.Template, D);
 
diff --git a/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp 
b/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp
new file mode 100644
index 0000000000000..7005fb7b3e66e
--- /dev/null
+++ b/clang-tools-extra/test/clang-doc/json/function-specifiers.cpp
@@ -0,0 +1,25 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: clang-doc --output=%t --format=json --executor=standalone %s
+// RUN: FileCheck %s < %t/GlobalNamespace/index.json
+
+static void myFunction() {}
+
+void noExceptFunction() noexcept {}
+
+inline void inlineFunction() {}
+
+extern void externFunction() {}
+
+constexpr void constexprFunction() {}
+
+// CHECK:          "Functions": [
+// CHECK-NEXT:       {
+// CHECK:              "IsStatic": true,
+// COM:                FIXME: Emit ExceptionSpecificationType
+// CHECK-NOT:          "ExceptionSpecifcation" : "noexcept",
+// COM:                FIXME: Emit inline
+// CHECK-NOT:          "IsInline": true,
+// COM:                FIXME: Emit extern
+// CHECK-NOT:          "IsExtern": true,
+// COM:                FIXME: Emit constexpr
+// CHECK-NOT:          "IsConstexpr": true,
diff --git a/clang-tools-extra/test/clang-doc/json/namespace.cpp 
b/clang-tools-extra/test/clang-doc/json/namespace.cpp
new file mode 100644
index 0000000000000..885dbd841ac2e
--- /dev/null
+++ b/clang-tools-extra/test/clang-doc/json/namespace.cpp
@@ -0,0 +1,108 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: clang-doc --output=%t --format=json --executor=standalone %s
+// RUN: FileCheck %s < %t/GlobalNamespace/index.json
+
+class MyClass {};
+
+void myFunction(int Param);
+
+namespace NestedNamespace {
+} // namespace NestedNamespace
+
+// FIXME: Global variables are not mapped or serialized.
+static int Global;
+
+enum Color {
+  RED,
+  GREEN,
+  BLUE = 5
+};
+
+typedef int MyTypedef;
+
+// CHECK:       { 
+// CHECK-NEXT:    "Enums": [
+// CHECK-NEXT:      {
+// CHECK-NEXT:        "Location": {
+// CHECK-NEXT:          "Filename": "{{.*}}namespace.cpp",
+// CHECK-NEXT:          "LineNumber": 15
+// CHECK-NEXT:        },
+// CHECK-NEXT:        "Members": [
+// CHECK-NEXT:          {
+// CHECK-NEXT:            "Name": "RED",
+// CHECK-NEXT:            "Value": "0"
+// CHECK-NEXT:          },
+// CHECK-NEXT:          {
+// CHECK-NEXT:            "Name": "GREEN",
+// CHECK-NEXT:            "Value": "1"
+// CHECK-NEXT:          },
+// CHECK-NEXT:          {
+// CHECK-NEXT:            "Name": "BLUE",
+// CHECK-NEXT:            "ValueExpr": "5"
+// CHECK-NEXT:          }
+// CHECK-NEXT:        ],
+// CHECK-NEXT:        "Name": "Color",
+// CHECK-NEXT:        "Scoped": false,
+// CHECK-NEXT:        "USR": "{{[0-9A-F]*}}"
+// CHECK-NEXT:      }
+// CHECK-NEXT:    ],
+// CHECK-NEXT:   "Functions": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "IsStatic": false,
+// CHECK-NEXT:       "Name": "myFunction",
+// CHECK-NEXT:       "Params": [
+// CHECK-NEXT:         {
+// CHECK-NEXT:           "Name": "Param",
+// CHECK-NEXT:           "Type": "int"
+// CHECK-NEXT:         }
+// CHECK-NEXT:       ],
+// CHECK-NEXT:       "ReturnType": {
+// CHECK-NEXT:         "IsBuiltIn": false,
+// CHECK-NEXT:         "IsTemplate": false,
+// CHECK-NEXT:         "Name": "void",
+// CHECK-NEXT:         "QualName": "void",
+// CHECK-NEXT:         "USR": "0000000000000000000000000000000000000000"
+// CHECK-NEXT:       },
+// CHECK-NEXT:       "USR": "{{[0-9A-F]*}}"
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ],
+// CHECK-NEXT:   "Name": "",
+// CHECK-NEXT:   "NamespacePath": "GlobalNamespace",
+// CHECK-NEXT:   "Namespaces": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "Link": "../NestedNamespace/index.json",
+// CHECK-NEXT:       "Name": "NestedNamespace",
+// CHECK-NEXT:       "QualName": "NestedNamespace",
+// CHECK-NEXT:       "USR": "{{[0-9A-F]*}}"
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ],
+// CHECK-NEXT:   "Records": [
+// CHECK-NEXT:     {
+// CHECK-NEXT:       "Link": "MyClass.json",
+// CHECK-NEXT:       "Name": "MyClass",
+// CHECK-NEXT:       "QualName": "MyClass",
+// CHECK-NEXT:       "USR": "{{[0-9A-F]*}}"
+// CHECK-NEXT:     }
+// CHECK-NEXT:   ],
+// CHECK-NEXT:   "Typedefs": [
+// CHECK-NEXT:    {
+// CHECK-NEXT:      "IsUsing": false,
+// CHECK-NEXT:      "Location": {
+// CHECK-NEXT:        "Filename": "{{.*}}namespace.cpp",
+// CHECK-NEXT:        "LineNumber": 21
+// CHECK-NEXT:      },
+// CHECK-NEXT:      "Name": "MyTypedef",
+// CHECK-NEXT:      "TypeDeclaration": "",
+// CHECK-NEXT:      "USR": "{{[0-9A-F]*}}",
+// CHECK-NEXT:      "Underlying": {
+// CHECK-NEXT:        "IsBuiltIn": false,
+// CHECK-NEXT:        "IsTemplate": false,
+// CHECK-NEXT:        "Name": "int",
+// CHECK-NEXT:        "QualName": "int",
+// CHECK-NEXT:        "USR": "0000000000000000000000000000000000000000"
+// CHECK-NEXT:        }
+// CHECK-NEXT:      }
+// CHECK-NEXT:    ],
+// CHECK-NEXT:    "USR": "0000000000000000000000000000000000000000"
+// CHECK-NOT:     "Variables": [
+// CHECK-NEXT:  }
diff --git a/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp 
b/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp
index 8e56c6fe3bae8..b60fe30993cd1 100644
--- a/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp
@@ -171,5 +171,78 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
 })raw";
   EXPECT_EQ(Expected, Actual.str());
 }
+
+TEST(JSONGeneratorTest, emitNamespaceJSON) {
+  NamespaceInfo I;
+  I.Name = "Namespace";
+  I.Path = "path/to/A";
+  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+  I.Children.Namespaces.emplace_back(
+      EmptySID, "ChildNamespace", InfoType::IT_namespace,
+      "path::to::A::Namespace::ChildNamespace", "path/to/A/Namespace");
+  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
+                                  "path::to::A::Namespace::ChildStruct",
+                                  "path/to/A/Namespace");
+  I.Children.Functions.emplace_back();
+  I.Children.Functions.back().Name = "OneFunction";
+  I.Children.Functions.back().Access = AccessSpecifier::AS_none;
+  I.Children.Enums.emplace_back();
+  I.Children.Enums.back().Name = "OneEnum";
+
+  auto G = getJSONGenerator();
+  assert(G);
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+  auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext());
+  assert(!Err);
+  std::string Expected = R"raw({
+  "Enums": [
+    {
+      "Name": "OneEnum",
+      "Scoped": false,
+      "USR": "0000000000000000000000000000000000000000"
+    }
+  ],
+  "Functions": [
+    {
+      "IsStatic": false,
+      "Name": "OneFunction",
+      "ReturnType": {
+        "IsBuiltIn": false,
+        "IsTemplate": false,
+        "Name": "",
+        "QualName": "",
+        "USR": "0000000000000000000000000000000000000000"
+      },
+      "USR": "0000000000000000000000000000000000000000"
+    }
+  ],
+  "Name": "Namespace",
+  "Namespace": [
+    "A"
+  ],
+  "NamespacePath": "path/to/A/Namespace",
+  "Namespaces": [
+    {
+      "Link": "ChildNamespace/index.json",
+      "Name": "ChildNamespace",
+      "QualName": "path::to::A::Namespace::ChildNamespace",
+      "USR": "0000000000000000000000000000000000000000"
+    }
+  ],
+  "Path": "path/to/A",
+  "Records": [
+    {
+      "Link": "ChildStruct.json",
+      "Name": "ChildStruct",
+      "QualName": "path::to::A::Namespace::ChildStruct",
+      "USR": "0000000000000000000000000000000000000000"
+    }
+  ],
+  "USR": "0000000000000000000000000000000000000000"
+})raw";
+  EXPECT_EQ(Expected, Actual.str());
+}
 } // namespace doc
 } // namespace clang

_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to