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

Conversation

ilovepi
Copy link
Contributor

@ilovepi ilovepi commented May 1, 2025

Split from #133161. This patch provides the implementation of a number
of extractValue overloads used with the different types of Info.

The new helper functions extract the relevant information from the
different *Infos and inserts them into the correct fields of the JSON
values that will be used with the specific Mustache templates, which
will land separately.

Co-authored-by: Peter Chou [email protected]

Split from #133161. This patch provides the implementation of a number
of extractValue overloads used with the different types of Info.

The new helper functions extract the relevant information from the
different *Infos and inserts them into the correct fields of the JSON
values that will be used with the specific Mustache templates, which
will land separately.

Co-authored-by: Peter Chou <[email protected]>
@llvmbot
Copy link
Member

llvmbot commented May 1, 2025

@llvm/pr-subscribers-clang-tools-extra

Author: Paul Kirth (ilovepi)

Changes

Split from #133161. This patch provides the implementation of a number
of extractValue overloads used with the different types of Info.

The new helper functions extract the relevant information from the
different *Infos and inserts them into the correct fields of the JSON
values that will be used with the specific Mustache templates, which
will land separately.

Co-authored-by: Peter Chou <[email protected]>


Full diff: https://github.com/llvm/llvm-project/pull/138063.diff

1 Files Affected:

  • (modified) clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp (+253-2)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 593d5d1221f44..29392f8bf17b9 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -141,21 +141,272 @@ Error MustacheHTMLGenerator::generateDocs(
   return Error::success();
 }
 
+static json::Value
+extractValue(const Location &L,
+             std::optional<StringRef> RepositoryUrl = std::nullopt) {
+  Object Obj = Object();
+  // Should there be Start/End line numbers?
+  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 ArrDesc = Array();
+  json::Array &ADescRef = *ArrDesc.getAsArray();
+  for (const CommentInfo &Child : Descriptions)
+    ADescRef.emplace_back(extractValue(Child));
+  EnumValObj.insert({"EnumValueComments", ArrDesc});
+}
+
+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();
+  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()});
+    ParamArr.getAsArray()->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 Arr = Array();
+  json::Array &ARef = *Arr.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);
+    ARef.emplace_back(EnumValue);
+  }
+  Obj.insert({"EnumValues", Arr});
+
+  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 ArrNamespace = Array();
+  for (const Reference &Child : S.Namespaces)
+    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
+
+  if (!ArrNamespace.getAsArray()->empty())
+    Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
+
+  json::Value ArrRecord = Array();
+  for (const Reference &Child : S.Records)
+    ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
+
+  if (!ArrRecord.getAsArray()->empty())
+    Obj.insert({"Record", Object{{"Links", ArrRecord}}});
+
+  json::Value ArrFunction = Array();
+  json::Value PublicFunction = Array();
+  json::Value ProtectedFunction = Array();
+  json::Value PrivateFunction = Array();
+
+  for (const FunctionInfo &Child : S.Functions) {
+    json::Value F = extractValue(Child, ParentInfoDir, CDCtx);
+    AccessSpecifier Access = Child.Access;
+    if (Access == AccessSpecifier::AS_public)
+      PublicFunction.getAsArray()->emplace_back(F);
+    else if (Access == AccessSpecifier::AS_protected)
+      ProtectedFunction.getAsArray()->emplace_back(F);
+    else
+      ArrFunction.getAsArray()->emplace_back(F);
+  }
+
+  if (!ArrFunction.getAsArray()->empty())
+    Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
+
+  if (!PublicFunction.getAsArray()->empty())
+    Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
+
+  if (!ProtectedFunction.getAsArray()->empty())
+    Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
+
+  json::Value ArrEnum = Array();
+  auto &ArrEnumRef = *ArrEnum.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();
+  auto &ArrTypedefsRef = *ArrTypedefs.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();
+
+  StringRef 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;
 }
 
 static void setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
-                               Info *I) {}
-
+                               Info *I) {
+}
 Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
                                                 const ClangDocContext &CDCtx) {
   switch (I->IT) {

Copy link

github-actions bot commented May 1, 2025

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff HEAD~1 HEAD --extensions cpp -- clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
View the diff from clang-format here.
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 29392f8bf..b6031e73e 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -405,8 +405,7 @@ static json::Value extractValue(const RecordInfo &I,
 }
 
 static void setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
-                               Info *I) {
-}
+                               Info *I) {}
 Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
                                                 const ClangDocContext &CDCtx) {
   switch (I->IT) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants