Repository: llvm/llvm-project
Pull request #138064: [clang-doc] Implement setupTemplateValue for HTMLMustacheGenerator
Author: login: ilovepi
Labels: name: clang-tools-extra
Created: 2025-05-01T00:58:00Z | Merged: 2025-05-23T22:59:42Z

This patch implements the business logic for setupTemplateValue, which
was split from #133161. The implementation configures the relative path
relationships between the various HTML components, and prepares them
prior to their use in the generator.

Co-authored-by: Peter Chou <peter.chou@mail.utoronto.ca>


Diff:
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 18ee3b8d0fc43..65dc2e93582e8 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -397,7 +397,7 @@ static json::Value extractValue(const RecordInfo &I,
 
   maybeInsertLocation(I.DefLoc, CDCtx, RecordValue);
 
-  StringRef BasePath = I.getRelativeFilePath("");
+  SmallString<64> BasePath = I.getRelativeFilePath("");
   extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
   json::Value PublicMembers = Array();
   json::Array &PubMemberRef = *PublicMembers.getAsArray();
@@ -431,8 +431,28 @@ static json::Value extractValue(const RecordInfo &I,
 
 static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
                                 Info *I) {
-  return createStringError(inconvertibleErrorCode(),
-                           "setupTemplateValue is unimplemented");
+  V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+  json::Value StylesheetArr = Array();
+  auto InfoPath = I->getRelativeFilePath("");
+  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+  sys::path::native(RelativePath, sys::path::Style::posix);
+  for (const auto &FilePath : CDCtx.UserStylesheets) {
+    SmallString<128> StylesheetPath = RelativePath;
+    sys::path::append(StylesheetPath, sys::path::Style::posix,
+                      sys::path::filename(FilePath));
+    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+  }
+  V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+
+  json::Value ScriptArr = Array();
+  for (auto Script : CDCtx.JsScripts) {
+    SmallString<128> JsPath = RelativePath;
+    sys::path::append(JsPath, sys::path::Style::posix,
+                      sys::path::filename(Script));
+    ScriptArr.getAsArray()->emplace_back(JsPath);
+  }
+  V.getAsObject()->insert({"Scripts", ScriptArr});
+  return Error::success();
 }
 
 Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
@@ -443,6 +463,7 @@ Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
         extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
     if (auto Err = setupTemplateValue(CDCtx, V, I))
       return Err;
+    assert(NamespaceTemplate && "NamespaceTemplate is nullptr.");
     NamespaceTemplate->render(V, OS);
     break;
   }
diff --git a/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp
index 4c8cf4fa7e460..95acd363a958e 100644
--- a/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp
@@ -20,10 +20,14 @@
 
 using namespace llvm;
 using namespace testing;
+using namespace clang;
 using namespace clang::doc;
 
-static const std::string ClangDocVersion =
-    clang::getClangToolFullVersion("clang-doc");
+// FIXME: Don't enable unit tests that can read files. Remove once we can use
+// lit to test these properties.
+#define ENABLE_LOCAL_TEST 0
+
+static const std::string ClangDocVersion = getClangToolFullVersion("clang-doc");
 
 static std::unique_ptr<Generator> getHTMLMustacheGenerator() {
   auto G = findGeneratorByName("mustache");
@@ -91,37 +95,190 @@ TEST(HTMLMustacheGeneratorTest, generateDocs) {
   unittest::TempDir RootTestDirectory("generateDocsTest", /*Unique=*/true);
   CDCtx.OutDirectory = RootTestDirectory.path();
 
+#if ENABLE_LOCAL_TEST
   // FIXME: We can't read files during unit tests. Migrate to lit once
   // tool support lands.
-  // getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
+  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
 
+  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
+                    Succeeded())
+      << "Failed to generate docs.";
+#else
   EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
                     Failed())
       << "Failed to generate docs.";
+#endif
 }
 
