Skip to content

[clang-doc] Extract Info into JSON values #138063

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: users/ilovepi/clang-doc-mustache-templete-helper
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 256 additions & 0 deletions clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,15 +162,271 @@ Error MustacheHTMLGenerator::generateDocs(
return Error::success();
}

static json::Value
extractValue(const Location &L,
std::optional<StringRef> RepositoryUrl = std::nullopt) {
Object Obj = Object();
// TODO: Consider using both Start/End line numbers to improve location report
Obj.insert({"LineNumber", L.StartLineNumber});
Obj.insert({"Filename", L.Filename});

if (!L.IsFileInRootDir || !RepositoryUrl)
return Obj;
SmallString<128> FileURL(*RepositoryUrl);
sys::path::append(FileURL, sys::path::Style::posix, L.Filename);
FileURL += "#" + std::to_string(L.StartLineNumber);
Obj.insert({"FileURL", FileURL});

return Obj;
}

static json::Value extractValue(const Reference &I,
StringRef CurrentDirectory) {
SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
sys::path::append(Path, I.getFileBaseName() + ".html");
sys::path::native(Path, sys::path::Style::posix);
Object Obj = Object();
Obj.insert({"Link", Path});
Obj.insert({"Name", I.Name});
Obj.insert({"QualName", I.QualName});
Obj.insert({"ID", toHex(toStringRef(I.USR))});
return Obj;
}

static json::Value extractValue(const TypedefInfo &I) {
// Not Supported
return nullptr;
}

static json::Value extractValue(const CommentInfo &I) {
assert((I.Kind == "BlockCommandComment" || I.Kind == "FullComment" ||
I.Kind == "ParagraphComment" || I.Kind == "TextComment") &&
"Unknown Comment type in CommentInfo.");

Object Obj = Object();
json::Value Child = Object();

// TextComment has no children, so return it.
if (I.Kind == "TextComment") {
Obj.insert({"TextComment", I.Text});
return Obj;
}

// BlockCommandComment needs to generate a Command key.
if (I.Kind == "BlockCommandComment")
Child.getAsObject()->insert({"Command", I.Name});

// Use the same handling for everything else.
// Only valid for:
// - BlockCommandComment
// - FullComment
// - ParagraphComment
json::Value ChildArr = Array();
auto &CARef = *ChildArr.getAsArray();
CARef.reserve(I.Children.size());
for (const auto &C : I.Children)
CARef.emplace_back(extractValue(*C));
Child.getAsObject()->insert({"Children", ChildArr});
Obj.insert({I.Kind, Child});

return Obj;
}

static void maybeInsertLocation(std::optional<Location> Loc,
const ClangDocContext &CDCtx, Object &Obj) {
if (!Loc)
return;
Location L = *Loc;
Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)});
}

static void extractDescriptionFromInfo(ArrayRef<CommentInfo> Descriptions,
json::Object &EnumValObj) {
if (Descriptions.empty())
return;
json::Value DescArr = Array();
json::Array &DescARef = *DescArr.getAsArray();
for (const CommentInfo &Child : Descriptions)
DescARef.emplace_back(extractValue(Child));
EnumValObj.insert({"EnumValueComments", DescArr});
}

static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
const ClangDocContext &CDCtx) {
Object Obj = Object();
Obj.insert({"Name", I.Name});
Obj.insert({"ID", toHex(toStringRef(I.USR))});
Obj.insert({"Access", getAccessSpelling(I.Access).str()});
Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});

json::Value ParamArr = Array();
json::Array &ParamARef = *ParamArr.getAsArray();
for (const auto Val : enumerate(I.Params)) {
json::Value V = Object();
auto &VRef = *V.getAsObject();
VRef.insert({"Name", Val.value().Name});
VRef.insert({"Type", Val.value().Type.Name});
VRef.insert({"End", Val.index() + 1 == I.Params.size()});
ParamARef.emplace_back(V);
}
Obj.insert({"Params", ParamArr});

maybeInsertLocation(I.DefLoc, CDCtx, Obj);
return Obj;
}

static json::Value extractValue(const EnumInfo &I,
const ClangDocContext &CDCtx) {
Object Obj = Object();
std::string EnumType = I.Scoped ? "enum class " : "enum ";
EnumType += I.Name;
bool HasComment = std::any_of(
I.Members.begin(), I.Members.end(),
[](const EnumValueInfo &M) { return !M.Description.empty(); });
Obj.insert({"EnumName", EnumType});
Obj.insert({"HasComment", HasComment});
Obj.insert({"ID", toHex(toStringRef(I.USR))});
json::Value EnumArr = Array();
json::Array &EnumARef = *EnumArr.getAsArray();
for (const EnumValueInfo &M : I.Members) {
json::Value EnumValue = Object();
auto &EnumValObj = *EnumValue.getAsObject();
EnumValObj.insert({"Name", M.Name});
if (!M.ValueExpr.empty())
EnumValObj.insert({"ValueExpr", M.ValueExpr});
else
EnumValObj.insert({"Value", M.Value});

extractDescriptionFromInfo(M.Description, EnumValObj);
EnumARef.emplace_back(EnumValue);
}
Obj.insert({"EnumValues", EnumArr});

extractDescriptionFromInfo(I.Description, Obj);
maybeInsertLocation(I.DefLoc, CDCtx, Obj);

return Obj;
}

