Author: Erick Velez Date: 2026-05-29T14:46:29-07:00 New Revision: 02cf363d17487378d0a2a89e1e95738d0945ab4c
URL: https://github.com/llvm/llvm-project/commit/02cf363d17487378d0a2a89e1e95738d0945ab4c DIFF: https://github.com/llvm/llvm-project/commit/02cf363d17487378d0a2a89e1e95738d0945ab4c.diff LOG: [clang-doc] Add specialization info to record references (#191252) Class specializations listed in a namespace's page had the problem of being indistinguishable from each other since they couldn't display their template arguments. They would just be displayed as a series of the base template's name. Now, we can display those arguments in HTML for a better experience. In JSONGenerator, we query the set of Infos to find the Reference's corresponding RecordInfo. That requires keeping a pointer to the map in JSONGenerator. Fixes #181771 Added: Modified: clang-tools-extra/clang-doc/JSONGenerator.cpp clang-tools-extra/clang-doc/assets/namespace-template.mustache clang-tools-extra/test/clang-doc/json/class-specialization.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp index ee1acc46c6f48..0ab71b4a5b5c9 100644 --- a/clang-tools-extra/clang-doc/JSONGenerator.cpp +++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp @@ -66,6 +66,8 @@ class JSONGenerator : public Generator { void serializeMDReference(const Reference &Ref, Object &ReferenceObj, StringRef BasePath); + void serializeClassSpecializations(SymbolID ClassUSR, Object &ReferenceObj); + // Convenience lambdas to pass to serializeArray. auto serializeInfoLambda() { return [this](const auto &Info, Object &Object) { @@ -79,6 +81,7 @@ class JSONGenerator : public Generator { } llvm::DenseMap<const Info *, SmallVector<Context, 4>> ContextsMap; + llvm::StringMap<doc::Info *> *Infos = nullptr; const ClangDocContext *CDCtx; bool Markdown; @@ -458,6 +461,42 @@ void JSONGenerator::serializeCommonAttributes(const Info &I, generateContext(I, Obj); } +static auto SerializeTemplateParam = [](const TemplateParamInfo &Param, + Object &JsonObj) { + JsonObj["Param"] = Param.Contents; +}; + +static void serializeTemplateSpecialization(TemplateInfo Template, + Object &TemplateObj) { + json::Value TemplateSpecializationVal = Object(); + auto &TemplateSpecializationObj = *TemplateSpecializationVal.getAsObject(); + TemplateSpecializationObj["SpecializationOf"] = + toHex(toStringRef(Template.Specialization->SpecializationOf)); + if (!Template.Specialization->Params.empty()) { + bool VerticalDisplay = + Template.Specialization->Params.size() > getMaxParamWrapLimit(); + serializeArray(Template.Specialization->Params, TemplateSpecializationObj, + "Parameters", SerializeTemplateParam, "SpecParamEnd", + [VerticalDisplay](Object &JsonObj) { + JsonObj["VerticalDisplay"] = VerticalDisplay; + }); + } + TemplateObj["Specialization"] = TemplateSpecializationVal; +} + +void JSONGenerator::serializeClassSpecializations(SymbolID ClassUSR, + Object &ReferenceObj) { + if (!Infos) + return; + auto *Class = Infos->lookup(toHex(ClassUSR)); + if (!Class || Class->IT != InfoType::IT_record) + return; + RecordInfo *ClassInfo = static_cast<RecordInfo *>(Class); + if (!ClassInfo->Template || !ClassInfo->Template->Specialization) + return; + serializeTemplateSpecialization(ClassInfo->Template.value(), ReferenceObj); +} + void JSONGenerator::serializeReference(const Reference &Ref, Object &ReferenceObj) { insertNonEmpty("Path", Ref.Path, ReferenceObj); @@ -501,9 +540,14 @@ void JSONGenerator::serializeCommonChildren( } if (!Children.Records.empty()) { - ReferenceFunc SerializeReferenceFunc = MDReferenceLambda - ? MDReferenceLambda.value() + ReferenceFunc BaseFunc = MDReferenceLambda ? MDReferenceLambda.value() : serializeReferenceLambda(); + + ReferenceFunc SerializeReferenceFunc = + [this, BaseFunc](const Reference &Ref, Object &Object) { + BaseFunc(Ref, Object); + serializeClassSpecializations(Ref.USR, Object); + }; serializeArray(Children.Records, Obj, "Records", SerializeReferenceFunc); Obj["HasRecords"] = true; } @@ -539,27 +583,9 @@ void JSONGenerator::serializeInfo(const ConstraintInfo &I, Object &Obj) { void JSONGenerator::serializeInfo(const TemplateInfo &Template, Object &Obj) { json::Value TemplateVal = Object(); auto &TemplateObj = *TemplateVal.getAsObject(); - auto SerializeTemplateParam = [](const TemplateParamInfo &Param, - Object &JsonObj) { - JsonObj["Param"] = Param.Contents; - }; - - if (Template.Specialization) { - json::Value TemplateSpecializationVal = Object(); - auto &TemplateSpecializationObj = *TemplateSpecializationVal.getAsObject(); - TemplateSpecializationObj["SpecializationOf"] = - toHex(toStringRef(Template.Specialization->SpecializationOf)); - if (!Template.Specialization->Params.empty()) { - bool VerticalDisplay = - Template.Specialization->Params.size() > getMaxParamWrapLimit(); - serializeArray(Template.Specialization->Params, TemplateSpecializationObj, - "Parameters", SerializeTemplateParam, "SpecParamEnd", - [VerticalDisplay](Object &JsonObj) { - JsonObj["VerticalDisplay"] = VerticalDisplay; - }); - } - TemplateObj["Specialization"] = TemplateSpecializationVal; - } + + if (Template.Specialization) + serializeTemplateSpecialization(Template, TemplateObj); if (!Template.Params.empty()) { bool VerticalDisplay = Template.Params.size() > getMaxParamWrapLimit(); @@ -972,6 +998,7 @@ Error JSONGenerator::generateDocumentation(StringRef RootDir, const ClangDocContext &CDCtx, std::string DirName) { this->CDCtx = &CDCtx; + this->Infos = &Infos; StringSet<> CreatedDirs; StringMap<std::vector<doc::Info *>> FileToInfos; for (const auto &Group : Infos) { diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/namespace-template.mustache index 3956cf0cd7ffd..13ad96254e9d1 100644 --- a/clang-tools-extra/clang-doc/assets/namespace-template.mustache +++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache @@ -164,7 +164,7 @@ {{#Records}} <li id="{{USR}}" style="max-height: 40px;"> <a href="{{DocumentationFileName}}.html"> - <pre><code class="language-cpp code-clang-doc">class {{Name}}</code></pre> + <pre><code class="language-cpp code-clang-doc">class {{Name}}{{#Specialization}}<{{#Parameters}}{{Param}}{{^SpecParamEnd}}, {{/SpecParamEnd}}{{/Parameters}}>{{/Specialization}}</code></pre> </a> </li> {{/Records}} diff --git a/clang-tools-extra/test/clang-doc/json/class-specialization.cpp b/clang-tools-extra/test/clang-doc/json/class-specialization.cpp index e7bb423f9bce7..c2b09ab53b0e9 100644 --- a/clang-tools-extra/test/clang-doc/json/class-specialization.cpp +++ b/clang-tools-extra/test/clang-doc/json/class-specialization.cpp @@ -1,7 +1,9 @@ // RUN: rm -rf %t && mkdir -p %t -// RUN: clang-doc --pretty-json --output=%t --format=json --executor=standalone %s +// RUN: clang-doc --pretty-json --output=%t --format=html --executor=standalone %s // RUN: FileCheck %s < %t/json/GlobalNamespace/_ZTV7MyClass.json --check-prefix=BASE // RUN: FileCheck %s < %t/json/GlobalNamespace/_ZTV7MyClassIiE.json --check-prefix=SPECIALIZATION +// RUN: FileCheck %s < %t/json/GlobalNamespace/index.json --check-prefix=JSON-NAMESPACE +// RUN: FileCheck %s < %t/html/GlobalNamespace/index.html --check-prefix=HTML-NAMESPACE template<typename T> struct MyClass {}; @@ -43,3 +45,47 @@ template<> struct MyClass<int> {}; // SPECIALIZATION-NEXT: "VerticalDisplay": false // SPECIALIZATION-NEXT: } // SPECIALIZATION-NEXT: }, + +// JSON-NAMESPACE: "Records": [ +// JSON-NAMESPACE-NEXT: { +// JSON-NAMESPACE-NEXT: "DocumentationFileName": "_ZTV7MyClass", +// JSON-NAMESPACE-NEXT: "Name": "MyClass", +// JSON-NAMESPACE-NEXT: "Path": "GlobalNamespace", +// JSON-NAMESPACE-NEXT: "QualName": "MyClass", +// JSON-NAMESPACE-NEXT: "USR": "{{([0-9A-F]{40})}}" +// JSON-NAMESPACE-NEXT: }, +// JSON-NAMESPACE-NEXT: { +// JSON-NAMESPACE-NEXT: "DocumentationFileName": "_ZTV7MyClassIiE", +// JSON-NAMESPACE-NEXT: "End": true, +// JSON-NAMESPACE-NEXT: "Name": "MyClass", +// JSON-NAMESPACE-NEXT: "Path": "GlobalNamespace", +// JSON-NAMESPACE-NEXT: "QualName": "MyClass", +// JSON-NAMESPACE-NEXT: "Specialization": { +// JSON-NAMESPACE-NEXT: "Parameters": [ +// JSON-NAMESPACE-NEXT: { +// JSON-NAMESPACE-NEXT: "Param": "int", +// JSON-NAMESPACE-NEXT: "SpecParamEnd": true +// JSON-NAMESPACE-NEXT: } +// JSON-NAMESPACE-NEXT: ], +// JSON-NAMESPACE-NEXT: "SpecializationOf": "{{([0-9A-F]{40})}}", +// JSON-NAMESPACE-NEXT: "VerticalDisplay": false +// JSON-NAMESPACE-NEXT: }, +// JSON-NAMESPACE-NEXT: "USR": "{{([0-9A-F]{40})}}" +// JSON-NAMESPACE-NEXT: } +// JSON-NAMESPACE-NEXT: ] + +// HTML-NAMESPACE: <section id="Records" class="section-container"> +// HTML-NAMESPACE-NEXT: <h2>Records</h2> +// HTML-NAMESPACE-NEXT: <ul class="class-container"> +// HTML-NAMESPACE-NEXT: <li id="{{([0-9A-F]{40})}}" style="max-height: 40px;"> +// HTML-NAMESPACE-NEXT: <a href="_ZTV7MyClass.html"> +// HTML-NAMESPACE-NEXT: <pre><code class="language-cpp code-clang-doc">class MyClass</code></pre> +// HTML-NAMESPACE-NEXT: </a> +// HTML-NAMESPACE-NEXT: </li> +// HTML-NAMESPACE-NEXT: <li id="{{([0-9A-F]{40})}}" style="max-height: 40px;"> +// HTML-NAMESPACE-NEXT: <a href="_ZTV7MyClassIiE.html"> +// HTML-NAMESPACE-NEXT: <pre><code class="language-cpp code-clang-doc">class MyClass<int></code></pre> +// HTML-NAMESPACE-NEXT: </a> +// HTML-NAMESPACE-NEXT: </li> +// HTML-NAMESPACE-NEXT: </ul> +// HTML-NAMESPACE-NEXT: </section> _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
