Skip to content

[clang-doc] refactor JSONGenerator array usage #145595

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

Merged
merged 1 commit into from
Jun 26, 2025

Conversation

evelez7
Copy link
Member

@evelez7 evelez7 commented Jun 24, 2025

Improve code reuse by calling serializeArray in more generic cases
instead of creating and reserving arrays on their own.

Copy link
Member Author

evelez7 commented Jun 24, 2025

@evelez7 evelez7 requested review from ilovepi and petrhosek June 24, 2025 20:58
@evelez7 evelez7 force-pushed the users/evelez7/clang-doc-json-refactor branch from b5af9be to df4ddc4 Compare June 24, 2025 23:55
Copy link
Contributor

@ilovepi ilovepi left a comment

Choose a reason for hiding this comment

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

LGTM. Glad to see so much code getting deleted! Overall great cleanups. Left a couple small comments inline.

Improve code reuse by calling serializeArray in more generic cases
instead of creating and reserving arrays on their own.
@evelez7 evelez7 force-pushed the users/evelez7/clang-doc-json-refactor branch from df4ddc4 to 19569ab Compare June 26, 2025 04:50
@evelez7 evelez7 marked this pull request as ready for review June 26, 2025 04:50
@llvmbot
Copy link
Member

llvmbot commented Jun 26, 2025

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

Author: Erick Velez (evelez7)

Changes

Improve code reuse by calling serializeArray in more generic cases
instead of creating and reserving arrays on their own.


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

