Author: Erick Velez Date: 2026-01-09T20:06:57-08:00 New Revision: 2723e9aad051ff214c955d29ceae1e58974e41a2
URL: https://github.com/llvm/llvm-project/commit/2723e9aad051ff214c955d29ceae1e58974e41a2 DIFF: https://github.com/llvm/llvm-project/commit/2723e9aad051ff214c955d29ceae1e58974e41a2.diff LOG: [clang-doc] Serialize repository URL in Mustache templates (#174914) Location tags didn't handle possible `FileURL` fields when a repository URL was specified in a clang-doc invocation. This also cleans up all location occurences to use `<p>` instead of `<div>` and allows linking to the repository URL. Added: Modified: clang-tools-extra/clang-doc/JSONGenerator.cpp clang-tools-extra/clang-doc/assets/alias-template.mustache clang-tools-extra/clang-doc/assets/class-template.mustache clang-tools-extra/clang-doc/assets/enum-template.mustache clang-tools-extra/clang-doc/assets/function-template.mustache clang-tools-extra/clang-doc/assets/namespace-template.mustache clang-tools-extra/test/clang-doc/enum.cpp clang-tools-extra/test/clang-doc/mustache-index.cpp clang-tools-extra/test/clang-doc/templates.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp index ad76c40d49db8..ec234db19ed1a 100644 --- a/clang-tools-extra/clang-doc/JSONGenerator.cpp +++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp @@ -8,6 +8,9 @@ using namespace llvm::json; namespace clang { namespace doc { +// FIXME: These static methods should be refactored into methods for +// `JSONGenerator`. It's cumbersome to pass around important properties from +// ClangDocContext using these static methods. class JSONGenerator : public Generator { public: static const char *Format; @@ -25,7 +28,8 @@ const char *JSONGenerator::Format = "json"; static void serializeInfo(const ConstraintInfo &I, Object &Obj); static void serializeInfo(const RecordInfo &I, Object &Obj, - const std::optional<StringRef> &RepositoryUrl); + const std::optional<StringRef> &RepositoryUrl, + const std::optional<StringRef> &RepositoryLinePrefix); static void serializeReference(const Reference &Ref, Object &ReferenceObj); @@ -75,7 +79,8 @@ static std::string infoTypeToString(InfoType IT) { static json::Object serializeLocation(const Location &Loc, - const std::optional<StringRef> RepositoryUrl) { + const std::optional<StringRef> RepositoryUrl, + const std::optional<StringRef> RepositoryLinePrefix) { Object LocationObj = Object(); LocationObj["LineNumber"] = Loc.StartLineNumber; LocationObj["Filename"] = Loc.Filename; @@ -84,7 +89,14 @@ serializeLocation(const Location &Loc, return LocationObj; SmallString<128> FileURL(*RepositoryUrl); sys::path::append(FileURL, sys::path::Style::posix, Loc.Filename); - FileURL += "#" + std::to_string(Loc.StartLineNumber); + + std::string LinePrefix; + if (!RepositoryLinePrefix) + LinePrefix = "#L"; + else + LinePrefix = *RepositoryLinePrefix; + + FileURL += LinePrefix + std::to_string(Loc.StartLineNumber); LocationObj["FileURL"] = FileURL; return LocationObj; } @@ -338,7 +350,8 @@ static void generateContext(const Info &I, Object &Obj) { static void serializeCommonAttributes(const Info &I, json::Object &Obj, - const std::optional<StringRef> RepositoryUrl) { + const std::optional<StringRef> RepositoryUrl, + const std::optional<StringRef> RepositoryLinePrefix) { insertNonEmpty("Name", I.Name, Obj); Obj["USR"] = toHex(toStringRef(I.USR)); Obj["InfoType"] = infoTypeToString(I.IT); @@ -378,8 +391,8 @@ serializeCommonAttributes(const Info &I, json::Object &Obj, if (I.IT != InfoType::IT_namespace) { const auto *Symbol = static_cast<const SymbolInfo *>(&I); if (Symbol->DefLoc) - Obj["Location"] = - serializeLocation(Symbol->DefLoc.value(), RepositoryUrl); + Obj["Location"] = serializeLocation(Symbol->DefLoc.value(), RepositoryUrl, + RepositoryLinePrefix); } if (!I.Contexts.empty()) @@ -405,11 +418,12 @@ static void serializeReference(const Reference &Ref, Object &ReferenceObj) { // diff erently. Only enums, records, and typedefs are handled here. 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); - }; + const std::optional<StringRef> RepositoryUrl, + const std::optional<StringRef> RepositoryLinePrefix) { + static auto SerializeInfo = + [RepositoryUrl, RepositoryLinePrefix](const auto &Info, Object &Object) { + serializeInfo(Info, Object, RepositoryUrl, RepositoryLinePrefix); + }; if (!Children.Enums.empty()) { serializeArray(Children.Enums, Obj, "Enums", SerializeInfo); @@ -492,8 +506,9 @@ static void serializeInfo(const TemplateInfo &Template, Object &Obj) { } static void serializeInfo(const ConceptInfo &I, Object &Obj, - const std::optional<StringRef> &RepositoryUrl) { - serializeCommonAttributes(I, Obj, RepositoryUrl); + const std::optional<StringRef> &RepositoryUrl, + const std::optional<StringRef> &RepositoryLine) { + serializeCommonAttributes(I, Obj, RepositoryUrl, RepositoryLine); Obj["IsType"] = I.IsType; Obj["ConstraintExpression"] = I.ConstraintExpression; serializeInfo(I.Template, Obj); @@ -517,8 +532,9 @@ static void serializeInfo(const FieldTypeInfo &I, Object &Obj) { } static void serializeInfo(const FunctionInfo &F, json::Object &Obj, - const std::optional<StringRef> RepositoryURL) { - serializeCommonAttributes(F, Obj, RepositoryURL); + const std::optional<StringRef> RepositoryURL, + const std::optional<StringRef> RepositoryLine) { + serializeCommonAttributes(F, Obj, RepositoryURL, RepositoryLine); Obj["IsStatic"] = F.IsStatic; auto ReturnTypeObj = Object(); @@ -541,8 +557,9 @@ static void serializeInfo(const EnumValueInfo &I, Object &Obj) { } static void serializeInfo(const EnumInfo &I, json::Object &Obj, - const std::optional<StringRef> &RepositoryUrl) { - serializeCommonAttributes(I, Obj, RepositoryUrl); + const std::optional<StringRef> &RepositoryUrl, + const std::optional<StringRef> &RepositoryLine) { + serializeCommonAttributes(I, Obj, RepositoryUrl, RepositoryLine); Obj["Scoped"] = I.Scoped; if (I.BaseType) { @@ -558,9 +575,11 @@ static void serializeInfo(const EnumInfo &I, json::Object &Obj, serializeArray(I.Members, Obj, "Members", SerializeInfoLambda); } -static void serializeInfo(const TypedefInfo &I, json::Object &Obj, - const std::optional<StringRef> &RepositoryUrl) { - serializeCommonAttributes(I, Obj, RepositoryUrl); +static void +serializeInfo(const TypedefInfo &I, json::Object &Obj, + const std::optional<StringRef> &RepositoryUrl, + const std::optional<StringRef> &RepositoryLinePrefix) { + serializeCommonAttributes(I, Obj, RepositoryUrl, RepositoryLinePrefix); Obj["TypeDeclaration"] = I.TypeDeclaration; Obj["IsUsing"] = I.IsUsing; json::Value TypeVal = Object(); @@ -571,9 +590,12 @@ static void serializeInfo(const TypedefInfo &I, json::Object &Obj, serializeInfo(I.Template.value(), Obj); } -static void serializeInfo(const BaseRecordInfo &I, Object &Obj, - const std::optional<StringRef> &RepositoryUrl) { - serializeInfo(static_cast<const RecordInfo &>(I), Obj, RepositoryUrl); +static void +serializeInfo(const BaseRecordInfo &I, Object &Obj, + const std::optional<StringRef> &RepositoryUrl, + const std::optional<StringRef> &RepositoryLinePrefix) { + serializeInfo(static_cast<const RecordInfo &>(I), Obj, RepositoryUrl, + RepositoryLinePrefix); Obj["IsVirtual"] = I.IsVirtual; Obj["Access"] = getAccessSpelling(I.Access); Obj["IsParent"] = I.IsParent; @@ -593,7 +615,7 @@ static void serializeInfo(const FriendInfo &I, Object &Obj) { serializeInfo(I.ReturnType.value(), ReturnTypeObj); Obj["ReturnType"] = std::move(ReturnTypeObj); } - serializeCommonAttributes(I, Obj, std::nullopt); + serializeCommonAttributes(I, Obj, std::nullopt, std::nullopt); } static void insertArray(Object &Obj, json::Value &Array, StringRef Key) { @@ -601,9 +623,11 @@ static void insertArray(Object &Obj, json::Value &Array, StringRef Key) { Obj["Has" + Key.str()] = true; } -static void serializeInfo(const RecordInfo &I, json::Object &Obj, - const std::optional<StringRef> &RepositoryUrl) { - serializeCommonAttributes(I, Obj, RepositoryUrl); +static void +serializeInfo(const RecordInfo &I, json::Object &Obj, + const std::optional<StringRef> &RepositoryUrl, + const std::optional<StringRef> &RepositoryLinePrefix) { + serializeCommonAttributes(I, Obj, RepositoryUrl, RepositoryLinePrefix); Obj["TagType"] = getTagType(I.TagType); Obj["IsTypedef"] = I.IsTypeDef; Obj["MangledName"] = I.MangledName; @@ -617,7 +641,7 @@ static void serializeInfo(const RecordInfo &I, json::Object &Obj, for (const auto &Function : I.Children.Functions) { json::Value FunctionVal = Object(); auto &FunctionObj = *FunctionVal.getAsObject(); - serializeInfo(Function, FunctionObj, RepositoryUrl); + serializeInfo(Function, FunctionObj, RepositoryUrl, RepositoryLinePrefix); AccessSpecifier Access = Function.Access; if (Access == AccessSpecifier::AS_public) PubFunctionsArrayRef.push_back(FunctionVal); @@ -663,11 +687,12 @@ static void serializeInfo(const RecordInfo &I, json::Object &Obj, } if (!I.Bases.empty()) - serializeArray( - I.Bases, Obj, "Bases", - [&RepositoryUrl](const BaseRecordInfo &Base, Object &BaseObj) { - serializeInfo(Base, BaseObj, RepositoryUrl); - }); + serializeArray(I.Bases, Obj, "Bases", + [&RepositoryUrl, &RepositoryLinePrefix]( + const BaseRecordInfo &Base, Object &BaseObj) { + serializeInfo(Base, BaseObj, RepositoryUrl, + RepositoryLinePrefix); + }); if (!I.Parents.empty()) { serializeArray(I.Parents, Obj, "Parents", SerializeReferenceLambda); @@ -688,12 +713,14 @@ static void serializeInfo(const RecordInfo &I, json::Object &Obj, Obj["HasFriends"] = true; } - serializeCommonChildren(I.Children, Obj, RepositoryUrl); + serializeCommonChildren(I.Children, Obj, RepositoryUrl, RepositoryLinePrefix); } -static void serializeInfo(const VarInfo &I, json::Object &Obj, - const std::optional<StringRef> RepositoryUrl) { - serializeCommonAttributes(I, Obj, RepositoryUrl); +static void +serializeInfo(const VarInfo &I, json::Object &Obj, + const std::optional<StringRef> RepositoryUrl, + const std::optional<StringRef> RepositoryUrlLinePrefix) { + serializeCommonAttributes(I, Obj, RepositoryUrl, RepositoryUrlLinePrefix); Obj["IsStatic"] = I.IsStatic; auto TypeObj = Object(); serializeInfo(I.Type, TypeObj); @@ -701,8 +728,9 @@ static void serializeInfo(const VarInfo &I, json::Object &Obj, } static void serializeInfo(const NamespaceInfo &I, json::Object &Obj, - const std::optional<StringRef> RepositoryUrl) { - serializeCommonAttributes(I, Obj, RepositoryUrl); + const std::optional<StringRef> RepositoryUrl, + const std::optional<StringRef> RepositoryLinePrefix) { + serializeCommonAttributes(I, Obj, RepositoryUrl, RepositoryLinePrefix); if (I.USR == GlobalNamespaceID) Obj["Name"] = "Global Namespace"; @@ -712,10 +740,10 @@ static void serializeInfo(const NamespaceInfo &I, json::Object &Obj, Obj["HasNamespaces"] = true; } - static auto SerializeInfo = [RepositoryUrl](const auto &Info, - Object &Object) { - serializeInfo(Info, Object, RepositoryUrl); - }; + static auto SerializeInfo = + [RepositoryUrl, RepositoryLinePrefix](const auto &Info, Object &Object) { + serializeInfo(Info, Object, RepositoryUrl, RepositoryLinePrefix); + }; if (!I.Children.Functions.empty()) { serializeArray(I.Children.Functions, Obj, "Functions", SerializeInfo); @@ -730,7 +758,7 @@ static void serializeInfo(const NamespaceInfo &I, json::Object &Obj, if (!I.Children.Variables.empty()) serializeArray(I.Children.Variables, Obj, "Variables", SerializeInfo); - serializeCommonChildren(I.Children, Obj, RepositoryUrl); + serializeCommonChildren(I.Children, Obj, RepositoryUrl, RepositoryLinePrefix); } static SmallString<16> determineFileName(Info *I, SmallString<128> &Path) { @@ -864,10 +892,12 @@ Error JSONGenerator::generateDocForInfo(Info *I, raw_ostream &OS, switch (I->IT) { case InfoType::IT_namespace: - serializeInfo(*static_cast<NamespaceInfo *>(I), Obj, CDCtx.RepositoryUrl); + serializeInfo(*static_cast<NamespaceInfo *>(I), Obj, CDCtx.RepositoryUrl, + CDCtx.RepositoryLinePrefix); break; case InfoType::IT_record: - serializeInfo(*static_cast<RecordInfo *>(I), Obj, CDCtx.RepositoryUrl); + serializeInfo(*static_cast<RecordInfo *>(I), Obj, CDCtx.RepositoryUrl, + CDCtx.RepositoryLinePrefix); break; case InfoType::IT_concept: case InfoType::IT_enum: diff --git a/clang-tools-extra/clang-doc/assets/alias-template.mustache b/clang-tools-extra/clang-doc/assets/alias-template.mustache index 9b32f132e9ef3..835d9aab1417e 100644 --- a/clang-tools-extra/clang-doc/assets/alias-template.mustache +++ b/clang-tools-extra/clang-doc/assets/alias-template.mustache @@ -7,6 +7,6 @@ {{>Comments}} {{/Description}} {{#Location}} - <p>Defined at line {{LineNumber}} of file {{Filename}}</p> + <p>Defined at line {{LineNumber}} of file {{^FileURL}}{{Filename}}{{/FileURL}}{{#FileURL}}<a href="{{FileURL}}">{{Filename}}</a>{{/FileURL}}</p> {{/Location}} </div> diff --git a/clang-tools-extra/clang-doc/assets/class-template.mustache b/clang-tools-extra/clang-doc/assets/class-template.mustache index 979a486816059..9631accc80287 100644 --- a/clang-tools-extra/clang-doc/assets/class-template.mustache +++ b/clang-tools-extra/clang-doc/assets/class-template.mustache @@ -155,7 +155,9 @@ {{/Template}} <div class="hero__title"> <h1 class="hero__title-large">{{TagType}} {{Name}}</h1> - <p>Defined at line {{Location.LineNumber}} of file {{Location.Filename}}</p> + {{#Location}} + <p>Defined at line {{LineNumber}} of file {{^FileURL}}{{Filename}}{{/FileURL}}{{#FileURL}}<a href="{{FileURL}}">{{Filename}}</a>{{/FileURL}}</p> + {{/Location}} {{#Description}} <div class="doc-card"> {{>Comments}} diff --git a/clang-tools-extra/clang-doc/assets/enum-template.mustache b/clang-tools-extra/clang-doc/assets/enum-template.mustache index af1364c4d37f6..22661c69f9c22 100644 --- a/clang-tools-extra/clang-doc/assets/enum-template.mustache +++ b/clang-tools-extra/clang-doc/assets/enum-template.mustache @@ -42,8 +42,6 @@ </div> {{/EnumComments}} {{#Location}} - <div> - Defined at line {{LineNumber}} of file {{Filename}} - </div> + <p>Defined at line {{LineNumber}} of file {{^FileURL}}{{Filename}}{{/FileURL}}{{#FileURL}}<a href="{{FileURL}}">{{Filename}}</a>{{/FileURL}}</p> {{/Location}} </div> diff --git a/clang-tools-extra/clang-doc/assets/function-template.mustache b/clang-tools-extra/clang-doc/assets/function-template.mustache index 354e26b7b05b0..510219a63d379 100644 --- a/clang-tools-extra/clang-doc/assets/function-template.mustache +++ b/clang-tools-extra/clang-doc/assets/function-template.mustache @@ -18,6 +18,6 @@ </div> {{/Description}} {{#Location}} - <p>Defined at line {{LineNumber}} of file {{Filename}}</p> + <p>Defined at line {{LineNumber}} of file {{^FileURL}}{{Filename}}{{/FileURL}}{{#FileURL}}<a href="{{FileURL}}">{{Filename}}</a>{{/FileURL}}</p> {{/Location}} </div> diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/namespace-template.mustache index f3f324568031f..04c31fb140962 100644 --- a/clang-tools-extra/clang-doc/assets/namespace-template.mustache +++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache @@ -178,7 +178,9 @@ {{#Description}} {{>Comments}} {{/Description}} - <p>Defined at line {{Location.LineNumber}} of file {{Location.Filename}}</p> + {{#Location}} + <p>Defined at line {{LineNumber}} of file {{^FileURL}}{{Filename}}{{/FileURL}}{{#FileURL}}<a href="{{FileURL}}">{{Filename}}</a>{{/FileURL}}</p> + {{/Location}} </div> {{/Concepts}} </section> diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp index ce844ec072564..f10d8808f185e 100644 --- a/clang-tools-extra/test/clang-doc/enum.cpp +++ b/clang-tools-extra/test/clang-doc/enum.cpp @@ -21,7 +21,7 @@ */ enum Color { // MD-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]* - // HTML-INDEX-LINE-NOT: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p> + // HTML-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p> Red, ///< Comment 1 Green, ///< Comment 2 Blue ///< Comment 3 @@ -64,7 +64,7 @@ enum Color { */ enum class Shapes { // MD-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]* - // HTML-INDEX-LINE-NOT: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p> + // HTML-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p> /// Comment 1 Circle, @@ -115,7 +115,7 @@ class Animals { */ enum AnimalType { // MD-ANIMAL-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]* - // HTML-ANIMAL-LINE-NOT: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p> + // HTML-ANIMAL-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p> Dog, ///< Man's best friend Cat, ///< Man's other best friend Iguana ///< A lizard @@ -148,9 +148,7 @@ class Animals { // HTML-ANIMAL-NEXT: </tr> // HTML-ANIMAL-NEXT: </tbody> // HTML-ANIMAL-NEXT: </table> -// HTML-ANIMAL-NEXT: <div> -// HTML-ANIMAL-NEXT: Defined at line 116 of file {{.*}}enum.cpp -// HTML-ANIMAL-NEXT: </div> +// HTML-ANIMAL-NEXT: <p>Defined at line 116 of file {{.*}}enum.cpp</p> // HTML-ANIMAL-NEXT: </div> // HTML-ANIMAL-NEXT: </section> diff --git a/clang-tools-extra/test/clang-doc/mustache-index.cpp b/clang-tools-extra/test/clang-doc/mustache-index.cpp index 9706307287f9b..caaa932d3e02f 100644 --- a/clang-tools-extra/test/clang-doc/mustache-index.cpp +++ b/clang-tools-extra/test/clang-doc/mustache-index.cpp @@ -54,9 +54,7 @@ class Foo; // CHECK-NEXT: </tr> // CHECK-NEXT: </tbody> // CHECK-NEXT: </table> -// CHECK-NEXT: <div> -// CHECK-NEXT: Defined at line 5 of file {{.*}}mustache-index.cpp -// CHECK-NEXT: </div> +// CHECK-NEXT: <p>Defined at line 5 of file {{.*}}mustache-index.cpp</p> // CHECK-NEXT: </div> // CHECK-NEXT: </div> // CHECK-NEXT: </section> diff --git a/clang-tools-extra/test/clang-doc/templates.cpp b/clang-tools-extra/test/clang-doc/templates.cpp index 27fff6ed5a09d..8c9b0b53a8b3d 100644 --- a/clang-tools-extra/test/clang-doc/templates.cpp +++ b/clang-tools-extra/test/clang-doc/templates.cpp @@ -78,8 +78,7 @@ void ParamPackFunction(T... args); // JSON-NEXT: }, // HTML: <pre><code class="language-cpp code-clang-doc">template <class... T></code></pre> -// HTML-NEXT: <pre><code class="language-cpp code-clang-doc">void ParamPackFunction (T... args)</code></pre> -// HTML-NEXT: </div> +// HTML-NEXT: <pre><code class="language-cpp code-clang-doc">void ParamPackFunction (T... args)</code></pre> template <typename T, int U = 1> void function(T x) {} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
