[clang-doc] add namespaces to JSON generator (#143209)

Emit namespaces to JSON. Also adds tests for namespaces and non-member constructs.
This commit is contained in:
Erick Velez
2025-06-10 10:35:53 -07:00
committed by GitHub
parent 84710b49f2
commit 47918e7cb7
5 changed files with 239 additions and 0 deletions

View File

@@ -440,6 +440,39 @@ 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);
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);
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) {
@@ -482,6 +515,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);

View File

@@ -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);

View File

@@ -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,

View File

@@ -0,0 +1,107 @@
// 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: "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: }

View File

@@ -171,5 +171,77 @@ 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"
],
"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