Skip to content

[clang-doc] add support for enums comments in html generation #101282

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 9 commits into from
Aug 16, 2024
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
4 changes: 4 additions & 0 deletions clang-tools-extra/clang-doc/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ template <> llvm::Expected<CommentInfo *> getCommentInfo(TypedefInfo *I) {
return &I->Description.emplace_back();
}

template <> llvm::Expected<CommentInfo *> getCommentInfo(EnumValueInfo *I) {
return &I->Description.emplace_back();
}

template <> llvm::Expected<CommentInfo *> getCommentInfo(CommentInfo *I) {
I->Children.emplace_back(std::make_unique<CommentInfo>());
return I->Children.back().get();
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-doc/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,8 @@ void ClangDocBitcodeWriter::emitBlock(const EnumValueInfo &I) {
emitRecord(I.Name, ENUM_VALUE_NAME);
emitRecord(I.Value, ENUM_VALUE_VALUE);
emitRecord(I.ValueExpr, ENUM_VALUE_EXPR);
for (const auto &CI : I.Description)
emitBlock(CI);
}

void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
Expand Down
88 changes: 72 additions & 16 deletions clang-tools-extra/clang-doc/HTMLGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ class HTMLTag {
TAG_SPAN,
TAG_TITLE,
TAG_UL,
TAG_TABLE,
TAG_THEAD,
TAG_TBODY,
TAG_TR,
TAG_TD,
TAG_TH
};

HTMLTag() = default;
Expand Down Expand Up @@ -133,6 +139,12 @@ bool HTMLTag::isSelfClosing() const {
case HTMLTag::TAG_SPAN:
case HTMLTag::TAG_TITLE:
case HTMLTag::TAG_UL:
case HTMLTag::TAG_TABLE:
case HTMLTag::TAG_THEAD:
case HTMLTag::TAG_TBODY:
case HTMLTag::TAG_TR:
case HTMLTag::TAG_TD:
case HTMLTag::TAG_TH:
return false;
}
llvm_unreachable("Unhandled HTMLTag::TagType");
Expand Down Expand Up @@ -174,6 +186,18 @@ StringRef HTMLTag::toString() const {
return "title";
case HTMLTag::TAG_UL:
return "ul";
case HTMLTag::TAG_TABLE:
return "table";
case HTMLTag::TAG_THEAD:
return "thead";
case HTMLTag::TAG_TBODY:
return "tbody";
case HTMLTag::TAG_TR:
return "tr";
case HTMLTag::TAG_TD:
return "td";
case HTMLTag::TAG_TH:
return "th";
}
llvm_unreachable("Unhandled HTMLTag::TagType");
}
Expand Down Expand Up @@ -352,6 +376,7 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx);
static std::vector<std::unique_ptr<TagNode>>
genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
StringRef ParentInfoDir);
static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C);

static std::vector<std::unique_ptr<TagNode>>
genEnumsBlock(const std::vector<EnumInfo> &Enums,
Expand All @@ -376,10 +401,27 @@ genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
if (Members.empty())
return nullptr;

auto List = std::make_unique<TagNode>(HTMLTag::TAG_UL);
for (const auto &M : Members)
List->Children.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_LI, M.Name));
auto List = std::make_unique<TagNode>(HTMLTag::TAG_TBODY);

for (const auto &M : Members) {
auto TRNode = std::make_unique<TagNode>(HTMLTag::TAG_TR);
TRNode->Children.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Name));
// Use user supplied value if it exists, otherwise use the value
if (!M.ValueExpr.empty()) {
TRNode->Children.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.ValueExpr));
} else {
TRNode->Children.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Value));
}
if (!M.Description.empty()) {
auto TD = std::make_unique<TagNode>(HTMLTag::TAG_TD);
TD->Children.emplace_back(genHTML(M.Description));
TRNode->Children.emplace_back(std::move(TD));
}
List->Children.emplace_back(std::move(TRNode));
}
return List;
}

Expand Down Expand Up @@ -619,7 +661,7 @@ static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
}
return std::move(FullComment);
}

if (I.Kind == "ParagraphComment") {
auto ParagraphComment = std::make_unique<TagNode>(HTMLTag::TAG_P);
for (const auto &Child : I.Children) {
Expand Down Expand Up @@ -653,22 +695,36 @@ static std::vector<std::unique_ptr<TagNode>>
genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
std::vector<std::unique_ptr<TagNode>> Out;
std::string EnumType = I.Scoped ? "enum class " : "enum ";

Out.emplace_back(
std::make_unique<TagNode>(HTMLTag::TAG_H3, EnumType + I.Name));
Out.back()->Attributes.emplace_back("id",
llvm::toHex(llvm::toStringRef(I.USR)));

std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members);
if (Node)
Out.emplace_back(std::move(Node));
// Determine if enum members have comments attached
bool HasComments = std::any_of(
I.Members.begin(), I.Members.end(),
[](const EnumValueInfo &M) { return !M.Description.empty(); });
std::unique_ptr<TagNode> Table =
std::make_unique<TagNode>(HTMLTag::TAG_TABLE);
std::unique_ptr<TagNode> THead =
std::make_unique<TagNode>(HTMLTag::TAG_THEAD);
std::unique_ptr<TagNode> TRow = std::make_unique<TagNode>(HTMLTag::TAG_TR);
std::unique_ptr<TagNode> TD =
std::make_unique<TagNode>(HTMLTag::TAG_TH, EnumType + I.Name);
// Span 3 columns if enum has comments
TD->Attributes.emplace_back("colspan", HasComments ? "3" : "2");

Table->Attributes.emplace_back("id", llvm::toHex(llvm::toStringRef(I.USR)));
TRow->Children.emplace_back(std::move(TD));
THead->Children.emplace_back(std::move(TRow));
Table->Children.emplace_back(std::move(THead));

if (std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members))
Table->Children.emplace_back(std::move(Node));

Out.emplace_back(std::move(Table));

if (I.DefLoc) {
if (!CDCtx.RepositoryUrl)
Out.emplace_back(writeFileDefinition(*I.DefLoc));
else
Out.emplace_back(writeFileDefinition(
*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
Out.emplace_back(
writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
}

std::string Description;
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clang-doc/Representation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ void SymbolInfo::merge(SymbolInfo &&Other) {
}

NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)
: Info(InfoType::IT_namespace, USR, Name, Path) {}
: Info(InfoType::IT_namespace, USR, Name, Path) {}

void NamespaceInfo::merge(NamespaceInfo &&Other) {
assert(mergeable(Other));
Expand Down
2 changes: 2 additions & 0 deletions clang-tools-extra/clang-doc/Representation.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,8 @@ struct EnumValueInfo {
// Stores the user-supplied initialization expression for this enumeration
// constant. This will be empty for implicit enumeration values.
SmallString<16> ValueExpr;

std::vector<CommentInfo> Description; /// Comment description of this field.
};

// TODO: Expand to allow for documenting templating.
Expand Down
16 changes: 13 additions & 3 deletions clang-tools-extra/clang-doc/Serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -394,10 +394,20 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
std::string ValueExpr;
if (const Expr *InitExpr = E->getInitExpr())
ValueExpr = getSourceCode(D, InitExpr->getSourceRange());

SmallString<16> ValueStr;
E->getInitVal().toString(ValueStr);
I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr);
I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
ASTContext &Context = E->getASTContext();
if (RawComment *Comment =
E->getASTContext().getRawCommentForDeclNoCache(E)) {
CommentInfo CInfo;
Comment->setAttached();
if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
EnumValueInfo &Member = I.Members.back();
Member.Description.emplace_back();
parseFullComment(Fc, Member.Description.back());
}
}
}
}

Expand Down Expand Up @@ -566,7 +576,7 @@ static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
return;

Comment->setAttached();
if (comments::FullComment* fc = Comment->parse(Context, nullptr, D)) {
if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) {
I.Description.emplace_back();
parseFullComment(fc, I.Description.back());
}
Expand Down
113 changes: 81 additions & 32 deletions clang-tools-extra/test/clang-doc/enum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
enum Color {
// MD-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
// HTML-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
Red, ///< Red
Green, ///< Green
Blue ///< Blue
Red, ///< Comment 1
Green, ///< Comment 2
Blue ///< Comment 3
};

// MD-INDEX: ## Enums
Expand All @@ -34,23 +34,29 @@ enum Color {
// MD-INDEX: | Blue |
// MD-INDEX: **brief** For specifying RGB colors

// HTML-INDEX: <h2 id="Enums">Enums</h2>
// HTML-INDEX: <h3 id="{{([0-9A-F]{40})}}">enum Color</h3>
// HTML-INDEX: <li>Red</li>
// HTML-INDEX: <li>Green</li>
// HTML-INDEX: <li>Blue</li>
// HTML-INDEX: <th colspan="3">enum Color</th>
// HTML-INDEX: <td>Red</td>
// HTML-INDEX: <td>0</td>
// HTML-INDEX: <p> Comment 1</p>
// HTML-INDEX: <td>Green</td>
// HTML-INDEX: <td>1</td>
// HTML-INDEX: <p> Comment 2</p>
// HTML-INDEX: <td>Blue</td>
// HTML-INDEX: <td>2</td>
// HTML-INDEX: <p> Comment 3</p>

/**
* @brief Shape Types
*/
enum class Shapes {
// MD-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
// HTML-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
/// Circle

/// Comment 1
Circle,
/// Rectangle
/// Comment 2
Rectangle,
/// Triangle
/// Comment 3
Triangle
};
// MD-INDEX: | enum class Shapes |
Expand All @@ -60,10 +66,17 @@ enum class Shapes {
// MD-INDEX: | Triangle |
// MD-INDEX: **brief** Shape Types

// HTML-INDEX: <h3 id="{{([0-9A-F]{40})}}">enum class Shapes</h3>
// HTML-INDEX: <li>Circle</li>
// HTML-INDEX: <li>Rectangle</li>
// HTML-INDEX: <li>Triangle</li>
// HTML-INDEX: <th colspan="3">enum class Shapes</th>
// HTML-INDEX: <td>Circle</td>
// HTML-INDEX: <td>0</td>
// HTML-INDEX: <p> Comment 1</p>
// HTML-INDEX: <td>Rectangle</td>
// HTML-INDEX: <td>1</td>
// HTML-INDEX: <p> Comment 2</p>
// HTML-INDEX: <td>Triangle</td>
// HTML-INDEX: <td>2</td>
// HTML-INDEX: <p> Comment 3</p>



class Animals {
Expand All @@ -76,18 +89,25 @@ class Animals {
enum AnimalType {
// MD-ANIMAL-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
// 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
Dog, ///< Man's best friend
Cat, ///< Man's other best friend
Iguana ///< A lizard
};
};

// HTML-ANIMAL: <h1>class Animals</h1>
// HTML-ANIMAL: <h2 id="Enums">Enums</h2>
// HTML-ANIMAL: <h3 id="{{([0-9A-F]{40})}}">enum AnimalType</h3>
// HTML-ANIMAL: <li>Dog</li>
// HTML-ANIMAL: <li>Cat</li>
// HTML-ANIMAL: <li>Iguana</li>
// HTML-ANIMAL: <th colspan="3">enum AnimalType</th>
// HTML-ANIMAL: <td>Dog</td>
// HTML-ANIMAL: <td>0</td>
// HTML-ANIMAL: <p> Man&apos;s best friend</p>
// HTML-ANIMAL: <td>Cat</td>
// HTML-ANIMAL: <td>1</td>
// HTML-ANIMAL: <p> Man&apos;s other best friend</p>
// HTML-ANIMAL: <td>Iguana</td>
// HTML-ANIMAL: <td>2</td>
// HTML-ANIMAL: <p> A lizard</p>


// MD-ANIMAL: # class Animals
// MD-ANIMAL: ## Enums
Expand All @@ -106,10 +126,11 @@ namespace Vehicles {
enum Car {
// MD-VEHICLES-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
// HTML-VEHICLES-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
Sedan, /// Sedan
SUV, /// SUV
Pickup, /// Pickup
Hatchback /// Hatchback

Sedan, ///< Comment 1
SUV, ///< Comment 2
Pickup, ///< Comment 3
Hatchback ///< Comment 4
};
}

Expand All @@ -124,9 +145,37 @@ namespace Vehicles {
// MD-VEHICLES: **brief** specify type of car

// HTML-VEHICLES: <h1>namespace Vehicles</h1>
// HTML-VEHICLES: <h2 id="Enums">Enums</h2>
// HTML-VEHICLES: <h3 id="{{([0-9A-F]{40})}}">enum Car</h3>
// HTML-VEHICLES: <li>Sedan</li>
// HTML-VEHICLES: <li>SUV</li>
// HTML-VEHICLES: <li>Pickup</li>
// HTML-VEHICLES: <li>Hatchback</li>
// HTML-VEHICLES: <th colspan="3">enum Car</th>
// HTML-VEHICLES: <td>Sedan</td>
// HTML-VEHICLES: <td>0</td>
// HTML-VEHICLES: <p> Comment 1</p>
// HTML-VEHICLES: <td>SUV</td>
// HTML-VEHICLES: <td>1</td>
// HTML-VEHICLES: <p> Comment 2</p>
// HTML-VEHICLES: <td>Pickup</td>
// HTML-VEHICLES: <td>2</td>
// HTML-VEHICLES: <p> Comment 3</p>
// HTML-VEHICLES: <td>Hatchback</td>
// HTML-VEHICLES: <td>3</td>
// HTML-VEHICLES: <p> Comment 4</p>


enum ColorUserSpecified {
RedUserSpecified = 'A',
GreenUserSpecified = 2,
BlueUserSpecified = 'C'
};

// MD-INDEX: | enum ColorUserSpecified |
// MD-INDEX: --
// MD-INDEX: | RedUserSpecified |
// MD-INDEX: | GreenUserSpecified |
// MD-INDEX: | BlueUserSpecified |

// HTML-INDEX: <th colspan="2">enum ColorUserSpecified</th>
// HTML-INDEX: <td>RedUserSpecified</td>
// HTML-INDEX: <td>&apos;A&apos;</td>
// HTML-INDEX: <td>GreenUserSpecified</td>
// HTML-INDEX: <td>2</td>
// HTML-INDEX: <td>BlueUserSpecified</td>
// HTML-INDEX: <td>&apos;C&apos;</td>
Loading
Loading