-TEST(HTMLMustacheGeneratorTest, generateDocsForInfo) {
+TEST(HTMLGeneratorTest, emitFunctionHTML) {
+#if ENABLE_LOCAL_TEST
   auto G = getHTMLMustacheGenerator();
   assert(G && "Could not find HTMLMustacheGenerator");
   ClangDocContext CDCtx = getClangDocContext();
   std::string Buffer;
   llvm::raw_string_ostream Actual(Buffer);
-  NamespaceInfo I;
-  I.Name = "Namespace";
+
+  unittest::TempDir RootTestDirectory("emitRecordHTML",
+                                      /*Unique=*/true);
+  CDCtx.OutDirectory = RootTestDirectory.path();
+
+  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
+
+  // FIXME: This is a terrible hack, since we can't initialize the templates
+  // directly. We'll need to update the interfaces so that we can call
+  // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp
+  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
+                    Succeeded())
+      << "Failed to generate docs.";
+
+  CDCtx.RepositoryUrl = "http://www.repository.com";
+
+  FunctionInfo I;
+  I.Name = "f";
   I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
 
-  I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
-                                     InfoType::IT_namespace,
-                                     "Namespace::ChildNamespace", "Namespace");
-  I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record,
-                                  "Namespace::ChildStruct", "Namespace");
-  I.Children.Functions.emplace_back();
-  I.Children.Functions.back().Access = clang::AccessSpecifier::AS_none;
-  I.Children.Functions.back().Name = "OneFunction";
-  I.Children.Enums.emplace_back();
+  I.DefLoc = Location(10, 10, "dir/test.cpp", true);
+  I.Loc.emplace_back(12, 12, "test.cpp");
+
+  I.Access = AccessSpecifier::AS_none;
+
+  SmallString<16> PathTo;
+  llvm::sys::path::native("path/to", PathTo);
+  I.ReturnType = doc::TypeInfo(
+      Reference(EmptySID, "float", InfoType::IT_default, "float", PathTo));
+  I.Params.emplace_back(doc::TypeInfo("int", PathTo), "P");
+  I.IsMethod = true;
+  I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
+
+  auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
+  assert(!Err);
+  std::string Expected = R"raw(IT_Function
+)raw";
+
+  // FIXME: Functions are not handled yet.
+  EXPECT_EQ(Expected, Actual.str());
+#endif
+}
+
+TEST(HTMLMustacheGeneratorTest, emitEnumHTML) {
+#if ENABLE_LOCAL_TEST
+  auto G = getHTMLMustacheGenerator();
+  assert(G && "Could not find HTMLMustacheGenerator");
+  ClangDocContext CDCtx = getClangDocContext();
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+
+  unittest::TempDir RootTestDirectory("emitEnumHTML",
+                                      /*Unique=*/true);
+  CDCtx.OutDirectory = RootTestDirectory.path();
+
+  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
+
+  // FIXME: This is a terrible hack, since we can't initialize the templates
+  // directly. We'll need to update the interfaces so that we can call
+  // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp
+  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
+                    Succeeded())
+      << "Failed to generate docs.";
+
+  CDCtx.RepositoryUrl = "http://www.repository.com";
+
+  EnumInfo I;
+  I.Name = "e";
+  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+  I.DefLoc = Location(10, 10, "test.cpp", true);
+  I.Loc.emplace_back(12, 12, "test.cpp");
+
+  I.Members.emplace_back("X");
+  I.Scoped = true;
+
+  auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
+  assert(!Err);
+
+  std::string Expected = R"raw(IT_enum
+)raw";
+
+  // FIXME: Enums are not handled yet.
+  EXPECT_EQ(Expected, Actual.str());
+#endif
+}
+
+TEST(HTMLMustacheGeneratorTest, emitCommentHTML) {
+#if ENABLE_LOCAL_TEST
+  auto G = getHTMLMustacheGenerator();
+  assert(G && "Could not find HTMLMustacheGenerator");
+  ClangDocContext CDCtx = getClangDocContext();
+  std::string Buffer;
+  llvm::raw_string_ostream Actual(Buffer);
+
+  unittest::TempDir RootTestDirectory("emitCommentHTML",
+                                      /*Unique=*/true);
+  CDCtx.OutDirectory = RootTestDirectory.path();
+
+  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
+
+  // FIXME: This is a terrible hack, since we can't initialize the templates
+  // directly. We'll need to update the interfaces so that we can call
+  // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp
+  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
+                    Succeeded())
+      << "Failed to generate docs.";
+
+  CDCtx.RepositoryUrl = "http://www.repository.com";
+
+  FunctionInfo I;
+  I.Name = "f";
+  I.DefLoc = Location(10, 10, "test.cpp", true);
+  I.ReturnType = doc::TypeInfo("void");
+  I.Params.emplace_back(doc::TypeInfo("int"), "I");
+  I.Params.emplace_back(doc::TypeInfo("int"), "J");
+  I.Access = AccessSpecifier::AS_none;
+
+  CommentInfo Top;
+  Top.Kind = "FullComment";
+
+  Top.Children.emplace_back(std::make_unique<CommentInfo>());
+  CommentInfo *BlankLine = Top.Children.back().get();
+  BlankLine->Kind = "ParagraphComment";
+  BlankLine->Children.emplace_back(std::make_unique<CommentInfo>());
+  BlankLine->Children.back()->Kind = "TextComment";
+
+  Top.Children.emplace_back(std::make_unique<CommentInfo>());
+  CommentInfo *Brief = Top.Children.back().get();
+  Brief->Kind = "ParagraphComment";
+  Brief->Children.emplace_back(std::make_unique<CommentInfo>());
+  Brief->Children.back()->Kind = "TextComment";
+  Brief->Children.back()->Name = "ParagraphComment";
+  Brief->Children.back()->Text = " Brief description.";
+
+  Top.Children.emplace_back(std::make_unique<CommentInfo>());
+  CommentInfo *Extended = Top.Children.back().get();
+  Extended->Kind = "ParagraphComment";
+  Extended->Children.emplace_back(std::make_unique<CommentInfo>());
+  Extended->Children.back()->Kind = "TextComment";
+  Extended->Children.back()->Text = " Extended description that";
+  Extended->Children.emplace_back(std::make_unique<CommentInfo>());
+  Extended->Children.back()->Kind = "TextComment";
+  Extended->Children.back()->Text = " continues onto the next line.";
+
+  Top.Children.emplace_back(std::make_unique<CommentInfo>());
+  CommentInfo *Entities = Top.Children.back().get();
+  Entities->Kind = "ParagraphComment";
+  Entities->Children.emplace_back(std::make_unique<CommentInfo>());
+  Entities->Children.back()->Kind = "TextComment";
+  Entities->Children.back()->Name = "ParagraphComment";
+  Entities->Children.back()->Text =
+      " Comment with html entities: &, <, >, \", \'.";
+
+  I.Description.emplace_back(std::move(Top));
 
-  EXPECT_THAT_ERROR(G->generateDocForInfo(&I, Actual, CDCtx), Failed());
+  auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
+  assert(!Err);
+  std::string Expected = R"raw(IT_Function
+)raw";
 
-  std::string Expected = R"raw()raw";
-  EXPECT_THAT(Actual.str(), Eq(Expected));
+  // FIXME: Functions are not handled yet.
+  EXPECT_EQ(Expected, Actual.str());
+#endif
 }


Reviews:
user: login: petrhosek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-07T01:54:30Z
user: login: ilovepi
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-07T16:28:54Z
user: login: petrhosek
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-23T22:50:10Z

Review comments:
diff_hunk: @@ -406,8 +406,26 @@ static json::Value extractValue(const RecordInfo &I,
 
 static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
                                 Info *I) {
-  return createStringError(inconvertibleErrorCode(),
-                           "setupTemplateValue is unimplemented");
+  V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+  json::Value StylesheetArr = Array();
+  auto InfoPath = I->getRelativeFilePath("");
+  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+  for (const auto &FilePath : CDCtx.UserStylesheets) {
+    SmallString<128> StylesheetPath = RelativePath;
+    sys::path::append(StylesheetPath, sys::path::filename(FilePath));
+    sys::path::native(StylesheetPath, sys::path::Style::posix);
+    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+  }
path: clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
user: login: petrhosek
body: We shouldn't be calling `sys::path::native` repeatedly inside the loop.
```suggestion
  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
  sys::path::native(RelativePath, sys::path::Style::posix);
  for (const auto &FilePath : CDCtx.UserStylesheets) {
    SmallString<128> StylesheetPath = RelativePath;
    sys::path::append(StylesheetPath, sys::path::filename(FilePath), sys::path::Style::posix);
    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
  }
```
created_at: 2025-05-07T01:54:30Z
author_association: MEMBER
diff_hunk: @@ -406,8 +406,26 @@ static json::Value extractValue(const RecordInfo &I,
 
 static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
                                 Info *I) {
-  return createStringError(inconvertibleErrorCode(),
-                           "setupTemplateValue is unimplemented");
+  V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+  json::Value StylesheetArr = Array();
+  auto InfoPath = I->getRelativeFilePath("");
+  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+  for (const auto &FilePath : CDCtx.UserStylesheets) {
+    SmallString<128> StylesheetPath = RelativePath;
+    sys::path::append(StylesheetPath, sys::path::filename(FilePath));
+    sys::path::native(StylesheetPath, sys::path::Style::posix);
+    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+  }
path: clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
user: login: ilovepi
body: Oh, good catch. I missed that. Thanks.
created_at: 2025-05-07T16:28:54Z
author_association: CONTRIBUTOR

PR comments:
user: login: ilovepi
created_at: 2025-05-01T00:58:13Z
body: * **#138067** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138067?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#138066** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138066?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#138065** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138065?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#138064** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138064?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138064?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* **#138063** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138063?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#138062** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138062?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#138061** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138061?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#138060** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138060?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#138059** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138059?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* **#138058** <a href="https://app.graphite.dev/github/pr/llvm/llvm-project/138058?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a>
* `main`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: CONTRIBUTOR
user: login: llvmbot
created_at: 2025-05-01T01:00:07Z
body: 
<!--LLVM PR SUMMARY COMMENT-->

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

Author: Paul Kirth (ilovepi)

<details>
<summary>Changes</summary>

This patch implements the business logic for setupTemplateValue, which
was split from #<!-- -->133161. The implementation configures the relative path
relationships between the various HTML components, and prepares them
prior to their use in the generator.

Co-authored-by: Peter Chou &lt;peter.chou@<!-- -->mail.utoronto.ca&gt;

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


1 Files Affected:

- (modified) clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp (+20) 


``````````diff
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 29392f8bf17b9..1f0a88c269cfa 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -406,7 +406,27 @@ static json::Value extractValue(const RecordInfo &I,
 
 static void setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
                                Info *I) {
+  V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+  json::Value StylesheetArr = Array();
+  auto InfoPath = I->getRelativeFilePath("");
+  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+  for (const auto &FilePath : CDCtx.UserStylesheets) {
+    SmallString<128> StylesheetPath = RelativePath;
+    sys::path::append(StylesheetPath, sys::path::filename(FilePath));
+    sys::path::native(StylesheetPath, sys::path::Style::posix);
+    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+  }
+  V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+
+  json::Value ScriptArr = Array();
+  for (auto Script : CDCtx.JsScripts) {
+    SmallString<128> JsPath = RelativePath;
+    sys::path::append(JsPath, sys::path::filename(Script));
+    ScriptArr.getAsArray()->emplace_back(JsPath);
+  }
+  V.getAsObject()->insert({"Scripts", ScriptArr});
 }
+
 Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
                                                 const ClangDocContext &CDCtx) {
   switch (I->IT) {

``````````

</details>

author_association: MEMBER
user: login: ilovepi
created_at: 2025-05-23T22:52:42Z
body: ### Merge activity

* **May 23, 10:52 PM UTC**: A user started a stack merge that includes this pull request via [Graphite](https://app.graphite.dev/github/pr/llvm/llvm-project/138064).
* **May 23, 10:57 PM UTC**: [Graphite](https://app.graphite.dev/github/pr/llvm/llvm-project/138064) rebased this pull request as part of a merge.
* **May 23, 10:59 PM UTC**: @ilovepi merged this pull request with [Graphite](https://app.graphite.dev/github/pr/llvm/llvm-project/138064).

author_association: CONTRIBUTOR