From a60fc292361a97b4957dc17173cdec79510affc2 Mon Sep 17 00:00:00 2001 From: Samrudh Nelli Date: Wed, 11 Mar 2026 23:20:22 +0530 Subject: [PATCH] [Clang-doc] Display values and comments in MD (#183754) Display enum members in a tabular format in markdown. Support displaying enum member value and comments. Output: | Name | Value | Comments | |---|---|---| | Small | 0 | A pearl.
Pearls are quite small.

Pearls are used in jewelry. | | Medium | 1 | A tennis ball. | | Large | 2 | A football. | --- clang-tools-extra/clang-doc/MDGenerator.cpp | 111 ++++++++++++++++-- clang-tools-extra/test/clang-doc/enum.cpp | 85 ++++++++------ .../unittests/clang-doc/MDGeneratorTest.cpp | 17 +-- 3 files changed, 156 insertions(+), 57 deletions(-) diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp index 60a880d21188..5fcd6da0492c 100644 --- a/clang-tools-extra/clang-doc/MDGenerator.cpp +++ b/clang-tools-extra/clang-doc/MDGenerator.cpp @@ -8,6 +8,7 @@ #include "Generators.h" #include "Representation.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" @@ -67,6 +68,80 @@ static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L, OS << "\n\n"; } +/// Writer for writing comments to a table cell in MD. +/// +/// The writer traverses the comments recursively and outputs the +/// comments into a stream. +/// The formatter inserts single/double line breaks to retain the comment +/// structure. +/// +/// Usage : +/// Initialize an object with a llvm::raw_ostream to output into. +/// Call the write(C) function with an array of Comments 'C'. +class TableCommentWriter { +public: + explicit TableCommentWriter(llvm::raw_ostream &OS) : OS(OS) {} + + void write(llvm::ArrayRef Comments) { + for (const auto &C : Comments) + writeTableSafeComment(C); + + if (!Started) + OS << "--"; + } + +private: + /// This function inserts breaks into the stream. + /// + /// We add a double break in between paragraphs. + /// Inside a paragraph, a single break between lines is maintained. + void insertSeparator() { + if (!Started) + return; + if (NeedsParagraphBreak) { + OS << "

"; + NeedsParagraphBreak = false; + } else { + OS << "
"; + } + } + + /// This function processes every comment and its children recursively. + void writeTableSafeComment(const CommentInfo &I) { + switch (I.Kind) { + case CommentKind::CK_FullComment: + for (const auto &Child : I.Children) + writeTableSafeComment(*Child); + break; + + case CommentKind::CK_ParagraphComment: + for (const auto &Child : I.Children) + writeTableSafeComment(*Child); + // Next content after a paragraph needs a break + NeedsParagraphBreak = true; + break; + + case CommentKind::CK_TextComment: + if (!I.Text.empty()) { + insertSeparator(); + OS << I.Text; + Started = true; + } + break; + + // Handle other comment types (BlockCommand, InlineCommand, etc.) + default: + for (const auto &Child : I.Children) + writeTableSafeComment(*Child); + break; + } + } + + llvm::raw_ostream &OS; + bool Started = false; + bool NeedsParagraphBreak = false; +}; + static void maybeWriteSourceFileRef(llvm::raw_ostream &OS, const ClangDocContext &CDCtx, const std::optional &DefLoc) { @@ -163,14 +238,36 @@ static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I, if (I.BaseType && !I.BaseType->Type.QualName.empty()) { OS << ": " << I.BaseType->Type.QualName << " "; } - OS << "|\n\n" << "--\n\n"; + OS << "|\n\n"; - std::string Buffer; - llvm::raw_string_ostream Members(Buffer); - if (!I.Members.empty()) - for (const auto &N : I.Members) - Members << "| " << N.Name << " |\n"; - writeLine(Members.str(), OS); + OS << "| Name | Value |"; + if (!I.Members.empty()) { + bool HasComments = false; + for (const auto &Member : I.Members) { + if (!Member.Description.empty()) { + HasComments = true; + OS << " Comments |"; + break; + } + } + OS << "\n|---|---|"; + if (HasComments) + OS << "---|"; + OS << "\n"; + for (const auto &N : I.Members) { + OS << "| " << N.Name << " "; + if (!N.Value.empty()) + OS << "| " << N.Value << " "; + if (HasComments) { + OS << "| "; + TableCommentWriter CommentWriter(OS); + CommentWriter.write(N.Description); + OS << " "; + } + OS << "|\n"; + } + } + OS << "\n"; maybeWriteSourceFileRef(OS, CDCtx, I.DefLoc); diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp index eec2bcb9f82e..6d54dcf8bf65 100644 --- a/clang-tools-extra/test/clang-doc/enum.cpp +++ b/clang-tools-extra/test/clang-doc/enum.cpp @@ -41,10 +41,11 @@ enum Color { // MD-INDEX: ## Enums // MD-INDEX: | enum Color | -// MD-INDEX: -- -// MD-INDEX: | Red | -// MD-INDEX: | Green | -// MD-INDEX: | Blue | +// MD-INDEX: | Name | Value | Comments | +// MD-INDEX: |---|---|---| +// MD-INDEX: | Red | 0 | Comment 1 | +// MD-INDEX: | Green | 1 | Comment 2 | +// MD-INDEX: | Blue | 2 | Comment 3 | // MD-INDEX: **brief** For specifying RGB colors // HTML-INDEX-LABEL:
@@ -92,7 +93,7 @@ enum Color { // HTML-INDEX-NEXT:

For specifying RGB colors

// HTML-INDEX-NEXT:
// HTML-INDEX-NEXT: -// HTML-INDEX-NEXT:

Defined at line [[@LINE-62]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

+// HTML-INDEX-NEXT:

Defined at line [[@LINE-63]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

// HTML-INDEX-NEXT: // MD-MUSTACHE-INDEX: ## Enums @@ -118,11 +119,13 @@ enum class Shapes { /// Comment 3 Triangle }; + // MD-INDEX: | enum class Shapes | -// MD-INDEX: -- -// MD-INDEX: | Circle | -// MD-INDEX: | Rectangle | -// MD-INDEX: | Triangle | +// MD-INDEX: | Name | Value | Comments | +// MD-INDEX: |---|---|---| +// MD-INDEX: | Circle | 0 | Comment 1 | +// MD-INDEX: | Rectangle | 1 | Comment 2 | +// MD-INDEX: | Triangle | 2 | Comment 3 | // MD-INDEX: **brief** Shape Types // HTML-INDEX-LABEL:
@@ -170,7 +173,7 @@ enum class Shapes { // HTML-INDEX-NEXT:

Shape Types

// HTML-INDEX-NEXT:
// HTML-INDEX-NEXT: -// HTML-INDEX-NEXT:

Defined at line [[@LINE-64]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

+// HTML-INDEX-NEXT:

Defined at line [[@LINE-66]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

// HTML-INDEX-NEXT: typedef unsigned char uint8_t; @@ -195,10 +198,11 @@ enum Size : uint8_t { }; // MD-INDEX: | enum Size : uint8_t | -// MD-INDEX: -- -// MD-INDEX: | Small | -// MD-INDEX: | Medium | -// MD-INDEX: | Large | +// MD-INDEX: | Name | Value | Comments | +// MD-INDEX: |---|---|---| +// MD-INDEX: | Small | 0 | A pearl.
Pearls are quite small.

Pearls are used in jewelry. | +// MD-INDEX: | Medium | 1 | A tennis ball. | +// MD-INDEX: | Large | 2 | A football. | // MD-INDEX: **brief** Specify the size // HTML-INDEX-LABEL:
@@ -248,7 +252,7 @@ enum Size : uint8_t { // HTML-INDEX-NEXT:

Specify the size

// HTML-INDEX-NEXT:
// HTML-INDEX-NEXT: -// HTML-INDEX-NEXT:

Defined at line [[@LINE-71]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

+// HTML-INDEX-NEXT:

Defined at line [[@LINE-72]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

// HTML-INDEX-NEXT: /** @@ -261,8 +265,9 @@ enum : long long { }; // MD-INDEX: | enum (unnamed) : long long | -// MD-INDEX: -- -// MD-INDEX: | BigVal | +// MD-INDEX: | Name | Value | Comments | +// MD-INDEX: |---|---|---| +// MD-INDEX: | BigVal | 999999999999 | A very large value | // MD-INDEX: **brief** Very long number // HTML-INDEX-LABEL:
@@ -292,7 +297,7 @@ enum : long long { // HTML-INDEX-NEXT:

Very long number

// HTML-INDEX-NEXT:
// HTML-INDEX-NEXT: -// HTML-INDEX-NEXT:

Defined at line [[@LINE-38]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

+// HTML-INDEX-NEXT:

Defined at line [[@LINE-39]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

// HTML-INDEX-NEXT: class FilePermissions { @@ -312,10 +317,11 @@ public: }; // MD-PERM: | enum (unnamed) | -// MD-PERM: -- -// MD-PERM: | Read | -// MD-PERM: | Write | -// MD-PERM: | Execute | +// MD-PERM: | Name | Value | Comments | +// MD-PERM: |---|---|---| +// MD-PERM: | Read | 1 | Permission to READ r | +// MD-PERM: | Write | 2 | Permission to WRITE w | +// MD-PERM: | Execute | 4 | Permission to EXECUTE x | // MD-PERM: **brief** File permission flags // HTML-PERM-LABEL:
@@ -365,7 +371,7 @@ public: // HTML-PERM-NEXT:

File permission flags

// HTML-PERM-NEXT: // HTML-PERM-NEXT: -// HTML-PERM-NEXT:

Defined at line [[@LINE-63]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

+// HTML-PERM-NEXT:

Defined at line [[@LINE-64]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

// HTML-PERM-NEXT: // HTML-PERM-NEXT:
@@ -442,10 +448,11 @@ public: // MD-ANIMAL: # class Animals // MD-ANIMAL: ## Enums // MD-ANIMAL: | enum AnimalType | -// MD-ANIMAL: -- -// MD-ANIMAL: | Dog | -// MD-ANIMAL: | Cat | -// MD-ANIMAL: | Iguana | +// MD-ANIMAL: | Name | Value | Comments | +// MD-ANIMAL: |---|---|---| +// MD-ANIMAL: | Dog | 0 | Man's best friend | +// MD-ANIMAL: | Cat | 1 | Man's other best friend | +// MD-ANIMAL: | Iguana | 2 | A lizard | // MD-ANIMAL: **brief** specify what animal the class is // MD-MUSTACHE-ANIMAL: # class Animals @@ -476,11 +483,12 @@ enum Car { // MD-VEHICLES: # namespace Vehicles // MD-VEHICLES: ## Enums // MD-VEHICLES: | enum Car | -// MD-VEHICLES: -- -// MD-VEHICLES: | Sedan | -// MD-VEHICLES: | SUV | -// MD-VEHICLES: | Pickup | -// MD-VEHICLES: | Hatchback | +// MD-VEHICLES: | Name | Value | Comments | +// MD-VEHICLES: |---|---|---| +// MD-VEHICLES: | Sedan | 0 | Comment 1 | +// MD-VEHICLES: | SUV | 1 | Comment 2 | +// MD-VEHICLES: | Pickup | 2 | -- | +// MD-VEHICLES: | Hatchback | 3 | Comment 4 | // MD-VEHICLES: **brief** specify type of car // HTML-VEHICLES-LABEL:
@@ -533,7 +541,7 @@ enum Car { // HTML-VEHICLES-NEXT:

specify type of car

// HTML-VEHICLES-NEXT:
// HTML-VEHICLES-NEXT: -// HTML-VEHICLES-NEXT:

Defined at line [[@LINE-72]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

+// HTML-VEHICLES-NEXT:

Defined at line [[@LINE-73]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

// HTML-VEHICLES-NEXT: // MD-MUSTACHE-VEHICLES: # namespace Vehicles @@ -553,10 +561,11 @@ enum ColorUserSpecified { }; // MD-INDEX: | enum ColorUserSpecified | -// MD-INDEX: -- -// MD-INDEX: | RedUserSpecified | -// MD-INDEX: | GreenUserSpecified | -// MD-INDEX: | BlueUserSpecified | +// MD-INDEX: | Name | Value | +// MD-INDEX: |---|---| +// MD-INDEX: | RedUserSpecified | 65 | +// MD-INDEX: | GreenUserSpecified | 2 | +// MD-INDEX: | BlueUserSpecified | 67 | // HTML-INDEX-LABEL:
// HTML-INDEX-NEXT:
@@ -582,7 +591,7 @@ enum ColorUserSpecified { // HTML-INDEX-NEXT: // HTML-INDEX-NEXT: // HTML-INDEX-NEXT: -// HTML-INDEX-NEXT:

Defined at line [[@LINE-36]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

+// HTML-INDEX-NEXT:

Defined at line [[@LINE-37]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp

// HTML-INDEX-NEXT:
// MD-MUSTACHE-INDEX: | enum ColorUserSpecified | diff --git a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp index da22d0083512..aec8a1bc288e 100644 --- a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp +++ b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp @@ -69,10 +69,7 @@ TEST_F(MDGeneratorTest, emitNamespaceMD) { | enum OneEnum | --- - - - +| Name | Value | )raw"; @@ -136,10 +133,7 @@ ChildStruct | enum OneEnum | --- - - - +| Name | Value | )raw"; @@ -197,10 +191,9 @@ TEST_F(MDGeneratorTest, emitEnumMD) { assert(!Err); std::string Expected = R"raw(| enum class e | --- - -| X | - +| Name | Value | +|---|---| +| X | 0 | *Defined at test.cpp#10*