Samarth Narang d5704097fc
Refactor clang doc comment structure (#142273)
This patch refactors CommentKind handling in clang-doc by introducing a
strongly typed enum class for better type safety and clarity. It updates
all relevant places, including YAML traits and serialization, to work
with the new enum. Additionally, it enhances the Mustache-based HTML
generation by fully supporting all comment kinds, ensuring accurate
structured rendering of comment blocks. The changes simplify future
maintenance, improve robustness by eliminating unchecked defaults, and
ensure consistency between generators.

Fixes https://github.com/llvm/llvm-project/issues/142083
2025-06-07 08:04:56 -07:00

366 lines
10 KiB
C++

//===-- clang-doc/MDGeneratorTest.cpp -------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "ClangDocTest.h"
#include "Generators.h"
#include "Representation.h"
#include "gtest/gtest.h"
namespace clang {
namespace doc {
static std::unique_ptr<Generator> getMDGenerator() {
auto G = doc::findGeneratorByName("md");
if (!G)
return nullptr;
return std::move(G.get());
}
TEST(MDGeneratorTest, emitNamespaceMD) {
NamespaceInfo I;
I.Name = "Namespace";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.Children.Namespaces.emplace_back(EmptySID, "ChildNamespace",
InfoType::IT_namespace);
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
I.Children.Functions.back().Access = AccessSpecifier::AS_none;
I.Children.Enums.emplace_back();
I.Children.Enums.back().Name = "OneEnum";
auto G = getMDGenerator();
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext());
assert(!Err);
std::string Expected = R"raw(# namespace Namespace
## Namespaces
* [ChildNamespace](../ChildNamespace/index.md)
## Records
* [ChildStruct](../ChildStruct.md)
## Functions
### OneFunction
* OneFunction()*
## Enums
| enum OneEnum |
--
)raw";
EXPECT_EQ(Expected, Actual.str());
}
TEST(MDGeneratorTest, emitRecordMD) {
RecordInfo I;
I.Name = "r";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, 10, "test.cpp");
I.Loc.emplace_back(12, 12, "test.cpp");
I.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private);
I.TagType = TagTypeKind::Class;
I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
I.Children.Records.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
I.Children.Functions.emplace_back();
I.Children.Functions.back().Name = "OneFunction";
I.Children.Enums.emplace_back();
I.Children.Enums.back().Name = "OneEnum";
auto G = getMDGenerator();
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext());
assert(!Err);
std::string Expected = R"raw(# class r
*Defined at test.cpp#10*
Inherits from F, G
## Members
private int X
## Records
ChildStruct
## Functions
### OneFunction
*public OneFunction()*
## Enums
| enum OneEnum |
--
)raw";
EXPECT_EQ(Expected, Actual.str());
}
TEST(MDGeneratorTest, emitFunctionMD) {
FunctionInfo I;
I.Name = "f";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, 10, "test.cpp");
I.Loc.emplace_back(12, 12, "test.cpp");
I.Access = AccessSpecifier::AS_none;
I.ReturnType = TypeInfo("void");
I.Params.emplace_back(TypeInfo("int"), "P");
I.IsMethod = true;
I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
auto G = getMDGenerator();
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext());
assert(!Err);
std::string Expected = R"raw(### f
*void f(int P)*
*Defined at test.cpp#10*
)raw";
EXPECT_EQ(Expected, Actual.str());
}
TEST(MDGeneratorTest, emitEnumMD) {
EnumInfo I;
I.Name = "e";
I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
I.DefLoc = Location(10, 10, "test.cpp");
I.Loc.emplace_back(12, 12, "test.cpp");
I.Members.emplace_back("X");
I.Scoped = true;
auto G = getMDGenerator();
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext());
assert(!Err);
std::string Expected = R"raw(| enum class e |
--
| X |
*Defined at test.cpp#10*
)raw";
EXPECT_EQ(Expected, Actual.str());
}
TEST(MDGeneratorTest, emitCommentMD) {
FunctionInfo I;
I.Name = "f";
I.DefLoc = Location(10, 10, "test.cpp");
I.ReturnType = TypeInfo("void");
I.Params.emplace_back(TypeInfo("int"), "I");
I.Params.emplace_back(TypeInfo("int"), "J");
I.Access = AccessSpecifier::AS_none;
CommentInfo Top;
Top.Kind = CommentKind::CK_FullComment;
Top.Children.emplace_back(std::make_unique<CommentInfo>());
CommentInfo *BlankLine = Top.Children.back().get();
BlankLine->Kind = CommentKind::CK_ParagraphComment;
BlankLine->Children.emplace_back(std::make_unique<CommentInfo>());
BlankLine->Children.back()->Kind = CommentKind::CK_TextComment;
Top.Children.emplace_back(std::make_unique<CommentInfo>());
CommentInfo *Brief = Top.Children.back().get();
Brief->Kind = CommentKind::CK_ParagraphComment;
Brief->Children.emplace_back(std::make_unique<CommentInfo>());
Brief->Children.back()->Kind = CommentKind::CK_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 = CommentKind::CK_ParagraphComment;
Extended->Children.emplace_back(std::make_unique<CommentInfo>());
Extended->Children.back()->Kind = CommentKind::CK_TextComment;
Extended->Children.back()->Text = " Extended description that";
Extended->Children.emplace_back(std::make_unique<CommentInfo>());
Extended->Children.back()->Kind = CommentKind::CK_TextComment;
Extended->Children.back()->Text = " continues onto the next line.";
Top.Children.emplace_back(std::make_unique<CommentInfo>());
CommentInfo *HTML = Top.Children.back().get();
HTML->Kind = CommentKind::CK_ParagraphComment;
HTML->Children.emplace_back(std::make_unique<CommentInfo>());
HTML->Children.back()->Kind = CommentKind::CK_TextComment;
HTML->Children.emplace_back(std::make_unique<CommentInfo>());
HTML->Children.back()->Kind = CommentKind::CK_HTMLStartTagComment;
HTML->Children.back()->Name = "ul";
HTML->Children.back()->AttrKeys.emplace_back("class");
HTML->Children.back()->AttrValues.emplace_back("test");
HTML->Children.emplace_back(std::make_unique<CommentInfo>());
HTML->Children.back()->Kind = CommentKind::CK_HTMLStartTagComment;
HTML->Children.back()->Name = "li";
HTML->Children.emplace_back(std::make_unique<CommentInfo>());
HTML->Children.back()->Kind = CommentKind::CK_TextComment;
HTML->Children.back()->Text = " Testing.";
HTML->Children.emplace_back(std::make_unique<CommentInfo>());
HTML->Children.back()->Kind = CommentKind::CK_HTMLEndTagComment;
HTML->Children.back()->Name = "ul";
HTML->Children.back()->SelfClosing = true;
Top.Children.emplace_back(std::make_unique<CommentInfo>());
CommentInfo *Verbatim = Top.Children.back().get();
Verbatim->Kind = CommentKind::CK_VerbatimBlockComment;
Verbatim->Name = "verbatim";
Verbatim->CloseName = "endverbatim";
Verbatim->Children.emplace_back(std::make_unique<CommentInfo>());
Verbatim->Children.back()->Kind = CommentKind::CK_VerbatimBlockLineComment;
Verbatim->Children.back()->Text = " The description continues.";
Top.Children.emplace_back(std::make_unique<CommentInfo>());
CommentInfo *ParamOut = Top.Children.back().get();
ParamOut->Kind = CommentKind::CK_ParamCommandComment;
ParamOut->Direction = "[out]";
ParamOut->ParamName = "I";
ParamOut->Explicit = true;
ParamOut->Children.emplace_back(std::make_unique<CommentInfo>());
ParamOut->Children.back()->Kind = CommentKind::CK_ParagraphComment;
ParamOut->Children.back()->Children.emplace_back(
std::make_unique<CommentInfo>());
ParamOut->Children.back()->Children.back()->Kind =
CommentKind::CK_TextComment;
ParamOut->Children.back()->Children.emplace_back(
std::make_unique<CommentInfo>());
ParamOut->Children.back()->Children.back()->Kind =
CommentKind::CK_TextComment;
ParamOut->Children.back()->Children.back()->Text = " is a parameter.";
Top.Children.emplace_back(std::make_unique<CommentInfo>());
CommentInfo *ParamIn = Top.Children.back().get();
ParamIn->Kind = CommentKind::CK_ParamCommandComment;
ParamIn->Direction = "[in]";
ParamIn->ParamName = "J";
ParamIn->Children.emplace_back(std::make_unique<CommentInfo>());
ParamIn->Children.back()->Kind = CommentKind::CK_ParagraphComment;
ParamIn->Children.back()->Children.emplace_back(
std::make_unique<CommentInfo>());
ParamIn->Children.back()->Children.back()->Kind = CommentKind::CK_TextComment;
ParamIn->Children.back()->Children.back()->Text = " is a parameter.";
ParamIn->Children.back()->Children.emplace_back(
std::make_unique<CommentInfo>());
ParamIn->Children.back()->Children.back()->Kind = CommentKind::CK_TextComment;
Top.Children.emplace_back(std::make_unique<CommentInfo>());
CommentInfo *Return = Top.Children.back().get();
Return->Kind = CommentKind::CK_BlockCommandComment;
Return->Name = "return";
Return->Explicit = true;
Return->Children.emplace_back(std::make_unique<CommentInfo>());
Return->Children.back()->Kind = CommentKind::CK_ParagraphComment;
Return->Children.back()->Children.emplace_back(
std::make_unique<CommentInfo>());
Return->Children.back()->Children.back()->Kind = CommentKind::CK_TextComment;
Return->Children.back()->Children.back()->Text = "void";
I.Description.emplace_back(std::move(Top));
auto G = getMDGenerator();
assert(G);
std::string Buffer;
llvm::raw_string_ostream Actual(Buffer);
auto Err = G->generateDocForInfo(&I, Actual, ClangDocContext());
assert(!Err);
std::string Expected =
R"raw(### f
*void f(int I, int J)*
*Defined at test.cpp#10*
Brief description.
Extended description that continues onto the next line.
<ul "class=test">
<li>
Testing.</ul>
The description continues.
**I** [out] is a parameter.
**J** is a parameter.
**return**void
)raw";
EXPECT_EQ(Expected, Actual.str());
}
} // namespace doc
} // namespace clang