https://github.com/PeterChou1 created https://github.com/llvm/llvm-project/pull/101282
Part of https://github.com/llvm/llvm-project/issues/101129 This patch adds support for enum attaching comments to enums for HTML in clang-doc. It changes the enum generation to table tags where as perviously we're using lists. It also adds the gives clang-doc the ability to show user specified enum values >From 4c7623635dee6acac9b732808934efd884cfecbf Mon Sep 17 00:00:00 2001 From: PeterChou1 <peter.c...@mail.utoronto.ca> Date: Tue, 30 Jul 2024 23:51:33 -0400 Subject: [PATCH] [clang-doc] add support for enums in html generation --- clang-tools-extra/clang-doc/BitcodeReader.cpp | 5 + clang-tools-extra/clang-doc/BitcodeWriter.cpp | 2 + clang-tools-extra/clang-doc/HTMLGenerator.cpp | 94 ++++++++++++--- .../clang-doc/Representation.cpp | 2 + clang-tools-extra/clang-doc/Representation.h | 6 +- clang-tools-extra/clang-doc/Serialize.cpp | 14 ++- clang-tools-extra/test/clang-doc/enum.cpp | 113 +++++++++++++----- 7 files changed, 187 insertions(+), 49 deletions(-) diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp index bfb04e7407b38..b65fc2a1c6bf5 100644 --- a/clang-tools-extra/clang-doc/BitcodeReader.cpp +++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp @@ -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(); @@ -425,6 +429,7 @@ llvm::Expected<CommentInfo *> getCommentInfo(std::unique_ptr<CommentInfo> &I) { return getCommentInfo(I.get()); } + // When readSubBlock encounters a TypeInfo sub-block, it calls addTypeInfo on // the parent block to set it. The template specializations define what to do // for each supported parent block. diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp index 7e5a11783d303..06f30f76e33d8 100644 --- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp +++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp @@ -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) { diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp index aef22453035c3..c2cc3fe61217c 100644 --- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -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; @@ -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"); @@ -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"); } @@ -352,7 +376,8 @@ 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, const ClangDocContext &CDCtx) { @@ -372,14 +397,33 @@ genEnumsBlock(const std::vector<EnumInfo> &Enums, } static std::unique_ptr<TagNode> -genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) { +genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members, + bool HasComments) { 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 (HasComments) { + 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; } @@ -653,16 +697,38 @@ 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); + // Determine if enum members have comments attached + bool HasComments = false; + for (const auto &M : I.Members) { + if (!M.Description.empty()) { + HasComments = true; + break; + } + } + 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)); + + std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members, HasComments); + if (Node) - Out.emplace_back(std::move(Node)); + 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)); diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp index d08afbb962189..028dffc21793a 100644 --- a/clang-tools-extra/clang-doc/Representation.cpp +++ b/clang-tools-extra/clang-doc/Representation.cpp @@ -266,6 +266,8 @@ void EnumInfo::merge(EnumInfo &&Other) { Scoped = Other.Scoped; if (Members.empty()) Members = std::move(Other.Members); + if (Other.HasComments || HasComments) + HasComments = true; SymbolInfo::merge(std::move(Other)); } diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h index d70c279f7a2bd..6814c8441ddaf 100644 --- a/clang-tools-extra/clang-doc/Representation.h +++ b/clang-tools-extra/clang-doc/Representation.h @@ -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. @@ -443,7 +445,9 @@ struct EnumInfo : public SymbolInfo { // Indicates whether this enum is scoped (e.g. enum class). bool Scoped = false; - + // Indicates whether or not enum members have comments attached + bool HasComments = false; + // Set to nonempty to the type when this is an explicitly typed enum. For // enum Foo : short { ... }; // this will be "short". diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp index 3b074d849e8a9..c15cf1e489809 100644 --- a/clang-tools-extra/clang-doc/Serialize.cpp +++ b/clang-tools-extra/clang-doc/Serialize.cpp @@ -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(); + RawComment *Comment = E->getASTContext().getRawCommentForDeclNoCache(E); + if (Comment) { + 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()); + } + } } } diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp index e559940a31de6..fd7bbcb53f2d2 100644 --- a/clang-tools-extra/test/clang-doc/enum.cpp +++ b/clang-tools-extra/test/clang-doc/enum.cpp @@ -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 @@ -34,11 +34,16 @@ 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 @@ -46,11 +51,12 @@ enum Color { 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 | @@ -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 { @@ -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's best friend</p> +// HTML-ANIMAL: <td>Cat</td> +// HTML-ANIMAL: <td>1</td> +// HTML-ANIMAL: <p> Man'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 @@ -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 }; } @@ -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> \ No newline at end of file +// 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>'A'</td> +// HTML-INDEX: <td>GreenUserSpecified</td> +// HTML-INDEX: <td>2</td> +// HTML-INDEX: <td>BlueUserSpecified</td> +// HTML-INDEX: <td>'C'</td> \ No newline at end of file _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits