[clang-tools-extra] [Clang-doc] Display values and comments in MD (PR #183754)

Samrudh Nelli via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 11 09:21:16 PDT 2026


https://github.com/SamrudhNelli updated https://github.com/llvm/llvm-project/pull/183754

>From d9e58f68ad9fa377e91d1778d2393c3bc8451c55 Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Fri, 27 Feb 2026 21:07:37 +0530
Subject: [PATCH 01/18] display in a table format

---
 clang-tools-extra/clang-doc/MDGenerator.cpp |  63 ++++-
 clang-tools-extra/test/clang-doc/enum.cpp   | 284 +++++++++++++++-----
 2 files changed, 277 insertions(+), 70 deletions(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index fcb75af80f9e9..6fd0bf2df038a 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -67,6 +67,30 @@ static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L,
   OS << "\n\n";
 }
 
+static std::string genRawText(const std::vector<CommentInfo> &Comments) {
+  std::string Result;
+  llvm::raw_string_ostream OS(Result);
+  std::queue<const CommentInfo *> Q;
+  for (const auto &CI : Comments)
+    Q.push(&CI);
+  const CommentInfo *Comment;
+  bool First = true;
+  while (Q.size()) {
+    Comment = Q.front();
+    Q.pop();
+    if (!Comment->Text.empty())
+    {
+      if (!First)
+        OS << "<br>";
+      OS << Comment->Text;
+      First = false;
+    }
+    for (const auto &CI : Comment->Children)
+      Q.push(CI.get());
+  }
+  return Result;
+}
+
 static void maybeWriteSourceFileRef(llvm::raw_ostream &OS,
                                     const ClangDocContext &CDCtx,
                                     const std::optional<Location> &DefLoc) {
@@ -157,16 +181,41 @@ static void writeNameLink(const StringRef &CurrentPath, const Reference &R,
 static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
                         llvm::raw_ostream &OS) {
   if (I.Scoped)
-    writeLine("| enum class " + I.Name + " |", OS);
-  else
-    writeLine("| enum " + I.Name + " |", OS);
-  writeLine("--", OS);
+    OS << "class ";
+  OS << (I.Name.empty() ? "(unnamed)" : StringRef(I.Name)) << " ";
+  if (I.BaseType && !I.BaseType->Type.QualName.empty()) {
+    OS << ": " << I.BaseType->Type.QualName << " ";
+  }
+  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";
+  Members << "| Name | Value |";
+  if (!I.Members.empty()) {
+    bool HasComments = false;
+    for (const auto &Member : I.Members) {
+      if (!Member.Description.empty()) {
+        HasComments = true;
+        Members << " Comments |";
+        break;
+      }
+    }
+    Members << "\n";
+    Members << "|:-:|:-:|";
+    if (HasComments)
+      Members << ":-:|";
+    Members << "\n";
+    for (const auto &N : I.Members) {
+      Members << "| " << N.Name << " ";
+      if (!N.Value.empty())
+        Members << "| " << N.Value << " ";
+      if (HasComments) {
+        std::string RawComment = StringRef(genRawText(N.Description)).trim().str();
+        Members << "| " << (RawComment.empty() ? "--" : RawComment) << " ";
+      }
+      Members << "|\n";
+    }
+  }
   writeLine(Members.str(), OS);
 
   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 6e11bbf065f25..c58d94404fcdc 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -30,9 +30,9 @@ enum Color {
 // MD-INDEX: ## Enums
 // MD-INDEX: | enum Color |
 // MD-INDEX: --
-// MD-INDEX: | Red |
-// MD-INDEX: | Green |
-// MD-INDEX: | Blue |
+// MD-INDEX: | Red | 0 | Comment 1 |
+// MD-INDEX: | Green | Comment 2 |
+// MD-INDEX: | Blue | Comment 3 |
 // MD-INDEX: **brief** For specifying RGB colors
 
 // HTML-INDEX:     <div>
@@ -73,37 +73,192 @@ enum class Shapes {
   /// Comment 3
   Triangle
 };
+
 // MD-INDEX: | enum class Shapes |
 // MD-INDEX: --
-// MD-INDEX: | Circle |
-// MD-INDEX: | Rectangle |
-// MD-INDEX: | Triangle |
+// MD-INDEX: | Circle | 0 | Comment 1 |
+// MD-INDEX: | Rectangle | 1 | Comment 2 |
+// MD-INDEX: | Triangle | 2 | Comment 3 |
 // MD-INDEX: **brief** Shape Types
 
-// COM: FIXME: Serialize "enum class" in template
-// HTML-INDEX:     <div>
-// HTML-INDEX:         <pre><code class="language-cpp code-clang-doc">enum Shapes</code></pre>
-// HTML-INDEX:     </div>
-// HTML-INDEX:     <table class="table-wrapper">
-// HTML-INDEX:         <tbody>
-// HTML-INDEX:             <tr>
-// HTML-INDEX:                 <th>Name</th>
-// HTML-INDEX:                 <th>Value</th>
-// HTML-INDEX:             </tr>
-// HTML-INDEX:             <tr>
-// HTML-INDEX:                 <td>Circle</td>
-// HTML-INDEX:                 <td>0</td>
-// HTML-INDEX:             </tr>
-// HTML-INDEX:             <tr>
-// HTML-INDEX:                 <td>Rectangle</td>
-// HTML-INDEX:                 <td>1</td>
-// HTML-INDEX:             </tr>
-// HTML-INDEX:             <tr>
-// HTML-INDEX:                 <td>Triangle</td>
-// HTML-INDEX:                 <td>2</td>
-// HTML-INDEX:             </tr>
-// HTML-INDEX:         </tbody>
-// HTML-INDEX:     </table>
+// HTML-INDEX-LABEL:  <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
+// HTML-INDEX-NEXT:     <div>
+// HTML-INDEX-NEXT:       <pre><code class="language-cpp code-clang-doc">enum class Shapes</code></pre>
+// HTML-INDEX-NEXT:     </div>
+// HTML-INDEX-NEXT:     <table class="table-wrapper">
+// HTML-INDEX-NEXT:         <tbody>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <th>Name</th>
+// HTML-INDEX-NEXT:                 <th>Value</th>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <td>Circle</td>
+// HTML-INDEX-NEXT:                 <td>0</td>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <td>Rectangle</td>
+// HTML-INDEX-NEXT:                 <td>1</td>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <td>Triangle</td>
+// HTML-INDEX-NEXT:                 <td>2</td>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:         </tbody>
+// HTML-INDEX-NEXT:     </table>
+// HTML-INDEX-NEXT:     <div class="doc-card">
+// HTML-INDEX-NEXT:       <div class="nested-delimiter-container">
+// HTML-INDEX-NEXT:           <p> Shape Types</p>
+// HTML-INDEX-NEXT:       </div>
+// HTML-INDEX-NEXT:     </div>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-48]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:   </div>
+
+typedef unsigned char uint8_t;
+/**
+ * @brief Specify the size
+ */
+enum Size : uint8_t {
+  // 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>
+  Small,   ///< A pearl
+  Medium,  ///< A tennis ball
+  Large    ///< A football
+};
+
+// MD-INDEX: | enum Size : uint8_t |
+// MD-INDEX: --
+// MD-INDEX: | Small | 0 | A pearl |
+// MD-INDEX: | Medium | 1 | A tennis ball |
+// MD-INDEX: | Large | 2 | A football |
+// MD-INDEX: **brief** Specify the size
+
+// HTML-INDEX-LABEL:   <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
+// HTML-INDEX-NEXT:     <div>
+// HTML-INDEX-NEXT:       <pre><code class="language-cpp code-clang-doc">enum Size : uint8_t</code></pre>
+// HTML-INDEX-NEXT:     </div>
+// HTML-INDEX-NEXT:     <table class="table-wrapper">
+// HTML-INDEX-NEXT:         <tbody>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <th>Name</th>
+// HTML-INDEX-NEXT:                 <th>Value</th>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <td>Small</td>
+// HTML-INDEX-NEXT:                 <td>0</td>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <td>Medium</td>
+// HTML-INDEX-NEXT:                 <td>1</td>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <td>Large</td>
+// HTML-INDEX-NEXT:                 <td>2</td>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:         </tbody>
+// HTML-INDEX-NEXT:     </table>
+// HTML-INDEX-NEXT:     <div class="doc-card">
+// HTML-INDEX-NEXT:       <div class="nested-delimiter-container">
+// HTML-INDEX-NEXT:           <p> Specify the size</p>
+// HTML-INDEX-NEXT:       </div>
+// HTML-INDEX-NEXT:     </div>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-44]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:   </div>
+
+/**
+ * @brief Very long number
+ */
+enum : long long {
+  // 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>
+  BigVal = 999999999999   ///< A very large value
+};
+
+// MD-INDEX: | enum (unnamed) : long long |
+// MD-INDEX: --
+// MD-INDEX: | BigVal | 999999999999 | A very large value |
+// MD-INDEX: **brief** Very long number
+
+// HTML-INDEX-LABEL:  <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
+// HTML-INDEX-NEXT:     <div>
+// HTML-INDEX-NEXT:       <pre><code class="language-cpp code-clang-doc">enum (unnamed) : long long</code></pre>
+// HTML-INDEX-NEXT:     </div>
+// HTML-INDEX-NEXT:     <table class="table-wrapper">
+// HTML-INDEX-NEXT:         <tbody>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <th>Name</th>
+// HTML-INDEX-NEXT:                 <th>Value</th>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <td>BigVal</td>
+// HTML-INDEX-NEXT:                 <td>999999999999</td>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:         </tbody>
+// HTML-INDEX-NEXT:     </table>
+// HTML-INDEX-NEXT:     <div class="doc-card">
+// HTML-INDEX-NEXT:       <div class="nested-delimiter-container">
+// HTML-INDEX-NEXT:           <p> Very long number</p>
+// HTML-INDEX-NEXT:       </div>
+// HTML-INDEX-NEXT:     </div>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-32]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:   </div>
+
+class FilePermissions {
+// MD-PERM-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
+// HTML-PERM-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+public:
+  /**
+   * @brief File permission flags
+   */
+  enum {
+  // MD-PERM-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
+  // HTML-PERM-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+    Read    = 1,     ///> Permission to READ r
+    Write   = 2,     ///> Permission to WRITE w
+    Execute = 4      ///> Permission to EXECUTE x
+  };
+};
+
+// MD-PERM: | enum (unnamed) |
+// 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:  <section id="Enums" class="section-container">
+// HTML-PERM-NEXT:     <h2>Enumerations</h2>
+// HTML-PERM-NEXT:     <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
+// HTML-PERM-NEXT:       <div>
+// HTML-PERM-NEXT:         <pre><code class="language-cpp code-clang-doc">enum (unnamed)</code></pre>
+// HTML-PERM-NEXT:       </div>
+// HTML-PERM-NEXT:       <table class="table-wrapper">
+// HTML-PERM-NEXT:           <tbody>
+// HTML-PERM-NEXT:               <tr>
+// HTML-PERM-NEXT:                   <th>Name</th>
+// HTML-PERM-NEXT:                   <th>Value</th>
+// HTML-PERM-NEXT:               </tr>
+// HTML-PERM-NEXT:               <tr>
+// HTML-PERM-NEXT:                   <td>Read</td>
+// HTML-PERM-NEXT:                   <td>1</td>
+// HTML-PERM-NEXT:               </tr>
+// HTML-PERM-NEXT:               <tr>
+// HTML-PERM-NEXT:                   <td>Write</td>
+// HTML-PERM-NEXT:                   <td>2</td>
+// HTML-PERM-NEXT:               </tr>
+// HTML-PERM-NEXT:               <tr>
+// HTML-PERM-NEXT:                   <td>Execute</td>
+// HTML-PERM-NEXT:                   <td>4</td>
+// HTML-PERM-NEXT:               </tr>
+// HTML-PERM-NEXT:           </tbody>
+// HTML-PERM-NEXT:       </table>
+// HTML-PERM-NEXT:       <div class="doc-card">
+// HTML-PERM-NEXT:         <div class="nested-delimiter-container">
+// HTML-PERM-NEXT:             <p> File permission flags</p>
+// HTML-PERM-NEXT:         </div>
+// HTML-PERM-NEXT:       </div>
+// HTML-PERM-NEXT:         <p>Defined at line [[@LINE-47]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-PERM-NEXT:     </div>
+// HTML-PERM-NEXT:   </section>
 
 // COM: FIXME: Add enums declared inside of classes to class template
 class Animals {
@@ -161,9 +316,9 @@ class Animals {
 // MD-ANIMAL: ## Enums
 // MD-ANIMAL: | enum AnimalType |
 // MD-ANIMAL: --
-// MD-ANIMAL: | Dog |
-// MD-ANIMAL: | Cat |
-// MD-ANIMAL: | Iguana |
+// 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
 
 namespace Vehicles {
@@ -185,10 +340,10 @@ enum Car {
 // MD-VEHICLES: ## Enums
 // MD-VEHICLES: | enum Car |
 // MD-VEHICLES: --
-// MD-VEHICLES: | Sedan |
-// MD-VEHICLES: | SUV |
-// MD-VEHICLES: | Pickup |
-// MD-VEHICLES: | Hatchback |
+// MD-VEHICLES: | Sedan | 0 | Comment 1 |
+// MD-VEHICLES: | SUV | 1 | Comment 2 |
+// MD-VEHICLES: | Pickup | 2 | Comment 3 |
+// MD-VEHICLES: | Hatchback | 3 | Comment 4 |
 // MD-VEHICLES: **brief** specify type of car
 
 // HTML-VEHICLES:     <div>
@@ -227,30 +382,33 @@ enum ColorUserSpecified {
 
 // MD-INDEX: | enum ColorUserSpecified |
 // MD-INDEX: --
-// MD-INDEX: | RedUserSpecified |
-// MD-INDEX: | GreenUserSpecified |
-// MD-INDEX: | BlueUserSpecified |
+// MD-INDEX: | RedUserSpecified | 65 |
+// MD-INDEX: | GreenUserSpecified | 2 |
+// MD-INDEX: | BlueUserSpecified | 67 |
 
-// HTML-INDEX:     <div>
-// HTML-INDEX:         <pre><code class="language-cpp code-clang-doc">enum ColorUserSpecified</code></pre>
-// HTML-INDEX:     </div>
-// HTML-INDEX:     <table class="table-wrapper">
-// HTML-INDEX:         <tbody>
-// HTML-INDEX:             <tr>
-// HTML-INDEX:                 <th>Name</th>
-// HTML-INDEX:                 <th>Value</th>
-// HTML-INDEX:             </tr>
-// HTML-INDEX:             <tr>
-// HTML-INDEX:                 <td>RedUserSpecified</td>
-// HTML-INDEX:                 <td>'A'</td>
-// HTML-INDEX:             </tr>
-// HTML-INDEX:             <tr>
-// HTML-INDEX:                 <td>GreenUserSpecified</td>
-// HTML-INDEX:                 <td>2</td>
-// HTML-INDEX:             </tr>
-// HTML-INDEX:             <tr>
-// HTML-INDEX:                 <td>BlueUserSpecified</td>
-// HTML-INDEX:                 <td>'C'</td>
-// HTML-INDEX:             </tr>
-// HTML-INDEX:         </tbody>
-// HTML-INDEX:     </table>
+// HTML-INDEX-LABEL:  <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
+// HTML-INDEX-NEXT:     <div>
+// HTML-INDEX-NEXT:       <pre><code class="language-cpp code-clang-doc">enum ColorUserSpecified</code></pre>
+// HTML-INDEX-NEXT:     </div>
+// HTML-INDEX-NEXT:     <table class="table-wrapper">
+// HTML-INDEX-NEXT:         <tbody>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <th>Name</th>
+// HTML-INDEX-NEXT:                 <th>Value</th>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <td>RedUserSpecified</td>
+// HTML-INDEX-NEXT:                 <td>'A'</td>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <td>GreenUserSpecified</td>
+// HTML-INDEX-NEXT:                 <td>2</td>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:             <tr>
+// HTML-INDEX-NEXT:                 <td>BlueUserSpecified</td>
+// HTML-INDEX-NEXT:                 <td>'C'</td>
+// HTML-INDEX-NEXT:             </tr>
+// HTML-INDEX-NEXT:         </tbody>
+// HTML-INDEX-NEXT:     </table>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-36]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:   </div>

>From 7ee114499bf2dd88895ed16a0b463fd9c9ad3ced Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Mon, 2 Mar 2026 14:46:12 +0530
Subject: [PATCH 02/18] fix tests

---
 clang-tools-extra/test/clang-doc/enum.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index c58d94404fcdc..280c8892e4d43 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -31,8 +31,8 @@ enum Color {
 // MD-INDEX: | enum Color |
 // MD-INDEX: --
 // MD-INDEX: | Red | 0 | Comment 1 |
-// MD-INDEX: | Green | Comment 2 |
-// MD-INDEX: | Blue | Comment 3 |
+// MD-INDEX: | Green | 1 | Comment 2 |
+// MD-INDEX: | Blue | 2 | Comment 3 |
 // MD-INDEX: **brief** For specifying RGB colors
 
 // HTML-INDEX:     <div>

>From 9b75c69c8aad9f5ecffe2d096a398d7358b40558 Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Mon, 2 Mar 2026 17:16:34 +0530
Subject: [PATCH 03/18] fix tests

---
 clang-tools-extra/clang-doc/MDGenerator.cpp   |  3 +-
 clang-tools-extra/test/clang-doc/enum.cpp     | 65 ++++++++++++-------
 .../unittests/clang-doc/MDGeneratorTest.cpp   | 14 ++--
 3 files changed, 48 insertions(+), 34 deletions(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 665c86bfa2016..53605850b7a47 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -82,8 +82,9 @@ static std::string genRawText(const std::vector<CommentInfo> &Comments) {
     {
       if (!First)
         OS << "<br>";
+      else 
+        First = false;
       OS << Comment->Text;
-      First = false;
     }
     for (const auto &CI : Comment->Children)
       Q.push(CI.get());
diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index 6730ad96934f1..d0a308c2d055f 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -33,7 +33,8 @@ enum Color {
 
 // MD-INDEX: ## Enums
 // MD-INDEX: | enum Color |
-// MD-INDEX: --
+// 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 |
@@ -68,7 +69,7 @@ enum Color {
 // HTML-INDEX-NEXT:           <p> For specifying RGB colors</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-45]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-46]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 /**
@@ -87,7 +88,8 @@ enum class Shapes {
 };
 
 // MD-INDEX: | enum class Shapes |
-// MD-INDEX: --
+// 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 |
@@ -122,7 +124,7 @@ enum class Shapes {
 // HTML-INDEX-NEXT:           <p> Shape Types</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-48]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-49]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 typedef unsigned char uint8_t;
@@ -132,16 +134,26 @@ typedef unsigned char uint8_t;
 enum Size : uint8_t {
   // 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>
-  Small,   ///< A pearl
-  Medium,  ///< A tennis ball
-  Large    ///< A football
+
+  /// A pearl.
+  /// Pearls are quite small.
+  ///
+  /// Pearls are used in jewelry.
+  Small,
+
+  /// @brief A tennis ball.
+  Medium,
+
+  /// A football.
+  Large
 };
 
 // MD-INDEX: | enum Size : uint8_t |
-// MD-INDEX: --
-// MD-INDEX: | Small | 0 | A pearl |
-// MD-INDEX: | Medium | 1 | A tennis ball |
-// MD-INDEX: | Large | 2 | A football |
+// MD-INDEX: | Name | Value | Comments |
+// MD-INDEX: |:-:|:-:|:-:|
+// MD-INDEX: | Small | 0 | A pearl.<br> Pearls are quite small.<br> 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:   <div id="{{([0-9A-F]{40})}}" class="delimiter-container">
@@ -173,7 +185,7 @@ enum Size : uint8_t {
 // HTML-INDEX-NEXT:           <p> Specify the size</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-44]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-54]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 /**
@@ -186,7 +198,8 @@ enum : long long {
 };
 
 // MD-INDEX: | enum (unnamed) : long long |
-// MD-INDEX: --
+// MD-INDEX: | Name | Value | Comments |
+// MD-INDEX: |:-:|:-:|:-:|
 // MD-INDEX: | BigVal | 999999999999 | A very large value |
 // MD-INDEX: **brief** Very long number
 
@@ -211,7 +224,7 @@ enum : long long {
 // HTML-INDEX-NEXT:           <p> Very long number</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-32]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-33]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 class FilePermissions {
@@ -224,14 +237,15 @@ class FilePermissions {
   enum {
   // MD-PERM-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
   // HTML-PERM-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
-    Read    = 1,     ///> Permission to READ r
-    Write   = 2,     ///> Permission to WRITE w
-    Execute = 4      ///> Permission to EXECUTE x
+    Read    = 1,     ///< Permission to READ r
+    Write   = 2,     ///< Permission to WRITE w
+    Execute = 4      ///< Permission to EXECUTE x
   };
 };
 
 // MD-PERM: | enum (unnamed) |
-// MD-PERM: --
+// 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 |
@@ -268,7 +282,7 @@ class FilePermissions {
 // HTML-PERM-NEXT:             <p> File permission flags</p>
 // HTML-PERM-NEXT:         </div>
 // HTML-PERM-NEXT:       </div>
-// HTML-PERM-NEXT:         <p>Defined at line [[@LINE-47]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-PERM-NEXT:         <p>Defined at line [[@LINE-48]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-PERM-NEXT:     </div>
 // HTML-PERM-NEXT:   </section>
 
@@ -327,7 +341,8 @@ class Animals {
 // MD-ANIMAL: # class Animals
 // MD-ANIMAL: ## Enums
 // MD-ANIMAL: | enum AnimalType |
-// MD-ANIMAL: --
+// 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 |
@@ -351,7 +366,8 @@ enum Car {
 // MD-VEHICLES: # namespace Vehicles
 // MD-VEHICLES: ## Enums
 // MD-VEHICLES: | enum Car |
-// MD-VEHICLES: --
+// MD-VEHICLES: | Name | Value | Comments |
+// MD-VEHICLES: |:-:|:-:|:-:|
 // MD-VEHICLES: | Sedan | 0 | Comment 1 |
 // MD-VEHICLES: | SUV | 1 | Comment 2 |
 // MD-VEHICLES: | Pickup | 2 | Comment 3 |
@@ -391,7 +407,7 @@ enum Car {
 // HTML-VEHICLES-NEXT:           <p> specify type of car</p>
 // HTML-VEHICLES-NEXT:        </div>
 // HTML-VEHICLES-NEXT:      </div>
-// HTML-VEHICLES-NEXT:      <p>Defined at line [[@LINE-54]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-VEHICLES-NEXT:      <p>Defined at line [[@LINE-55]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-VEHICLES-NEXT:    </div>
 
 enum ColorUserSpecified {
@@ -401,7 +417,8 @@ enum ColorUserSpecified {
 };
 
 // MD-INDEX: | enum ColorUserSpecified |
-// MD-INDEX: --
+// MD-INDEX: | Name | Value |
+// MD-INDEX: |:-:|:-:|
 // MD-INDEX: | RedUserSpecified | 65 |
 // MD-INDEX: | GreenUserSpecified | 2 |
 // MD-INDEX: | BlueUserSpecified | 67 |
@@ -430,5 +447,5 @@ enum ColorUserSpecified {
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:         </tbody>
 // HTML-INDEX-NEXT:     </table>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-36]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-37]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
diff --git a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
index b44b09b28e90f..d9f32f8eabc41 100644
--- a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
@@ -69,9 +69,7 @@ TEST_F(MDGeneratorTest, emitNamespaceMD) {
 
 | enum OneEnum |
 
---
-
-
+| Name | Value |
 
 
 
@@ -136,9 +134,7 @@ ChildStruct
 
 | enum OneEnum |
 
---
-
-
+| Name | Value |
 
 
 
@@ -197,9 +193,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*

>From 9980251ef58bc63792bcb23b59e4a3fc0403b071 Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Mon, 2 Mar 2026 17:19:15 +0530
Subject: [PATCH 04/18] format the code

---
 clang-tools-extra/clang-doc/MDGenerator.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 53605850b7a47..c410be0b1769b 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -82,7 +82,7 @@ static std::string genRawText(const std::vector<CommentInfo> &Comments) {
     {
       if (!First)
         OS << "<br>";
-      else 
+      else
         First = false;
       OS << Comment->Text;
     }

>From 87cb5c17c728906b67d86bc0557a7f974daf9960 Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Mon, 2 Mar 2026 17:28:00 +0530
Subject: [PATCH 05/18] clang-format the code

---
 clang-tools-extra/clang-doc/MDGenerator.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index c410be0b1769b..e6b67fdec81e3 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -78,8 +78,7 @@ static std::string genRawText(const std::vector<CommentInfo> &Comments) {
   while (Q.size()) {
     Comment = Q.front();
     Q.pop();
-    if (!Comment->Text.empty())
-    {
+    if (!Comment->Text.empty()) {
       if (!First)
         OS << "<br>";
       else
@@ -212,7 +211,8 @@ static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
       if (!N.Value.empty())
         Members << "| " << N.Value << " ";
       if (HasComments) {
-        std::string RawComment = StringRef(genRawText(N.Description)).trim().str();
+        std::string RawComment =
+            StringRef(genRawText(N.Description)).trim().str();
         Members << "| " << (RawComment.empty() ? "--" : RawComment) << " ";
       }
       Members << "|\n";

>From 96a9e19a3593acd7c385332a1677807dde0cc1da Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Wed, 4 Mar 2026 19:37:22 +0530
Subject: [PATCH 06/18] Switch to a recursive solution to handle paragraphs

---
 clang-tools-extra/clang-doc/MDGenerator.cpp | 63 +++++++++++++--------
 clang-tools-extra/test/clang-doc/enum.cpp   |  2 +-
 2 files changed, 41 insertions(+), 24 deletions(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index e6b67fdec81e3..bec44a88944bb 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -8,7 +8,9 @@
 
 #include "Generators.h"
 #include "Representation.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Path.h"
@@ -67,28 +69,42 @@ static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L,
   OS << "\n\n";
 }
 
-static std::string genRawText(const std::vector<CommentInfo> &Comments) {
-  std::string Result;
-  llvm::raw_string_ostream OS(Result);
-  std::queue<const CommentInfo *> Q;
-  for (const auto &CI : Comments)
-    Q.push(&CI);
-  const CommentInfo *Comment;
-  bool First = true;
-  while (Q.size()) {
-    Comment = Q.front();
-    Q.pop();
-    if (!Comment->Text.empty()) {
-      if (!First)
-        OS << "<br>";
-      else
-        First = false;
-      OS << Comment->Text;
+static void extractChildComment(const CommentInfo &Comment,
+                                llvm::raw_ostream &OS, bool &FirstLine,
+                                bool &FirstParagraph, bool &ParagraphBreak) {
+  if (Comment.Kind == CommentKind::CK_ParagraphComment) {
+    if (!FirstParagraph)
+      ParagraphBreak = true;
+  }
+
+  if (!Comment.Text.empty()) {
+    if (FirstParagraph)
+      FirstParagraph = false;
+    if (ParagraphBreak) {
+      OS << "<br><br>";
+      ParagraphBreak = false;
+      FirstLine = true;
     }
-    for (const auto &CI : Comment->Children)
-      Q.push(CI.get());
+
+    if (!FirstLine)
+      OS << "<br>";
+    else
+      FirstLine = false;
+
+    OS << Comment.Text;
   }
-  return Result;
+
+  for (const auto &Child : Comment.Children)
+    extractChildComment(*Child, OS, FirstLine, FirstParagraph, ParagraphBreak);
+}
+
+static void genCommentString(ArrayRef<CommentInfo> Comments,
+                             std::string &Buffer) {
+  llvm::raw_string_ostream OS(Buffer);
+  bool FirstLine = true, FirstParagraph = true, ParagraphBreak = false;
+
+  for (const auto &Child : Comments)
+    extractChildComment(Child, OS, FirstLine, FirstParagraph, ParagraphBreak);
 }
 
 static void maybeWriteSourceFileRef(llvm::raw_ostream &OS,
@@ -211,9 +227,10 @@ static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
       if (!N.Value.empty())
         Members << "| " << N.Value << " ";
       if (HasComments) {
-        std::string RawComment =
-            StringRef(genRawText(N.Description)).trim().str();
-        Members << "| " << (RawComment.empty() ? "--" : RawComment) << " ";
+        std::string CommentString;
+        genCommentString(ArrayRef(N.Description), CommentString);
+        Members << "| " << (CommentString.empty() ? "--" : CommentString)
+                << " ";
       }
       Members << "|\n";
     }
diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index d0a308c2d055f..6cb7c3fcdbdc5 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -151,7 +151,7 @@ enum Size : uint8_t {
 // MD-INDEX: | enum Size : uint8_t |
 // MD-INDEX: | Name | Value | Comments |
 // MD-INDEX: |:-:|:-:|:-:|
-// MD-INDEX: | Small | 0 | A pearl.<br> Pearls are quite small.<br> Pearls are used in jewelry. |
+// MD-INDEX: | Small | 0 | A pearl.<br> Pearls are quite small.<br><br> Pearls are used in jewelry. |
 // MD-INDEX: | Medium | 1 | A tennis ball. |
 // MD-INDEX: | Large | 2 | A football. |
 // MD-INDEX: **brief** Specify the size

>From fdae5455175fd2952b68afa22283234fe42259b2 Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Wed, 4 Mar 2026 19:47:30 +0530
Subject: [PATCH 07/18] remove unused header file

---
 clang-tools-extra/clang-doc/MDGenerator.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index bec44a88944bb..ebbeda08dad28 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -10,7 +10,6 @@
 #include "Representation.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Path.h"

>From ed2830985d476d38dcfdf1c7f9d499a12aead6fd Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Fri, 6 Mar 2026 03:47:14 +0530
Subject: [PATCH 08/18] implement paragraph breaks without using boolean flags

---
 clang-tools-extra/clang-doc/MDGenerator.cpp | 68 +++++++++++----------
 1 file changed, 36 insertions(+), 32 deletions(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index ebbeda08dad28..3629307350a41 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -68,42 +68,47 @@ static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L,
   OS << "\n\n";
 }
 
-static void extractChildComment(const CommentInfo &Comment,
-                                llvm::raw_ostream &OS, bool &FirstLine,
-                                bool &FirstParagraph, bool &ParagraphBreak) {
-  if (Comment.Kind == CommentKind::CK_ParagraphComment) {
-    if (!FirstParagraph)
-      ParagraphBreak = true;
-  }
+static std::string extractCommentText(const CommentInfo &Comment) {
+  std::vector<std::string> Parts;
+
+  if (!Comment.Text.empty())
+    Parts.push_back(StringRef(Comment.Text).str());
+
+  for (size_t Idx = 0; Idx < Comment.Children.size(); ++Idx) {
+    const auto &Child = Comment.Children[Idx];
+    std::string ChildText = extractCommentText(*Child);
 
-  if (!Comment.Text.empty()) {
-    if (FirstParagraph)
-      FirstParagraph = false;
-    if (ParagraphBreak) {
-      OS << "<br><br>";
-      ParagraphBreak = false;
-      FirstLine = true;
+    if (ChildText.empty())
+      continue;
+
+    if (Idx > 0 && Comment.Kind == CommentKind::CK_ParagraphComment) {
+      if (Comment.Children[Idx - 1]->Kind == CommentKind::CK_TextComment &&
+          Child->Kind == CommentKind::CK_TextComment) {
+        Parts.push_back("<br>");
+      }
     }
 
-    if (!FirstLine)
-      OS << "<br>";
-    else
-      FirstLine = false;
+    Parts.push_back(ChildText);
+  }
 
-    OS << Comment.Text;
+  if (Comment.Kind == CommentKind::CK_BlockCommandComment ||
+      Comment.Kind == CommentKind::CK_FullComment) {
+    return llvm::join(Parts, "<br><br>");
   }
 
-  for (const auto &Child : Comment.Children)
-    extractChildComment(*Child, OS, FirstLine, FirstParagraph, ParagraphBreak);
+  return llvm::join(Parts, "");
 }
 
-static void genCommentString(ArrayRef<CommentInfo> Comments,
-                             std::string &Buffer) {
-  llvm::raw_string_ostream OS(Buffer);
-  bool FirstLine = true, FirstParagraph = true, ParagraphBreak = false;
-
-  for (const auto &Child : Comments)
-    extractChildComment(Child, OS, FirstLine, FirstParagraph, ParagraphBreak);
+static void genCommentString(llvm::ArrayRef<CommentInfo> Comments,
+                             llvm::raw_ostream &OS) {
+  std::string CommentText = "";
+  for (const auto &Child : Comments) {
+    CommentText += extractCommentText(Child);
+  }
+  if (!CommentText.empty())
+    OS << CommentText;
+  else
+    OS << "--";
 }
 
 static void maybeWriteSourceFileRef(llvm::raw_ostream &OS,
@@ -226,10 +231,9 @@ static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
       if (!N.Value.empty())
         Members << "| " << N.Value << " ";
       if (HasComments) {
-        std::string CommentString;
-        genCommentString(ArrayRef(N.Description), CommentString);
-        Members << "| " << (CommentString.empty() ? "--" : CommentString)
-                << " ";
+        Members << "| ";
+        genCommentString(ArrayRef(N.Description), Members);
+        Members << " ";
       }
       Members << "|\n";
     }

>From 4468b4bcb897e79ce8ad83461676b41ddecf0d0d Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Fri, 6 Mar 2026 22:11:41 +0530
Subject: [PATCH 09/18] use streams rather than vector of strings

---
 clang-tools-extra/clang-doc/MDGenerator.cpp | 49 ++++++++++-----------
 1 file changed, 24 insertions(+), 25 deletions(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 3629307350a41..e0b05cd5f5d5b 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -9,11 +9,13 @@
 #include "Generators.h"
 #include "Representation.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
+#include <cstdint>
 #include <string>
 
 using namespace llvm;
@@ -68,46 +70,43 @@ static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L,
   OS << "\n\n";
 }
 
-static std::string extractCommentText(const CommentInfo &Comment) {
-  std::vector<std::string> Parts;
+static void extractCommentText(const CommentInfo &Comment,
+                               llvm::raw_ostream &OS) {
 
-  if (!Comment.Text.empty())
-    Parts.push_back(StringRef(Comment.Text).str());
+  OS << Comment.Text;
+  bool FirstChild = true;
 
-  for (size_t Idx = 0; Idx < Comment.Children.size(); ++Idx) {
+  for (size_t Idx = 0, End = Comment.Children.size(); Idx < End; ++Idx) {
     const auto &Child = Comment.Children[Idx];
-    std::string ChildText = extractCommentText(*Child);
+    llvm::SmallString<128> ChildBuffer;
+    llvm::raw_svector_ostream ChildOS(ChildBuffer);
+    extractCommentText(*Child, ChildOS);
 
-    if (ChildText.empty())
+    if (ChildBuffer.empty())
       continue;
 
-    if (Idx > 0 && Comment.Kind == CommentKind::CK_ParagraphComment) {
-      if (Comment.Children[Idx - 1]->Kind == CommentKind::CK_TextComment &&
-          Child->Kind == CommentKind::CK_TextComment) {
-        Parts.push_back("<br>");
-      }
-    }
+    if (Idx > 0 && Comment.Kind == CommentKind::CK_ParagraphComment &&
+        Comment.Children[Idx - 1]->Kind == CommentKind::CK_TextComment &&
+        Child->Kind == CommentKind::CK_TextComment)
+      OS << "<br>";
 
-    Parts.push_back(ChildText);
-  }
+    if (FirstChild)
+      FirstChild = false;
+    else if (Comment.Kind == CommentKind::CK_BlockCommandComment ||
+             Comment.Kind == CommentKind::CK_FullComment)
+      OS << "<br><br>";
 
-  if (Comment.Kind == CommentKind::CK_BlockCommandComment ||
-      Comment.Kind == CommentKind::CK_FullComment) {
-    return llvm::join(Parts, "<br><br>");
+    OS << ChildBuffer;
   }
-
-  return llvm::join(Parts, "");
 }
 
 static void genCommentString(llvm::ArrayRef<CommentInfo> Comments,
                              llvm::raw_ostream &OS) {
-  std::string CommentText = "";
+  uint64_t InitPos = OS.tell();
   for (const auto &Child : Comments) {
-    CommentText += extractCommentText(Child);
+    extractCommentText(Child, OS);
   }
-  if (!CommentText.empty())
-    OS << CommentText;
-  else
+  if (InitPos == OS.tell())
     OS << "--";
 }
 

>From 7de7e01db6377b616370a65234f4347f14091f90 Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Sat, 7 Mar 2026 18:28:02 +0530
Subject: [PATCH 10/18] use an efficient logic for formatting comments

A big thanks to @ilovepi for the code and approach.
Also revert to left-padding the table content.
---
 clang-tools-extra/clang-doc/MDGenerator.cpp   | 104 ++++++++++--------
 clang-tools-extra/test/clang-doc/enum.cpp     |  16 +--
 .../unittests/clang-doc/MDGeneratorTest.cpp   |   5 +-
 3 files changed, 68 insertions(+), 57 deletions(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index e0b05cd5f5d5b..4a9f159659b25 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -15,7 +15,6 @@
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/raw_ostream.h"
-#include <cstdint>
 #include <string>
 
 using namespace llvm;
@@ -70,43 +69,60 @@ static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L,
   OS << "\n\n";
 }
 
-static void extractCommentText(const CommentInfo &Comment,
-                               llvm::raw_ostream &OS) {
+struct CommentState {
+  bool Started = false;
+  bool NeedsParagraphBreak = false;
 
-  OS << Comment.Text;
-  bool FirstChild = true;
-
-  for (size_t Idx = 0, End = Comment.Children.size(); Idx < End; ++Idx) {
-    const auto &Child = Comment.Children[Idx];
-    llvm::SmallString<128> ChildBuffer;
-    llvm::raw_svector_ostream ChildOS(ChildBuffer);
-    extractCommentText(*Child, ChildOS);
+  void insertSeparator(llvm::raw_ostream &OS) {
+    if (!Started)
+      return;
+    if (NeedsParagraphBreak) {
+      OS << "<br><br>";
+      NeedsParagraphBreak = false;
+    } else {
+      OS << "<br>";
+    }
+  }
+};
 
-    if (ChildBuffer.empty())
-      continue;
+static void writeTableSafeComment(const CommentInfo &I, llvm::raw_ostream &OS,
+                                  CommentState &State) {
+  switch (I.Kind) {
+  case CommentKind::CK_FullComment:
+    for (const auto &Child : I.Children)
+      writeTableSafeComment(*Child, OS, State);
+    break;
 
-    if (Idx > 0 && Comment.Kind == CommentKind::CK_ParagraphComment &&
-        Comment.Children[Idx - 1]->Kind == CommentKind::CK_TextComment &&
-        Child->Kind == CommentKind::CK_TextComment)
-      OS << "<br>";
+  case CommentKind::CK_ParagraphComment:
+    for (const auto &Child : I.Children)
+      writeTableSafeComment(*Child, OS, State);
+    State.NeedsParagraphBreak =
+        true; // Next content after a paragraph needs a break
+    break;
 
-    if (FirstChild)
-      FirstChild = false;
-    else if (Comment.Kind == CommentKind::CK_BlockCommandComment ||
-             Comment.Kind == CommentKind::CK_FullComment)
-      OS << "<br><br>";
+  case CommentKind::CK_TextComment:
+    if (!I.Text.empty()) {
+      State.insertSeparator(OS);
+      OS << I.Text;
+      State.Started = true;
+    }
+    break;
 
-    OS << ChildBuffer;
+  // Handle other comment types (BlockCommand, InlineCommand, etc.)
+  default:
+    for (const auto &Child : I.Children)
+      writeTableSafeComment(*Child, OS, State);
+    break;
   }
 }
 
-static void genCommentString(llvm::ArrayRef<CommentInfo> Comments,
-                             llvm::raw_ostream &OS) {
-  uint64_t InitPos = OS.tell();
-  for (const auto &Child : Comments) {
-    extractCommentText(Child, OS);
-  }
-  if (InitPos == OS.tell())
+static void genMDComment(llvm::ArrayRef<CommentInfo> Comments,
+                         llvm::raw_ostream &OS) {
+  CommentState State;
+  for (const auto &C : Comments)
+    writeTableSafeComment(C, OS, State);
+
+  if (!State.Started)
     OS << "--";
 }
 
@@ -208,36 +224,34 @@ static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
   }
   OS << "|\n\n";
 
-  std::string Buffer;
-  llvm::raw_string_ostream Members(Buffer);
-  Members << "| Name | Value |";
+  OS << "| Name | Value |";
   if (!I.Members.empty()) {
     bool HasComments = false;
     for (const auto &Member : I.Members) {
       if (!Member.Description.empty()) {
         HasComments = true;
-        Members << " Comments |";
+        OS << " Comments |";
         break;
       }
     }
-    Members << "\n";
-    Members << "|:-:|:-:|";
+    OS << "\n";
+    OS << "|---|---|";
     if (HasComments)
-      Members << ":-:|";
-    Members << "\n";
+      OS << "---|";
+    OS << "\n";
     for (const auto &N : I.Members) {
-      Members << "| " << N.Name << " ";
+      OS << "| " << N.Name << " ";
       if (!N.Value.empty())
-        Members << "| " << N.Value << " ";
+        OS << "| " << N.Value << " ";
       if (HasComments) {
-        Members << "| ";
-        genCommentString(ArrayRef(N.Description), Members);
-        Members << " ";
+        OS << "| ";
+        genMDComment(ArrayRef(N.Description), OS);
+        OS << " ";
       }
-      Members << "|\n";
+      OS << "|\n";
     }
   }
-  writeLine(Members.str(), OS);
+  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 6cb7c3fcdbdc5..5168afc7c74b0 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -34,7 +34,7 @@ enum Color {
 // MD-INDEX: ## Enums
 // MD-INDEX: | enum Color |
 // MD-INDEX: | Name | Value | Comments |
-// MD-INDEX: |:-:|:-:|:-:|
+// MD-INDEX: |---|---|---|
 // MD-INDEX: | Red | 0 | Comment 1 |
 // MD-INDEX: | Green | 1 | Comment 2 |
 // MD-INDEX: | Blue | 2 | Comment 3 |
@@ -89,7 +89,7 @@ enum class Shapes {
 
 // MD-INDEX: | enum class Shapes |
 // MD-INDEX: | Name | Value | Comments |
-// MD-INDEX: |:-:|:-:|:-:|
+// MD-INDEX: |---|---|---|
 // MD-INDEX: | Circle | 0 | Comment 1 |
 // MD-INDEX: | Rectangle | 1 | Comment 2 |
 // MD-INDEX: | Triangle | 2 | Comment 3 |
@@ -150,7 +150,7 @@ enum Size : uint8_t {
 
 // MD-INDEX: | enum Size : uint8_t |
 // MD-INDEX: | Name | Value | Comments |
-// MD-INDEX: |:-:|:-:|:-:|
+// MD-INDEX: |---|---|---|
 // MD-INDEX: | Small | 0 | A pearl.<br> Pearls are quite small.<br><br> Pearls are used in jewelry. |
 // MD-INDEX: | Medium | 1 | A tennis ball. |
 // MD-INDEX: | Large | 2 | A football. |
@@ -199,7 +199,7 @@ enum : long long {
 
 // MD-INDEX: | enum (unnamed) : long long |
 // MD-INDEX: | Name | Value | Comments |
-// MD-INDEX: |:-:|:-:|:-:|
+// MD-INDEX: |---|---|---|
 // MD-INDEX: | BigVal | 999999999999 | A very large value |
 // MD-INDEX: **brief** Very long number
 
@@ -245,7 +245,7 @@ class FilePermissions {
 
 // MD-PERM: | enum (unnamed) |
 // MD-PERM: | Name | Value | Comments |
-// MD-PERM: |:-:|:-:|:-:|
+// 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 |
@@ -342,7 +342,7 @@ class Animals {
 // MD-ANIMAL: ## Enums
 // MD-ANIMAL: | enum AnimalType |
 // MD-ANIMAL: | Name | Value | Comments |
-// MD-ANIMAL: |:-:|:-:|:-:|
+// 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 |
@@ -367,7 +367,7 @@ enum Car {
 // MD-VEHICLES: ## Enums
 // MD-VEHICLES: | enum Car |
 // MD-VEHICLES: | Name | Value | Comments |
-// MD-VEHICLES: |:-:|:-:|:-:|
+// MD-VEHICLES: |---|---|---|
 // MD-VEHICLES: | Sedan | 0 | Comment 1 |
 // MD-VEHICLES: | SUV | 1 | Comment 2 |
 // MD-VEHICLES: | Pickup | 2 | Comment 3 |
@@ -418,7 +418,7 @@ enum ColorUserSpecified {
 
 // MD-INDEX: | enum ColorUserSpecified |
 // MD-INDEX: | Name | Value |
-// MD-INDEX: |:-:|:-:|
+// MD-INDEX: |---|---|
 // MD-INDEX: | RedUserSpecified | 65 |
 // MD-INDEX: | GreenUserSpecified | 2 |
 // MD-INDEX: | BlueUserSpecified | 67 |
diff --git a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
index d9f32f8eabc41..53d8b8444b3ad 100644
--- a/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp
@@ -72,7 +72,6 @@ TEST_F(MDGeneratorTest, emitNamespaceMD) {
 | Name | Value |
 
 
-
 )raw";
   EXPECT_EQ(Expected, Actual.str());
 }
@@ -137,7 +136,6 @@ ChildStruct
 | Name | Value |
 
 
-
 )raw";
   EXPECT_EQ(Expected, Actual.str());
 }
@@ -194,10 +192,9 @@ TEST_F(MDGeneratorTest, emitEnumMD) {
   std::string Expected = R"raw(| enum class e |
 
 | Name | Value |
-|:-:|:-:|
+|---|---|
 | X | 0 |
 
-
 *Defined at test.cpp#10*
 
 )raw";

>From 6c9152300c3d5188b1646663a589f52551faeacb Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Sat, 7 Mar 2026 18:33:35 +0530
Subject: [PATCH 11/18] remove unused header SmallString.h

---
 clang-tools-extra/clang-doc/MDGenerator.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 4a9f159659b25..18e9895ea590a 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -9,7 +9,6 @@
 #include "Generators.h"
 #include "Representation.h"
 #include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/FileSystem.h"
 #include "llvm/Support/FormatVariadic.h"

>From 302c185f62867c22036ea2c7baeaf638fc6cbd06 Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Tue, 10 Mar 2026 02:05:44 +0530
Subject: [PATCH 12/18] implement suggested changes

---
 clang-tools-extra/clang-doc/MDGenerator.cpp | 99 ++++++++++++---------
 1 file changed, 58 insertions(+), 41 deletions(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 18e9895ea590a..e5e5e1cf6fe68 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -68,11 +68,27 @@ static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L,
   OS << "\n\n";
 }
 
-struct CommentState {
+/// 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 MDCommentWriter {
+private:
+  llvm::raw_ostream &OS;
   bool Started = false;
   bool NeedsParagraphBreak = false;
 
-  void insertSeparator(llvm::raw_ostream &OS) {
+  /// 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) {
@@ -82,48 +98,49 @@ struct CommentState {
       OS << "<br>";
     }
   }
-};
-
-static void writeTableSafeComment(const CommentInfo &I, llvm::raw_ostream &OS,
-                                  CommentState &State) {
-  switch (I.Kind) {
-  case CommentKind::CK_FullComment:
-    for (const auto &Child : I.Children)
-      writeTableSafeComment(*Child, OS, State);
-    break;
 
-  case CommentKind::CK_ParagraphComment:
-    for (const auto &Child : I.Children)
-      writeTableSafeComment(*Child, OS, State);
-    State.NeedsParagraphBreak =
-        true; // Next content after a paragraph needs a break
-    break;
+  /// 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;
 
-  case CommentKind::CK_TextComment:
-    if (!I.Text.empty()) {
-      State.insertSeparator(OS);
-      OS << I.Text;
-      State.Started = true;
+    // Handle other comment types (BlockCommand, InlineCommand, etc.)
+    default:
+      for (const auto &Child : I.Children)
+        writeTableSafeComment(*Child);
+      break;
     }
-    break;
-
-  // Handle other comment types (BlockCommand, InlineCommand, etc.)
-  default:
-    for (const auto &Child : I.Children)
-      writeTableSafeComment(*Child, OS, State);
-    break;
   }
-}
 
-static void genMDComment(llvm::ArrayRef<CommentInfo> Comments,
-                         llvm::raw_ostream &OS) {
-  CommentState State;
-  for (const auto &C : Comments)
-    writeTableSafeComment(C, OS, State);
+public:
+  explicit MDCommentWriter(llvm::raw_ostream &OS) : OS(OS) {}
 
-  if (!State.Started)
-    OS << "--";
-}
+  void write(llvm::ArrayRef<CommentInfo> Comments) {
+    for (const auto &C : Comments)
+      writeTableSafeComment(C);
+
+    if (!Started)
+      OS << "--";
+  }
+};
 
 static void maybeWriteSourceFileRef(llvm::raw_ostream &OS,
                                     const ClangDocContext &CDCtx,
@@ -233,8 +250,7 @@ static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
         break;
       }
     }
-    OS << "\n";
-    OS << "|---|---|";
+    OS << "\n|---|---|";
     if (HasComments)
       OS << "---|";
     OS << "\n";
@@ -244,7 +260,8 @@ static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
         OS << "| " << N.Value << " ";
       if (HasComments) {
         OS << "| ";
-        genMDComment(ArrayRef(N.Description), OS);
+        MDCommentWriter CommentWriter(OS);
+        CommentWriter.write(N.Description);
         OS << " ";
       }
       OS << "|\n";

>From 96b7bcfef8ac1ab74e5ed457374e775a0f16da76 Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Tue, 10 Mar 2026 04:05:13 +0530
Subject: [PATCH 13/18] rename the class and reorder the functions

---
 clang-tools-extra/clang-doc/MDGenerator.cpp | 32 ++++++++++-----------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index e5e5e1cf6fe68..de1703d7b6fa1 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -78,12 +78,19 @@ static void writeSourceFileRef(const ClangDocContext &CDCtx, const Location &L,
 /// Usage :
 /// Initialize an object with a llvm::raw_ostream to output into.
 /// Call the write(C) function with an array of Comments 'C'.
-class MDCommentWriter {
-private:
-  llvm::raw_ostream &OS;
-  bool Started = false;
-  bool NeedsParagraphBreak = false;
+class TableCommentWriter {
+public:
+  explicit TableCommentWriter(llvm::raw_ostream &OS) : OS(OS) {}
+
+  void write(llvm::ArrayRef<CommentInfo> 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.
@@ -130,16 +137,9 @@ class MDCommentWriter {
     }
   }
 
-public:
-  explicit MDCommentWriter(llvm::raw_ostream &OS) : OS(OS) {}
-
-  void write(llvm::ArrayRef<CommentInfo> Comments) {
-    for (const auto &C : Comments)
-      writeTableSafeComment(C);
-
-    if (!Started)
-      OS << "--";
-  }
+  llvm::raw_ostream &OS;
+  bool Started = false;
+  bool NeedsParagraphBreak = false;
 };
 
 static void maybeWriteSourceFileRef(llvm::raw_ostream &OS,
@@ -260,7 +260,7 @@ static void genMarkdown(const ClangDocContext &CDCtx, const EnumInfo &I,
         OS << "| " << N.Value << " ";
       if (HasComments) {
         OS << "| ";
-        MDCommentWriter CommentWriter(OS);
+        TableCommentWriter CommentWriter(OS);
         CommentWriter.write(N.Description);
         OS << " ";
       }

>From 45a875c044605e6b4565327764676ff5315cd0ea Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Tue, 10 Mar 2026 04:22:44 +0530
Subject: [PATCH 14/18] fix tests

---
 clang-tools-extra/test/clang-doc/enum.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index 5168afc7c74b0..7e71aaa67d8de 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -69,7 +69,7 @@ enum Color {
 // HTML-INDEX-NEXT:           <p> For specifying RGB colors</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-46]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-47]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 /**
@@ -124,7 +124,7 @@ enum class Shapes {
 // HTML-INDEX-NEXT:           <p> Shape Types</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-49]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-50]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 typedef unsigned char uint8_t;

>From 88a055dee09babe21f88874a6373c506846d194b Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Tue, 10 Mar 2026 04:41:31 +0530
Subject: [PATCH 15/18] fix tests

---
 clang-tools-extra/test/clang-doc/enum.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index 7e71aaa67d8de..db9144b239df2 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -407,7 +407,7 @@ enum Car {
 // HTML-VEHICLES-NEXT:           <p> specify type of car</p>
 // HTML-VEHICLES-NEXT:        </div>
 // HTML-VEHICLES-NEXT:      </div>
-// HTML-VEHICLES-NEXT:      <p>Defined at line [[@LINE-55]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-VEHICLES-NEXT:      <p>Defined at line [[@LINE-56]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-VEHICLES-NEXT:    </div>
 
 enum ColorUserSpecified {

>From 59f1cddb53c4f8e38acd00939b882d2efdc5edbe Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Wed, 11 Mar 2026 21:05:28 +0530
Subject: [PATCH 16/18] [Clang-doc] Display enum comments in HTML (#183085)

Currently comments of enum variables are not displayed in HTML.
Add support to display the comments.
---
 clang-tools-extra/clang-doc/JSONGenerator.cpp |  66 +++---
 .../clang-doc/assets/clang-doc-mustache.css   |  10 +-
 .../clang-doc/assets/enum-template.mustache   |  22 ++
 clang-tools-extra/test/clang-doc/enum.cpp     | 122 ++++++++++-
 .../test/clang-doc/json/enum.cpp              | 201 ++++++++++++++++++
 5 files changed, 386 insertions(+), 35 deletions(-)
 create mode 100644 clang-tools-extra/test/clang-doc/json/enum.cpp

diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index 9a8f51b808cab..7c0485692fad2 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -1,5 +1,6 @@
 #include "Generators.h"
 #include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/JSON.h"
 
 using namespace llvm;
@@ -353,10 +354,34 @@ static void generateContext(const Info &I, Object &Obj) {
   Obj["HasContexts"] = true;
 }
 
-static void
-serializeCommonAttributes(const Info &I, json::Object &Obj,
-                          const std::optional<StringRef> RepositoryUrl,
-                          const std::optional<StringRef> RepositoryLinePrefix) {
+static void serializeDescription(llvm::ArrayRef<CommentInfo> Description,
+                                 json::Object &Obj, StringRef Key = "") {
+  if (Description.empty())
+    return;
+
+  // Skip straight to the FullComment's children
+  auto &Comments = Description.front().Children;
+  Object DescriptionObj = Object();
+  for (const auto &CommentInfo : Comments) {
+    json::Value Comment = serializeComment(*CommentInfo, DescriptionObj);
+    // if a ParagraphComment is returned, then it is a top-level comment that
+    // needs to be inserted manually.
+    if (auto *ParagraphComment = Comment.getAsObject();
+        ParagraphComment->get("ParagraphComment")) {
+      auto TextCommentsArray = extractTextComments(ParagraphComment);
+      if (TextCommentsArray.kind() == json::Value::Null ||
+          TextCommentsArray.getAsArray()->empty())
+        continue;
+      insertComment(DescriptionObj, TextCommentsArray, "ParagraphComments");
+    }
+  }
+  Obj["Description"] = std::move(DescriptionObj);
+  if (!Key.empty())
+    Obj[Key] = true;
+}
+
+void JSONGenerator::serializeCommonAttributes(const Info &I,
+                                              json::Object &Obj) {
   insertNonEmpty("Name", I.Name, Obj);
   Obj["USR"] = toHex(toStringRef(I.USR));
   Obj["InfoType"] = infoTypeToString(I.IT);
@@ -372,25 +397,7 @@ serializeCommonAttributes(const Info &I, json::Object &Obj,
       Obj["Namespace"].getAsArray()->push_back(NS.Name);
   }
 
-  if (!I.Description.empty()) {
-    Object Description = Object();
-    // Skip straight to the FullComment's children
-    auto &Comments = I.Description.at(0).Children;
-    for (const auto &CommentInfo : Comments) {
-      json::Value Comment = serializeComment(*CommentInfo, Description);
-      // if a ParagraphComment is returned, then it is a top-level comment that
-      // needs to be inserted manually.
-      if (auto *ParagraphComment = Comment.getAsObject();
-          ParagraphComment->get("ParagraphComment")) {
-        auto TextCommentsArray = extractTextComments(ParagraphComment);
-        if (TextCommentsArray.kind() == json::Value::Null ||
-            TextCommentsArray.getAsArray()->empty())
-          continue;
-        insertComment(Description, TextCommentsArray, "ParagraphComments");
-      }
-    }
-    Obj["Description"] = std::move(Description);
-  }
+  serializeDescription(I.Description, Obj);
 
   // Namespaces aren't SymbolInfos, so they dont have a DefLoc
   if (I.IT != InfoType::IT_namespace) {
@@ -565,6 +572,8 @@ static void serializeInfo(const EnumValueInfo &I, Object &Obj) {
     Obj["ValueExpr"] = I.ValueExpr;
   else
     Obj["Value"] = I.Value;
+
+  serializeDescription(I.Description, Obj, "HasEnumMemberComments");
 }
 
 static void serializeInfo(const EnumInfo &I, json::Object &Obj,
@@ -582,8 +591,15 @@ static void serializeInfo(const EnumInfo &I, json::Object &Obj,
     Obj["BaseType"] = BaseTypeVal;
   }
 
-  if (!I.Members.empty())
-    serializeArray(I.Members, Obj, "Members", SerializeInfoLambda);
+  if (!I.Members.empty()) {
+    for (const auto &Member : I.Members) {
+      if (!Member.Description.empty()) {
+        Obj["HasComments"] = true;
+        break;
+      }
+    }
+    serializeArray(I.Members, Obj, "Members", serializeInfoLambda());
+  }
 }
 
 static void
diff --git a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
index 3727981efddc5..fcdf2df013f59 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -528,4 +528,12 @@ a, a:visited, a:hover, a:active {
 
 .param-container {
     display: block;
-}
\ No newline at end of file
+}
+
+.paragraph-container {
+    margin-bottom: 1rem;
+}
+
+.paragraph-container:last-of-type {
+    margin-bottom: 0rem;
+}
diff --git a/clang-tools-extra/clang-doc/assets/enum-template.mustache b/clang-tools-extra/clang-doc/assets/enum-template.mustache
index d88f88cc30cee..60429dc6df9d3 100644
--- a/clang-tools-extra/clang-doc/assets/enum-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/enum-template.mustache
@@ -15,6 +15,9 @@
             <tr>
                 <th>Name</th>
                 <th>Value</th>
+                {{#HasComments}}
+                <th>Comments</th>
+                {{/HasComments}}
             </tr>
             {{#Members}}
             <tr>
@@ -26,6 +29,25 @@
                 {{^Value}}
                 <td>{{ValueExpr}}</td>
                 {{/Value}}
+                {{#HasComments}}
+                <td>{{#HasEnumMemberComments}}
+                    {{#Description}}
+                        {{#BriefComments}}
+                            {{#.}}
+                            <p class="paragraph-container">{{TextComment}}</p>
+                            {{/.}}
+                        {{/BriefComments}}
+                        {{#ParagraphComments}}
+                        <p class="paragraph-container">
+                            {{#.}}
+                            {{TextComment}}<br>
+                            {{/.}}
+                        </p>
+                        {{/ParagraphComments}}
+                    {{/Description}}
+                    {{/HasEnumMemberComments}}
+                    {{^HasEnumMemberComments}} -- {{/HasEnumMemberComments}}</td>
+                {{/HasComments}}
             </tr>
             {{/Members}}
         </tbody>
diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index db9144b239df2..1355a5a6fbff3 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -18,7 +18,6 @@
 // RUN: FileCheck %s < %t/Vehicles/index.md --check-prefix=MD-VEHICLES-LINE
 // RUN: FileCheck %s < %t/Vehicles/index.md --check-prefix=MD-VEHICLES
 
-// COM: FIXME: Add enum value comments to template
 
 /**
  * @brief For specifying RGB colors
@@ -49,18 +48,34 @@ enum Color {
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <th>Name</th>
 // HTML-INDEX-NEXT:                 <th>Value</th>
+// HTML-INDEX-NEXT:                 <th>Comments</th>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <td>Red</td>
 // HTML-INDEX-NEXT:                 <td>0</td>
+// HTML-INDEX-NEXT:                 <td>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">
+// HTML-INDEX-NEXT:                       Comment 1<br>
+// HTML-INDEX-NEXT:                   </p>
+// HTML-INDEX-NEXT:                 </td>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <td>Green</td>
 // HTML-INDEX-NEXT:                 <td>1</td>
+// HTML-INDEX-NEXT:                 <td>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">
+// HTML-INDEX-NEXT:                       Comment 2<br>
+// HTML-INDEX-NEXT:                   </p>
+// HTML-INDEX-NEXT:                 </td>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <td>Blue</td>
 // HTML-INDEX-NEXT:                 <td>2</td>
+// HTML-INDEX-NEXT:                 <td>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">
+// HTML-INDEX-NEXT:                       Comment 3<br>
+// HTML-INDEX-NEXT:                   </p>
+// HTML-INDEX-NEXT:                 </td>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:         </tbody>
 // HTML-INDEX-NEXT:     </table>
@@ -69,7 +84,7 @@ enum Color {
 // HTML-INDEX-NEXT:           <p> For specifying RGB colors</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-47]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-62]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 /**
@@ -104,18 +119,34 @@ enum class Shapes {
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <th>Name</th>
 // HTML-INDEX-NEXT:                 <th>Value</th>
+// HTML-INDEX-NEXT:                 <th>Comments</th>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <td>Circle</td>
 // HTML-INDEX-NEXT:                 <td>0</td>
+// HTML-INDEX-NEXT:                 <td>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">
+// HTML-INDEX-NEXT:                       Comment 1<br>
+// HTML-INDEX-NEXT:                   </p>
+// HTML-INDEX-NEXT:                 </td>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <td>Rectangle</td>
 // HTML-INDEX-NEXT:                 <td>1</td>
+// HTML-INDEX-NEXT:                 <td>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">
+// HTML-INDEX-NEXT:                       Comment 2<br>
+// HTML-INDEX-NEXT:                   </p>
+// HTML-INDEX-NEXT:                 </td>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <td>Triangle</td>
 // HTML-INDEX-NEXT:                 <td>2</td>
+// HTML-INDEX-NEXT:                 <td>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">
+// HTML-INDEX-NEXT:                       Comment 3<br>
+// HTML-INDEX-NEXT:                   </p>
+// HTML-INDEX-NEXT:                 </td>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:         </tbody>
 // HTML-INDEX-NEXT:     </table>
@@ -124,7 +155,7 @@ enum class Shapes {
 // HTML-INDEX-NEXT:           <p> Shape Types</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-50]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-64]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 typedef unsigned char uint8_t;
@@ -165,18 +196,36 @@ enum Size : uint8_t {
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <th>Name</th>
 // HTML-INDEX-NEXT:                 <th>Value</th>
+// HTML-INDEX-NEXT:                 <th>Comments</th>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <td>Small</td>
 // HTML-INDEX-NEXT:                 <td>0</td>
+// HTML-INDEX-NEXT:                 <td>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">
+// HTML-INDEX-NEXT:                       A pearl.<br>
+// HTML-INDEX-NEXT:                       Pearls are quite small.<br>
+// HTML-INDEX-NEXT:                   </p>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">
+// HTML-INDEX-NEXT:                       Pearls are used in jewelry.<br>
+// HTML-INDEX-NEXT:                   </p>
+// HTML-INDEX-NEXT:                 </td>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <td>Medium</td>
 // HTML-INDEX-NEXT:                 <td>1</td>
+// HTML-INDEX-NEXT:                 <td>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">A tennis ball.</p>
+// HTML-INDEX-NEXT:                 </td>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <td>Large</td>
 // HTML-INDEX-NEXT:                 <td>2</td>
+// HTML-INDEX-NEXT:                 <td>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">
+// HTML-INDEX-NEXT:                       A football.<br>
+// HTML-INDEX-NEXT:                   </p>
+// HTML-INDEX-NEXT:                 </td>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:         </tbody>
 // HTML-INDEX-NEXT:     </table>
@@ -185,7 +234,7 @@ enum Size : uint8_t {
 // HTML-INDEX-NEXT:           <p> Specify the size</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-54]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-71]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 /**
@@ -212,10 +261,16 @@ enum : long long {
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <th>Name</th>
 // HTML-INDEX-NEXT:                 <th>Value</th>
+// HTML-INDEX-NEXT:                 <th>Comments</th>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:             <tr>
 // HTML-INDEX-NEXT:                 <td>BigVal</td>
 // HTML-INDEX-NEXT:                 <td>999999999999</td>
+// HTML-INDEX-NEXT:                 <td>
+// HTML-INDEX-NEXT:                   <p class="paragraph-container">
+// HTML-INDEX-NEXT:                       A very large value<br>
+// HTML-INDEX-NEXT:                   </p>
+// HTML-INDEX-NEXT:                 </td>
 // HTML-INDEX-NEXT:             </tr>
 // HTML-INDEX-NEXT:         </tbody>
 // HTML-INDEX-NEXT:     </table>
@@ -224,7 +279,7 @@ enum : long long {
 // HTML-INDEX-NEXT:           <p> Very long number</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-33]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-38]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 class FilePermissions {
@@ -262,18 +317,34 @@ class FilePermissions {
 // HTML-PERM-NEXT:               <tr>
 // HTML-PERM-NEXT:                   <th>Name</th>
 // HTML-PERM-NEXT:                   <th>Value</th>
+// HTML-PERM-NEXT:                   <th>Comments</th>
 // HTML-PERM-NEXT:               </tr>
 // HTML-PERM-NEXT:               <tr>
 // HTML-PERM-NEXT:                   <td>Read</td>
 // HTML-PERM-NEXT:                   <td>1</td>
+// HTML-PERM-NEXT:                   <td>
+// HTML-PERM-NEXT:                     <p class="paragraph-container">
+// HTML-PERM-NEXT:                         Permission to READ r<br>
+// HTML-PERM-NEXT:                     </p>
+// HTML-PERM-NEXT:                   </td>
 // HTML-PERM-NEXT:               </tr>
 // HTML-PERM-NEXT:               <tr>
 // HTML-PERM-NEXT:                   <td>Write</td>
 // HTML-PERM-NEXT:                   <td>2</td>
+// HTML-PERM-NEXT:                  <td>
+// HTML-PERM-NEXT:                     <p class="paragraph-container">
+// HTML-PERM-NEXT:                         Permission to WRITE w<br>
+// HTML-PERM-NEXT:                     </p>
+// HTML-PERM-NEXT:                   </td>
 // HTML-PERM-NEXT:               </tr>
 // HTML-PERM-NEXT:               <tr>
 // HTML-PERM-NEXT:                   <td>Execute</td>
 // HTML-PERM-NEXT:                   <td>4</td>
+// HTML-PERM-NEXT:                   <td>
+// HTML-PERM-NEXT:                     <p class="paragraph-container">
+// HTML-PERM-NEXT:                         Permission to EXECUTE x<br>
+// HTML-PERM-NEXT:                     </p>
+// HTML-PERM-NEXT:                   </td>
 // HTML-PERM-NEXT:               </tr>
 // HTML-PERM-NEXT:           </tbody>
 // HTML-PERM-NEXT:       </table>
@@ -282,7 +353,7 @@ class FilePermissions {
 // HTML-PERM-NEXT:             <p> File permission flags</p>
 // HTML-PERM-NEXT:         </div>
 // HTML-PERM-NEXT:       </div>
-// HTML-PERM-NEXT:         <p>Defined at line [[@LINE-48]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-PERM-NEXT:         <p>Defined at line [[@LINE-63]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-PERM-NEXT:     </div>
 // HTML-PERM-NEXT:   </section>
 
@@ -314,18 +385,34 @@ class Animals {
 // HTML-ANIMAL-NEXT:                 <tr>
 // HTML-ANIMAL-NEXT:                     <th>Name</th>
 // HTML-ANIMAL-NEXT:                     <th>Value</th>
+// HTML-ANIMAL-NEXT:                     <th>Comments</th>
 // HTML-ANIMAL-NEXT:                 </tr>
 // HTML-ANIMAL-NEXT:                 <tr>
 // HTML-ANIMAL-NEXT:                     <td>Dog</td>
 // HTML-ANIMAL-NEXT:                     <td>0</td>
+// HTML-ANIMAL-NEXT:                     <td>
+// HTML-ANIMAL-NEXT:                       <p class="paragraph-container">
+// HTML-ANIMAL-NEXT:                           Man's best friend<br>
+// HTML-ANIMAL-NEXT:                       </p>
+// HTML-ANIMAL-NEXT:                     </td>
 // HTML-ANIMAL-NEXT:                 </tr>
 // HTML-ANIMAL-NEXT:                 <tr>
 // HTML-ANIMAL-NEXT:                     <td>Cat</td>
 // HTML-ANIMAL-NEXT:                     <td>1</td>
+// HTML-ANIMAL-NEXT:                     <td>
+// HTML-ANIMAL-NEXT:                       <p class="paragraph-container">
+// HTML-ANIMAL-NEXT:                           Man's other best friend<br>
+// HTML-ANIMAL-NEXT:                       </p>
+// HTML-ANIMAL-NEXT:                     </td>
 // HTML-ANIMAL-NEXT:                 </tr>
 // HTML-ANIMAL-NEXT:                 <tr>
 // HTML-ANIMAL-NEXT:                     <td>Iguana</td>
 // HTML-ANIMAL-NEXT:                     <td>2</td>
+// HTML-ANIMAL-NEXT:                     <td>
+// HTML-ANIMAL-NEXT:                       <p class="paragraph-container">
+// HTML-ANIMAL-NEXT:                           A lizard<br>
+// HTML-ANIMAL-NEXT:                       </p>
+// HTML-ANIMAL-NEXT:                     </td>
 // HTML-ANIMAL-NEXT:                 </tr>
 // HTML-ANIMAL-NEXT:             </tbody>
 // HTML-ANIMAL-NEXT:         </table>
@@ -334,7 +421,7 @@ class Animals {
 // HTML-ANIMAL-NEXT:                 <p> specify what animal the class is</p>
 // HTML-ANIMAL-NEXT:             </div>
 // HTML-ANIMAL-NEXT:         </div>
-// HTML-ANIMAL-NEXT:         <p>Defined at line [[@LINE-40]] of file {{.*}}enum.cpp</p>
+// HTML-ANIMAL-NEXT:         <p>Defined at line [[@LINE-57]] of file {{.*}}enum.cpp</p>
 // HTML-ANIMAL-NEXT:      </div>
 // HTML-ANIMAL-NEXT:    </section>
 
@@ -358,7 +445,7 @@ enum Car {
 
   Sedan,    ///< Comment 1
   SUV,      ///< Comment 2
-  Pickup,   ///< Comment 3
+  Pickup,
   Hatchback ///< Comment 4
 };
 } // namespace Vehicles
@@ -383,22 +470,39 @@ enum Car {
 // HTML-VEHICLES-NEXT:              <tr>
 // HTML-VEHICLES-NEXT:                  <th>Name</th>
 // HTML-VEHICLES-NEXT:                  <th>Value</th>
+// HTML-VEHICLES-NEXT:                  <th>Comments</th>
 // HTML-VEHICLES-NEXT:              </tr>
 // HTML-VEHICLES-NEXT:              <tr>
 // HTML-VEHICLES-NEXT:                  <td>Sedan</td>
 // HTML-VEHICLES-NEXT:                  <td>0</td>
+// HTML-VEHICLES-NEXT:                  <td>
+// HTML-VEHICLES-NEXT:                    <p class="paragraph-container">
+// HTML-VEHICLES-NEXT:                        Comment 1<br>
+// HTML-VEHICLES-NEXT:                    </p>
+// HTML-VEHICLES-NEXT:                  </td>
 // HTML-VEHICLES-NEXT:              </tr>
 // HTML-VEHICLES-NEXT:              <tr>
 // HTML-VEHICLES-NEXT:                  <td>SUV</td>
 // HTML-VEHICLES-NEXT:                  <td>1</td>
+// HTML-VEHICLES-NEXT:                  <td>
+// HTML-VEHICLES-NEXT:                    <p class="paragraph-container">
+// HTML-VEHICLES-NEXT:                        Comment 2<br>
+// HTML-VEHICLES-NEXT:                    </p>
+// HTML-VEHICLES-NEXT:                  </td>
 // HTML-VEHICLES-NEXT:              </tr>
 // HTML-VEHICLES-NEXT:              <tr>
 // HTML-VEHICLES-NEXT:                  <td>Pickup</td>
 // HTML-VEHICLES-NEXT:                  <td>2</td>
+// HTML-VEHICLES-NEXT:                  <td> -- </td>
 // HTML-VEHICLES-NEXT:              </tr>
 // HTML-VEHICLES-NEXT:              <tr>
 // HTML-VEHICLES-NEXT:                  <td>Hatchback</td>
 // HTML-VEHICLES-NEXT:                  <td>3</td>
+// HTML-VEHICLES-NEXT:                  <td>
+// HTML-VEHICLES-NEXT:                    <p class="paragraph-container">
+// HTML-VEHICLES-NEXT:                        Comment 4<br>
+// HTML-VEHICLES-NEXT:                    </p>
+// HTML-VEHICLES-NEXT:                  </td>
 // HTML-VEHICLES-NEXT:              </tr>
 // HTML-VEHICLES-NEXT:          </tbody>
 // HTML-VEHICLES-NEXT:      </table>
@@ -407,7 +511,7 @@ enum Car {
 // HTML-VEHICLES-NEXT:           <p> specify type of car</p>
 // HTML-VEHICLES-NEXT:        </div>
 // HTML-VEHICLES-NEXT:      </div>
-// HTML-VEHICLES-NEXT:      <p>Defined at line [[@LINE-56]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-VEHICLES-NEXT:      <p>Defined at line [[@LINE-72]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-VEHICLES-NEXT:    </div>
 
 enum ColorUserSpecified {
diff --git a/clang-tools-extra/test/clang-doc/json/enum.cpp b/clang-tools-extra/test/clang-doc/json/enum.cpp
new file mode 100644
index 0000000000000..442de78a03d77
--- /dev/null
+++ b/clang-tools-extra/test/clang-doc/json/enum.cpp
@@ -0,0 +1,201 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: clang-doc --output=%t --format=json --executor=standalone %s
+// RUN: FileCheck %s < %t/json/GlobalNamespace/index.json --check-prefix=JSON-INDEX
+// RUN: FileCheck %s < %t/json/Vehicles/index.json --check-prefix=JSON-VEHICLES-INDEX
+
+typedef unsigned char uint8_t;
+/**
+ * @brief Specify the size
+ */
+enum Size : uint8_t {
+  /// A pearl.
+  /// Pearls are quite small.
+  ///
+  /// Pearls are used in jewelry.
+  Small,
+
+  /// @brief A tennis ball.
+  Medium,
+
+  /// A football.
+  Large
+};
+
+// JSON-INDEX-LABEL: {
+// JSON-INDEX-NEXT:    "DocumentationFileName": "index",
+// JSON-INDEX-NEXT:    "Enums": [
+// JSON-INDEX-NEXT:      {
+// JSON-INDEX-NEXT:        "BaseType": {
+// JSON-INDEX-NEXT:          "Name": "uint8_t",
+// JSON-INDEX-NEXT:          "QualName": "uint8_t",
+// JSON-INDEX-NEXT:          "USR": "0000000000000000000000000000000000000000"
+// JSON-INDEX-NEXT:        },
+// JSON-INDEX-NEXT:        "Description": {
+// JSON-INDEX-NEXT:          "BriefComments": [
+// JSON-INDEX-NEXT:            [
+// JSON-INDEX-NEXT:              {
+// JSON-INDEX-NEXT:                "TextComment": "Specify the size"
+// JSON-INDEX-NEXT:              }
+// JSON-INDEX-NEXT:            ]
+// JSON-INDEX-NEXT:          ],
+// JSON-INDEX-NEXT:          "HasBriefComments": true
+// JSON-INDEX-NEXT:        },
+// JSON-INDEX-NEXT:        "End": true,
+// JSON-INDEX-NEXT:        "HasComments": true,
+// JSON-INDEX-NEXT:        "InfoType": "enum",
+// JSON-INDEX-NEXT:        "Location": {
+// JSON-INDEX-NEXT:          "Filename": "{{.*}}enum.cpp",
+// JSON-INDEX-NEXT:          "LineNumber": [[@LINE-38]]
+// JSON-INDEX-NEXT:        },
+// JSON-INDEX-NEXT:        "Members": [
+// JSON-INDEX-NEXT:          {
+// JSON-INDEX-NEXT:            "Description": {
+// JSON-INDEX-NEXT:              "HasParagraphComments": true,
+// JSON-INDEX-NEXT:              "ParagraphComments": [
+// JSON-INDEX-NEXT:                [
+// JSON-INDEX-NEXT:                  {
+// JSON-INDEX-NEXT:                    "TextComment": "A pearl."
+// JSON-INDEX-NEXT:                  },
+// JSON-INDEX-NEXT:                  {
+// JSON-INDEX-NEXT:                    "TextComment": "Pearls are quite small."
+// JSON-INDEX-NEXT:                  }
+// JSON-INDEX-NEXT:                ],
+// JSON-INDEX-NEXT:                [
+// JSON-INDEX-NEXT:                  {
+// JSON-INDEX-NEXT:                    "TextComment": "Pearls are used in jewelry."
+// JSON-INDEX-NEXT:                  }
+// JSON-INDEX-NEXT:                ]
+// JSON-INDEX-NEXT:              ]
+// JSON-INDEX-NEXT:            },
+// JSON-INDEX-NEXT:            "HasEnumMemberComments": true,
+// JSON-INDEX-NEXT:            "Name": "Small",
+// JSON-INDEX-NEXT:            "Value": "0"
+// JSON-INDEX-NEXT:          },
+// JSON-INDEX-NEXT:          {
+// JSON-INDEX-NEXT:            "Description": {
+// JSON-INDEX-NEXT:              "BriefComments": [
+// JSON-INDEX-NEXT:                [
+// JSON-INDEX-NEXT:                  {
+// JSON-INDEX-NEXT:                    "TextComment": "A tennis ball."
+// JSON-INDEX-NEXT:                  }
+// JSON-INDEX-NEXT:                ]
+// JSON-INDEX-NEXT:              ],
+// JSON-INDEX-NEXT:              "HasBriefComments": true
+// JSON-INDEX-NEXT:            },
+// JSON-INDEX-NEXT:            "HasEnumMemberComments": true,
+// JSON-INDEX-NEXT:            "Name": "Medium",
+// JSON-INDEX-NEXT:            "Value": "1"
+// JSON-INDEX-NEXT:          },
+// JSON-INDEX-NEXT:          {
+// JSON-INDEX-NEXT:            "Description": {
+// JSON-INDEX-NEXT:              "HasParagraphComments": true,
+// JSON-INDEX-NEXT:              "ParagraphComments": [
+// JSON-INDEX-NEXT:                [
+// JSON-INDEX-NEXT:                  {
+// JSON-INDEX-NEXT:                    "TextComment": "A football."
+// JSON-INDEX-NEXT:                  }
+// JSON-INDEX-NEXT:                ]
+// JSON-INDEX-NEXT:              ]
+// JSON-INDEX-NEXT:            },
+// JSON-INDEX-NEXT:            "End": true,
+// JSON-INDEX-NEXT:            "HasEnumMemberComments": true,
+// JSON-INDEX-NEXT:            "Name": "Large",
+// JSON-INDEX-NEXT:            "Value": "2"
+// JSON-INDEX-NEXT:          }
+// JSON-INDEX-NEXT:        ],
+// JSON-INDEX-NEXT:        "Name": "Size",
+// JSON-INDEX-NEXT:        "Scoped": false,
+// JSON-INDEX-NEXT:        "USR": "{{[0-9A-F]*}}"
+// JSON-INDEX-NEXT:      }
+
+namespace Vehicles {
+/**
+ * @brief specify type of car
+ */
+enum Car {
+  Sedan,    ///< Comment 1
+  SUV,      ///< Comment 2
+  Pickup,
+  Hatchback ///< Comment 4
+};
+} // namespace Vehicles
+
+// JSON-VEHICLES-INDEX-LABEL:   "DocumentationFileName": "index",
+// JSON-VEHICLES-INDEX-NEXT:    "Enums": [
+// JSON-VEHICLES-INDEX-NEXT:      {
+// JSON-VEHICLES-INDEX-NEXT:        "Description": {
+// JSON-VEHICLES-INDEX-NEXT:          "BriefComments": [
+// JSON-VEHICLES-INDEX-NEXT:            [
+// JSON-VEHICLES-INDEX-NEXT:              {
+// JSON-VEHICLES-INDEX-NEXT:                "TextComment": "specify type of car"
+// JSON-VEHICLES-INDEX-NEXT:              }
+// JSON-VEHICLES-INDEX-NEXT:            ]
+// JSON-VEHICLES-INDEX-NEXT:          ],
+// JSON-VEHICLES-INDEX-NEXT:          "HasBriefComments": true
+// JSON-VEHICLES-INDEX-NEXT:        },
+// JSON-VEHICLES-INDEX-NEXT:        "End": true,
+// JSON-VEHICLES-INDEX-NEXT:        "HasComments": true,
+// JSON-VEHICLES-INDEX-NEXT:        "InfoType": "enum",
+// JSON-VEHICLES-INDEX-NEXT:        "Location": {
+// JSON-VEHICLES-INDEX-NEXT:          "Filename": "{{.*}}enum.cpp",
+// JSON-VEHICLES-INDEX-NEXT:          "LineNumber": [[@LINE-26]]
+// JSON-VEHICLES-INDEX-NEXT:        },
+// JSON-VEHICLES-INDEX-NEXT:        "Members": [
+// JSON-VEHICLES-INDEX-NEXT:          {
+// JSON-VEHICLES-INDEX-NEXT:            "Description": {
+// JSON-VEHICLES-INDEX-NEXT:              "HasParagraphComments": true,
+// JSON-VEHICLES-INDEX-NEXT:              "ParagraphComments": [
+// JSON-VEHICLES-INDEX-NEXT:                [
+// JSON-VEHICLES-INDEX-NEXT:                  {
+// JSON-VEHICLES-INDEX-NEXT:                    "TextComment": "Comment 1"
+// JSON-VEHICLES-INDEX-NEXT:                  }
+// JSON-VEHICLES-INDEX-NEXT:                ]
+// JSON-VEHICLES-INDEX-NEXT:              ]
+// JSON-VEHICLES-INDEX-NEXT:            },
+// JSON-VEHICLES-INDEX-NEXT:            "HasEnumMemberComments": true,
+// JSON-VEHICLES-INDEX-NEXT:            "Name": "Sedan",
+// JSON-VEHICLES-INDEX-NEXT:            "Value": "0"
+// JSON-VEHICLES-INDEX-NEXT:          },
+// JSON-VEHICLES-INDEX-NEXT:          {
+// JSON-VEHICLES-INDEX-NEXT:            "Description": {
+// JSON-VEHICLES-INDEX-NEXT:              "HasParagraphComments": true,
+// JSON-VEHICLES-INDEX-NEXT:              "ParagraphComments": [
+// JSON-VEHICLES-INDEX-NEXT:                [
+// JSON-VEHICLES-INDEX-NEXT:                  {
+// JSON-VEHICLES-INDEX-NEXT:                    "TextComment": "Comment 2"
+// JSON-VEHICLES-INDEX-NEXT:                  }
+// JSON-VEHICLES-INDEX-NEXT:                ]
+// JSON-VEHICLES-INDEX-NEXT:              ]
+// JSON-VEHICLES-INDEX-NEXT:            },
+// JSON-VEHICLES-INDEX-NEXT:            "HasEnumMemberComments": true,
+// JSON-VEHICLES-INDEX-NEXT:            "Name": "SUV",
+// JSON-VEHICLES-INDEX-NEXT:            "Value": "1"
+// JSON-VEHICLES-INDEX-NEXT:          },
+// JSON-VEHICLES-INDEX-NEXT:          {
+// JSON-VEHICLES-INDEX-NEXT:            "Name": "Pickup",
+// JSON-VEHICLES-INDEX-NEXT:            "Value": "2"
+// JSON-VEHICLES-INDEX-NEXT:          },
+// JSON-VEHICLES-INDEX-NEXT:          {
+// JSON-VEHICLES-INDEX-NEXT:            "Description": {
+// JSON-VEHICLES-INDEX-NEXT:              "HasParagraphComments": true,
+// JSON-VEHICLES-INDEX-NEXT:              "ParagraphComments": [
+// JSON-VEHICLES-INDEX-NEXT:                [
+// JSON-VEHICLES-INDEX-NEXT:                  {
+// JSON-VEHICLES-INDEX-NEXT:                    "TextComment": "Comment 4"
+// JSON-VEHICLES-INDEX-NEXT:                  }
+// JSON-VEHICLES-INDEX-NEXT:                ]
+// JSON-VEHICLES-INDEX-NEXT:              ]
+// JSON-VEHICLES-INDEX-NEXT:            },
+// JSON-VEHICLES-INDEX-NEXT:            "End": true,
+// JSON-VEHICLES-INDEX-NEXT:            "HasEnumMemberComments": true,
+// JSON-VEHICLES-INDEX-NEXT:            "Name": "Hatchback",
+// JSON-VEHICLES-INDEX-NEXT:            "Value": "3"
+// JSON-VEHICLES-INDEX-NEXT:          }
+// JSON-VEHICLES-INDEX-NEXT:        ],
+// JSON-VEHICLES-INDEX-NEXT:        "Name": "Car",
+// JSON-VEHICLES-INDEX-NEXT:        "Namespace": [
+// JSON-VEHICLES-INDEX-NEXT:          "Vehicles"
+// JSON-VEHICLES-INDEX-NEXT:        ],
+// JSON-VEHICLES-INDEX-NEXT:        "Scoped": false,
+// JSON-VEHICLES-INDEX-NEXT:        "USR": "{{[0-9A-F]*}}"
+// JSON-VEHICLES-INDEX-NEXT:      }

>From 5de9046865ce92fb14a5ede149f1104bb081cd3e Mon Sep 17 00:00:00 2001
From: Samrudh Nelli <samrudhnelli at gmail.com>
Date: Wed, 11 Mar 2026 21:45:17 +0530
Subject: [PATCH 17/18] fix tests

---
 clang-tools-extra/test/clang-doc/enum.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index 1355a5a6fbff3..854de5ee007e2 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -155,7 +155,7 @@ enum class Shapes {
 // HTML-INDEX-NEXT:           <p> Shape Types</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-64]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-65]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 typedef unsigned char uint8_t;
@@ -234,7 +234,7 @@ enum Size : uint8_t {
 // HTML-INDEX-NEXT:           <p> Specify the size</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-71]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-72]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 /**
@@ -279,7 +279,7 @@ enum : long long {
 // HTML-INDEX-NEXT:           <p> Very long number</p>
 // HTML-INDEX-NEXT:       </div>
 // HTML-INDEX-NEXT:     </div>
-// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-38]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-39]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
 class FilePermissions {
@@ -353,7 +353,7 @@ class FilePermissions {
 // HTML-PERM-NEXT:             <p> File permission flags</p>
 // HTML-PERM-NEXT:         </div>
 // HTML-PERM-NEXT:       </div>
-// HTML-PERM-NEXT:         <p>Defined at line [[@LINE-63]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
+// HTML-PERM-NEXT:         <p>Defined at line [[@LINE-64]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-PERM-NEXT:     </div>
 // HTML-PERM-NEXT:   </section>
 
@@ -421,7 +421,7 @@ class Animals {
 // HTML-ANIMAL-NEXT:                 <p> specify what animal the class is</p>
 // HTML-ANIMAL-NEXT:             </div>
 // HTML-ANIMAL-NEXT:         </div>
-// HTML-ANIMAL-NEXT:         <p>Defined at line [[@LINE-57]] of file {{.*}}enum.cpp</p>
+// HTML-ANIMAL-NEXT:         <p>Defined at line [[@LINE-56]] of file {{.*}}enum.cpp</p>
 // HTML-ANIMAL-NEXT:      </div>
 // HTML-ANIMAL-NEXT:    </section>
 

>From 670769093f69b11ee4b557513b499dc703044829 Mon Sep 17 00:00:00 2001
From: Erick Velez <erickvelez7 at gmail.com>
Date: Wed, 4 Mar 2026 09:26:52 -0800
Subject: [PATCH 18/18] [clang-doc] Add a Mustache Markdown generator (#177221)

Adds a Markdown generator that uses Mustache templates. This patch adds
the templates themselves and implements changes to the JSONGenerator to
allow for the creation of specific files needed by the MD tests like
`all-files.json`.

This backend should be considered experimental. It satisfies all the
same tests that the current MD backend is tested against, but those
don't seem to provide full coverage for all functionality inside that
backend. It also doesn't output everything provided by JSON. It doesn't
use the MD unittests because the Mustache templates must currently be
written to files.
---
 clang-tools-extra/clang-doc/CMakeLists.txt    |   1 +
 clang-tools-extra/clang-doc/Generators.cpp    |  10 +-
 clang-tools-extra/clang-doc/Generators.h      |   5 +
 clang-tools-extra/clang-doc/JSONGenerator.cpp | 117 +++++++++++++---
 .../clang-doc/MDMustacheGenerator.cpp         | 115 ++++++++++++++++
 .../clang-doc/Representation.cpp              |   5 +-
 clang-tools-extra/clang-doc/Representation.h  |   6 +-
 .../assets/md/all-files-template.mustache     |   5 +
 .../assets/md/class-template.mustache         |  62 +++++++++
 .../assets/md/comments-partial.mustache       |  23 ++++
 .../assets/md/index-template.mustache         |   5 +
 .../assets/md/namespace-template.mustache     |  63 +++++++++
 clang-tools-extra/clang-doc/support/Utils.cpp |  24 ++++
 clang-tools-extra/clang-doc/support/Utils.h   |   2 +
 .../clang-doc/tool/CMakeLists.txt             |   5 +
 .../clang-doc/tool/ClangDocMain.cpp           |  62 ++++++---
 .../clang-doc/basic-project.mustache.test     | 127 ++++++++++++++++++
 .../test/clang-doc/builtin_types.cpp          |  32 ++++-
 .../test/clang-doc/comments-in-macros.cpp     |  15 ++-
 clang-tools-extra/test/clang-doc/enum.cpp     |  47 +++++++
 clang-tools-extra/test/clang-doc/index.cpp    |   4 +
 .../test/clang-doc/json/class.cpp             |   3 +-
 .../test/clang-doc/json/namespace.cpp         |   1 -
 .../test/clang-doc/namespace.cpp              | 123 ++++++++++++++++-
 .../test/clang-doc/templates.cpp              |  24 ++++
 .../unittests/clang-doc/ClangDocTest.cpp      |   2 +-
 .../unittests/clang-doc/HTMLGeneratorTest.cpp |   1 +
 .../unittests/clang-doc/JSONGeneratorTest.cpp |  24 ++--
 28 files changed, 842 insertions(+), 71 deletions(-)
 create mode 100644 clang-tools-extra/clang-doc/MDMustacheGenerator.cpp
 create mode 100644 clang-tools-extra/clang-doc/assets/md/all-files-template.mustache
 create mode 100644 clang-tools-extra/clang-doc/assets/md/class-template.mustache
 create mode 100644 clang-tools-extra/clang-doc/assets/md/comments-partial.mustache
 create mode 100644 clang-tools-extra/clang-doc/assets/md/index-template.mustache
 create mode 100644 clang-tools-extra/clang-doc/assets/md/namespace-template.mustache

diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 7a375d7cd0524..a64c086a98910 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -17,6 +17,7 @@ add_clang_library(clangDoc STATIC
   Serialize.cpp
   YAMLGenerator.cpp
   JSONGenerator.cpp
+  MDMustacheGenerator.cpp
 
   DEPENDS
   omp_gen
diff --git a/clang-tools-extra/clang-doc/Generators.cpp b/clang-tools-extra/clang-doc/Generators.cpp
index eca1f288d5ba1..9fab97fc8825a 100644
--- a/clang-tools-extra/clang-doc/Generators.cpp
+++ b/clang-tools-extra/clang-doc/Generators.cpp
@@ -164,9 +164,11 @@ Error MustacheGenerator::generateDocumentation(
 
 Expected<std::string> MustacheGenerator::getInfoTypeStr(Object *Info,
                                                         StringRef Filename) {
-  // Checking for a USR ensures that only the special top-level index file is
-  // caught here, since it is not an Info.
-  if (Filename == "index" && !Info->get("USR"))
+  if (Filename == "all_files")
+    return "all_files";
+  // Checking for an InfoType ensures that only the special top-level index file
+  // is caught here, since it is not an Info.
+  if (Filename == "index" && !Info->get("InfoType"))
     return "index";
   auto StrValue = (*Info)["InfoType"];
   if (StrValue.kind() != json::Value::Kind::String)
@@ -248,5 +250,7 @@ void Generator::addInfoToIndex(Index &Idx, const doc::Info *Info) {
 [[maybe_unused]] static int MDGeneratorAnchorDest = MDGeneratorAnchorSource;
 [[maybe_unused]] static int HTMLGeneratorAnchorDest = HTMLGeneratorAnchorSource;
 [[maybe_unused]] static int JSONGeneratorAnchorDest = JSONGeneratorAnchorSource;
+[[maybe_unused]] static int MDMustacheGeneratorAnchorDest =
+    MDMustacheGeneratorAnchorSource;
 } // namespace doc
 } // namespace clang
diff --git a/clang-tools-extra/clang-doc/Generators.h b/clang-tools-extra/clang-doc/Generators.h
index ade0bb53eba7e..7ba66947a830a 100644
--- a/clang-tools-extra/clang-doc/Generators.h
+++ b/clang-tools-extra/clang-doc/Generators.h
@@ -87,6 +87,10 @@ class MustacheTemplateFile {
 
   void render(llvm::json::Value &V, raw_ostream &OS) { T.render(V, OS); }
 
+  void setEscapeCharacters(const llvm::DenseMap<char, std::string> Characters) {
+    T.overrideEscapeCharacters(Characters);
+  }
+
   MustacheTemplateFile(std::unique_ptr<llvm::MemoryBuffer> &&B)
       : Saver(Allocator), Ctx(Allocator, Saver), T(B->getBuffer(), Ctx),
         Buffer(std::move(B)) {}
@@ -138,6 +142,7 @@ extern volatile int YAMLGeneratorAnchorSource;
 extern volatile int MDGeneratorAnchorSource;
 extern volatile int HTMLGeneratorAnchorSource;
 extern volatile int JSONGeneratorAnchorSource;
+extern volatile int MDMustacheGeneratorAnchorSource;
 
 } // namespace doc
 } // namespace clang
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index 7c0485692fad2..21e25e539eefa 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -15,6 +15,7 @@ namespace doc {
 class JSONGenerator : public Generator {
 public:
   static const char *Format;
+  bool Markdown = false;
 
   Error generateDocumentation(StringRef RootDir,
                               llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
@@ -383,7 +384,8 @@ static void serializeDescription(llvm::ArrayRef<CommentInfo> Description,
 void JSONGenerator::serializeCommonAttributes(const Info &I,
                                               json::Object &Obj) {
   insertNonEmpty("Name", I.Name, Obj);
-  Obj["USR"] = toHex(toStringRef(I.USR));
+  if (!(I.USR == GlobalNamespaceID))
+    Obj["USR"] = toHex(toStringRef(I.USR));
   Obj["InfoType"] = infoTypeToString(I.IT);
   // Conditionally insert fields.
   // Empty properties are omitted because Mustache templates use existence
@@ -426,12 +428,25 @@ static void serializeReference(const Reference &Ref, Object &ReferenceObj) {
   }
 }
 
+static void serializeMDReference(const Reference &Ref, Object &ReferenceObj,
+                                 StringRef BasePath) {
+  serializeReference(Ref, ReferenceObj);
+  SmallString<64> Path = Ref.getRelativeFilePath(BasePath);
+  sys::path::native(Path, sys::path::Style::posix);
+  sys::path::append(Path, sys::path::Style::posix,
+                    Ref.getFileBaseName() + ".md");
+  ReferenceObj["BasePath"] = Path;
+}
+
+typedef std::function<void(const Reference &, Object &)> ReferenceFunc;
+
 // Although namespaces and records both have ScopeChildren, they serialize them
 // differently. Only enums, records, and typedefs are handled here.
-static void
-serializeCommonChildren(const ScopeChildren &Children, json::Object &Obj,
-                        const std::optional<StringRef> RepositoryUrl,
-                        const std::optional<StringRef> RepositoryLinePrefix) {
+static void serializeCommonChildren(
+    const ScopeChildren &Children, json::Object &Obj,
+    const std::optional<StringRef> RepositoryUrl,
+    const std::optional<StringRef> RepositoryLinePrefix,
+    std::optional<ReferenceFunc> MDReferenceLambda = std::nullopt) {
   static auto SerializeInfo =
       [RepositoryUrl, RepositoryLinePrefix](const auto &Info, Object &Object) {
         serializeInfo(Info, Object, RepositoryUrl, RepositoryLinePrefix);
@@ -448,7 +463,11 @@ serializeCommonChildren(const ScopeChildren &Children, json::Object &Obj,
   }
 
   if (!Children.Records.empty()) {
-    serializeArray(Children.Records, Obj, "Records", SerializeReferenceLambda);
+
+    ReferenceFunc SerializeReferenceFunc = MDReferenceLambda
+                                               ? MDReferenceLambda.value()
+                                               : SerializeReferenceLambda;
+    serializeArray(Children.Records, Obj, "Records", SerializeReferenceFunc);
     Obj["HasRecords"] = true;
   }
 }
@@ -683,6 +702,7 @@ serializeInfo(const RecordInfo &I, json::Object &Obj,
   }
 
   if (!I.Members.empty()) {
+    Obj["HasMembers"] = true;
     json::Value PublicMembersArray = Array();
     json::Array &PubMembersArrayRef = *PublicMembersArray.getAsArray();
     json::Value ProtectedMembersArray = Array();
@@ -756,17 +776,12 @@ serializeInfo(const VarInfo &I, json::Object &Obj,
 
 static void serializeInfo(const NamespaceInfo &I, json::Object &Obj,
                           const std::optional<StringRef> RepositoryUrl,
-                          const std::optional<StringRef> RepositoryLinePrefix) {
+                          const std::optional<StringRef> RepositoryLinePrefix,
+                          bool Markdown) {
   serializeCommonAttributes(I, Obj, RepositoryUrl, RepositoryLinePrefix);
   if (I.USR == GlobalNamespaceID)
     Obj["Name"] = "Global Namespace";
 
-  if (!I.Children.Namespaces.empty()) {
-    serializeArray(I.Children.Namespaces, Obj, "Namespaces",
-                   SerializeReferenceLambda);
-    Obj["HasNamespaces"] = true;
-  }
-
   static auto SerializeInfo =
       [RepositoryUrl, RepositoryLinePrefix](const auto &Info, Object &Object) {
         serializeInfo(Info, Object, RepositoryUrl, RepositoryLinePrefix);
@@ -787,7 +802,28 @@ static void serializeInfo(const NamespaceInfo &I, json::Object &Obj,
     Obj["HasVariables"] = true;
   }
 
-  serializeCommonChildren(I.Children, Obj, RepositoryUrl, RepositoryLinePrefix);
+  ReferenceFunc SerializeReferenceFunc;
+  if (Markdown) {
+    SmallString<64> BasePath = I.getRelativeFilePath("");
+    // serializeCommonChildren doesn't accept Infos, so this lambda needs to be
+    // created here. To avoid making serializeCommonChildren a template, this
+    // lambda is an std::function
+    SerializeReferenceFunc = [BasePath](const Reference &Ref, Object &Object) {
+      serializeMDReference(Ref, Object, BasePath);
+    };
+    serializeCommonChildren(I.Children, Obj, RepositoryUrl,
+                            RepositoryLinePrefix, SerializeReferenceFunc);
+  } else {
+    SerializeReferenceFunc = SerializeReferenceLambda;
+    serializeCommonChildren(I.Children, Obj, RepositoryUrl,
+                            RepositoryLinePrefix);
+  }
+
+  if (!I.Children.Namespaces.empty()) {
+    serializeArray(I.Children.Namespaces, Obj, "Namespaces",
+                   SerializeReferenceFunc);
+    Obj["HasNamespaces"] = true;
+  }
 }
 
 static SmallString<16> determineFileName(Info *I, SmallString<128> &Path) {
@@ -803,10 +839,46 @@ static SmallString<16> determineFileName(Info *I, SmallString<128> &Path) {
   return FileName;
 }
 
+/// \param CDCtxIndex Passed by copy since clang-doc's context is passed to the
+/// generator as `const`
+static std::vector<Index> preprocessCDCtxIndex(Index CDCtxIndex) {
+  CDCtxIndex.sort();
+  std::vector<Index> Processed;
+  Processed.reserve(CDCtxIndex.Children.size());
+  for (auto &Entry : CDCtxIndex.Children) {
+    auto NewPath = Entry.second.getRelativeFilePath("");
+    sys::path::native(NewPath, sys::path::Style::posix);
+    sys::path::append(NewPath, sys::path::Style::posix,
+                      Entry.second.getFileBaseName() + ".md");
+    Entry.second.Path = NewPath;
+    Processed.push_back(Entry.second);
+  }
+
+  return Processed;
+}
+
+/// Serialize ClangDocContext's Index for Markdown output
+static Error serializeAllFiles(const ClangDocContext &CDCtx,
+                               StringRef RootDir) {
+  json::Value ObjVal = Object();
+  Object &Obj = *ObjVal.getAsObject();
+  std::vector<Index> IndexCopy = preprocessCDCtxIndex(CDCtx.Idx);
+  serializeArray(IndexCopy, Obj, "Index", SerializeReferenceLambda);
+  SmallString<128> Path;
+  sys::path::append(Path, RootDir, "json", "all_files.json");
+  std::error_code FileErr;
+  raw_fd_ostream RootOS(Path, FileErr, sys::fs::OF_Text);
+  if (FileErr)
+    return createFileError("cannot open file " + Path, FileErr);
+  RootOS << llvm::formatv("{0:2}", ObjVal);
+  return Error::success();
+}
+
 // Creates a JSON file above the global namespace directory.
 // An index can be used to create the top-level HTML index page or the Markdown
 // index file.
-static Error serializeIndex(const ClangDocContext &CDCtx, StringRef RootDir) {
+static Error serializeIndex(const ClangDocContext &CDCtx, StringRef RootDir,
+                            bool Markdown) {
   if (CDCtx.Idx.Children.empty())
     return Error::success();
 
@@ -833,7 +905,10 @@ static Error serializeIndex(const ClangDocContext &CDCtx, StringRef RootDir) {
     std::string TypeStr = infoTypeToString(Idx.RefType);
     json::Value IdxVal = Object();
     auto &IdxObj = *IdxVal.getAsObject();
-    serializeReference(Idx, IdxObj);
+    if (Markdown)
+      TypeStr.at(0) = toUppercase(TypeStr.at(0));
+    IdxObj["Type"] = TypeStr;
+    serializeReference(*Idx, IdxObj);
     IndexArrayRef.push_back(IdxVal);
   }
   Obj["Index"] = IndexArray;
@@ -898,6 +973,12 @@ Error JSONGenerator::generateDocumentation(
     Info->DocumentationFileName = FileName;
   }
 
+  if (CDCtx.Format == OutputFormatTy::md_mustache) {
+    Markdown = true;
+    if (auto Err = serializeAllFiles(CDCtx, RootDir))
+      return Err;
+  }
+
   for (const auto &Group : FileToInfos) {
     std::error_code FileErr;
     raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_Text);
@@ -912,7 +993,7 @@ Error JSONGenerator::generateDocumentation(
     }
   }
 
-  return serializeIndex(CDCtx, RootDir);
+  return serializeIndex(CDCtx, RootDir, Markdown);
 }
 
 Error JSONGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
@@ -922,7 +1003,7 @@ Error JSONGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace:
     serializeInfo(*static_cast<NamespaceInfo *>(I), Obj, CDCtx.RepositoryUrl,
-                  CDCtx.RepositoryLinePrefix);
+                  CDCtx.RepositoryLinePrefix, Markdown);
     break;
   case InfoType::IT_record:
     serializeInfo(*static_cast<RecordInfo *>(I), Obj, CDCtx.RepositoryUrl,
diff --git a/clang-tools-extra/clang-doc/MDMustacheGenerator.cpp b/clang-tools-extra/clang-doc/MDMustacheGenerator.cpp
new file mode 100644
index 0000000000000..2b0b9a2f89fe1
--- /dev/null
+++ b/clang-tools-extra/clang-doc/MDMustacheGenerator.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Contains the Markdown generator using Mustache template files.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Generators.h"
+
+namespace clang {
+using namespace llvm;
+namespace doc {
+static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
+
+static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
+
+static std::unique_ptr<MustacheTemplateFile> AllFilesTemplate = nullptr;
+
+static std::unique_ptr<MustacheTemplateFile> IndexTemplate = nullptr;
+
+struct MDMustacheGenerator : public MustacheGenerator {
+  static const char *Format;
+  Error generateDocumentation(StringRef RootDir,
+                              StringMap<std::unique_ptr<doc::Info>> Infos,
+                              const ClangDocContext &CDCtx,
+                              std::string DirName) override;
+  Error setupTemplateFiles(const ClangDocContext &CDCtx) override;
+  Error generateDocForJSON(json::Value &JSON, raw_fd_ostream &OS,
+                           const ClangDocContext &CDCtx,
+                           StringRef ObjectTypeStr,
+                           StringRef RelativeRootPath) override;
+  // This generator doesn't need this function, but it inherits from the
+  // original generator interface.
+  Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
+                           const ClangDocContext &CDCtx) override;
+};
+
+Error MDMustacheGenerator::setupTemplateFiles(const ClangDocContext &CDCtx) {
+  std::string ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
+  std::string NamespaceFilePath =
+      CDCtx.MustacheTemplates.lookup("namespace-template");
+  std::string AllFilesPath = CDCtx.MustacheTemplates.lookup("all-files");
+  std::string IndexFilePath = CDCtx.MustacheTemplates.lookup("index");
+  std::string CommentsFilePath = CDCtx.MustacheTemplates.lookup("comments");
+  std::vector<std::pair<StringRef, StringRef>> Partials = {
+      {"Comments", CommentsFilePath}};
+
+  if (Error Err = setupTemplate(RecordTemplate, ClassFilePath, Partials))
+    return Err;
+  if (Error Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials))
+    return Err;
+  if (Error Err = setupTemplate(AllFilesTemplate, AllFilesPath, Partials))
+    return Err;
+  if (Error Err = setupTemplate(IndexTemplate, IndexFilePath, Partials))
+    return Err;
+
+  // Override the default HTML Mustache escape characters. We don't need to
+  // override `<` here.
+  static const DenseMap<char, std::string> EscapeChars;
+  RecordTemplate->setEscapeCharacters(EscapeChars);
+  NamespaceTemplate->setEscapeCharacters(EscapeChars);
+  AllFilesTemplate->setEscapeCharacters(EscapeChars);
+  IndexTemplate->setEscapeCharacters(EscapeChars);
+
+  return Error::success();
+}
+
+Error MDMustacheGenerator::generateDocumentation(
+    StringRef RootDir, StringMap<std::unique_ptr<doc::Info>> Infos,
+    const clang::doc::ClangDocContext &CDCtx, std::string Dirname) {
+  return MustacheGenerator::generateDocumentation(RootDir, std::move(Infos),
+                                                  CDCtx, "md");
+}
+
+Error MDMustacheGenerator::generateDocForJSON(json::Value &JSON,
+                                              raw_fd_ostream &OS,
+                                              const ClangDocContext &CDCtx,
+                                              StringRef ObjTypeStr,
+                                              StringRef RelativeRootPath) {
+  if (ObjTypeStr == "record") {
+    assert(RecordTemplate && "RecordTemplate is nullptr.");
+    RecordTemplate->render(JSON, OS);
+  } else if (ObjTypeStr == "namespace") {
+    assert(NamespaceTemplate && "NamespaceTemplate is nullptr.");
+    NamespaceTemplate->render(JSON, OS);
+  } else if (ObjTypeStr == "all_files") {
+    assert(AllFilesTemplate && "AllFilesTemplate is nullptr.");
+    AllFilesTemplate->render(JSON, OS);
+  } else if (ObjTypeStr == "index") {
+    assert(IndexTemplate && "IndexTemplate is nullptr");
+    IndexTemplate->render(JSON, OS);
+  }
+  return Error::success();
+}
+
+Error MDMustacheGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
+                                              const ClangDocContext &CDCtx) {
+  return Error::success();
+}
+
+const char *MDMustacheGenerator::Format = "md_mustache";
+
+static GeneratorRegistry::Add<MDMustacheGenerator>
+    MDMustache(MDMustacheGenerator::Format,
+               "Generator for mustache Markdown output.");
+
+volatile int MDMustacheGeneratorAnchorSource = 0;
+} // namespace doc
+} // namespace clang
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index a69129041516f..5104458117cf2 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -485,10 +485,11 @@ ClangDocContext::ClangDocContext(tooling::ExecutionContext *ECtx,
                                  StringRef RepositoryLinePrefix, StringRef Base,
                                  std::vector<std::string> UserStylesheets,
                                  clang::DiagnosticsEngine &Diags,
-                                 bool FTimeTrace)
+                                 OutputFormatTy Format, bool FTimeTrace)
     : ECtx(ECtx), ProjectName(ProjectName), OutDirectory(OutDirectory),
       SourceRoot(std::string(SourceRoot)), UserStylesheets(UserStylesheets),
-      Base(Base), Diags(Diags), PublicOnly(PublicOnly), FTimeTrace(FTimeTrace) {
+      Base(Base), Diags(Diags), Format(Format), PublicOnly(PublicOnly),
+      FTimeTrace(FTimeTrace) {
   llvm::SmallString<128> SourceRootDir(SourceRoot);
   if (SourceRoot.empty())
     // If no SourceRoot was provided the current path is used as the default
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 297e564ea7866..c9f68274b5beb 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -68,6 +68,8 @@ enum class CommentKind {
   CK_Unknown
 };
 
+enum OutputFormatTy { md, yaml, html, json, md_mustache };
+
 CommentKind stringToCommentKind(llvm::StringRef KindStr);
 llvm::StringRef commentKindToString(CommentKind Kind);
 
@@ -629,7 +631,8 @@ struct ClangDocContext {
                   bool PublicOnly, StringRef OutDirectory, StringRef SourceRoot,
                   StringRef RepositoryUrl, StringRef RepositoryCodeLinePrefix,
                   StringRef Base, std::vector<std::string> UserStylesheets,
-                  clang::DiagnosticsEngine &Diags, bool FTimeTrace = false);
+                  clang::DiagnosticsEngine &Diags, OutputFormatTy Format,
+                  bool FTimeTrace = false);
   tooling::ExecutionContext *ECtx;
   std::string ProjectName;  // Name of project clang-doc is documenting.
   std::string OutDirectory; // Directory for outputting generated files.
@@ -653,6 +656,7 @@ struct ClangDocContext {
   // A pointer to a DiagnosticsEngine for error reporting.
   clang::DiagnosticsEngine &Diags;
   Index Idx;
+  OutputFormatTy Format;
   int Granularity; // Granularity of ftime trace
   bool PublicOnly; // Indicates if only public declarations are documented.
   bool FTimeTrace; // Indicates if ftime trace is turned on
diff --git a/clang-tools-extra/clang-doc/assets/md/all-files-template.mustache b/clang-tools-extra/clang-doc/assets/md/all-files-template.mustache
new file mode 100644
index 0000000000000..b1ab36aa199c1
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/md/all-files-template.mustache
@@ -0,0 +1,5 @@
+# All Files
+
+{{#Index}}
+## [{{Name}}]({{Path}})
+{{/Index}}
diff --git a/clang-tools-extra/clang-doc/assets/md/class-template.mustache b/clang-tools-extra/clang-doc/assets/md/class-template.mustache
new file mode 100644
index 0000000000000..c80fc1b24b2b6
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/md/class-template.mustache
@@ -0,0 +1,62 @@
+# {{TagType}} {{Name}}
+
+*Defined at {{Location.Filename}}#{{Location.LineNumber}}*
+{{#Description}}
+
+{{>Comments}}
+{{/Description}}
+{{#HasParents}}
+Inherits from {{#Parents}}{{Name}}{{^End}}, {{/End}}{{/Parents}}{{#VParents}}{{Name}}{{^End}}, {{/End}}{{/VParents}}
+{{/HasParents}}
+{{#HasMembers}}
+## Members
+{{/HasMembers}}
+{{#PublicMembers}}
+public {{#IsStatic}}static {{/IsStatic}}{{Type}} {{Name}}
+
+{{/PublicMembers}}
+{{#PrivateMembers}}
+private {{#IsStatic}}static {{/IsStatic}}{{Type}} {{Name}}
+
+{{/PrivateMembers}}
+
+{{#HasPublicMethods}}
+## Functions
+
+{{#PublicMethods}}
+### {{Name}}
+
+*public {{#IsStatic}}static {{/IsStatic}}{{ReturnType.QualName}} {{Name}}({{#Params}}{{Type.QualName}} {{Name}}{{^ParamEnd}}, {{/ParamEnd}}{{/Params}})*
+{{#Location}}
+
+*Defined at {{Filename}}#{{LineNumber}}*
+{{/Location}}
+{{#Description}}
+
+{{>Comments}}
+{{/Description}}
+{{/PublicMethods}}
+{{/HasPublicMethods}}
+{{#HasEnums}}
+## Enums
+
+{{#Enums}}
+| enum {{Name}} |
+
+--
+
+{{#Members}}
+| {{ .Name }} |
+{{/Members}}
+{{#Location}}
+
+*Defined at {{Filename}}#{{LineNumber}}*
+{{/Location}}
+{{#Description}}
+
+{{>Comments}}
+{{/Description}}
+{{^End}}
+{{/End}}
+{{/Enums}}
+{{/HasEnums}}
diff --git a/clang-tools-extra/clang-doc/assets/md/comments-partial.mustache b/clang-tools-extra/clang-doc/assets/md/comments-partial.mustache
new file mode 100644
index 0000000000000..e0719d7a7baca
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/md/comments-partial.mustache
@@ -0,0 +1,23 @@
+{{#HasBriefComments}}
+{{#BriefComments}}
+**brief**{{#.}}{{TextComment}}{{/.}}
+{{/BriefComments}}
+{{/HasBriefComments}}
+
+{{#ParagraphComments}}
+{{#.}}{{TextComment}}{{/.}}
+{{/ParagraphComments}}
+
+{{#HasParamComments}}
+{{#ParamComments}}
+**{{ParamName}}** {{#Children}}{{TextComment}}{{/Children}}
+{{/ParamComments}}
+{{/HasParamComments}}
+
+{{#ReturnComments}}
+**return** {{#.}}{{TextComment}}{{/.}}
+{{/ReturnComments}}
+
+{{#ThrowsComments}}
+**throw** {{#Children}}{{TextComment}}{{/Children}}
+{{/ThrowsComments}}
diff --git a/clang-tools-extra/clang-doc/assets/md/index-template.mustache b/clang-tools-extra/clang-doc/assets/md/index-template.mustache
new file mode 100644
index 0000000000000..867c9dc151dd2
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/md/index-template.mustache
@@ -0,0 +1,5 @@
+# {{ProjectName}} C/C++ Reference
+
+{{#Index}}
+* {{Type}}: [{{Name}}]({{#Path}}{{Path}}/{{/Path}}{{Name}})
+{{/Index}}
diff --git a/clang-tools-extra/clang-doc/assets/md/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/md/namespace-template.mustache
new file mode 100644
index 0000000000000..aef80c0516fcb
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/md/namespace-template.mustache
@@ -0,0 +1,63 @@
+# {{#USR}}namespace {{/USR}}{{Name}}
+
+{{#Description}}
+{{>Comments}}
+{{/Description}}
+
+{{#HasNamespaces}}
+## Namespaces
+
+{{#Namespaces}}
+* [{{Name}}]({{BasePath}})
+
+{{/Namespaces}}
+{{/HasNamespaces}}
+{{#HasRecords}}
+## Records
+
+{{#Records}}
+* [{{Name}}]({{BasePath}})
+
+{{/Records}}
+{{/HasRecords}}
+{{#HasFunctions}}
+## Functions
+
+{{#Functions}}
+### {{Name}}
+
+*{{#IsStatic}}static {{/IsStatic}}{{ReturnType.QualName}} {{Name}}({{#Params}}{{Type.QualName}} {{Name}}{{^ParamEnd}}, {{/ParamEnd}}{{/Params}})*
+{{#Location}}
+
+*Defined at {{Filename}}#{{LineNumber}}*
+{{/Location}}
+{{#Description}}
+
+{{>Comments}}
+{{/Description}}
+{{/Functions}}
+{{/HasFunctions}}
+{{#HasEnums}}
+## Enums
+
+{{#Enums}}
+| enum {{Name}} |
+
+--
+
+{{#Members}}
+| {{ .Name }} |
+{{/Members}}
+
+{{#Location}}
+*Defined at {{Filename}}#{{LineNumber}}*
+{{/Location}}
+
+{{#Description}}
+{{>Comments}}
+{{/Description}}
+{{^End}}
+
+{{/End}}
+{{/Enums}}
+{{/HasEnums}}
diff --git a/clang-tools-extra/clang-doc/support/Utils.cpp b/clang-tools-extra/clang-doc/support/Utils.cpp
index 1dce98f06d144..0691db06a06ca 100644
--- a/clang-tools-extra/clang-doc/support/Utils.cpp
+++ b/clang-tools-extra/clang-doc/support/Utils.cpp
@@ -77,3 +77,27 @@ void getHtmlFiles(StringRef AssetsPath, clang::doc::ClangDocContext &CDCtx) {
   CDCtx.MustacheTemplates.insert({"index-template", IndexTemplate.c_str()});
   CDCtx.MustacheTemplates.insert({"alias-template", AliasTemplate.c_str()});
 }
+
+void getMdFiles(llvm::StringRef AssetsPath,
+                clang::doc::ClangDocContext &CDCtx) {
+  assert(!AssetsPath.empty());
+  assert(sys::fs::is_directory(AssetsPath));
+
+  SmallString<128> ClassTemplate =
+      appendPathPosix(AssetsPath, "class-template.mustache");
+  SmallString<128> NamespaceTemplate =
+      appendPathPosix(AssetsPath, "namespace-template.mustache");
+  SmallString<128> AllFilesTemplate =
+      appendPathPosix(AssetsPath, "all-files-template.mustache");
+  SmallString<128> IndexTemplate =
+      appendPathPosix(AssetsPath, "index-template.mustache");
+  SmallString<128> CommentsTemplate =
+      appendPathPosix(AssetsPath, "comments-partial.mustache");
+
+  CDCtx.MustacheTemplates.insert({"class-template", ClassTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert(
+      {"namespace-template", NamespaceTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"all-files", AllFilesTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"index", IndexTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"comments", CommentsTemplate.c_str()});
+}
diff --git a/clang-tools-extra/clang-doc/support/Utils.h b/clang-tools-extra/clang-doc/support/Utils.h
index f4ed9ec42dce4..d862f53c55b9a 100644
--- a/clang-tools-extra/clang-doc/support/Utils.h
+++ b/clang-tools-extra/clang-doc/support/Utils.h
@@ -23,4 +23,6 @@ llvm::SmallString<128> appendPathPosix(llvm::StringRef Base,
 void getHtmlFiles(llvm::StringRef AssetsPath,
                   clang::doc::ClangDocContext &CDCtx);
 
+void getMdFiles(llvm::StringRef AssetsPath, clang::doc::ClangDocContext &CDCtx);
+
 #endif
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index aef2acfde0ca3..8728a8275bed3 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -35,6 +35,11 @@ set(assets
   navbar-template.mustache
   index-template.mustache
   alias-template.mustache
+  md/all-files-template.mustache
+  md/class-template.mustache
+  md/comments-partial.mustache
+  md/index-template.mustache
+  md/namespace-template.mustache
 )
 
 set(asset_dir "${CMAKE_CURRENT_SOURCE_DIR}/../assets")
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index bc30f61264174..1197f726464aa 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -45,6 +45,7 @@
 
 using namespace clang::tooling;
 using namespace clang;
+using clang::doc::OutputFormatTy;
 
 static llvm::cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
 static llvm::cl::OptionCategory ClangDocCategory("clang-doc options");
@@ -111,20 +112,19 @@ Turn on time profiler. Generates clang-doc-tracing.json)"),
                                       llvm::cl::init(false),
                                       llvm::cl::cat(ClangDocCategory));
 
-enum OutputFormatTy { md, yaml, html, json };
-
-static llvm::cl::opt<OutputFormatTy>
-    FormatEnum("format", llvm::cl::desc("Format for outputted docs."),
-               llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml",
-                                           "Documentation in YAML format."),
-                                clEnumValN(OutputFormatTy::md, "md",
-                                           "Documentation in MD format."),
-                                clEnumValN(OutputFormatTy::html, "html",
-                                           "Documentation in HTML format."),
-                                clEnumValN(OutputFormatTy::json, "json",
-                                           "Documentation in JSON format")),
-               llvm::cl::init(OutputFormatTy::yaml),
-               llvm::cl::cat(ClangDocCategory));
+static llvm::cl::opt<OutputFormatTy> FormatEnum(
+    "format", llvm::cl::desc("Format for outputted docs."),
+    llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml",
+                                "Documentation in YAML format."),
+                     clEnumValN(OutputFormatTy::md, "md",
+                                "Documentation in MD format."),
+                     clEnumValN(OutputFormatTy::html, "html",
+                                "Documentation in HTML format."),
+                     clEnumValN(OutputFormatTy::json, "json",
+                                "Documentation in JSON format"),
+                     clEnumValN(OutputFormatTy::md_mustache, "md_mustache",
+                                "Documentation in MD format.")),
+    llvm::cl::init(OutputFormatTy::yaml), llvm::cl::cat(ClangDocCategory));
 
 static llvm::ExitOnError ExitOnErr;
 
@@ -138,6 +138,8 @@ static llvm::StringRef getFormatString() {
     return "html";
   case OutputFormatTy::json:
     return "json";
+  case OutputFormatTy::md_mustache:
+    return "md_mustache";
   }
   llvm_unreachable("Unknown OutputFormatTy");
 }
@@ -179,8 +181,10 @@ static llvm::Error getHtmlFiles(const char *Argv0,
     llvm::outs() << "Asset path supply is not a directory: " << UserAssetPath
                  << " falling back to default\n";
   if (IsDir) {
-    if (auto Err = getAssetFiles(CDCtx))
-      return Err;
+    if (FormatEnum == OutputFormatTy::html) {
+      if (auto Err = getAssetFiles(CDCtx))
+        return Err;
+    }
   }
   void *MainAddr = (void *)(intptr_t)getExecutablePath;
   std::string ClangDocPath = getExecutablePath(Argv0, MainAddr);
@@ -196,6 +200,27 @@ static llvm::Error getHtmlFiles(const char *Argv0,
   return llvm::Error::success();
 }
 
+static llvm::Error getMdFiles(const char *Argv0,
+                              clang::doc::ClangDocContext &CDCtx) {
+  bool IsDir = llvm::sys::fs::is_directory(UserAssetPath);
+  if (!UserAssetPath.empty() && !IsDir)
+    llvm::outs() << "Asset path supply is not a directory: " << UserAssetPath
+                 << " falling back to default\n";
+
+  void *MainAddr = (void *)(intptr_t)getExecutablePath;
+  std::string ClangDocPath = getExecutablePath(Argv0, MainAddr);
+  llvm::SmallString<128> NativeClangDocPath;
+  llvm::sys::path::native(ClangDocPath, NativeClangDocPath);
+
+  llvm::SmallString<128> AssetsPath;
+  AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
+  llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc", "md");
+
+  getMdFiles(AssetsPath, CDCtx);
+
+  return llvm::Error::success();
+}
+
 /// Make the output of clang-doc deterministic by sorting the children of
 /// namespaces and records.
 static void
@@ -283,10 +308,13 @@ Example usage for a project using a compile commands database:
     clang::doc::ClangDocContext CDCtx(
         Executor->getExecutionContext(), ProjectName, PublicOnly, OutDirectory,
         SourceRoot, RepositoryUrl, RepositoryCodeLinePrefix, BaseDirectory,
-        {UserStylesheets.begin(), UserStylesheets.end()}, Diags, FTimeTrace);
+        {UserStylesheets.begin(), UserStylesheets.end()}, Diags, FormatEnum,
+        FTimeTrace);
 
     if (Format == "html")
       ExitOnErr(getHtmlFiles(argv[0], CDCtx));
+    else if (Format == "md_mustache")
+      ExitOnErr(getMdFiles(argv[0], CDCtx));
 
     llvm::timeTraceProfilerBegin("Executor Launch", "total runtime");
     // Mapping phase
diff --git a/clang-tools-extra/test/clang-doc/basic-project.mustache.test b/clang-tools-extra/test/clang-doc/basic-project.mustache.test
index d29bdaf8a05fd..200e763d28147 100644
--- a/clang-tools-extra/test/clang-doc/basic-project.mustache.test
+++ b/clang-tools-extra/test/clang-doc/basic-project.mustache.test
@@ -7,6 +7,14 @@
 // RUN: FileCheck %s -input-file=%t/docs/html/GlobalNamespace/_ZTV9Rectangle.html -check-prefix=HTML-RECTANGLE
 // RUN: FileCheck %s -input-file=%t/docs/html/GlobalNamespace/_ZTV6Circle.html -check-prefix=HTML-CIRCLE
 
+// RUN: clang-doc --format=md_mustache --output=%t/docs --executor=all-TUs %t/build/compile_commands.json
+// RUN: FileCheck %s -input-file=%t/docs/md/all_files.md -check-prefixes=MD-ALL-FILES
+// RUN: FileCheck %s -input-file=%t/docs/md/index.md -check-prefixes=MD-INDEX
+// RUN: FileCheck %s -input-file=%t/docs/md/GlobalNamespace/_ZTV5Shape.md -check-prefixes=MD-SHAPE
+// RUN: FileCheck %s -input-file=%t/docs/md/GlobalNamespace/_ZTV10Calculator.md -check-prefixes=MD-CALC
+// RUN: FileCheck %s -input-file=%t/docs/md/GlobalNamespace/_ZTV9Rectangle.md -check-prefixes=MD-RECTANGLE
+// RUN: FileCheck %s -input-file=%t/docs/md/GlobalNamespace/_ZTV6Circle.md -check-prefixes=MD-CIRCLE
+
 HTML-SHAPE-LABEL:<html lang="en-US">
 HTML-SHAPE-NEXT: <head>
 HTML-SHAPE:      </head>
@@ -524,3 +532,122 @@ HTML-CIRCLE-NEXT:     </div>
 HTML-CIRCLE-NEXT: </main>
 HTML-CIRCLE-NEXT: </body>
 HTML-CIRCLE-NEXT: </html>
+
+MD-CALC: # class Calculator
+MD-CALC: *Defined at .{{[\/]}}include{{[\/]}}Calculator.h#8*
+MD-CALC: **brief** A simple calculator class.
+MD-CALC: Provides basic arithmetic operations.
+MD-CALC: ## Members
+MD-CALC: public int public_val
+MD-CALC: public static const int static_val
+MD-CALC: ## Functions
+MD-CALC: ### add
+MD-CALC: *public int add(int a, int b)*
+MD-CALC: *Defined at .{{[\/]}}src{{[\/]}}Calculator.cpp#3*
+MD-CALC: **brief** Adds two integers.
+MD-CALC: **a** First integer.
+MD-CALC: **b** Second integer.
+MD-CALC: **return** int The sum of a and b.
+MD-CALC: ### subtract
+MD-CALC: *public int subtract(int a, int b)*
+MD-CALC: *Defined at .{{[\/]}}src{{[\/]}}Calculator.cpp#7*
+MD-CALC: **brief** Subtracts the second integer from the first.
+MD-CALC: **a** First integer.
+MD-CALC: **b** Second integer.
+MD-CALC: **return** int The result of a - b.
+MD-CALC: ### multiply
+MD-CALC: *public int multiply(int a, int b)*
+MD-CALC: *Defined at .{{[\/]}}src{{[\/]}}Calculator.cpp#11*
+MD-CALC: **brief** Multiplies two integers.
+MD-CALC: **a** First integer.
+MD-CALC: **b** Second integer.
+MD-CALC: **return** int The product of a and b.
+MD-CALC: ### divide
+MD-CALC: *public double divide(int a, int b)*
+MD-CALC: *Defined at .{{[\/]}}src{{[\/]}}Calculator.cpp#15*
+MD-CALC: **brief** Divides the first integer by the second.
+MD-CALC: **a** First integer.
+MD-CALC: **b** Second integer.
+MD-CALC: **return** double The result of a / b.
+MD-CALC: **throw** if b is zero.
+MD-CALC: ### mod
+MD-CALC: *public static int mod(int a, int b)*
+MD-CALC: *Defined at ./include{{[\/]}}Calculator.h#54*
+MD-CALC: **brief** Performs the mod operation on integers.
+MD-CALC: **a** First integer.
+MD-CALC: **b** Second integer.
+MD-CALC: **return** The result of a % b.
+
+MD-CIRCLE: # class Circle
+MD-CIRCLE: *Defined at .{{[\/]}}include{{[\/]}}Circle.h#10*
+MD-CIRCLE: **brief** Circle class derived from Shape.
+MD-CIRCLE: Represents a circle with a given radius.
+MD-CIRCLE: Inherits from Shape
+MD-CIRCLE: ## Members
+MD-CIRCLE: private double radius_
+MD-CIRCLE: ## Functions
+MD-CIRCLE: ### Circle
+MD-CIRCLE: *public void Circle(double radius)*
+MD-CIRCLE: *Defined at .{{[\/]}}src{{[\/]}}Circle.cpp#3*
+MD-CIRCLE: **brief** Constructs a new Circle object.
+MD-CIRCLE: **radius** Radius of the circle.
+MD-CIRCLE: ### area
+MD-CIRCLE: *public double area()*
+MD-CIRCLE: *Defined at .{{[\/]}}src{{[\/]}}Circle.cpp#5*
+MD-CIRCLE: **brief** Calculates the area of the circle.
+MD-CIRCLE: **return** double The area of the circle.
+MD-CIRCLE: ### perimeter
+MD-CIRCLE: *public double perimeter()*
+MD-CIRCLE: *Defined at .{{[\/]}}src{{[\/]}}Circle.cpp#9*
+MD-CIRCLE: **brief** Calculates the perimeter of the circle.
+MD-CIRCLE: **return** double The perimeter of the circle.
+
+MD-RECTANGLE: # class Rectangle
+MD-RECTANGLE: *Defined at .{{[\/]}}include{{[\/]}}Rectangle.h#10*
+MD-RECTANGLE: **brief** Rectangle class derived from Shape.
+MD-RECTANGLE: Represents a rectangle with a given width and height.
+MD-RECTANGLE: Inherits from Shape
+MD-RECTANGLE: ## Members
+MD-RECTANGLE: private double width_
+MD-RECTANGLE: private double height_
+MD-RECTANGLE: ## Functions
+MD-RECTANGLE: ### Rectangle
+MD-RECTANGLE: *public void Rectangle(double width, double height)*
+MD-RECTANGLE: *Defined at .{{[\/]}}src{{[\/]}}Rectangle.cpp#3*
+MD-RECTANGLE: **brief** Constructs a new Rectangle object.
+MD-RECTANGLE: **width** Width of the rectangle.
+MD-RECTANGLE: **height** Height of the rectangle.
+MD-RECTANGLE: ### area
+MD-RECTANGLE: *public double area()*
+MD-RECTANGLE: *Defined at .{{[\/]}}src{{[\/]}}Rectangle.cpp#6*
+MD-RECTANGLE: **brief** Calculates the area of the rectangle.
+MD-RECTANGLE: **return** double The area of the rectangle.
+MD-RECTANGLE: ### perimeter
+MD-RECTANGLE: *public double perimeter()*
+MD-RECTANGLE: *Defined at .{{[\/]}}src{{[\/]}}Rectangle.cpp#10*
+MD-RECTANGLE: **brief** Calculates the perimeter of the rectangle.
+MD-RECTANGLE: **return** double The perimeter of the rectangle.
+
+MD-SHAPE: # class Shape
+MD-SHAPE: *Defined at .{{[\/]}}include{{[\/]}}Shape.h#8*
+MD-SHAPE: **brief** Abstract base class for shapes.
+MD-SHAPE: Provides a common interface for different types of shapes.
+MD-SHAPE: ## Functions
+MD-SHAPE: ### area
+MD-SHAPE: *public double area()*
+MD-SHAPE: **brief** Calculates the area of the shape.
+MD-SHAPE: **return** double The area of the shape.
+MD-SHAPE: ### perimeter
+MD-SHAPE: *public double perimeter()*
+MD-SHAPE: **brief** Calculates the perimeter of the shape.
+MD-SHAPE: **return** double The perimeter of the shape.
+MD-SHAPE: ### ~Shape
+MD-SHAPE: *public void ~Shape()*
+MD-SHAPE: *Defined at .{{[\/]}}include{{[\/]}}Shape.h#13*
+MD-SHAPE: **brief** Virtual destructor.
+
+MD-ALL-FILES: # All Files
+MD-ALL-FILES: ## [GlobalNamespace](GlobalNamespace{{[\/]}}index.md)
+
+MD-INDEX: #  C/C++ Reference
+MD-INDEX: * Namespace: [GlobalNamespace](GlobalNamespace)
diff --git a/clang-tools-extra/test/clang-doc/builtin_types.cpp b/clang-tools-extra/test/clang-doc/builtin_types.cpp
index 6c1fc8a1d7879..3f416261ff7ea 100644
--- a/clang-tools-extra/test/clang-doc/builtin_types.cpp
+++ b/clang-tools-extra/test/clang-doc/builtin_types.cpp
@@ -1,5 +1,5 @@
 // RUN: rm -rf %t
-// RUN: mkdir -p %t/yaml %t/md
+// RUN: mkdir -p %t/yaml %t/md %t/md_mustache
 
 // RUN: clang-doc --doxygen --executor=standalone %s -output=%t/yaml
 // RUN: FileCheck %s < %t/yaml/index.yaml --check-prefix=YAML
@@ -7,6 +7,9 @@
 // RUN: clang-doc --doxygen --executor=standalone %s -output=%t/md --format=md
 // RUN: FileCheck %s < %t/md/GlobalNamespace/index.md  --check-prefix=MD
 
+// RUN: clang-doc --doxygen --executor=standalone %s -output=%t/md_mustache --format=md_mustache
+// RUN: FileCheck %s < %t/md/GlobalNamespace/index.md  --check-prefix=MD-MUSTACHE
+
 //      YAML: ---
 // YAML-NEXT: USR:             '0000000000000000000000000000000000000000'
 // YAML-NEXT: ChildFunctions:
@@ -14,6 +17,9 @@
 // MD: # Global Namespace
 // MD: ## Functions
 
+// MD-MUSTACHE: # Global Namespace
+// MD-MUSTACHE: ## Functions
+
 extern bool b();
 
 // YAML-NEXT:   - USR:             '88A104C263241E354ECF5B55B04AE8CEAD625B71'
@@ -29,6 +35,9 @@ extern bool b();
 // MD: ### b
 // MD: *bool b()*
 
+// MD-MUSTACHE: ### b
+// MD-MUSTACHE: *bool b()*
+
 char c();
 
 // YAML-NEXT:   - USR:             'EA3287837B3F175C8DB154406B4DAD2924F479B5'
@@ -44,6 +53,9 @@ char c();
 // MD: ### c
 // MD: *char c()*
 
+// MD-MUSTACHE: ### c
+// MD-MUSTACHE: *char c()*
+
 double d();
 
 // YAML-NEXT:   - USR:             '60A47E4696CEFC411AB2E1EEFA2DD914E2A7E450'
@@ -59,6 +71,9 @@ double d();
 // MD: ### d
 // MD: *double d()*
 
+// MD-MUSTACHE: ### d
+// MD-MUSTACHE: *double d()*
+
 float f();
 
 // YAML-NEXT:   - USR:             'B3A9EC6BECD5869CF3ACDFB25153CFE6BBDD5EAB'
@@ -74,6 +89,9 @@ float f();
 // MD: ### f
 // MD: *float f()*
 
+// MD-MUSTACHE: ### f
+// MD-MUSTACHE: *float f()*
+
 int i();
 
 // YAML-NEXT:   - USR:             '307041280A81EB46F949A94AD52587C659FD801C'
@@ -89,6 +107,9 @@ int i();
 // MD: ### i
 // MD: *int i()*
 
+// MD-MUSTACHE: ### i
+// MD-MUSTACHE: *int i()*
+
 long l();
 
 // YAML-NEXT:   - USR:             'A1CE9AB0064C412F857592E01332C641C1A06F37'
@@ -104,6 +125,9 @@ long l();
 // MD: ### l
 // MD: *long l()*
 
+// MD-MUSTACHE: ### l
+// MD-MUSTACHE: *long l()*
+
 long long ll();
 
 // YAML-NEXT:   - USR:             '5C2C44ED4825C066EF6ED796863586F343C8BCA9'
@@ -119,6 +143,9 @@ long long ll();
 // MD: ### ll
 // MD: *long long ll()*
 
+// MD-MUSTACHE: ### ll
+// MD-MUSTACHE: *long long ll()*
+
 short s();
 
 // YAML-NEXT:   - USR:             '412341570FD3AD2C3A1E9A1DE7B3C01C07BEACFE'
@@ -134,3 +161,6 @@ short s();
 
 // MD: ### s
 // MD: *short s()*
+
+// MD-MUSTACHE: ### s
+// MD-MUSTACHE: *short s()*
diff --git a/clang-tools-extra/test/clang-doc/comments-in-macros.cpp b/clang-tools-extra/test/clang-doc/comments-in-macros.cpp
index 9e022314edaeb..20bbf4cd087c0 100644
--- a/clang-tools-extra/test/clang-doc/comments-in-macros.cpp
+++ b/clang-tools-extra/test/clang-doc/comments-in-macros.cpp
@@ -2,12 +2,14 @@
 
 // RUN: rm -rf %t && mkdir -p %t
 // RUN: clang-doc --format=md --doxygen --output=%t --executor=standalone %s
+// RUN: clang-doc --format=html --doxygen --output=%t --executor=standalone %s
+// RUN: clang-doc --format=md_mustache --doxygen --output=%t --executor=standalone %s
 // RUN: FileCheck %s < %t/GlobalNamespace/MyClass.md --check-prefix=MD-MYCLASS-LINE
 // RUN: FileCheck %s < %t/GlobalNamespace/MyClass.md --check-prefix=MD-MYCLASS
-
-// RUN: clang-doc --format=html --doxygen --output=%t --executor=standalone %s
 // RUN: FileCheck %s < %t/html/GlobalNamespace/_ZTV7MyClass.html --check-prefix=HTML-MYCLASS-LINE
 // RUN: FileCheck %s < %t/html/GlobalNamespace/_ZTV7MyClass.html --check-prefix=HTML-MYCLASS
+// RUN: FileCheck %s < %t/md/GlobalNamespace/_ZTV7MyClass.md --check-prefix=MD-MUSTACHE-MYCLASS-LINE
+// RUN: FileCheck %s < %t/md/GlobalNamespace/_ZTV7MyClass.md --check-prefix=MD-MUSTACHE-MYCLASS
 
 #define DECLARE_METHODS                                           \
     /**   							  
@@ -21,6 +23,9 @@
 // MD-MYCLASS: *public int Add(int a, int b)*
 // MD-MYCLASS: **brief** Declare a method to calculate the sum of two numbers
 
+// MD-MUSTACHE-MYCLASS: ### Add
+// MD-MUSTACHE-MYCLASS: *public int Add(int a, int b)*
+// MD-MUSTACHE-MYCLASS: **brief** Declare a method to calculate the sum of two numbers
 
 // HTML-MYCLASS: <pre><code class="language-cpp code-clang-doc">int Add (int a, int b)</code></pre>
 // HTML-MYCLASS: <div class="doc-card">
@@ -28,11 +33,11 @@
 // HTML-MYCLASS:         <p> Declare a method to calculate the sum of two numbers</p>
 // HTML-MYCLASS:     </div>
 
-
 class MyClass {
 public:
-// MD-MYCLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}comments-in-macros.cpp#[[@LINE-2]]*
-// HTML-MYCLASS-LINE: <p>Defined at line [[@LINE-3]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}comments-in-macros.cpp</p>
+// MD-MYCLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}comments-in-macros.cpp#[[@LINE+3]]*
+// HTML-MYCLASS-LINE: <p>Defined at line [[@LINE+2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}comments-in-macros.cpp</p>
+// MD-MUSTACHE-MYCLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}comments-in-macros.cpp#[[@LINE+1]]*
     DECLARE_METHODS
 };
 
diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index 854de5ee007e2..8d5c9e77a7493 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -18,6 +18,15 @@
 // RUN: FileCheck %s < %t/Vehicles/index.md --check-prefix=MD-VEHICLES-LINE
 // RUN: FileCheck %s < %t/Vehicles/index.md --check-prefix=MD-VEHICLES
 
+// COM: FIXME: Add enum value comments to template
+
+// RUN: clang-doc --format=md_mustache --doxygen --output=%t --executor=standalone %s
+// RUN: FileCheck %s < %t/md/GlobalNamespace/index.md --check-prefix=MD-MUSTACHE-INDEX-LINE
+// RUN: FileCheck %s < %t/md/GlobalNamespace/index.md --check-prefix=MD-MUSTACHE-INDEX
+// RUN: FileCheck %s < %t/md/GlobalNamespace/_ZTV7Animals.md --check-prefix=MD-MUSTACHE-ANIMAL-LINE
+// RUN: FileCheck %s < %t/md/GlobalNamespace/_ZTV7Animals.md --check-prefix=MD-MUSTACHE-ANIMAL
+// RUN: FileCheck %s < %t/md/Vehicles/index.md --check-prefix=MD-MUSTACHE-VEHICLES-LINE
+// RUN: FileCheck %s < %t/md/Vehicles/index.md --check-prefix=MD-MUSTACHE-VEHICLES
 
 /**
  * @brief For specifying RGB colors
@@ -25,6 +34,7 @@
 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>
+  // MD-MUSTACHE-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-3]]*
   Red,   ///< Comment 1
   Green, ///< Comment 2
   Blue   ///< Comment 3
@@ -87,12 +97,21 @@ enum Color {
 // HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-62]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
 
+// MD-MUSTACHE-INDEX: ## Enums
+// MD-MUSTACHE-INDEX: | enum Color |
+// MD-MUSTACHE-INDEX: --
+// MD-MUSTACHE-INDEX: | Red |
+// MD-MUSTACHE-INDEX: | Green |
+// MD-MUSTACHE-INDEX: | Blue |
+// MD-MUSTACHE-INDEX: **brief** For specifying RGB colors
+
 /**
  * @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>
+  // MD-MUSTACHE-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-3]]*
 
   /// Comment 1
   Circle,
@@ -361,6 +380,7 @@ class FilePermissions {
 class Animals {
   // 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>
+  // MD-MUSTACHE-ANIMAL-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-3]]*
 public:
   /**
    * @brief specify what animal the class is
@@ -368,6 +388,7 @@ 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>
+    // MD-MUSTACHE-ANIMAL-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-3]]*
     Dog,   ///< Man's best friend
     Cat,   ///< Man's other best friend
     Iguana ///< A lizard
@@ -435,6 +456,15 @@ class Animals {
 // MD-ANIMAL: | Iguana | 2 | A lizard |
 // MD-ANIMAL: **brief** specify what animal the class is
 
+// MD-MUSTACHE-ANIMAL: # class Animals
+// MD-MUSTACHE-ANIMAL: ## Enums
+// MD-MUSTACHE-ANIMAL: | enum AnimalType |
+// MD-MUSTACHE-ANIMAL: --
+// MD-MUSTACHE-ANIMAL: | Dog |
+// MD-MUSTACHE-ANIMAL: | Cat |
+// MD-MUSTACHE-ANIMAL: | Iguana |
+// MD-MUSTACHE-ANIMAL: **brief** specify what animal the class is
+
 namespace Vehicles {
 /**
  * @brief specify type of car
@@ -442,6 +472,7 @@ namespace Vehicles {
 enum Car {
   // MD-VEHICLES-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
   // HTML-VEHICLES-LINE: Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp
+  // MD-MUSTACHE-VEHICLES-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-3]]*
 
   Sedan,    ///< Comment 1
   SUV,      ///< Comment 2
@@ -514,6 +545,16 @@ enum Car {
 // HTML-VEHICLES-NEXT:      <p>Defined at line [[@LINE-72]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-VEHICLES-NEXT:    </div>
 
+// MD-MUSTACHE-VEHICLES: # namespace Vehicles
+// MD-MUSTACHE-VEHICLES: ## Enums
+// MD-MUSTACHE-VEHICLES: | enum Car |
+// MD-MUSTACHE-VEHICLES: --
+// MD-MUSTACHE-VEHICLES: | Sedan |
+// MD-MUSTACHE-VEHICLES: | SUV |
+// MD-MUSTACHE-VEHICLES: | Pickup |
+// MD-MUSTACHE-VEHICLES: | Hatchback |
+// MD-MUSTACHE-VEHICLES: **brief** specify type of car
+
 enum ColorUserSpecified {
   RedUserSpecified = 'A',
   GreenUserSpecified = 2,
@@ -553,3 +594,9 @@ enum ColorUserSpecified {
 // HTML-INDEX-NEXT:     </table>
 // HTML-INDEX-NEXT:     <p>Defined at line [[@LINE-37]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
 // HTML-INDEX-NEXT:   </div>
+
+// MD-MUSTACHE-INDEX: | enum ColorUserSpecified |
+// MD-MUSTACHE-INDEX: --
+// MD-MUSTACHE-INDEX: | RedUserSpecified |
+// MD-MUSTACHE-INDEX: | GreenUserSpecified |
+// MD-MUSTACHE-INDEX: | BlueUserSpecified |
diff --git a/clang-tools-extra/test/clang-doc/index.cpp b/clang-tools-extra/test/clang-doc/index.cpp
index af72720740fae..2004e6ae5659a 100644
--- a/clang-tools-extra/test/clang-doc/index.cpp
+++ b/clang-tools-extra/test/clang-doc/index.cpp
@@ -13,11 +13,13 @@ namespace inner {
 // CHECK-JSON-NEXT:    {
 // CHECK-JSON-NEXT:      "Name": "GlobalNamespace",
 // CHECK-JSON-NEXT:      "QualName": "GlobalNamespace",
+// CHECK-JSON-NEXT:      "Type": "namespace",
 // CHECK-JSON-NEXT:      "USR": "0000000000000000000000000000000000000000"
 // CHECK-JSON-NEXT:    },
 // CHECK-JSON-NEXT:    {
 // CHECK-JSON-NEXT:      "Name": "inner",
 // CHECK-JSON-NEXT:      "QualName": "inner",
+// CHECK-JSON-NEXT:      "Type": "namespace",
 // CHECK-JSON-NEXT:      "USR": "{{([0-9A-F]{40})}}"
 // CHECK-JSON-NEXT:    }
 // CHECK-JSON-NEXT:  ]
@@ -62,3 +64,5 @@ namespace inner {
 // CHECK-HTML-NEXT:            </div>
 // CHECK-HTML-NEXT:        </div>
 // CHECK-HTML-NEXT:    </main>
+
+// COM: TODO: Add Markdown index test
diff --git a/clang-tools-extra/test/clang-doc/json/class.cpp b/clang-tools-extra/test/clang-doc/json/class.cpp
index 6783e71a201db..3bb9f5e6ec6bb 100644
--- a/clang-tools-extra/test/clang-doc/json/class.cpp
+++ b/clang-tools-extra/test/clang-doc/json/class.cpp
@@ -142,7 +142,6 @@ struct MyClass {
 // CHECK-NEXT:          ],
 // CHECK-NEXT:          "VerticalDisplay": false
 // CHECK-NEXT:        }
-// CHECK-NEXT:        "USR": "0000000000000000000000000000000000000000"
 // CHECK-NEXT:      },
 // CHECK-NEXT:      {
 // CHECK-NEXT:        "Description": {
@@ -164,12 +163,12 @@ struct MyClass {
 // CHECK-NEXT:          "QualName": "Foo",
 // CHECK-NEXT:          "USR": "{{[0-9A-F]*}}"
 // CHECK-NEXT:        }
-// CHECK-NEXT:        "USR": "0000000000000000000000000000000000000000"
 // CHECK-NEXT:      }
 // CHECK-NEXT:    ],
 // CHECK-NEXT:    "HasContexts": true,
 // CHECK-NEXT:    "HasEnums": true,
 // CHECK-NEXT:    "HasFriends": true,
+// CHECK-NEXT:    "HasMembers": true,
 // CHECK-NEXT:    "HasPrivateMembers": true,
 // CHECK-NEXT:    "HasProtectedMembers": true,
 // CHECK-NEXT:    "HasProtectedMethods": true,
diff --git a/clang-tools-extra/test/clang-doc/json/namespace.cpp b/clang-tools-extra/test/clang-doc/json/namespace.cpp
index 59f6f5491cdc5..a801d86ffffdd 100644
--- a/clang-tools-extra/test/clang-doc/json/namespace.cpp
+++ b/clang-tools-extra/test/clang-doc/json/namespace.cpp
@@ -125,7 +125,6 @@ typedef int MyTypedef;
 // CHECK-NEXT:        }
 // CHECK-NEXT:      }
 // CHECK-NEXT:    ],
-// CHECK-NEXT:    "USR": "0000000000000000000000000000000000000000"
 // CHECK-NEXT:   "Variables": [
 // CHECK-NEXT:     {
 // CHECK-NEXT:       "End": true,
diff --git a/clang-tools-extra/test/clang-doc/namespace.cpp b/clang-tools-extra/test/clang-doc/namespace.cpp
index 49e61dc61872e..5ed27944c92fe 100644
--- a/clang-tools-extra/test/clang-doc/namespace.cpp
+++ b/clang-tools-extra/test/clang-doc/namespace.cpp
@@ -41,18 +41,43 @@
 // COM: FIXME: Add global functions to the namespace template
 // COM: FIXME: Add namespaces to the namespace template
 
+// RUN: clang-doc --format=md_mustache --output=%t --executor=standalone %s
+// RUN: FileCheck %s < %t/md/@nonymous_namespace/_ZTVN12_GLOBAL__N_19AnonClassE.md -check-prefix=MD-MUSTACHE-ANON-CLASS-LINE
+// RUN: FileCheck %s < %t/md/@nonymous_namespace/_ZTVN12_GLOBAL__N_19AnonClassE.md -check-prefix=MD-MUSTACHE-ANON-CLASS
+// RUN: FileCheck %s < %t/md/@nonymous_namespace/index.md -check-prefix=MD-MUSTACHE-ANON-INDEX-LINE
+// RUN: FileCheck %s < %t/md/@nonymous_namespace/index.md -check-prefix=MD-MUSTACHE-ANON-INDEX
+// RUN: FileCheck %s < %t/md/AnotherNamespace/_ZTVN16AnotherNamespace23ClassInAnotherNamespaceE.md -check-prefix=MD-MUSTACHE-ANOTHER-CLASS-LINE
+// RUN: FileCheck %s < %t/md/AnotherNamespace/_ZTVN16AnotherNamespace23ClassInAnotherNamespaceE.md -check-prefix=MD-MUSTACHE-ANOTHER-CLASS
+// RUN: FileCheck %s < %t/md/AnotherNamespace/index.md -check-prefix=MD-MUSTACHE-ANOTHER-INDEX-LINE
+// RUN: FileCheck %s < %t/md/AnotherNamespace/index.md -check-prefix=MD-MUSTACHE-ANOTHER-INDEX
+// RUN: FileCheck %s < %t/md/PrimaryNamespace/NestedNamespace/_ZTVN16PrimaryNamespace15NestedNamespace22ClassInNestedNamespaceE.md -check-prefix=MD-MUSTACHE-NESTED-CLASS-LINE
+// RUN: FileCheck %s < %t/md/PrimaryNamespace/NestedNamespace/_ZTVN16PrimaryNamespace15NestedNamespace22ClassInNestedNamespaceE.md -check-prefix=MD-MUSTACHE-NESTED-CLASS
+// RUN: FileCheck %s < %t/md/PrimaryNamespace/NestedNamespace/index.md -check-prefix=MD-MUSTACHE-NESTED-INDEX-LINE
+// RUN: FileCheck %s < %t/md/PrimaryNamespace/NestedNamespace/index.md -check-prefix=MD-MUSTACHE-NESTED-INDEX
+// RUN: FileCheck %s < %t/md/PrimaryNamespace/index.md -check-prefix=MD-MUSTACHE-PRIMARY-INDEX-LINE
+// RUN: FileCheck %s < %t/md/PrimaryNamespace/index.md -check-prefix=MD-MUSTACHE-PRIMARY-INDEX
+// RUN: FileCheck %s < %t/md/PrimaryNamespace/_ZTVN16PrimaryNamespace23ClassInPrimaryNamespaceE.md -check-prefix=MD-MUSTACHE-PRIMARY-CLASS-LINE
+// RUN: FileCheck %s < %t/md/PrimaryNamespace/_ZTVN16PrimaryNamespace23ClassInPrimaryNamespaceE.md -check-prefix=MD-MUSTACHE-PRIMARY-CLASS
+// RUN: FileCheck %s < %t/md/GlobalNamespace/index.md -check-prefix=MD-MUSTACHE-GLOBAL-INDEX
+// RUN: FileCheck %s < %t/md/all_files.md -check-prefix=MD-MUSTACHE-ALL-FILES
+// RUN: FileCheck %s < %t/md/index.md -check-prefix=MD-MUSTACHE-INDEX
+
 // Anonymous Namespace
 namespace {
 void anonFunction() {}
 // MD-ANON-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-1]]*
-// HTML-ANON-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// MD-MUSTACHE-ANON-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-2]]*
+// HTML-ANON-INDEX-LINE: <p>Defined at line [[@LINE-3]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
 
 class AnonClass {};
 // MD-ANON-CLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-1]]*
-// HTML-ANON-CLASS-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// MD-MUSTACHE-ANON-CLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-2]]*
+// HTML-ANON-CLASS-LINE: <p>Defined at line [[@LINE-3]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
 
 // MD-ANON-CLASS: # class AnonClass
+// MD-MUSTACHE-ANON-CLASS: # class AnonClass
 // HTML-ANON-CLASS: <h1 class="hero__title-large">class AnonClass</h1>
+// MUSTACHE-ANON-CLASS: <h1 class="hero__title-large">class AnonClass</h1>
 } // namespace
 
 // MD-ANON-INDEX: # namespace @nonymous_namespace
@@ -78,17 +103,27 @@ class AnonClass {};
 // HTML-ANON-INDEX-NOT: <h3 id="{{([0-9A-F]{40})}}">anonFunction</h3>
 // HTML-ANON-INDEX-NOT: <p>void anonFunction()</p>
 
+// MD-MUSTACHE-ANON-INDEX: # namespace @nonymous_namespace
+// MD-MUSTACHE-ANON-INDEX: Anonymous Namespace
+// MD-MUSTACHE-ANON-INDEX: ## Records
+// MD-MUSTACHE-ANON-INDEX: * [AnonClass](AnonClass.md)
+// MD-MUSTACHE-ANON-INDEX: ## Functions
+// MD-MUSTACHE-ANON-INDEX: ### anonFunction
+// MD-MUSTACHE-ANON-INDEX: *void anonFunction()*
+
 // Primary Namespace
 namespace PrimaryNamespace {
 // Function in PrimaryNamespace
 void functionInPrimaryNamespace() {}
 // MD-PRIMARY-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-1]]*
 // HTML-PRIMARY-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// MD-MUSTACHE-PRIMARY-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-3]]*
 
 // Class in PrimaryNamespace
 class ClassInPrimaryNamespace {};
 // MD-PRIMARY-CLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-1]]*
 // HTML-PRIMARY-CLASS-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// MD-MUSTACHE-PRIMARY-CLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-3]]*
 
 // MD-PRIMARY-CLASS: # class ClassInPrimaryNamespace
 // MD-PRIMARY-CLASS: Class in PrimaryNamespace
@@ -97,19 +132,27 @@ class ClassInPrimaryNamespace {};
 // HTML-PRIMARY-CLASS:     <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>::
 // HTML-PRIMARY-CLASS:     <a href="./index.html"><div class="navbar-breadcrumb-item">PrimaryNamespace</div></a>
 // HTML-PRIMARY-CLASS: </div>
+
+// MD-MUSTACHE-PRIMARY-CLASS: # class ClassInPrimaryNamespace
+// MD-MUSTACHE-PRIMARY-CLASS: Class in PrimaryNamespace
+
 // HTML-PRIMARY-CLASS: <h1 class="hero__title-large">class ClassInPrimaryNamespace</h1>
 
+// MUSTACHE-PRIMARY-CLASS: <h1 class="hero__title-large">class ClassInPrimaryNamespace</h1>
+
 // Nested namespace
 namespace NestedNamespace {
 // Function in NestedNamespace
 void functionInNestedNamespace() {}
 // MD-NESTED-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-1]]*
 // HTML-NESTED-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// MD-MUSTACHE-NESTED-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-3]]*
 
 // Class in NestedNamespace
 class ClassInNestedNamespace {};
 // MD-NESTED-CLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-1]]*
 // HTML-NESTED-CLASS-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// MD-MUSTACHE-NESTED-CLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-3]]*
 
 // MD-NESTED-CLASS: # class ClassInNestedNamespace
 // MD-NESTED-CLASS: Class in NestedNamespace
@@ -119,7 +162,13 @@ class ClassInNestedNamespace {};
 // HTML-NESTED-CLASS:     <a href="../index.html"><div class="navbar-breadcrumb-item">PrimaryNamespace</div></a>::
 // HTML-NESTED-CLASS:     <a href="./index.html"><div class="navbar-breadcrumb-item">NestedNamespace</div></a>
 // HTML-NESTED-CLASS: </div>
+
+// MD-MUSTACHE-NESTED-CLASS: # class ClassInNestedNamespace
+// MD-MUSTACHE-NESTED-CLASS: Class in NestedNamespace
+
 // HTML-NESTED-CLASS: <h1 class="hero__title-large">class ClassInNestedNamespace</h1>
+
+// MUSTACHE-NESTED-CLASS: <h1 class="hero__title-large">class ClassInNestedNamespace</h1>
 } // namespace NestedNamespace
 
 // MD-NESTED-INDEX: # namespace NestedNamespace
@@ -150,8 +199,18 @@ class ClassInNestedNamespace {};
 // HTML-NESTED-INDEX:                 <p> Function in NestedNamespace</p>
 // HTML-NESTED-INDEX:             </div>
 // HTML-NESTED-INDEX:         </div>
-// HTML-NESTED-INDEX:         <p>Defined at line 105 of file {{.*}}namespace.cpp</p>
+// HTML-NESTED-INDEX:         <p>Defined at line [[@LINE-56]] of file {{.*}}namespace.cpp</p>
 // HTML-NESTED-INDEX:     </div>
+
+// MD-MUSTACHE-NESTED-INDEX: # namespace NestedNamespace
+// MD-MUSTACHE-NESTED-INDEX: Nested namespace
+// MD-MUSTACHE-NESTED-INDEX: ## Records
+// MD-MUSTACHE-NESTED-INDEX: * [ClassInNestedNamespace](ClassInNestedNamespace.md)
+// MD-MUSTACHE-NESTED-INDEX: ## Functions
+// MD-MUSTACHE-NESTED-INDEX: ### functionInNestedNamespace
+// MD-MUSTACHE-NESTED-INDEX: *void functionInNestedNamespace()*
+// MD-MUSTACHE-NESTED-INDEX: Function in NestedNamespace
+
 } // namespace PrimaryNamespace
 
 // MD-PRIMARY-INDEX: # namespace PrimaryNamespace
@@ -185,19 +244,41 @@ class ClassInNestedNamespace {};
 // HTML-PRIMARY-INDEX:                  <p> Function in PrimaryNamespace</p>
 // HTML-PRIMARY-INDEX:              </div>
 // HTML-PRIMARY-INDEX:          </div>
-// HTML-PRIMARY-INDEX:          <p>Defined at line 84 of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// HTML-PRIMARY-INDEX:          <p>Defined at line [[@LINE-130]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
 // HTML-PRIMARY-INDEX:      </div>
+// HTML-PRIMARY-INDEX      <h2>Inner Classes</h2>
+// HTML-PRIMARY-INDEX          <ul class="class-container">
+// HTML-PRIMARY-INDEX              <li id="{{([0-9A-F]{40})}}" style="max-height: 40px;">
+// HTML-PRIMARY-INDEX                  <a href="_ZTVN16PrimaryNamespace23ClassInPrimaryNamespaceE.html">
+// HTML-PRIMARY-INDEX                      <pre><code class="language-cpp code-clang-doc">class ClassInPrimaryNamespace</code></pre>
+// HTML-PRIMARY-INDEX                  </a>
+// HTML-PRIMARY-INDEX              </li>
+// HTML-PRIMARY-INDEX          </ul>
+
+// MD-MUSTACHE-PRIMARY-INDEX: # namespace PrimaryNamespace
+// MD-MUSTACHE-PRIMARY-INDEX: Primary Namespace
+// MD-MUSTACHE-PRIMARY-INDEX: ## Namespaces
+// MD-MUSTACHE-PRIMARY-INDEX: * [NestedNamespace](NestedNamespace{{[\/]}}index.md)
+// MD-MUSTACHE-PRIMARY-INDEX: ## Records
+// MD-MUSTACHE-PRIMARY-INDEX: * [ClassInPrimaryNamespace](ClassInPrimaryNamespace.md)
+// MD-MUSTACHE-PRIMARY-INDEX: ## Functions
+// MD-MUSTACHE-PRIMARY-INDEX: ### functionInPrimaryNamespace
+// MD-MUSTACHE-PRIMARY-INDEX: *void functionInPrimaryNamespace()*
+// MD-MUSTACHE-PRIMARY-INDEX: Function in PrimaryNamespace
+
 // AnotherNamespace
 namespace AnotherNamespace {
 // Function in AnotherNamespace
 void functionInAnotherNamespace() {}
 // MD-ANOTHER-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-1]]*
 // HTML-ANOTHER-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// MD-MUSTACHE-ANOTHER-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-3]]*
 
 // Class in AnotherNamespace
 class ClassInAnotherNamespace {};
 // MD-ANOTHER-CLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-1]]*
 // HTML-ANOTHER-CLASS-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// MD-MUSTACHE-ANOTHER-CLASS-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp#[[@LINE-3]]*
 
 // MD-ANOTHER-CLASS: # class ClassInAnotherNamespace
 // MD-ANOTHER-CLASS:  Class in AnotherNamespace
@@ -206,8 +287,14 @@ class ClassInAnotherNamespace {};
 // HTML-ANOTHER-CLASS:     <a href="../GlobalNamespace/index.html"><div class="navbar-breadcrumb-item">Global Namespace</div></a>::
 // HTML-ANOTHER-CLASS:     <a href="./index.html"><div class="navbar-breadcrumb-item">AnotherNamespace</div></a>
 // HTML-ANOTHER-CLASS: </div>
+
+// MD-MUSTACHE-ANOTHER-CLASS: # class ClassInAnotherNamespace
+// MD-MUSTACHE-ANOTHER-CLASS: Class in AnotherNamespace
+
 // HTML-ANOTHER-CLASS: <h1 class="hero__title-large">class ClassInAnotherNamespace</h1>
 
+// MUSTACHE-ANOTHER-CLASS: <h1 class="hero__title-large">class ClassInAnotherNamespace</h1>
+
 } // namespace AnotherNamespace
 
 // MD-ANOTHER-INDEX: # namespace AnotherNamespace
@@ -237,10 +324,19 @@ class ClassInAnotherNamespace {};
 // HTML-ANOTHER-INDEX:                 <p> Function in AnotherNamespace</p>
 // HTML-ANOTHER-INDEX:             </div>
 // HTML-ANOTHER-INDEX:         </div>
-// HTML-ANOTHER-INDEX:         <p>Defined at line 193 of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
+// HTML-ANOTHER-INDEX:         <p>Defined at line [[@LINE-55]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}namespace.cpp</p>
 // HTML-ANOTHER-INDEX:     </div>
 // HTML-ANOTHER-INDEX: </div>
 
+// MD-MUSTACHE-ANOTHER-INDEX: # namespace AnotherNamespace
+// MD-MUSTACHE-ANOTHER-INDEX: AnotherNamespace
+// MD-MUSTACHE-ANOTHER-INDEX: ## Records
+// MD-MUSTACHE-ANOTHER-INDEX: * [ClassInAnotherNamespace](ClassInAnotherNamespace.md)
+// MD-MUSTACHE-ANOTHER-INDEX: ## Functions
+// MD-MUSTACHE-ANOTHER-INDEX: ### functionInAnotherNamespace
+// MD-MUSTACHE-ANOTHER-INDEX: *void functionInAnotherNamespace()*
+// MD-MUSTACHE-ANOTHER-INDEX: Function in AnotherNamespace
+
 // COM: FIXME: Add namespaces to namespace template
 // HTML-GLOBAL-INDEX-NOT: <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
 // HTML-GLOBAL-INDEX-NOT: <h1>Global Namespace</h1>
@@ -255,13 +351,30 @@ class ClassInAnotherNamespace {};
 // MD-GLOBAL-INDEX: * [AnotherNamespace](..{{[\/]}}AnotherNamespace{{[\/]}}index.md)
 // MD-GLOBAL-INDEX: * [PrimaryNamespace](..{{[\/]}}PrimaryNamespace{{[\/]}}index.md)
 
+// MD-MUSTACHE-GLOBAL-INDEX: # Global Namespace
+// MD-MUSTACHE-GLOBAL-INDEX: ## Namespaces
+// MD-MUSTACHE-GLOBAL-INDEX: * [@nonymous_namespace]({{.*}}{{[\/]}}@nonymous_namespace{{[\/]}}index.md)
+// MD-MUSTACHE-GLOBAL-INDEX: * [AnotherNamespace]({{.*}}{{[\/]}}AnotherNamespace{{[\/]}}index.md)
+// MD-MUSTACHE-GLOBAL-INDEX: * [PrimaryNamespace]({{.*}}{{[\/]}}PrimaryNamespace{{[\/]}}index.md)
+
 // MD-ALL-FILES: # All Files
 // MD-ALL-FILES: ## [@nonymous_namespace](@nonymous_namespace{{[\/]}}index.md)
 // MD-ALL-FILES: ## [AnotherNamespace](AnotherNamespace{{[\/]}}index.md)
 // MD-ALL-FILES: ## [GlobalNamespace](GlobalNamespace{{[\/]}}index.md)
 // MD-ALL-FILES: ## [PrimaryNamespace](PrimaryNamespace{{[\/]}}index.md)
 
+// MD-MUSTACHE-ALL-FILES: # All Files
+// MD-MUSTACHE-ALL-FILES: ## [GlobalNamespace](GlobalNamespace{{[\/]}}index.md)
+// MD-MUSTACHE-ALL-FILES: ## [PrimaryNamespace](PrimaryNamespace{{[\/]}}index.md)
+// MD-MUSTACHE-ALL-FILES: ## [@nonymous_namespace](@nonymous_namespace{{[\/]}}index.md)
+// MD-MUSTACHE-ALL-FILES: ## [AnotherNamespace](AnotherNamespace{{[\/]}}index.md)
+
 // MD-INDEX: #  C/C++ Reference
 // MD-INDEX: * Namespace: [@nonymous_namespace](@nonymous_namespace)
 // MD-INDEX: * Namespace: [AnotherNamespace](AnotherNamespace)
 // MD-INDEX: * Namespace: [PrimaryNamespace](PrimaryNamespace)
+
+// MD-MUSTACHE-INDEX: #  C/C++ Reference
+// MD-MUSTACHE-INDEX: * Namespace: [@nonymous_namespace](@nonymous_namespace)
+// MD-MUSTACHE-INDEX: * Namespace: [AnotherNamespace](AnotherNamespace)
+// MD-MUSTACHE-INDEX: * Namespace: [PrimaryNamespace](PrimaryNamespace)
diff --git a/clang-tools-extra/test/clang-doc/templates.cpp b/clang-tools-extra/test/clang-doc/templates.cpp
index 870841e110dde..e7b003670fbf6 100644
--- a/clang-tools-extra/test/clang-doc/templates.cpp
+++ b/clang-tools-extra/test/clang-doc/templates.cpp
@@ -12,6 +12,9 @@
 // RUN: cat %t/docs/html/GlobalNamespace/_ZTV5tuple.html | FileCheck %s --check-prefix=HTML-STRUCT
 // RUN: cat %t/docs/html/GlobalNamespace/index.html | FileCheck %s --check-prefix=HTML
 
+// RUN: clang-doc --doxygen --executor=standalone %s -output=%t/docs --format=md_mustache
+// RUN: cat %t/docs/md/GlobalNamespace/index.md | FileCheck %s --check-prefix=MD-MUSTACHE
+
 // YAML: ---
 // YAML-NEXT: USR:             '{{([0-9A-F]{40})}}'
 // YAML-NEXT: ChildRecords:
@@ -24,6 +27,9 @@
 // MD: # Global Namespace
 // MD: ## Functions
 
+// MD-MUSTACHE: # Global Namespace
+// MD-MUSTACHE: ## Functions
+
 template <class... T>
 void ParamPackFunction(T... args);
 
@@ -81,6 +87,9 @@ void ParamPackFunction(T... args);
 // HTML:        <pre><code class="language-cpp code-clang-doc">template <class... T</code><code class="language-cpp code-clang-doc">></code></pre>
 // HTML-NEXT:   <pre><code class="language-cpp code-clang-doc">void ParamPackFunction (T... args)</code></pre>
 
+// MD-MUSTACHE: ### ParamPackFunction
+// MD-MUSTACHE: *void ParamPackFunction(T... args)*
+
 template <typename T, int U = 1>
 void function(T x) {}
 
@@ -144,6 +153,10 @@ void function(T x) {}
 // HTML-NEXT:      <p>Defined at line [[# @LINE - 59]] of file {{.*}}templates.cpp</p>
 // HTML-NEXT:  </div>
 
+// MD-MUSTACHE: ### function
+// MD-MUSTACHE: *void function(T x)*
+// MD-MUSTACHE: *Defined at {{.*}}templates.cpp#[[# @LINE - 64]]*
+
 template <typename A, typename B, typename C, typename D, typename E>
 void longFunction(A a, B b, C c, D d, E e) {}
 
@@ -358,6 +371,10 @@ void function<bool, 0>(bool x) {}
 // HTML-NEXT:      <p>Defined at line [[# @LINE - 65]] of file {{.*}}templates.cpp</p>
 // HTML-NEXT:  </div>
 
+// MD-MUSTACHE: ### function
+// MD-MUSTACHE: *void function(bool x)*
+// MD-MUSTACHE: *Defined at {{.*}}templates.cpp#[[# @LINE - 70]]*
+
 /// A Tuple type
 ///
 /// Does Tuple things.
@@ -465,3 +482,10 @@ tuple<int, int, bool> func_with_tuple_param(tuple<int, int, bool> t) { return t;
 // HTML-NEXT:          </div>
 // HTML-NEXT:      </div>
 // HTML-NEXT:      <p>Defined at line [[# @LINE - 81]] of file {{.*}}templates.cpp</p>
+// HTML-NEXT:  </div>
+
+// MD-MUSTACHE: ### func_with_tuple_param
+// MD-MUSTACHE: *tuple<int, int, bool> func_with_tuple_param(tuple<int, int, bool> t)*
+// MD-MUSTACHE: *Defined at {{.*}}templates.cpp#[[# @LINE - 86]]*
+// MD-MUSTACHE:  A function with a tuple parameter
+// MD-MUSTACHE: **t** The input to func_with_tuple_param
diff --git a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
index 73891abd53f99..2b1e04a819781 100644
--- a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
@@ -26,7 +26,7 @@ ClangDocContext ClangDocContextTest::getClangDocContext(
     StringRef RepositoryLinePrefix, StringRef Base) {
   return ClangDocContext(nullptr, "test-project", false, "", "", RepositoryUrl,
                          RepositoryLinePrefix, Base, UserStylesheets, Diags,
-                         false);
+                         OutputFormatTy::html, false);
 }
 
 NamespaceInfo *InfoAsNamespace(Info *I) {
diff --git a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
index cf510afe214dd..2072d19a25b95 100644
--- a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
@@ -46,6 +46,7 @@ class HTMLGeneratorTest : public ClangDocContextTest {
                           Base,
                           UserStylesheets,
                           Diags,
+                          OutputFormatTy::html,
                           false};
     CDCtx.UserStylesheets.insert(
         CDCtx.UserStylesheets.begin(),
diff --git a/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp
index 7e880aa93b635..ae2a0e0f23f00 100644
--- a/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp
@@ -64,6 +64,7 @@ TEST_F(JSONGeneratorTest, emitRecordJSON) {
     {
       "Access": "public",
       "End": true,
+      "HasMembers": true,
       "HasPublicMembers": true,
       "HasPublicMethods": true,
       "InfoType": "record",
@@ -91,12 +92,10 @@ TEST_F(JSONGeneratorTest, emitRecordJSON) {
             "Name": "",
             "QualName": "",
             "USR": "0000000000000000000000000000000000000000"
-          },
-          "USR": "0000000000000000000000000000000000000000"
+          }
         }
       ],
-      "TagType": "struct",
-      "USR": "0000000000000000000000000000000000000000"
+      "TagType": "struct"
     }
   ],
   "Enums": [
@@ -111,11 +110,11 @@ TEST_F(JSONGeneratorTest, emitRecordJSON) {
         }
       ],
       "Name": "Color",
-      "Scoped": false,
-      "USR": "0000000000000000000000000000000000000000"
+      "Scoped": false
     }
   ],
   "HasEnums": true,
+  "HasMembers": true,
   "HasParents": true,
   "HasProtectedMembers": true,
   "HasPublicMethods": true,
@@ -159,8 +158,7 @@ TEST_F(JSONGeneratorTest, emitRecordJSON) {
         "Name": "",
         "QualName": "",
         "USR": "0000000000000000000000000000000000000000"
-      },
-      "USR": "0000000000000000000000000000000000000000"
+      }
     }
   ],
   "Records": [
@@ -182,7 +180,6 @@ TEST_F(JSONGeneratorTest, emitRecordJSON) {
     ],
     "VerticalDisplay": false
   },
-  "USR": "0000000000000000000000000000000000000000",
   "VirtualParents": [
     {
       "End": true,
@@ -226,8 +223,7 @@ TEST_F(JSONGeneratorTest, emitNamespaceJSON) {
       "End": true,
       "InfoType": "enum",
       "Name": "OneEnum",
-      "Scoped": false,
-      "USR": "0000000000000000000000000000000000000000"
+      "Scoped": false
     }
   ],
   "Functions": [
@@ -242,8 +238,7 @@ TEST_F(JSONGeneratorTest, emitNamespaceJSON) {
         "Name": "",
         "QualName": "",
         "USR": "0000000000000000000000000000000000000000"
-      },
-      "USR": "0000000000000000000000000000000000000000"
+      }
     }
   ],
   "HasEnums": true,
@@ -273,8 +268,7 @@ TEST_F(JSONGeneratorTest, emitNamespaceJSON) {
       "QualName": "path::to::A::Namespace::ChildStruct",
       "USR": "0000000000000000000000000000000000000000"
     }
-  ],
-  "USR": "0000000000000000000000000000000000000000"
+  ]
 })raw";
   EXPECT_EQ(Expected, Actual.str());
 }



More information about the cfe-commits mailing list