Skip to content

[clang-doc] Implement setupTemplateValue for HTMLMustacheGenerator #138064

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
May 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand All @@ -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;
}
Expand Down
193 changes: 175 additions & 18 deletions clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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
}