[llvm-branch-commits] [clang-tools-extra] [clang-doc] add namespaces to JSON generator (PR #143209)
Erick Velez via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Jun 8 23:15:24 PDT 2025
https://github.com/evelez7 updated https://github.com/llvm/llvm-project/pull/143209
>From 21a472c17ce5f4fae19a32cbd58f5625a12a6509 Mon Sep 17 00:00:00 2001
From: Erick Velez <erickvelez7 at 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 37773cf3f5651..789951dcc6440 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -443,6 +443,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) {
@@ -485,6 +520,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..ca9fd0a6aa7d1
--- /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: "Name": "NestedNamespace",
+// CHECK-NEXT: "Path": "",
+// CHECK-NEXT: "QualName": "NestedNamespace",
+// CHECK-NEXT: "USR": "{{[0-9A-F]*}}"
+// CHECK-NEXT: }
+// CHECK-NEXT: ],
+// CHECK-NEXT: "Records": [
+// CHECK-NEXT: {
+// CHECK-NEXT: "Name": "MyClass",
+// CHECK-NEXT: "Path": "GlobalNamespace",
+// 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 24516cbc34a5a..bc468698cf150 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": [
+ {
+ "Name": "ChildNamespace",
+ "Path": "path/to/A/Namespace",
+ "QualName": "path::to::A::Namespace::ChildNamespace",
+ "USR": "0000000000000000000000000000000000000000"
+ }
+ ],
+ "Path": "path/to/A",
+ "Records": [
+ {
+ "Name": "ChildStruct",
+ "Path": "path/to/A/Namespace",
+ "QualName": "path::to::A::Namespace::ChildStruct",
+ "USR": "0000000000000000000000000000000000000000"
+ }
+ ],
+ "USR": "0000000000000000000000000000000000000000"
+})raw";
+ EXPECT_EQ(Expected, Actual.str());
+}
} // namespace doc
} // namespace clang
More information about the llvm-branch-commits
mailing list