static void extractScopeChildren(const ScopeChildren &S, Object &Obj,
StringRef ParentInfoDir,
const ClangDocContext &CDCtx) {
json::Value NamespaceArr = Array();
json::Array &NamespaceARef = *NamespaceArr.getAsArray();
for (const Reference &Child : S.Namespaces)
NamespaceARef.emplace_back(extractValue(Child, ParentInfoDir));

if (!NamespaceARef.empty())
Obj.insert({"Namespace", Object{{"Links", NamespaceArr}}});

json::Value RecordArr = Array();
json::Array &RecordARef = *RecordArr.getAsArray();
for (const Reference &Child : S.Records)
RecordARef.emplace_back(extractValue(Child, ParentInfoDir));

if (!RecordARef.empty())
Obj.insert({"Record", Object{{"Links", RecordArr}}});

json::Value FunctionArr = Array();
json::Array &FunctionARef = *FunctionArr.getAsArray();

json::Value PublicFunction = Array();
Copy link
Contributor

@evelez7 evelez7 May 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
json::Value PublicFunction = Array();
json::Value PublicFunctionArr = Array();

nit

json::Array &PublicFunctionARef = *PublicFunction.getAsArray();

json::Value ProtectedFunction = Array();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
json::Value ProtectedFunction = Array();
json::Value ProtectedFunctionArr = Array();

nit

json::Array &ProtectedFunctionARef = *ProtectedFunction.getAsArray();

for (const FunctionInfo &Child : S.Functions) {
json::Value F = extractValue(Child, ParentInfoDir, CDCtx);
AccessSpecifier Access = Child.Access;
if (Access == AccessSpecifier::AS_public)
PublicFunctionARef.emplace_back(F);
else if (Access == AccessSpecifier::AS_protected)
ProtectedFunctionARef.emplace_back(F);
else
FunctionARef.emplace_back(F);
}

if (!FunctionARef.empty())
Obj.insert({"Function", Object{{"Obj", FunctionArr}}});

if (!PublicFunctionARef.empty())
Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});

if (!ProtectedFunctionARef.empty())
Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});

json::Value ArrEnum = Array();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
json::Value ArrEnum = Array();
json::Value EnumArr = Array();

auto &ArrEnumRef = *ArrEnum.getAsArray();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto &ArrEnumRef = *ArrEnum.getAsArray();
auto &EnumARef = *EnumArr.getAsArray();

for (const EnumInfo &Child : S.Enums)
ArrEnumRef.emplace_back(extractValue(Child, CDCtx));

if (!ArrEnumRef.empty())
Obj.insert({"Enums", Object{{"Obj", ArrEnum}}});

json::Value ArrTypedefs = Array();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
json::Value ArrTypedefs = Array();
json::Value TypedefsArr = Array();

auto &ArrTypedefsRef = *ArrTypedefs.getAsArray();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto &ArrTypedefsRef = *ArrTypedefs.getAsArray();
auto &TypedefsARef = *TypedefsArr.getAsArray();

for (const TypedefInfo &Child : S.Typedefs)
ArrTypedefsRef.emplace_back(extractValue(Child));

if (!ArrTypedefsRef.empty())
Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs}}});
}

static json::Value extractValue(const NamespaceInfo &I,
const ClangDocContext &CDCtx) {
Object NamespaceValue = Object();
std::string InfoTitle = I.Name.empty() ? "Global Namespace"
: (Twine("namespace ") + I.Name).str();

SmallString<64> BasePath = I.getRelativeFilePath("");
NamespaceValue.insert({"NamespaceTitle", InfoTitle});
NamespaceValue.insert({"NamespacePath", BasePath});

extractDescriptionFromInfo(I.Description, NamespaceValue);
extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
return NamespaceValue;
}

static json::Value extractValue(const RecordInfo &I,
const ClangDocContext &CDCtx) {
Object RecordValue = Object();
extractDescriptionFromInfo(I.Description, RecordValue);
RecordValue.insert({"Name", I.Name});
RecordValue.insert({"FullName", I.FullName});
RecordValue.insert({"RecordType", getTagType(I.TagType)});

maybeInsertLocation(I.DefLoc, CDCtx, RecordValue);

StringRef BasePath = I.getRelativeFilePath("");
extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
json::Value PublicMembers = Array();
json::Array &PubMemberRef = *PublicMembers.getAsArray();
json::Value ProtectedMembers = Array();
json::Array &ProtMemberRef = *ProtectedMembers.getAsArray();
json::Value PrivateMembers = Array();
json::Array &PrivMemberRef = *PrivateMembers.getAsArray();
for (const MemberTypeInfo &Member : I.Members) {
json::Value MemberValue = Object();
auto &MVRef = *MemberValue.getAsObject();
MVRef.insert({"Name", Member.Name});
MVRef.insert({"Type", Member.Type.Name});
extractDescriptionFromInfo(Member.Description, MVRef);

if (Member.Access == AccessSpecifier::AS_public)
PubMemberRef.emplace_back(MemberValue);
else if (Member.Access == AccessSpecifier::AS_protected)
ProtMemberRef.emplace_back(MemberValue);
else if (Member.Access == AccessSpecifier::AS_private)
ProtMemberRef.emplace_back(MemberValue);
}
if (!PubMemberRef.empty())
RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
if (!ProtMemberRef.empty())
RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
if (!PrivMemberRef.empty())
RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});

return RecordValue;
}

Expand Down
Loading