1 Files Affected:

  • (modified) clang-tools-extra/clang-doc/JSONGenerator.cpp (+99-161)
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index 91f5ba4080619..1f6167f7f9b8d 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -22,22 +22,31 @@ class JSONGenerator : public Generator {
 
 const char *JSONGenerator::Format = "json";
 
-static void serializeInfo(const TypedefInfo &I, json::Object &Obj,
-                          std::optional<StringRef> RepositoryUrl);
-static void serializeInfo(const EnumInfo &I, json::Object &Obj,
-                          std::optional<StringRef> RepositoryUrl);
 static void serializeInfo(const ConstraintInfo &I, Object &Obj);
+static void serializeInfo(const RecordInfo &I, Object &Obj,
+                          const std::optional<StringRef> &RepositoryUrl);
+
+static void serializeReference(const Reference &Ref, Object &ReferenceObj);
+
+template <typename Container, typename SerializationFunc>
+static void serializeArray(const Container &Records, Object &Obj,
+                           const std::string &Key,
+                           SerializationFunc SerializeInfo);
 
 // Convenience lambda to pass to serializeArray.
 // If a serializeInfo needs a RepositoryUrl, create a local lambda that captures
 // the optional.
-static auto SerializeInfoLambda = [](const ConstraintInfo &Info,
-                                     Object &Object) {
+static auto SerializeInfoLambda = [](const auto &Info, Object &Object) {
   serializeInfo(Info, Object);
 };
+static auto SerializeReferenceLambda = [](const Reference &Ref,
+                                          Object &Object) {
+  serializeReference(Ref, Object);
+};
 
-static json::Object serializeLocation(const Location &Loc,
-                                      std::optional<StringRef> RepositoryUrl) {
+static json::Object
+serializeLocation(const Location &Loc,
+                  const std::optional<StringRef> &RepositoryUrl) {
   Object LocationObj = Object();
   LocationObj["LineNumber"] = Loc.StartLineNumber;
   LocationObj["Filename"] = Loc.Filename;
@@ -159,8 +168,9 @@ static json::Value serializeComment(const CommentInfo &I) {
   llvm_unreachable("Unknown comment kind encountered.");
 }
 
-static void serializeCommonAttributes(const Info &I, json::Object &Obj,
-                                      std::optional<StringRef> RepositoryUrl) {
+static void
+serializeCommonAttributes(const Info &I, json::Object &Obj,
+                          const std::optional<StringRef> &RepositoryUrl) {
   Obj["Name"] = I.Name;
   Obj["USR"] = toHex(toStringRef(I.USR));
 
@@ -198,67 +208,28 @@ static void serializeReference(const Reference &Ref, Object &ReferenceObj) {
   ReferenceObj["USR"] = toHex(toStringRef(Ref.USR));
 }
 
-static void serializeReference(const SmallVector<Reference, 4> &References,
-                               Object &Obj, std::string Key) {
-  json::Value ReferencesArray = Array();
-  json::Array &ReferencesArrayRef = *ReferencesArray.getAsArray();
-  ReferencesArrayRef.reserve(References.size());
-  for (const auto &Reference : References) {
-    json::Value ReferenceVal = Object();
-    auto &ReferenceObj = *ReferenceVal.getAsObject();
-    serializeReference(Reference, ReferenceObj);
-    ReferencesArrayRef.push_back(ReferenceVal);
-  }
-  Obj[Key] = ReferencesArray;
-}
-
 // Although namespaces and records both have ScopeChildren, they serialize them
 // differently. Only enums, records, and typedefs are handled here.
-static void serializeCommonChildren(const ScopeChildren &Children,
-                                    json::Object &Obj,
-                                    std::optional<StringRef> RepositoryUrl) {
-  if (!Children.Enums.empty()) {
-    json::Value EnumsArray = Array();
-    auto &EnumsArrayRef = *EnumsArray.getAsArray();
-    EnumsArrayRef.reserve(Children.Enums.size());
-    for (const auto &Enum : Children.Enums) {
-      json::Value EnumVal = Object();
-      auto &EnumObj = *EnumVal.getAsObject();
-      serializeInfo(Enum, EnumObj, RepositoryUrl);
-      EnumsArrayRef.push_back(EnumVal);
-    }
-    Obj["Enums"] = EnumsArray;
-  }
+static void
+serializeCommonChildren(const ScopeChildren &Children, json::Object &Obj,
+                        const std::optional<StringRef> &RepositoryUrl) {
+  static auto SerializeInfo = [&RepositoryUrl](const auto &Info,
+                                               Object &Object) {
+    serializeInfo(Info, Object, RepositoryUrl);
+  };
 
-  if (!Children.Typedefs.empty()) {
-    json::Value TypedefsArray = Array();
-    auto &TypedefsArrayRef = *TypedefsArray.getAsArray();
-    TypedefsArrayRef.reserve(Children.Typedefs.size());
-    for (const auto &Typedef : Children.Typedefs) {
-      json::Value TypedefVal = Object();
-      auto &TypedefObj = *TypedefVal.getAsObject();
-      serializeInfo(Typedef, TypedefObj, RepositoryUrl);
-      TypedefsArrayRef.push_back(TypedefVal);
-    }
-    Obj["Typedefs"] = TypedefsArray;
-  }
+  if (!Children.Enums.empty())
+    serializeArray(Children.Enums, Obj, "Enums", SerializeInfo);
 
-  if (!Children.Records.empty()) {
-    json::Value RecordsArray = Array();
-    auto &RecordsArrayRef = *RecordsArray.getAsArray();
-    RecordsArrayRef.reserve(Children.Records.size());
-    for (const auto &Record : Children.Records) {
-      json::Value RecordVal = Object();
-      auto &RecordObj = *RecordVal.getAsObject();
-      serializeReference(Record, RecordObj);
-      RecordsArrayRef.push_back(RecordVal);
-    }
-    Obj["Records"] = RecordsArray;
-  }
+  if (!Children.Typedefs.empty())
+    serializeArray(Children.Typedefs, Obj, "Typedefs", SerializeInfo);
+
+  if (!Children.Records.empty())
+    serializeArray(Children.Records, Obj, "Records", SerializeReferenceLambda);
 }
 
-template <typename T, typename SerializationFunc>
-static void serializeArray(const std::vector<T> &Records, Object &Obj,
+template <typename Container, typename SerializationFunc>
+static void serializeArray(const Container &Records, Object &Obj,
                            const std::string &Key,
                            SerializationFunc SerializeInfo) {
   json::Value RecordsArray = Array();
@@ -278,6 +249,16 @@ static void serializeInfo(const ConstraintInfo &I, Object &Obj) {
   Obj["Expression"] = I.ConstraintExpr;
 }
 
+static void serializeInfo(const ArrayRef<TemplateParamInfo> &Params,
+                          Object &Obj) {
+  json::Value ParamsArray = Array();
+  auto &ParamsArrayRef = *ParamsArray.getAsArray();
+  ParamsArrayRef.reserve(Params.size());
+  for (const auto &Param : Params)
+    ParamsArrayRef.push_back(Param.Contents);
+  Obj["Parameters"] = ParamsArray;
+}
+
 static void serializeInfo(const TemplateInfo &Template, Object &Obj) {
   json::Value TemplateVal = Object();
   auto &TemplateObj = *TemplateVal.getAsObject();
@@ -287,25 +268,13 @@ static void serializeInfo(const TemplateInfo &Template, Object &Obj) {
     auto &TemplateSpecializationObj = *TemplateSpecializationVal.getAsObject();
     TemplateSpecializationObj["SpecializationOf"] =
         toHex(toStringRef(Template.Specialization->SpecializationOf));
-    if (!Template.Specialization->Params.empty()) {
-      json::Value ParamsArray = Array();
-      auto &ParamsArrayRef = *ParamsArray.getAsArray();
-      ParamsArrayRef.reserve(Template.Specialization->Params.size());
-      for (const auto &Param : Template.Specialization->Params)
-        ParamsArrayRef.push_back(Param.Contents);
-      TemplateSpecializationObj["Parameters"] = ParamsArray;
-    }
+    if (!Template.Specialization->Params.empty())
+      serializeInfo(Template.Specialization->Params, TemplateSpecializationObj);
     TemplateObj["Specialization"] = TemplateSpecializationVal;
   }
 
-  if (!Template.Params.empty()) {
-    json::Value ParamsArray = Array();
-    auto &ParamsArrayRef = *ParamsArray.getAsArray();
-    ParamsArrayRef.reserve(Template.Params.size());
-    for (const auto &Param : Template.Params)
-      ParamsArrayRef.push_back(Param.Contents);
-    TemplateObj["Parameters"] = ParamsArray;
-  }
+  if (!Template.Params.empty())
+    serializeInfo(Template.Params, TemplateObj);
 
   if (!Template.Constraints.empty())
     serializeArray(Template.Constraints, TemplateObj, "Constraints",
@@ -315,7 +284,7 @@ static void serializeInfo(const TemplateInfo &Template, Object &Obj) {
 }
 
 static void serializeInfo(const ConceptInfo &I, Object &Obj,
-                          std::optional<StringRef> RepositoryUrl) {
+                          const std::optional<StringRef> &RepositoryUrl) {
   serializeCommonAttributes(I, Obj, RepositoryUrl);
   Obj["IsType"] = I.IsType;
   Obj["ConstraintExpression"] = I.ConstraintExpression;
@@ -330,8 +299,13 @@ static void serializeInfo(const TypeInfo &I, Object &Obj) {
   Obj["IsBuiltIn"] = I.IsBuiltIn;
 }
 
+static void serializeInfo(const FieldTypeInfo &I, Object &Obj) {
+  Obj["Name"] = I.Name;
+  Obj["Type"] = I.Type.Name;
+}
+
 static void serializeInfo(const FunctionInfo &F, json::Object &Obj,
-                          std::optional<StringRef> RepositoryURL) {
+                          const std::optional<StringRef> &RepositoryURL) {
   serializeCommonAttributes(F, Obj, RepositoryURL);
   Obj["IsStatic"] = F.IsStatic;
 
@@ -339,26 +313,23 @@ static void serializeInfo(const FunctionInfo &F, json::Object &Obj,
   serializeInfo(F.ReturnType, ReturnTypeObj);
   Obj["ReturnType"] = std::move(ReturnTypeObj);
 
-  if (!F.Params.empty()) {
-    json::Value ParamsArray = json::Array();
-    auto &ParamsArrayRef = *ParamsArray.getAsArray();
-    ParamsArrayRef.reserve(F.Params.size());
-    for (const auto &Param : F.Params) {
-      json::Value ParamVal = Object();
-      auto &ParamObj = *ParamVal.getAsObject();
-      ParamObj["Name"] = Param.Name;
-      ParamObj["Type"] = Param.Type.Name;
-      ParamsArrayRef.push_back(ParamVal);
-    }
-    Obj["Params"] = ParamsArray;
-  }
+  if (!F.Params.empty())
+    serializeArray(F.Params, Obj, "Params", SerializeInfoLambda);
 
   if (F.Template)
     serializeInfo(F.Template.value(), Obj);
 }
 
+static void serializeInfo(const EnumValueInfo &I, Object &Obj) {
+  Obj["Name"] = I.Name;
+  if (!I.ValueExpr.empty())
+    Obj["ValueExpr"] = I.ValueExpr;
+  else
+    Obj["Value"] = I.Value;
+}
+
 static void serializeInfo(const EnumInfo &I, json::Object &Obj,
-                          std::optional<StringRef> RepositoryUrl) {
+                          const std::optional<StringRef> &RepositoryUrl) {
   serializeCommonAttributes(I, Obj, RepositoryUrl);
   Obj["Scoped"] = I.Scoped;
 
@@ -371,26 +342,12 @@ static void serializeInfo(const EnumInfo &I, json::Object &Obj,
     Obj["BaseType"] = BaseTypeVal;
   }
 
-  if (!I.Members.empty()) {
-    json::Value MembersArray = Array();
-    auto &MembersArrayRef = *MembersArray.getAsArray();
-    MembersArrayRef.reserve(I.Members.size());
-    for (const auto &Member : I.Members) {
-      json::Value MemberVal = Object();
-      auto &MemberObj = *MemberVal.getAsObject();
-      MemberObj["Name"] = Member.Name;
-      if (!Member.ValueExpr.empty())
-        MemberObj["ValueExpr"] = Member.ValueExpr;
-      else
-        MemberObj["Value"] = Member.Value;
-      MembersArrayRef.push_back(MemberVal);
-    }
-    Obj["Members"] = MembersArray;
-  }
+  if (!I.Members.empty())
+    serializeArray(I.Members, Obj, "Members", SerializeInfoLambda);
 }
 
 static void serializeInfo(const TypedefInfo &I, json::Object &Obj,
-                          std::optional<StringRef> RepositoryUrl) {
+                          const std::optional<StringRef> &RepositoryUrl) {
   serializeCommonAttributes(I, Obj, RepositoryUrl);
   Obj["TypeDeclaration"] = I.TypeDeclaration;
   Obj["IsUsing"] = I.IsUsing;
@@ -400,8 +357,16 @@ static void serializeInfo(const TypedefInfo &I, json::Object &Obj,
   Obj["Underlying"] = TypeVal;
 }
 
+static void serializeInfo(const BaseRecordInfo &I, Object &Obj,
+                          const std::optional<StringRef> &RepositoryUrl) {
+  serializeInfo(static_cast<const RecordInfo &>(I), Obj, RepositoryUrl);
+  Obj["IsVirtual"] = I.IsVirtual;
+  Obj["Access"] = getAccessSpelling(I.Access);
+  Obj["IsParent"] = I.IsParent;
+}
+
 static void serializeInfo(const RecordInfo &I, json::Object &Obj,
-                          std::optional<StringRef> RepositoryUrl) {
+                          const std::optional<StringRef> &RepositoryUrl) {
   serializeCommonAttributes(I, Obj, RepositoryUrl);
   Obj["FullName"] = I.FullName;
   Obj["TagType"] = getTagType(I.TagType);
@@ -454,27 +419,19 @@ static void serializeInfo(const RecordInfo &I, json::Object &Obj,
       Obj["ProtectedMembers"] = ProtectedMembersArray;
   }
 
-  if (!I.Bases.empty()) {
-    json::Value BasesArray = Array();
-    json::Array &BasesArrayRef = *BasesArray.getAsArray();
-    BasesArrayRef.reserve(I.Bases.size());
-    for (const auto &BaseInfo : I.Bases) {
-      json::Value BaseInfoVal = Object();
-      auto &BaseInfoObj = *BaseInfoVal.getAsObject();
-      serializeInfo(BaseInfo, BaseInfoObj, RepositoryUrl);
-      BaseInfoObj["IsVirtual"] = BaseInfo.IsVirtual;
-      BaseInfoObj["Access"] = getAccessSpelling(BaseInfo.Access);
-      BaseInfoObj["IsParent"] = BaseInfo.IsParent;
-      BasesArrayRef.push_back(BaseInfoVal);
-    }
-    Obj["Bases"] = BasesArray;
-  }
+  if (!I.Bases.empty())
+    serializeArray(
+        I.Bases, Obj, "Bases",
+        [&RepositoryUrl](const BaseRecordInfo &Base, Object &BaseObj) {
+          serializeInfo(Base, BaseObj, RepositoryUrl);
+        });
 
   if (!I.Parents.empty())
-    serializeReference(I.Parents, Obj, "Parents");
+    serializeArray(I.Parents, Obj, "Parents", SerializeReferenceLambda);
 
   if (!I.VirtualParents.empty())
-    serializeReference(I.VirtualParents, Obj, "VirtualParents");
+    serializeArray(I.VirtualParents, Obj, "VirtualParents",
+                   SerializeReferenceLambda);
 
   if (I.Template)
     serializeInfo(I.Template.value(), Obj);
@@ -483,7 +440,7 @@ static void serializeInfo(const RecordInfo &I, json::Object &Obj,
 }
 
 static void serializeInfo(const VarInfo &I, json::Object &Obj,
-                          std::optional<StringRef> RepositoryUrl) {
+                          const std::optional<StringRef> &RepositoryUrl) {
   serializeCommonAttributes(I, Obj, RepositoryUrl);
   Obj["IsStatic"] = I.IsStatic;
   auto TypeObj = Object();
@@ -492,45 +449,26 @@ static void serializeInfo(const VarInfo &I, json::Object &Obj,
 }
 
 static void serializeInfo(const NamespaceInfo &I, json::Object &Obj,
-                          std::optional<StringRef> RepositoryUrl) {
+                          const 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.Namespaces.empty())
+    serializeArray(I.Children.Namespaces, Obj, "Namespaces",
+                   SerializeReferenceLambda);
 
-  auto SerializeInfo = [RepositoryUrl](const auto &Info, Object &Object) {
+  static auto SerializeInfo = [&RepositoryUrl](const auto &Info,
+                                               Object &Object) {
     serializeInfo(Info, Object, RepositoryUrl);
   };
 
-  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;
-  }
+  if (!I.Children.Functions.empty())
+    serializeArray(I.Children.Functions, Obj, "Functions", SerializeInfo);
 
   if (!I.Children.Concepts.empty())
     serializeArray(I.Children.Concepts, Obj, "Concepts", SerializeInfo);
 
-  if (!I.Children.Variables.empty()) {
+  if (!I.Children.Variables.empty())
     serializeArray(I.Children.Variables, Obj, "Variables", SerializeInfo);
-  }
 
   serializeCommonChildren(I.Children, Obj, RepositoryUrl);
 }

Copy link
Member

@petrhosek petrhosek left a comment

Choose a reason for hiding this comment

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

Looks great! One idea for future improvement would be to wrap std::optional<StringRef> RepositoryUrl in a object (e.g. SerializationContext) so in the future if we need to pass around more information beside the URL, we don't need to modify every one of these functions and lambdas.

Copy link
Member Author

evelez7 commented Jun 26, 2025

Merge activity

  • Jun 26, 5:09 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Jun 26, 5:11 PM UTC: @evelez7 merged this pull request with Graphite.

@evelez7 evelez7 merged commit 066a14d into main Jun 26, 2025
10 checks passed
@evelez7 evelez7 deleted the users/evelez7/clang-doc-json-refactor branch June 26, 2025 17:11
anthonyhatran pushed a commit to anthonyhatran/llvm-project that referenced this pull request Jun 26, 2025
Improve code reuse by calling serializeArray in more generic cases
instead of creating and reserving arrays on their own.
rlavaee pushed a commit to rlavaee/llvm-project that referenced this pull request Jul 1, 2025
Improve code reuse by calling serializeArray in more generic cases
instead of creating and reserving arrays on their own.
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.

4 participants