[clang-tools-extra] [llvm] [WIP] add a mustache backend (PR #108653)

via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 11 19:58:45 PST 2024


https://github.com/PeterChou1 updated https://github.com/llvm/llvm-project/pull/108653

>From ac1c7b5cbf3024bf8cd4021174a47b914d7f7dea Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 30 Jul 2024 23:58:27 -0400
Subject: [PATCH 01/62] [clang-doc] add suport for clang-doc enum generation

---
 clang-tools-extra/clang-doc/BitcodeReader.cpp |   4 +
 clang-tools-extra/clang-doc/BitcodeWriter.cpp |   2 +
 clang-tools-extra/clang-doc/HTMLGenerator.cpp |  88 ++++++++++++--
 .../clang-doc/Representation.cpp              |   2 +
 clang-tools-extra/clang-doc/Representation.h  |   4 +
 clang-tools-extra/clang-doc/Serialize.cpp     |  14 ++-
 clang-tools-extra/test/clang-doc/enum.cpp     | 113 +++++++++++++-----
 7 files changed, 181 insertions(+), 46 deletions(-)

diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index bfb04e7407b380..1f2fb0a8b2b855 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -415,6 +415,10 @@ template <> llvm::Expected<CommentInfo *> getCommentInfo(TypedefInfo *I) {
   return &I->Description.emplace_back();
 }
 
+template <> llvm::Expected<CommentInfo *> getCommentInfo(EnumValueInfo *I) {
+  return &I->Description.emplace_back();
+}
+
 template <> llvm::Expected<CommentInfo *> getCommentInfo(CommentInfo *I) {
   I->Children.emplace_back(std::make_unique<CommentInfo>());
   return I->Children.back().get();
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 7e5a11783d303a..06f30f76e33d8c 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -536,6 +536,8 @@ void ClangDocBitcodeWriter::emitBlock(const EnumValueInfo &I) {
   emitRecord(I.Name, ENUM_VALUE_NAME);
   emitRecord(I.Value, ENUM_VALUE_VALUE);
   emitRecord(I.ValueExpr, ENUM_VALUE_EXPR);
+  for (const auto &CI : I.Description)
+    emitBlock(CI);
 }
 
 void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index aef22453035c30..a37192d6ceb9b0 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -48,6 +48,12 @@ class HTMLTag {
     TAG_SPAN,
     TAG_TITLE,
     TAG_UL,
+    TAG_TABLE,
+    TAG_THEAD,
+    TAG_TBODY,
+    TAG_TR,
+    TAG_TD,
+    TAG_TH
   };
 
   HTMLTag() = default;
@@ -133,6 +139,12 @@ bool HTMLTag::isSelfClosing() const {
   case HTMLTag::TAG_SPAN:
   case HTMLTag::TAG_TITLE:
   case HTMLTag::TAG_UL:
+  case HTMLTag::TAG_TABLE:
+  case HTMLTag::TAG_THEAD:
+  case HTMLTag::TAG_TBODY:
+  case HTMLTag::TAG_TR:
+  case HTMLTag::TAG_TD:
+  case HTMLTag::TAG_TH:
     return false;
   }
   llvm_unreachable("Unhandled HTMLTag::TagType");
@@ -174,6 +186,18 @@ StringRef HTMLTag::toString() const {
     return "title";
   case HTMLTag::TAG_UL:
     return "ul";
+  case HTMLTag::TAG_TABLE:
+    return "table";
+  case HTMLTag::TAG_THEAD:
+    return "thead";
+  case HTMLTag::TAG_TBODY:
+    return "tbody";
+  case HTMLTag::TAG_TR:
+    return "tr";
+  case HTMLTag::TAG_TD:
+    return "td";
+  case HTMLTag::TAG_TH:
+    return "th";
   }
   llvm_unreachable("Unhandled HTMLTag::TagType");
 }
@@ -352,6 +376,7 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx);
 static std::vector<std::unique_ptr<TagNode>>
 genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
         StringRef ParentInfoDir);
+static std::unique_ptr<TagNode> genHTML(const std::vector<CommentInfo> &C);
 
 static std::vector<std::unique_ptr<TagNode>>
 genEnumsBlock(const std::vector<EnumInfo> &Enums,
@@ -372,14 +397,33 @@ genEnumsBlock(const std::vector<EnumInfo> &Enums,
 }
 
 static std::unique_ptr<TagNode>
-genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
+genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members,
+                    bool HasComments) {
   if (Members.empty())
     return nullptr;
 
-  auto List = std::make_unique<TagNode>(HTMLTag::TAG_UL);
-  for (const auto &M : Members)
-    List->Children.emplace_back(
-        std::make_unique<TagNode>(HTMLTag::TAG_LI, M.Name));
+  auto List = std::make_unique<TagNode>(HTMLTag::TAG_TBODY);
+
+  for (const auto &M : Members) {
+    auto TRNode = std::make_unique<TagNode>(HTMLTag::TAG_TR);
+    TRNode->Children.emplace_back(
+        std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Name));
+    // Use user supplied value if it exists, otherwise use the value
+    if (!M.ValueExpr.empty()) {
+      TRNode->Children.emplace_back(
+          std::make_unique<TagNode>(HTMLTag::TAG_TD, M.ValueExpr));
+    } else {
+      TRNode->Children.emplace_back(
+          std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Value));
+    }
+
+    if (HasComments) {
+      auto TD = std::make_unique<TagNode>(HTMLTag::TAG_TD);
+      TD->Children.emplace_back(genHTML(M.Description));
+      TRNode->Children.emplace_back(std::move(TD));
+    }
+    List->Children.emplace_back(std::move(TRNode));
+  }
   return List;
 }
 
@@ -653,15 +697,35 @@ static std::vector<std::unique_ptr<TagNode>>
 genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
   std::vector<std::unique_ptr<TagNode>> Out;
   std::string EnumType = I.Scoped ? "enum class " : "enum ";
+  // Determine if enum members have comments attached
+  bool HasComments = false;
+  for (const auto &M : I.Members) {
+    if (!M.Description.empty()) {
+      HasComments = true;
+      break;
+    }
+  }
+  std::unique_ptr<TagNode> Table =
+      std::make_unique<TagNode>(HTMLTag::TAG_TABLE);
+  std::unique_ptr<TagNode> Thead =
+      std::make_unique<TagNode>(HTMLTag::TAG_THEAD);
+  std::unique_ptr<TagNode> TRow = std::make_unique<TagNode>(HTMLTag::TAG_TR);
+  std::unique_ptr<TagNode> TD =
+      std::make_unique<TagNode>(HTMLTag::TAG_TH, EnumType + I.Name);
+  // Span 3 columns if enum has comments
+  TD->Attributes.emplace_back("colspan", HasComments ? "3" : "2");
+
+  Table->Attributes.emplace_back("id", llvm::toHex(llvm::toStringRef(I.USR)));
+  TRow->Children.emplace_back(std::move(TD));
+  Thead->Children.emplace_back(std::move(TRow));
+  Table->Children.emplace_back(std::move(Thead));
+
+  std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members, HasComments);
 
-  Out.emplace_back(
-      std::make_unique<TagNode>(HTMLTag::TAG_H3, EnumType + I.Name));
-  Out.back()->Attributes.emplace_back("id",
-                                      llvm::toHex(llvm::toStringRef(I.USR)));
-
-  std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members);
   if (Node)
-    Out.emplace_back(std::move(Node));
+    Table->Children.emplace_back(std::move(Node));
+
+  Out.emplace_back(std::move(Table));
 
   if (I.DefLoc) {
     if (!CDCtx.RepositoryUrl)
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index d08afbb9621890..028dffc21793ae 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -266,6 +266,8 @@ void EnumInfo::merge(EnumInfo &&Other) {
     Scoped = Other.Scoped;
   if (Members.empty())
     Members = std::move(Other.Members);
+  if (Other.HasComments || HasComments)
+    HasComments = true;
   SymbolInfo::merge(std::move(Other));
 }
 
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index d70c279f7a2bdc..db3aff2d60a1b5 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -431,6 +431,8 @@ struct EnumValueInfo {
   // Stores the user-supplied initialization expression for this enumeration
   // constant. This will be empty for implicit enumeration values.
   SmallString<16> ValueExpr;
+
+  std::vector<CommentInfo> Description; // Comment description of this field.
 };
 
 // TODO: Expand to allow for documenting templating.
@@ -443,6 +445,8 @@ struct EnumInfo : public SymbolInfo {
 
   // Indicates whether this enum is scoped (e.g. enum class).
   bool Scoped = false;
+  // Indicates whether or not enum members have comments attached
+  bool HasComments = false;
 
   // Set to nonempty to the type when this is an explicitly typed enum. For
   //   enum Foo : short { ... };
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 3b074d849e8a9c..78b7041368d6df 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -394,10 +394,20 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
     std::string ValueExpr;
     if (const Expr *InitExpr = E->getInitExpr())
       ValueExpr = getSourceCode(D, InitExpr->getSourceRange());
-
     SmallString<16> ValueStr;
     E->getInitVal().toString(ValueStr);
-    I.Members.emplace_back(E->getNameAsString(), ValueStr, ValueExpr);
+    I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
+    ASTContext &Context = E->getASTContext();
+    RawComment *Comment = E->getASTContext().getRawCommentForDeclNoCache(E);
+    if (Comment) {
+      CommentInfo CInfo;
+      Comment->setAttached();
+      if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
+        EnumValueInfo &Member = I.Members.back();
+        Member.Description.emplace_back();
+        parseFullComment(Fc, Member.Description.back());
+      }
+    }
   }
 }
 
diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index e559940a31de69..fd7bbcb53f2d2b 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -21,9 +21,9 @@
 enum Color {
 // MD-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
 // HTML-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
-  Red, ///< Red
-  Green, ///< Green
-  Blue ///< Blue
+  Red, ///< Comment 1
+  Green, ///< Comment 2
+  Blue ///< Comment 3
 };
 
 // MD-INDEX: ## Enums
@@ -34,11 +34,16 @@ enum Color {
 // MD-INDEX: | Blue |
 // MD-INDEX: **brief** For specifying RGB colors
 
-// HTML-INDEX: <h2 id="Enums">Enums</h2>
-// HTML-INDEX: <h3 id="{{([0-9A-F]{40})}}">enum Color</h3>
-// HTML-INDEX: <li>Red</li>
-// HTML-INDEX: <li>Green</li>
-// HTML-INDEX: <li>Blue</li>
+// HTML-INDEX: <th colspan="3">enum Color</th>
+// HTML-INDEX: <td>Red</td>
+// HTML-INDEX: <td>0</td>
+// HTML-INDEX: <p> Comment 1</p>
+// HTML-INDEX: <td>Green</td>
+// HTML-INDEX: <td>1</td>
+// HTML-INDEX: <p> Comment 2</p>
+// HTML-INDEX: <td>Blue</td>
+// HTML-INDEX: <td>2</td>
+// HTML-INDEX: <p> Comment 3</p>
 
 /**
  * @brief Shape Types
@@ -46,11 +51,12 @@ enum Color {
 enum class Shapes {
 // MD-INDEX-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
 // HTML-INDEX-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
-  /// Circle
+
+  /// Comment 1
   Circle,
-  /// Rectangle
+  /// Comment 2
   Rectangle,
-  /// Triangle
+  /// Comment 3
   Triangle
 };
 // MD-INDEX: | enum class Shapes |
@@ -60,10 +66,17 @@ enum class Shapes {
 // MD-INDEX: | Triangle |
 // MD-INDEX: **brief** Shape Types
 
-// HTML-INDEX: <h3 id="{{([0-9A-F]{40})}}">enum class Shapes</h3>
-// HTML-INDEX: <li>Circle</li>
-// HTML-INDEX: <li>Rectangle</li>
-// HTML-INDEX: <li>Triangle</li>
+// HTML-INDEX: <th colspan="3">enum class Shapes</th>
+// HTML-INDEX: <td>Circle</td>
+// HTML-INDEX: <td>0</td>
+// HTML-INDEX: <p> Comment 1</p>
+// HTML-INDEX: <td>Rectangle</td>
+// HTML-INDEX: <td>1</td>
+// HTML-INDEX: <p> Comment 2</p>
+// HTML-INDEX: <td>Triangle</td>
+// HTML-INDEX: <td>2</td>
+// HTML-INDEX: <p> Comment 3</p>
+
 
 
 class Animals {
@@ -76,18 +89,25 @@ class Animals {
       enum AnimalType {
 // MD-ANIMAL-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
 // HTML-ANIMAL-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
-          Dog, /// Man's best friend
-          Cat, /// Man's other best friend
-          Iguana /// A lizard
+          Dog, ///< Man's best friend
+          Cat, ///< Man's other best friend
+          Iguana ///< A lizard
       };
 };
 
 // HTML-ANIMAL: <h1>class Animals</h1>
 // HTML-ANIMAL: <h2 id="Enums">Enums</h2>
-// HTML-ANIMAL: <h3 id="{{([0-9A-F]{40})}}">enum AnimalType</h3>
-// HTML-ANIMAL: <li>Dog</li>
-// HTML-ANIMAL: <li>Cat</li>
-// HTML-ANIMAL: <li>Iguana</li>
+// HTML-ANIMAL: <th colspan="3">enum AnimalType</th>
+// HTML-ANIMAL: <td>Dog</td>
+// HTML-ANIMAL: <td>0</td>
+// HTML-ANIMAL: <p> Man's best friend</p>
+// HTML-ANIMAL: <td>Cat</td>
+// HTML-ANIMAL: <td>1</td>
+// HTML-ANIMAL: <p> Man's other best friend</p>
+// HTML-ANIMAL: <td>Iguana</td>
+// HTML-ANIMAL: <td>2</td>
+// HTML-ANIMAL: <p> A lizard</p>
+
 
 // MD-ANIMAL: # class Animals
 // MD-ANIMAL: ## Enums
@@ -106,10 +126,11 @@ namespace Vehicles {
     enum Car {
 // MD-VEHICLES-LINE: *Defined at {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp#[[@LINE-1]]*
 // HTML-VEHICLES-LINE: <p>Defined at line [[@LINE-2]] of file {{.*}}clang-tools-extra{{[\/]}}test{{[\/]}}clang-doc{{[\/]}}enum.cpp</p>
-       Sedan, /// Sedan
-       SUV, /// SUV
-       Pickup, /// Pickup
-       Hatchback /// Hatchback
+
+       Sedan, ///< Comment 1
+       SUV, ///< Comment 2
+       Pickup, ///< Comment 3
+       Hatchback ///< Comment 4
     };
 }
 
@@ -124,9 +145,37 @@ namespace Vehicles {
 // MD-VEHICLES: **brief** specify type of car
 
 // HTML-VEHICLES: <h1>namespace Vehicles</h1>
-// HTML-VEHICLES: <h2 id="Enums">Enums</h2>
-// HTML-VEHICLES: <h3 id="{{([0-9A-F]{40})}}">enum Car</h3>
-// HTML-VEHICLES: <li>Sedan</li>
-// HTML-VEHICLES: <li>SUV</li>
-// HTML-VEHICLES: <li>Pickup</li>
-// HTML-VEHICLES: <li>Hatchback</li>
\ No newline at end of file
+// HTML-VEHICLES: <th colspan="3">enum Car</th>
+// HTML-VEHICLES: <td>Sedan</td>
+// HTML-VEHICLES: <td>0</td>
+// HTML-VEHICLES: <p> Comment 1</p>
+// HTML-VEHICLES: <td>SUV</td>
+// HTML-VEHICLES: <td>1</td>
+// HTML-VEHICLES: <p> Comment 2</p>
+// HTML-VEHICLES: <td>Pickup</td>
+// HTML-VEHICLES: <td>2</td>
+// HTML-VEHICLES: <p> Comment 3</p>
+// HTML-VEHICLES: <td>Hatchback</td>
+// HTML-VEHICLES: <td>3</td>
+// HTML-VEHICLES: <p> Comment 4</p>
+
+
+enum ColorUserSpecified {
+  RedUserSpecified = 'A',
+  GreenUserSpecified = 2,
+  BlueUserSpecified = 'C'
+};
+
+// MD-INDEX: | enum ColorUserSpecified |
+// MD-INDEX: --
+// MD-INDEX: | RedUserSpecified |
+// MD-INDEX: | GreenUserSpecified |
+// MD-INDEX: | BlueUserSpecified |
+
+// HTML-INDEX: <th colspan="2">enum ColorUserSpecified</th>
+// HTML-INDEX: <td>RedUserSpecified</td>
+// HTML-INDEX: <td>'A'</td>
+// HTML-INDEX: <td>GreenUserSpecified</td>
+// HTML-INDEX: <td>2</td>
+// HTML-INDEX: <td>BlueUserSpecified</td>
+// HTML-INDEX: <td>'C'</td>
\ No newline at end of file

>From bcc4b0dc8b3b0216e6249c4de6f2cbcf14006bf6 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Wed, 31 Jul 2024 00:02:13 -0400
Subject: [PATCH 02/62] [clang-doc] remove useless code

---
 clang-tools-extra/clang-doc/Representation.cpp | 2 --
 clang-tools-extra/clang-doc/Representation.h   | 2 --
 2 files changed, 4 deletions(-)

diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index 028dffc21793ae..d08afbb9621890 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -266,8 +266,6 @@ void EnumInfo::merge(EnumInfo &&Other) {
     Scoped = Other.Scoped;
   if (Members.empty())
     Members = std::move(Other.Members);
-  if (Other.HasComments || HasComments)
-    HasComments = true;
   SymbolInfo::merge(std::move(Other));
 }
 
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index db3aff2d60a1b5..bd5254b0a84657 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -445,8 +445,6 @@ struct EnumInfo : public SymbolInfo {
 
   // Indicates whether this enum is scoped (e.g. enum class).
   bool Scoped = false;
-  // Indicates whether or not enum members have comments attached
-  bool HasComments = false;
 
   // Set to nonempty to the type when this is an explicitly typed enum. For
   //   enum Foo : short { ... };

>From 5fe47ca87f8dd592fee7a45401eed2620152e5c1 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Wed, 31 Jul 2024 00:33:48 -0400
Subject: [PATCH 03/62] [clang-doc] modify unittest

---
 .../unittests/clang-doc/HTMLGeneratorTest.cpp | 33 +++++++++++++++----
 1 file changed, 27 insertions(+), 6 deletions(-)

diff --git a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
index e4a7340318b934..7ee482e275149d 100644
--- a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
@@ -92,7 +92,13 @@ TEST(HTMLGeneratorTest, emitNamespaceHTML) {
     </div>
     <h2 id="Enums">Enums</h2>
     <div>
-      <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3>
+      <table id="0000000000000000000000000000000000000000">
+        <thead>
+          <tr>
+            <th colspan="2">enum OneEnum</th>
+          </tr>
+        </thead>
+      </table>
     </div>
   </div>
   <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
@@ -212,7 +218,13 @@ TEST(HTMLGeneratorTest, emitRecordHTML) {
     </div>
     <h2 id="Enums">Enums</h2>
     <div>
-      <h3 id="0000000000000000000000000000000000000000">enum OneEnum</h3>
+      <table id="0000000000000000000000000000000000000000">
+        <thead>
+          <tr>
+            <th colspan="2">enum OneEnum</th>
+          </tr>
+        </thead>
+      </table>
     </div>
   </div>
   <div id="sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
@@ -346,10 +358,19 @@ TEST(HTMLGeneratorTest, emitEnumHTML) {
 <main>
   <div id="sidebar-left" path="" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left"></div>
   <div id="main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
-    <h3 id="0000000000000000000000000000000000000000">enum class e</h3>
-    <ul>
-      <li>X</li>
-    </ul>
+    <table id="0000000000000000000000000000000000000000">
+      <thead>
+        <tr>
+          <th colspan="2">enum class e</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr>
+          <td>X</td>
+          <td>0</td>
+        </tr>
+      </tbody>
+    </table>
     <p>
       Defined at line 
       <a href="https://www.repository.com/test.cpp#10">10</a>

>From 28fb40f0cdbe37257d8aea9f05519519d9f2c470 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 17:12:48 -0400
Subject: [PATCH 04/62] [clang-doc] address pr comments

---
 clang-tools-extra/clang-doc/HTMLGenerator.cpp | 38 ++++++++-----------
 .../clang-doc/Representation.cpp              |  2 +-
 clang-tools-extra/clang-doc/Representation.h  |  4 +-
 clang-tools-extra/clang-doc/Serialize.cpp     |  8 ++--
 4 files changed, 23 insertions(+), 29 deletions(-)

diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index a37192d6ceb9b0..ad7e08667e5cbc 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -397,8 +397,7 @@ genEnumsBlock(const std::vector<EnumInfo> &Enums,
 }
 
 static std::unique_ptr<TagNode>
-genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members,
-                    bool HasComments) {
+genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
   if (Members.empty())
     return nullptr;
 
@@ -416,8 +415,7 @@ genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members,
       TRNode->Children.emplace_back(
           std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Value));
     }
-
-    if (HasComments) {
+    if (M.Description.empty()) {
       auto TD = std::make_unique<TagNode>(HTMLTag::TAG_TD);
       TD->Children.emplace_back(genHTML(M.Description));
       TRNode->Children.emplace_back(std::move(TD));
@@ -663,7 +661,7 @@ static std::unique_ptr<HTMLNode> genHTML(const CommentInfo &I) {
     }
     return std::move(FullComment);
   }
- 
+
   if (I.Kind == "ParagraphComment") {
     auto ParagraphComment = std::make_unique<TagNode>(HTMLTag::TAG_P);
     for (const auto &Child : I.Children) {
@@ -698,16 +696,12 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
   std::vector<std::unique_ptr<TagNode>> Out;
   std::string EnumType = I.Scoped ? "enum class " : "enum ";
   // Determine if enum members have comments attached
-  bool HasComments = false;
-  for (const auto &M : I.Members) {
-    if (!M.Description.empty()) {
-      HasComments = true;
-      break;
-    }
-  }
+  bool HasComments =
+      std::any_of(I.Members.begin(), I.Members.end(),
+                  [](const EnumValueInfo &M) { return M.Description.empty(); });
   std::unique_ptr<TagNode> Table =
       std::make_unique<TagNode>(HTMLTag::TAG_TABLE);
-  std::unique_ptr<TagNode> Thead =
+  std::unique_ptr<TagNode> THead =
       std::make_unique<TagNode>(HTMLTag::TAG_THEAD);
   std::unique_ptr<TagNode> TRow = std::make_unique<TagNode>(HTMLTag::TAG_TR);
   std::unique_ptr<TagNode> TD =
@@ -717,10 +711,10 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
 
   Table->Attributes.emplace_back("id", llvm::toHex(llvm::toStringRef(I.USR)));
   TRow->Children.emplace_back(std::move(TD));
-  Thead->Children.emplace_back(std::move(TRow));
-  Table->Children.emplace_back(std::move(Thead));
+  THead->Children.emplace_back(std::move(TRow));
+  Table->Children.emplace_back(std::move(THead));
 
-  std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members, HasComments);
+  std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members);
 
   if (Node)
     Table->Children.emplace_back(std::move(Node));
@@ -731,8 +725,8 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
     if (!CDCtx.RepositoryUrl)
       Out.emplace_back(writeFileDefinition(*I.DefLoc));
     else
-      Out.emplace_back(writeFileDefinition(
-          *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
+      Out.emplace_back(
+          writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
   }
 
   std::string Description;
@@ -780,8 +774,8 @@ genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
     if (!CDCtx.RepositoryUrl)
       Out.emplace_back(writeFileDefinition(*I.DefLoc));
     else
-      Out.emplace_back(writeFileDefinition(
-          *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
+      Out.emplace_back(
+          writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
   }
 
   std::string Description;
@@ -847,8 +841,8 @@ genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
     if (!CDCtx.RepositoryUrl)
       Out.emplace_back(writeFileDefinition(*I.DefLoc));
     else
-      Out.emplace_back(writeFileDefinition(
-          *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
+      Out.emplace_back(
+          writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
   }
 
   std::string Description;
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index d08afbb9621890..da948ee74c9d63 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -221,7 +221,7 @@ void SymbolInfo::merge(SymbolInfo &&Other) {
 }
 
 NamespaceInfo::NamespaceInfo(SymbolID USR, StringRef Name, StringRef Path)
-      : Info(InfoType::IT_namespace, USR, Name, Path) {}
+    : Info(InfoType::IT_namespace, USR, Name, Path) {}
 
 void NamespaceInfo::merge(NamespaceInfo &&Other) {
   assert(mergeable(Other));
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index bd5254b0a84657..873ac728066261 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -48,7 +48,7 @@ enum class InfoType {
 // A representation of a parsed comment.
 struct CommentInfo {
   CommentInfo() = default;
-  CommentInfo(CommentInfo &Other) = delete;
+  CommentInfo(CommentInfo &Other) = default;
   CommentInfo(CommentInfo &&Other) = default;
   CommentInfo &operator=(CommentInfo &&Other) = default;
 
@@ -432,7 +432,7 @@ struct EnumValueInfo {
   // constant. This will be empty for implicit enumeration values.
   SmallString<16> ValueExpr;
 
-  std::vector<CommentInfo> Description; // Comment description of this field.
+  std::vector<CommentInfo> Description; /// Comment description of this field.
 };
 
 // TODO: Expand to allow for documenting templating.
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 78b7041368d6df..273bc10d3b55d8 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -398,8 +398,8 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
     E->getInitVal().toString(ValueStr);
     I.Members.emplace_back(E->getNameAsString(), ValueStr.str(), ValueExpr);
     ASTContext &Context = E->getASTContext();
-    RawComment *Comment = E->getASTContext().getRawCommentForDeclNoCache(E);
-    if (Comment) {
+    if (RawComment *Comment =
+            E->getASTContext().getRawCommentForDeclNoCache(E)) {
       CommentInfo CInfo;
       Comment->setAttached();
       if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
@@ -568,7 +568,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
   assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
 
-  ASTContext& Context = D->getASTContext();
+  ASTContext &Context = D->getASTContext();
   // TODO investigate whether we can use ASTContext::getCommentForDecl instead
   // of this logic. See also similar code in Mapper.cpp.
   RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
@@ -576,7 +576,7 @@ static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
     return;
 
   Comment->setAttached();
-  if (comments::FullComment* fc = Comment->parse(Context, nullptr, D)) {
+  if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) {
     I.Description.emplace_back();
     parseFullComment(fc, I.Description.back());
   }

>From 0d150ea08af767017e108096533f0c0d8b31668a Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 17:24:55 -0400
Subject: [PATCH 05/62] [clang-doc] revert CommentInfo change

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

diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 873ac728066261..8f2bba786316fe 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -48,7 +48,7 @@ enum class InfoType {
 // A representation of a parsed comment.
 struct CommentInfo {
   CommentInfo() = default;
-  CommentInfo(CommentInfo &Other) = default;
+  CommentInfo(CommentInfo &Other) = delete;
   CommentInfo(CommentInfo &&Other) = default;
   CommentInfo &operator=(CommentInfo &&Other) = default;
 

>From e5e70b87003e4e7b8e68d23fc89f7edd6961764f Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 18:29:39 -0400
Subject: [PATCH 06/62] [clang-doc] fix test

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

diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index ad7e08667e5cbc..00f94788ced44c 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -415,7 +415,7 @@ genEnumMembersBlock(const llvm::SmallVector<EnumValueInfo, 4> &Members) {
       TRNode->Children.emplace_back(
           std::make_unique<TagNode>(HTMLTag::TAG_TD, M.Value));
     }
-    if (M.Description.empty()) {
+    if (!M.Description.empty()) {
       auto TD = std::make_unique<TagNode>(HTMLTag::TAG_TD);
       TD->Children.emplace_back(genHTML(M.Description));
       TRNode->Children.emplace_back(std::move(TD));

>From 1c4f631df65c1560523ea63c82229102f3d99f54 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 19:19:39 -0400
Subject: [PATCH 07/62] [clang-doc] fix unittest

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

diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index 00f94788ced44c..6baed082af4a81 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -698,7 +698,7 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
   // Determine if enum members have comments attached
   bool HasComments =
       std::any_of(I.Members.begin(), I.Members.end(),
-                  [](const EnumValueInfo &M) { return M.Description.empty(); });
+                  [](const EnumValueInfo &M) { return !M.Description.empty(); });
   std::unique_ptr<TagNode> Table =
       std::make_unique<TagNode>(HTMLTag::TAG_TABLE);
   std::unique_ptr<TagNode> THead =

>From b8f3f9c97ccd503c07387f1364cb3b357fa92821 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 19:35:15 -0400
Subject: [PATCH 08/62] [clang-doc] address pr comments

---
 clang-tools-extra/clang-doc/HTMLGenerator.cpp | 20 +++++++++----------
 clang-tools-extra/clang-doc/Serialize.cpp     |  2 +-
 clang-tools-extra/test/clang-doc/enum.cpp     |  2 +-
 3 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index 6baed082af4a81..5b023a409c364f 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -696,9 +696,9 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
   std::vector<std::unique_ptr<TagNode>> Out;
   std::string EnumType = I.Scoped ? "enum class " : "enum ";
   // Determine if enum members have comments attached
-  bool HasComments =
-      std::any_of(I.Members.begin(), I.Members.end(),
-                  [](const EnumValueInfo &M) { return !M.Description.empty(); });
+  bool HasComments = std::any_of(
+      I.Members.begin(), I.Members.end(),
+      [](const EnumValueInfo &M) { return !M.Description.empty(); });
   std::unique_ptr<TagNode> Table =
       std::make_unique<TagNode>(HTMLTag::TAG_TABLE);
   std::unique_ptr<TagNode> THead =
@@ -713,10 +713,8 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
   TRow->Children.emplace_back(std::move(TD));
   THead->Children.emplace_back(std::move(TRow));
   Table->Children.emplace_back(std::move(THead));
-
-  std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members);
-
-  if (Node)
+  
+  if (std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members))
     Table->Children.emplace_back(std::move(Node));
 
   Out.emplace_back(std::move(Table));
@@ -774,8 +772,8 @@ genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
     if (!CDCtx.RepositoryUrl)
       Out.emplace_back(writeFileDefinition(*I.DefLoc));
     else
-      Out.emplace_back(
-          writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
+      Out.emplace_back(writeFileDefinition(
+          *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
   }
 
   std::string Description;
@@ -841,8 +839,8 @@ genHTML(const RecordInfo &I, Index &InfoIndex, const ClangDocContext &CDCtx,
     if (!CDCtx.RepositoryUrl)
       Out.emplace_back(writeFileDefinition(*I.DefLoc));
     else
-      Out.emplace_back(
-          writeFileDefinition(*I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
+      Out.emplace_back(writeFileDefinition(
+          *I.DefLoc, StringRef{*CDCtx.RepositoryUrl}));
   }
 
   std::string Description;
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 273bc10d3b55d8..b9db78cf7d688f 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -568,7 +568,7 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
   assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
 
-  ASTContext &Context = D->getASTContext();
+  ASTContext& Context = D->getASTContext();
   // TODO investigate whether we can use ASTContext::getCommentForDecl instead
   // of this logic. See also similar code in Mapper.cpp.
   RawComment *Comment = Context.getRawCommentForDeclNoCache(D);
diff --git a/clang-tools-extra/test/clang-doc/enum.cpp b/clang-tools-extra/test/clang-doc/enum.cpp
index fd7bbcb53f2d2b..ef768e33b45668 100644
--- a/clang-tools-extra/test/clang-doc/enum.cpp
+++ b/clang-tools-extra/test/clang-doc/enum.cpp
@@ -178,4 +178,4 @@ enum ColorUserSpecified {
 // HTML-INDEX: <td>GreenUserSpecified</td>
 // HTML-INDEX: <td>2</td>
 // HTML-INDEX: <td>BlueUserSpecified</td>
-// HTML-INDEX: <td>'C'</td>
\ No newline at end of file
+// HTML-INDEX: <td>'C'</td>

>From bb98aad362353e44267f2df6ffb985005e67f147 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 12 Aug 2024 19:45:02 -0400
Subject: [PATCH 09/62] [clang-doc] clang-format

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

diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index 5b023a409c364f..0ced83f91724ff 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -713,7 +713,7 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx) {
   TRow->Children.emplace_back(std::move(TD));
   THead->Children.emplace_back(std::move(TRow));
   Table->Children.emplace_back(std::move(THead));
-  
+
   if (std::unique_ptr<TagNode> Node = genEnumMembersBlock(I.Members))
     Table->Children.emplace_back(std::move(Node));
 

>From c60e2b505e27cebffac27b0085fee12d68bbedf7 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 23 Aug 2024 17:39:16 -0400
Subject: [PATCH 10/62] [llvm] implement support for mustache template language

---
 llvm/include/llvm/Support/Mustache.h    | 109 ++++++++++
 llvm/lib/Support/CMakeLists.txt         |   1 +
 llvm/lib/Support/Mustache.cpp           | 276 ++++++++++++++++++++++++
 llvm/unittests/Support/CMakeLists.txt   |   1 +
 llvm/unittests/Support/MustacheTest.cpp | 135 ++++++++++++
 5 files changed, 522 insertions(+)
 create mode 100644 llvm/include/llvm/Support/Mustache.h
 create mode 100644 llvm/lib/Support/Mustache.cpp
 create mode 100644 llvm/unittests/Support/MustacheTest.cpp

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
new file mode 100644
index 00000000000000..a1ce9d945a37c5
--- /dev/null
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -0,0 +1,109 @@
+//===--- Mustache.h ---------------------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of the Mustache templating language supports version 1.4.2
+// (https://mustache.github.io/mustache.5.html).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_MUSTACHE
+#define LLVM_SUPPORT_MUSTACHE
+
+#include "Error.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/JSON.h"
+#include <string>
+#include <variant>
+#include <vector>
+
+namespace llvm {
+namespace mustache {
+
+using Accessor = std::vector<std::string>;
+
+class Token {
+public:
+  enum class Type {
+    Text,
+    Variable,
+    Partial,
+    SectionOpen,
+    SectionClose,
+    InvertSectionOpen,
+    UnescapeVariable,
+    Comment,
+  };
+
+  Token(std::string Str);
+
+  Token(std::string Str, char Identifier);
+
+  std::string getTokenBody() const { return TokenBody; };
+
+  Accessor getAccessor() const { return Accessor; };
+
+  Type getType() const { return TokenType; };
+
+private:
+  Type TokenType;
+  Accessor Accessor;
+  std::string TokenBody;
+};
+
+class ASTNode {
+public:
+  enum Type {
+    Root,
+    Text,
+    Partial,
+    Variable,
+    UnescapeVariable,
+    Section,
+    InvertSection,
+  };
+
+  ASTNode() : T(Type::Root), LocalContext(nullptr){};
+
+  ASTNode(std::string Body, std::shared_ptr<ASTNode> Parent)
+      : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr){};
+
+  // Constructor for Section/InvertSection/Variable/UnescapeVariable
+  ASTNode(Type T, Accessor Accessor, std::shared_ptr<ASTNode> Parent)
+      : T(T), Accessor(Accessor), Parent(Parent), LocalContext(nullptr),
+        Children({}){};
+
+  void addChild(std::shared_ptr<ASTNode> Child) {
+    Children.emplace_back(Child);
+  };
+
+  std::string render(llvm::json::Value Data);
+
+  llvm::json::Value findContext();
+
+  Type T;
+  std::string Body;
+  std::weak_ptr<ASTNode> Parent;
+  std::vector<std::shared_ptr<ASTNode>> Children;
+  Accessor Accessor;
+  llvm::json::Value LocalContext;
+};
+
+class Template {
+public:
+  static Expected<Template> createTemplate(std::string TemplateStr);
+
+  std::string render(llvm::json::Value Data);
+
+private:
+  Template(std::shared_ptr<ASTNode> Tree) : Tree(Tree){};
+  std::shared_ptr<ASTNode> Tree;
+};
+
+} // namespace mustache
+} // end namespace llvm
+#endif // LLVM_SUPPORT_MUSTACHE
\ No newline at end of file
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index f653379e303349..91c8c3fd7de0e6 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -207,6 +207,7 @@ add_llvm_component_library(LLVMSupport
   MD5.cpp
   MSP430Attributes.cpp
   MSP430AttributeParser.cpp
+  Mustache.cpp      
   NativeFormatting.cpp
   OptimizedStructLayout.cpp
   Optional.cpp
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
new file mode 100644
index 00000000000000..19a0ccffe87acc
--- /dev/null
+++ b/llvm/lib/Support/Mustache.cpp
@@ -0,0 +1,276 @@
+//===-- Mustache.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Mustache.h"
+#include "llvm/Support/Error.h"
+#include <iostream>
+#include <regex>
+#include <sstream>
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace llvm::mustache;
+
+std::string escapeHtml(const std::string &Input) {
+  DenseMap<char, std::string> HtmlEntities = {{'&', "&"},
+                                              {'<', "<"},
+                                              {'>', ">"},
+                                              {'"', """},
+                                              {'"', "'"}};
+  std::string EscapedString;
+  EscapedString.reserve(Input.size());
+
+  for (char C : Input) {
+    if (HtmlEntities.find(C) != HtmlEntities.end()) {
+      EscapedString += HtmlEntities[C];
+    } else {
+      EscapedString += C;
+    }
+  }
+
+  return EscapedString;
+}
+
+std::vector<std::string> split(const std::string &Str, char Delimiter) {
+  std::vector<std::string> Tokens;
+  std::string Token;
+  std::stringstream SS(Str);
+  if (Str == ".") {
+    Tokens.push_back(Str);
+    return Tokens;
+  }
+  while (std::getline(SS, Token, Delimiter)) {
+    Tokens.push_back(Token);
+  }
+  return Tokens;
+}
+
+Token::Token(std::string Str, char Identifier) {
+  switch (Identifier) {
+  case '#':
+    TokenType = Type::SectionOpen;
+    break;
+  case '/':
+    TokenType = Type::SectionClose;
+    break;
+  case '^':
+    TokenType = Type::InvertSectionOpen;
+    break;
+  case '!':
+    TokenType = Type::Comment;
+    break;
+  case '>':
+    TokenType = Type::Partial;
+    break;
+  case '&':
+    TokenType = Type::UnescapeVariable;
+    break;
+  default:
+    TokenType = Type::Variable;
+  }
+  if (TokenType == Type::Comment)
+    return;
+
+  TokenBody = Str;
+  std::string AccessorStr = Str;
+  if (TokenType != Type::Variable) {
+    AccessorStr = Str.substr(1);
+  }
+  Accessor = split(StringRef(AccessorStr).trim().str(), '.');
+}
+
+Token::Token(std::string Str)
+    : TokenType(Type::Text), TokenBody(Str), Accessor({}) {}
+
+std::vector<Token> tokenize(std::string Template) {
+  std::vector<Token> Tokens;
+  std::regex Re(R"(\{\{(.*?)\}\})");
+  std::sregex_token_iterator Iter(Template.begin(), Template.end(), Re,
+                                  {-1, 0});
+  std::sregex_token_iterator End;
+
+  for (; Iter != End; ++Iter) {
+    if (!Iter->str().empty()) {
+      std::string Token = *Iter;
+      std::smatch Match;
+      if (std::regex_match(Token, Match, Re)) {
+        std::string Group = Match[1];
+        Tokens.emplace_back(Group, Group[0]);
+      } else {
+        Tokens.emplace_back(Token);
+      }
+    }
+  }
+
+  return Tokens;
+}
+
+class Parser {
+public:
+  Parser(std::string TemplateStr) : TemplateStr(TemplateStr) {}
+
+  std::shared_ptr<ASTNode> parse();
+
+private:
+  void parseMustache(std::shared_ptr<ASTNode> Parent);
+
+  std::vector<Token> Tokens;
+  std::size_t CurrentPtr;
+  std::string TemplateStr;
+};
+
+std::shared_ptr<ASTNode> Parser::parse() {
+  Tokens = tokenize(TemplateStr);
+  CurrentPtr = 0;
+  std::shared_ptr<ASTNode> Root = std::make_shared<ASTNode>();
+  parseMustache(Root);
+  return Root;
+}
+
+void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
+
+  while (CurrentPtr < Tokens.size()) {
+    Token CurrentToken = Tokens[CurrentPtr];
+    CurrentPtr++;
+    Accessor A = CurrentToken.getAccessor();
+    std::shared_ptr<ASTNode> CurrentNode;
+
+    switch (CurrentToken.getType()) {
+    case Token::Type::Text: {
+      CurrentNode =
+          std::make_shared<ASTNode>(CurrentToken.getTokenBody(), Parent);
+      Parent->addChild(CurrentNode);
+      break;
+    }
+    case Token::Type::Variable: {
+      CurrentNode = std::make_shared<ASTNode>(ASTNode::Variable, A, Parent);
+      Parent->addChild(CurrentNode);
+      break;
+    }
+    case Token::Type::UnescapeVariable: {
+      CurrentNode =
+          std::make_shared<ASTNode>(ASTNode::UnescapeVariable, A, Parent);
+      Parent->addChild(CurrentNode);
+      break;
+    }
+    case Token::Type::Partial: {
+      CurrentNode = std::make_shared<ASTNode>(ASTNode::Partial, A, Parent);
+      Parent->addChild(CurrentNode);
+      break;
+    }
+    case Token::Type::SectionOpen: {
+      CurrentNode = std::make_shared<ASTNode>(ASTNode::Section, A, Parent);
+      parseMustache(CurrentNode);
+      Parent->addChild(CurrentNode);
+      break;
+    }
+    case Token::Type::InvertSectionOpen: {
+      CurrentNode =
+          std::make_shared<ASTNode>(ASTNode::InvertSection, A, Parent);
+      parseMustache(CurrentNode);
+      Parent->addChild(CurrentNode);
+      break;
+    }
+    case Token::Type::SectionClose: {
+      return;
+    }
+    default:
+      break;
+    }
+  }
+}
+
+Expected<Template> Template::createTemplate(std::string TemplateStr) {
+  Parser P = Parser(TemplateStr);
+  Expected<std::shared_ptr<ASTNode>> MustacheTree = P.parse();
+  if (!MustacheTree)
+    return MustacheTree.takeError();
+  return Template(MustacheTree.get());
+}
+std::string Template::render(Value Data) { return Tree->render(Data); }
+
+std::string printJson(Value &Data) {
+  if (Data.getAsNull().has_value()) {
+    return "";
+  }
+  if (auto *Arr = Data.getAsArray()) {
+    if (Arr->empty()) {
+      return "";
+    }
+  }
+  if (Data.getAsString().has_value()) {
+    return Data.getAsString()->str();
+  }
+  return llvm::formatv("{0:2}", Data);
+}
+
+std::string ASTNode::render(Value Data) {
+  LocalContext = Data;
+  Value Context = T == Root ? Data : findContext();
+  switch (T) {
+  case Root: {
+    std::string Result = "";
+    for (std::shared_ptr<ASTNode> Child : Children) {
+      Result += Child->render(Context);
+    }
+    return Result;
+  }
+  case Text:
+    return escapeHtml(Body);
+  case Partial:
+    break;
+  case Variable:
+    return escapeHtml(printJson(Context));
+  case UnescapeVariable:
+    return printJson(Context);
+  case Section:
+    break;
+  case InvertSection:
+    break;
+  }
+
+  return std::string();
+}
+
+Value ASTNode::findContext() {
+  if (Accessor.empty()) {
+    return nullptr;
+  }
+  if (Accessor[0] == ".") {
+    return LocalContext;
+  }
+  json::Object *CurrentContext = LocalContext.getAsObject();
+  std::string &CurrentAccessor = Accessor[0];
+  std::weak_ptr<ASTNode> CurrentParent = Parent;
+
+  while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
+    if (auto Ptr = CurrentParent.lock()) {
+      CurrentContext = Ptr->LocalContext.getAsObject();
+      CurrentParent = Ptr->Parent;
+      continue;
+    }
+    return nullptr;
+  }
+  Value Context = nullptr;
+  for (std::size_t i = 0; i < Accessor.size(); i++) {
+    CurrentAccessor = Accessor[i];
+    Value *CurrentValue = CurrentContext->get(CurrentAccessor);
+    if (!CurrentValue) {
+      return nullptr;
+    }
+    if (i < Accessor.size() - 1) {
+      CurrentContext = CurrentValue->getAsObject();
+      if (!CurrentContext) {
+        return nullptr;
+      }
+    } else {
+      Context = *CurrentValue;
+    }
+  }
+  return Context;
+}
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index 631f2e6bf00df0..63a6a13019bdcf 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -60,6 +60,7 @@ add_llvm_unittest(SupportTests
   MemoryBufferRefTest.cpp
   MemoryBufferTest.cpp
   MemoryTest.cpp
+  MustacheTest.cpp      
   NativeFormatTests.cpp
   OptimizedStructLayoutTest.cpp
   ParallelTest.cpp
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
new file mode 100644
index 00000000000000..7479e29816e944
--- /dev/null
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -0,0 +1,135 @@
+//===- llvm/unittest/Support/MustacheTest.cpp ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Test conforming to Mustache 1.4.2 spec found here:
+// https://github.com/mustache/spec
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Mustache.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace llvm::mustache;
+using namespace llvm::json;
+
+TEST(MustacheInterpolation, NoInterpolation) {
+  // Mustache-free templates should render as-is.
+  Value D = {};
+  auto T = Template::createTemplate("Hello from {Mustache}!\n");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("Hello from {Mustache}!\n", Out);
+}
+
+TEST(MustacheInterpolation, BasicInterpolation) {
+  // Unadorned tags should interpolate content into the template.
+  Value D = Object{{"subject", "World"}};
+  auto T = Template::createTemplate("Hello, {{subject}}!");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("Hello, World!", Out);
+}
+
+TEST(MustacheInterpolation, NoReinterpolation) {
+  // Interpolated tag output should not be re-interpolated.
+  Value D = Object{{"template", "{{planet}}"}, {"planet", "Earth"}};
+  auto T = Template::createTemplate("{{template}}: {{planet}}");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("{{planet}}: Earth", Out);
+}
+
+TEST(MustacheInterpolation, HTMLEscaping) {
+  // Interpolated tag output should not be re-interpolated.
+  Value D = Object{
+      {"forbidden", "& \" < >"},
+  };
+  auto T = Template::createTemplate(
+      "These characters should be HTML escaped: {{forbidden}}\n");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("These characters should be HTML escaped: & " < >\n",
+            Out);
+}
+
+TEST(MustacheInterpolation, Ampersand) {
+  // Interpolated tag output should not be re-interpolated.
+  Value D = Object{
+      {"forbidden", "& \" < >"},
+  };
+  auto T = Template::createTemplate(
+      "These characters should not be HTML escaped: {{&forbidden}}\n");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
+}
+
+TEST(MustacheInterpolation, BasicIntegerInterpolation) {
+  Value D = Object{{"mph", 85}};
+  auto T = Template::createTemplate("{{mph}} miles an hour!");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("85 miles an hour!", Out);
+}
+
+TEST(MustacheInterpolation, BasicDecimalInterpolation) {
+  Value D = Object{{"power", 1.21}};
+  auto T = Template::createTemplate("{{power}} jiggawatts!");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("1.21 jiggawatts!", Out);
+}
+
+TEST(MustacheInterpolation, BasicNullInterpolation) {
+  Value D = Object{{"cannot", nullptr}};
+  auto T = Template::createTemplate("I ({{cannot}}) be seen!");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("I () be seen!", Out);
+}
+
+TEST(MustacheInterpolation, BasicContextMissInterpolation) {
+  Value D = Object{};
+  auto T = Template::createTemplate("I ({{cannot}}) be seen!");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("I () be seen!", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
+  Value D = Object{{"person", Object{{"name", "Joe"}}}};
+  auto T = Template::createTemplate(
+      "{{person.name}} == {{#person}}{{name}}{{/person}}");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("Joe == Joe", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
+  Value D = Object{
+      {"a",
+       Object{{"b",
+               Object{{"c",
+                       Object{{"d",
+                               Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
+  auto T = Template::createTemplate("{{a.b.c.d.e.name}} == Phil");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("Phil == Phil", Out);
+}
+
+TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
+  Value D = "world";
+  auto T = Template::createTemplate("Hello, {{.}}!\n");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("Hello, world!\n", Out);
+}
+
+TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
+  Value D = Object{{"string", "---"}};
+  auto T = Template::createTemplate("| {{string}} |");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("| --- |", Out);
+}
+
+TEST(MustacheInterpolation, InterpolationWithPadding) {
+  Value D = Object{{"string", "---"}};
+  auto T = Template::createTemplate("|{{ string }}|");
+  auto Out = T.get().render(D);
+  EXPECT_EQ("|---|", Out);
+}

>From 8290c381a099864db4c827e6102c015f09cfed5c Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 02:35:33 -0400
Subject: [PATCH 11/62] [llvm][Support] Finish implementation of Mustache

---
 llvm/include/llvm/Support/Mustache.h    |  63 +-
 llvm/lib/Support/Mustache.cpp           | 383 ++++++++---
 llvm/unittests/Support/MustacheTest.cpp | 877 +++++++++++++++++++++++-
 3 files changed, 1204 insertions(+), 119 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index a1ce9d945a37c5..cb7d80e47809e8 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -24,7 +24,9 @@
 namespace llvm {
 namespace mustache {
 
-using Accessor = std::vector<std::string>;
+using Accessor = std::vector<SmallString<128>>;
+using Lambda = std::function<llvm::json::Value()>;
+using SectionLambda = std::function<llvm::json::Value(StringRef)>;
 
 class Token {
 public:
@@ -39,20 +41,27 @@ class Token {
     Comment,
   };
 
-  Token(std::string Str);
+  Token(StringRef Str);
 
-  Token(std::string Str, char Identifier);
+  Token(StringRef RawBody, StringRef Str, char Identifier);
 
-  std::string getTokenBody() const { return TokenBody; };
+  StringRef getTokenBody() const { return TokenBody; };
+
+  StringRef getRawBody() const { return RawBody; };
+
+  void setTokenBody(SmallString<128> NewBody) { TokenBody = NewBody; };
 
   Accessor getAccessor() const { return Accessor; };
 
   Type getType() const { return TokenType; };
 
+  static Type getTokenType(char Identifier);
+
 private:
   Type TokenType;
+  SmallString<128> RawBody;
   Accessor Accessor;
-  std::string TokenBody;
+  SmallString<128> TokenBody;
 };
 
 class ASTNode {
@@ -69,7 +78,7 @@ class ASTNode {
 
   ASTNode() : T(Type::Root), LocalContext(nullptr){};
 
-  ASTNode(std::string Body, std::shared_ptr<ASTNode> Parent)
+  ASTNode(StringRef Body, std::shared_ptr<ASTNode> Parent)
       : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr){};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
@@ -81,26 +90,56 @@ class ASTNode {
     Children.emplace_back(Child);
   };
 
-  std::string render(llvm::json::Value Data);
+  SmallString<128> getBody() const { return Body; };
 
-  llvm::json::Value findContext();
+  void setBody(StringRef NewBody) { Body = NewBody; };
+
+  void setRawBody(StringRef NewBody) { RawBody = NewBody; };
+
+  SmallString<128> getRawBody() const { return RawBody; };
+
+  std::shared_ptr<ASTNode> getLastChild() const {
+    return Children.empty() ? nullptr : Children.back();
+  };
 
+  SmallString<128>
+  render(llvm::json::Value Data,
+         DenseMap<StringRef, std::shared_ptr<ASTNode>> &Partials,
+         DenseMap<StringRef, Lambda> &Lambdas,
+         DenseMap<StringRef, SectionLambda> &SectionLambdas,
+         DenseMap<char, StringRef> &Escapes);
+
+private:
+  llvm::json::Value findContext();
   Type T;
-  std::string Body;
+  SmallString<128> RawBody;
+  SmallString<128> Body;
   std::weak_ptr<ASTNode> Parent;
   std::vector<std::shared_ptr<ASTNode>> Children;
-  Accessor Accessor;
+  const Accessor Accessor;
   llvm::json::Value LocalContext;
 };
 
 class Template {
 public:
-  static Expected<Template> createTemplate(std::string TemplateStr);
+  static Template createTemplate(StringRef TemplateStr);
+
+  SmallString<128> render(llvm::json::Value Data);
+
+  void registerPartial(StringRef Name, StringRef Partial);
+
+  void registerLambda(StringRef Name, Lambda Lambda);
+
+  void registerLambda(StringRef Name, SectionLambda Lambda);
 
-  std::string render(llvm::json::Value Data);
+  void registerEscape(DenseMap<char, StringRef> Escapes);
 
 private:
   Template(std::shared_ptr<ASTNode> Tree) : Tree(Tree){};
+  DenseMap<StringRef, std::shared_ptr<ASTNode>> Partials;
+  DenseMap<StringRef, Lambda> Lambdas;
+  DenseMap<StringRef, SectionLambda> SectionLambdas;
+  DenseMap<char, StringRef> Escapes;
   std::shared_ptr<ASTNode> Tree;
 };
 
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 19a0ccffe87acc..80b3d8bc53651b 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -16,103 +16,172 @@ using namespace llvm;
 using namespace llvm::json;
 using namespace llvm::mustache;
 
-std::string escapeHtml(const std::string &Input) {
-  DenseMap<char, std::string> HtmlEntities = {{'&', "&"},
-                                              {'<', "<"},
-                                              {'>', ">"},
-                                              {'"', """},
-                                              {'"', "'"}};
-  std::string EscapedString;
-  EscapedString.reserve(Input.size());
-
+SmallString<128> escapeString(StringRef Input,
+                              DenseMap<char, StringRef> &Escape) {
+  SmallString<128> EscapedString("");
   for (char C : Input) {
-    if (HtmlEntities.find(C) != HtmlEntities.end()) {
-      EscapedString += HtmlEntities[C];
+    if (Escape.find(C) != Escape.end()) {
+      EscapedString += Escape[C];
     } else {
       EscapedString += C;
     }
   }
-
   return EscapedString;
 }
 
-std::vector<std::string> split(const std::string &Str, char Delimiter) {
-  std::vector<std::string> Tokens;
-  std::string Token;
-  std::stringstream SS(Str);
+std::vector<SmallString<128>> split(StringRef Str, char Delimiter) {
+  std::vector<SmallString<128>> Tokens;
   if (Str == ".") {
     Tokens.push_back(Str);
     return Tokens;
   }
-  while (std::getline(SS, Token, Delimiter)) {
-    Tokens.push_back(Token);
+  StringRef Ref(Str);
+  while (!Ref.empty()) {
+    llvm::StringRef Part;
+    std::tie(Part, Ref) = Ref.split(Delimiter);
+    Tokens.push_back(Part.trim());
   }
   return Tokens;
 }
 
-Token::Token(std::string Str, char Identifier) {
+Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
+    : RawBody(RawBody), TokenBody(InnerBody) {
+
+  TokenType = getTokenType(Identifier);
+  if (TokenType == Type::Comment)
+    return;
+
+  StringRef AccessorStr = InnerBody;
+  if (TokenType != Type::Variable) {
+    AccessorStr = InnerBody.substr(1);
+  }
+  Accessor = split(AccessorStr.trim(), '.');
+}
+
+Token::Token(StringRef Str)
+    : RawBody(Str), TokenType(Type::Text), TokenBody(Str), Accessor({}) {}
+
+Token::Type Token::getTokenType(char Identifier) {
   switch (Identifier) {
   case '#':
-    TokenType = Type::SectionOpen;
-    break;
+    return Type::SectionOpen;
   case '/':
-    TokenType = Type::SectionClose;
-    break;
+    return Type::SectionClose;
   case '^':
-    TokenType = Type::InvertSectionOpen;
-    break;
+    return Type::InvertSectionOpen;
   case '!':
-    TokenType = Type::Comment;
-    break;
+    return Type::Comment;
   case '>':
-    TokenType = Type::Partial;
-    break;
+    return Type::Partial;
   case '&':
-    TokenType = Type::UnescapeVariable;
-    break;
+    return Type::UnescapeVariable;
   default:
-    TokenType = Type::Variable;
+    return Type::Variable;
   }
-  if (TokenType == Type::Comment)
-    return;
+}
 
-  TokenBody = Str;
-  std::string AccessorStr = Str;
-  if (TokenType != Type::Variable) {
-    AccessorStr = Str.substr(1);
+std::vector<Token> tokenize(StringRef Template) {
+  // Simple tokenizer that splits the template into tokens
+  // the mustache spec allows {{{ }}} to unescape variables
+  // but we don't support that here unescape variable
+  // is represented only by {{& variable}}
+  std::vector<Token> Tokens;
+  SmallString<128> Open("{{");
+  SmallString<128> Close("}}");
+  std::size_t Start = 0;
+  std::size_t DelimiterStart = Template.find(Open);
+  if (DelimiterStart == StringRef::npos) {
+    Tokens.push_back(Token(Template));
+    return Tokens;
   }
-  Accessor = split(StringRef(AccessorStr).trim().str(), '.');
-}
+  while (DelimiterStart != StringRef::npos) {
+    if (DelimiterStart != Start) {
+      Token TextToken = Token(Template.substr(Start, DelimiterStart - Start));
+      Tokens.push_back(TextToken);
+    }
+
+    std::size_t DelimiterEnd = Template.find(Close, DelimiterStart);
+    if (DelimiterEnd == StringRef::npos) {
+      break;
+    }
 
-Token::Token(std::string Str)
-    : TokenType(Type::Text), TokenBody(Str), Accessor({}) {}
+    SmallString<128> Interpolated =
+        Template.substr(DelimiterStart + Open.size(),
+                        DelimiterEnd - DelimiterStart - Close.size());
+    SmallString<128> RawBody;
+    RawBody += Open;
+    RawBody += Interpolated;
+    RawBody += Close;
 
-std::vector<Token> tokenize(std::string Template) {
-  std::vector<Token> Tokens;
-  std::regex Re(R"(\{\{(.*?)\}\})");
-  std::sregex_token_iterator Iter(Template.begin(), Template.end(), Re,
-                                  {-1, 0});
-  std::sregex_token_iterator End;
-
-  for (; Iter != End; ++Iter) {
-    if (!Iter->str().empty()) {
-      std::string Token = *Iter;
-      std::smatch Match;
-      if (std::regex_match(Token, Match, Re)) {
-        std::string Group = Match[1];
-        Tokens.emplace_back(Group, Group[0]);
-      } else {
-        Tokens.emplace_back(Token);
+    Tokens.push_back(Token(RawBody, Interpolated, Interpolated[0]));
+    Start = DelimiterEnd + Close.size();
+    DelimiterStart = Template.find(Open, Start);
+  }
+
+  if (Start < Template.size()) {
+    Tokens.push_back(Token(Template.substr(Start)));
+  }
+
+  // fix up white spaces for
+  // open sections/inverted sections/close section/comment
+  for (std::size_t I = 0; I < Tokens.size(); I++) {
+    Token::Type CurrentType = Tokens[I].getType();
+    bool RequiresCleanUp = CurrentType == Token::Type::SectionOpen ||
+                           CurrentType == Token::Type::InvertSectionOpen ||
+                           CurrentType == Token::Type::SectionClose ||
+                           CurrentType == Token::Type::Comment ||
+                           CurrentType == Token::Type::Partial;
+
+    bool NoTextBehind = false;
+    bool NoTextAhead = false;
+    if (I > 0 && Tokens[I - 1].getType() == Token::Type::Text &&
+        RequiresCleanUp) {
+      Token &PrevToken = Tokens[I - 1];
+      StringRef TokenBody = PrevToken.getTokenBody().rtrim(" \t\v\t");
+      if (TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
+          TokenBody.empty()) {
+        NoTextBehind = true;
+      }
+    }
+    if (I < Tokens.size() - 1 && Tokens[I + 1].getType() == Token::Type::Text &&
+        RequiresCleanUp) {
+      Token &NextToken = Tokens[I + 1];
+      StringRef TokenBody = NextToken.getTokenBody().ltrim(" ");
+      if (TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n")) {
+        NoTextAhead = true;
       }
     }
-  }
 
+    if (NoTextBehind && NoTextAhead) {
+      Token &PrevToken = Tokens[I - 1];
+      Token &NextToken = Tokens[I + 1];
+      StringRef NextTokenBody = NextToken.getTokenBody();
+      PrevToken.setTokenBody(PrevToken.getTokenBody().rtrim(" \t\v\t"));
+      if (NextTokenBody.starts_with("\r\n")) {
+        NextToken.setTokenBody(NextTokenBody.substr(2));
+      } else if (NextToken.getTokenBody().starts_with("\n")) {
+        NextToken.setTokenBody(NextTokenBody.substr(1));
+      }
+    } else if (NoTextAhead && I == 0) {
+      Token &NextToken = Tokens[I + 1];
+      StringRef NextTokenBody = NextToken.getTokenBody();
+      if (NextTokenBody.starts_with("\r\n")) {
+        NextToken.setTokenBody(NextTokenBody.substr(2));
+      } else if (NextToken.getTokenBody().starts_with("\n")) {
+        NextToken.setTokenBody(NextTokenBody.substr(1));
+      }
+    } else if (NoTextBehind && I == Tokens.size() - 1) {
+      Token &PrevToken = Tokens[I - 1];
+      StringRef PrevTokenBody = PrevToken.getTokenBody();
+      PrevToken.setTokenBody(PrevTokenBody.rtrim(" \t\v\t"));
+    }
+  }
   return Tokens;
 }
 
 class Parser {
 public:
-  Parser(std::string TemplateStr) : TemplateStr(TemplateStr) {}
+  Parser(StringRef TemplateStr) : TemplateStr(TemplateStr) {}
 
   std::shared_ptr<ASTNode> parse();
 
@@ -121,7 +190,7 @@ class Parser {
 
   std::vector<Token> Tokens;
   std::size_t CurrentPtr;
-  std::string TemplateStr;
+  StringRef TemplateStr;
 };
 
 std::shared_ptr<ASTNode> Parser::parse() {
@@ -165,14 +234,34 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
     }
     case Token::Type::SectionOpen: {
       CurrentNode = std::make_shared<ASTNode>(ASTNode::Section, A, Parent);
+      std::size_t Start = CurrentPtr;
       parseMustache(CurrentNode);
+      std::size_t End = CurrentPtr;
+      SmallString<128> RawBody;
+      if (Start + 1 < End - 1)
+        for (std::size_t I = Start + 1; I < End - 1; I++) {
+          RawBody += Tokens[I].getRawBody();
+        }
+      else if (Start + 1 == End - 1)
+        RawBody = Tokens[Start].getRawBody();
+      CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
       break;
     }
     case Token::Type::InvertSectionOpen: {
       CurrentNode =
           std::make_shared<ASTNode>(ASTNode::InvertSection, A, Parent);
+      std::size_t Start = CurrentPtr;
       parseMustache(CurrentNode);
+      std::size_t End = CurrentPtr;
+      SmallString<128> RawBody;
+      if (Start + 1 < End - 1)
+        for (std::size_t I = Start + 1; I < End - 1; I++) {
+          RawBody += Tokens[I].getRawBody();
+        }
+      else if (Start + 1 == End - 1)
+        RawBody = Tokens[Start].getRawBody();
+      CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
       break;
     }
@@ -185,59 +274,167 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
   }
 }
 
-Expected<Template> Template::createTemplate(std::string TemplateStr) {
+Template Template::createTemplate(StringRef TemplateStr) {
   Parser P = Parser(TemplateStr);
-  Expected<std::shared_ptr<ASTNode>> MustacheTree = P.parse();
-  if (!MustacheTree)
-    return MustacheTree.takeError();
-  return Template(MustacheTree.get());
+  std::shared_ptr<ASTNode> MustacheTree = P.parse();
+  Template T = Template(MustacheTree);
+  // the default behaviour is to escape html entities
+  DenseMap<char, StringRef> HtmlEntities = {{'&', "&"},
+                                            {'<', "<"},
+                                            {'>', ">"},
+                                            {'"', """},
+                                            {'\'', "'"}};
+  T.registerEscape(HtmlEntities);
+  return T;
+}
+
+SmallString<128> Template::render(Value Data) {
+  return Tree->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+}
+
+void Template::registerPartial(StringRef Name, StringRef Partial) {
+  Parser P = Parser(Partial);
+  std::shared_ptr<ASTNode> PartialTree = P.parse();
+  Partials[Name] = PartialTree;
+}
+
+void Template::registerLambda(StringRef Name, Lambda L) { Lambdas[Name] = L; }
+
+void Template::registerLambda(StringRef Name, SectionLambda L) {
+  SectionLambdas[Name] = L;
 }
-std::string Template::render(Value Data) { return Tree->render(Data); }
 
-std::string printJson(Value &Data) {
-  if (Data.getAsNull().has_value()) {
-    return "";
+void Template::registerEscape(DenseMap<char, StringRef> E) { Escapes = E; }
+
+SmallString<128> printJson(Value &Data) {
+
+  SmallString<128> Result;
+  if (Data.getAsNull()) {
+    return Result;
   }
   if (auto *Arr = Data.getAsArray()) {
     if (Arr->empty()) {
-      return "";
+      return Result;
     }
   }
-  if (Data.getAsString().has_value()) {
-    return Data.getAsString()->str();
+  if (Data.getAsString()) {
+    Result += Data.getAsString()->str();
+    return Result;
   }
   return llvm::formatv("{0:2}", Data);
 }
 
-std::string ASTNode::render(Value Data) {
+bool isFalsey(Value &V) {
+  return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
+         (V.getAsArray() && V.getAsArray()->empty()) ||
+         (V.getAsObject() && V.getAsObject()->empty());
+}
+
+SmallString<128>
+ASTNode::render(Value Data,
+                DenseMap<StringRef, std::shared_ptr<ASTNode>> &Partials,
+                DenseMap<StringRef, Lambda> &Lambdas,
+                DenseMap<StringRef, SectionLambda> &SectionLambdas,
+                DenseMap<char, StringRef> &Escapes) {
   LocalContext = Data;
   Value Context = T == Root ? Data : findContext();
+  SmallString<128> Result;
   switch (T) {
   case Root: {
-    std::string Result = "";
-    for (std::shared_ptr<ASTNode> Child : Children) {
-      Result += Child->render(Context);
-    }
+    for (std::shared_ptr<ASTNode> Child : Children)
+      Result +=
+          Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
     return Result;
   }
   case Text:
-    return escapeHtml(Body);
-  case Partial:
-    break;
-  case Variable:
-    return escapeHtml(printJson(Context));
-  case UnescapeVariable:
+    return Body;
+  case Partial: {
+    if (Partials.find(Accessor[0]) != Partials.end()) {
+      std::shared_ptr<ASTNode> Partial = Partials[Accessor[0]];
+      Result +=
+          Partial->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+      return Result;
+    }
+  }
+  case Variable: {
+    if (Lambdas.find(Accessor[0]) != Lambdas.end()) {
+      Lambda &L = Lambdas[Accessor[0]];
+      Value LambdaResult = L();
+      StringRef LambdaStr = printJson(LambdaResult);
+      Parser P = Parser(LambdaStr);
+      std::shared_ptr<ASTNode> LambdaNode = P.parse();
+      return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
+                                Escapes);
+    }
+    return escapeString(printJson(Context), Escapes);
+  }
+  case UnescapeVariable: {
+    if (Lambdas.find(Accessor[0]) != Lambdas.end()) {
+      Lambda &L = Lambdas[Accessor[0]];
+      Value LambdaResult = L();
+      StringRef LambdaStr = printJson(LambdaResult);
+      Parser P = Parser(LambdaStr);
+      std::shared_ptr<ASTNode> LambdaNode = P.parse();
+      DenseMap<char, StringRef> EmptyEscapes;
+      return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
+                                EmptyEscapes);
+    }
     return printJson(Context);
-  case Section:
-    break;
-  case InvertSection:
-    break;
   }
+  case Section: {
+    // Sections are not rendered if the context is falsey
+    bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
+
+    if (isFalsey(Context) && !IsLambda)
+      return Result;
+
+    if (IsLambda) {
+      SectionLambda &Lambda = SectionLambdas[Accessor[0]];
+      Value Return = Lambda(RawBody);
+      if (isFalsey(Return))
+        return Result;
+      StringRef LambdaStr = printJson(Return);
+      Parser P = Parser(LambdaStr);
+      std::shared_ptr<ASTNode> LambdaNode = P.parse();
+      return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
+                                Escapes);
+    }
+
+    if (Context.getAsArray()) {
+      json::Array *Arr = Context.getAsArray();
+      for (Value &V : *Arr) {
+        for (std::shared_ptr<ASTNode> Child : Children)
+          Result +=
+              Child->render(V, Partials, Lambdas, SectionLambdas, Escapes);
+      }
+      return Result;
+    }
 
-  return std::string();
+    for (std::shared_ptr<ASTNode> Child : Children)
+      Result +=
+          Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
+
+    return Result;
+  }
+  case InvertSection: {
+    bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
+    if (!isFalsey(Context) || IsLambda)
+      return Result;
+    for (std::shared_ptr<ASTNode> Child : Children)
+      Result +=
+          Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
+    return Result;
+  }
+  }
+  llvm_unreachable("Invalid ASTNode type");
 }
 
 Value ASTNode::findContext() {
+  // The mustache spec allows for dot notation to access nested values
+  // a single dot refers to the current context
+  // We attempt to find the JSON context in the current node if it is not found
+  // we traverse the parent nodes to find the context until we reach the root
+  // node or the context is found
   if (Accessor.empty()) {
     return nullptr;
   }
@@ -245,7 +442,7 @@ Value ASTNode::findContext() {
     return LocalContext;
   }
   json::Object *CurrentContext = LocalContext.getAsObject();
-  std::string &CurrentAccessor = Accessor[0];
+  SmallString<128> CurrentAccessor = Accessor[0];
   std::weak_ptr<ASTNode> CurrentParent = Parent;
 
   while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
@@ -257,13 +454,13 @@ Value ASTNode::findContext() {
     return nullptr;
   }
   Value Context = nullptr;
-  for (std::size_t i = 0; i < Accessor.size(); i++) {
-    CurrentAccessor = Accessor[i];
+  for (std::size_t I = 0; I < Accessor.size(); I++) {
+    CurrentAccessor = Accessor[I];
     Value *CurrentValue = CurrentContext->get(CurrentAccessor);
     if (!CurrentValue) {
       return nullptr;
     }
-    if (i < Accessor.size() - 1) {
+    if (I < Accessor.size() - 1) {
       CurrentContext = CurrentValue->getAsObject();
       if (!CurrentContext) {
         return nullptr;
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index 7479e29816e944..ceee8942f88f4e 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -22,7 +22,7 @@ TEST(MustacheInterpolation, NoInterpolation) {
   // Mustache-free templates should render as-is.
   Value D = {};
   auto T = Template::createTemplate("Hello from {Mustache}!\n");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
   EXPECT_EQ("Hello from {Mustache}!\n", Out);
 }
 
@@ -30,7 +30,7 @@ TEST(MustacheInterpolation, BasicInterpolation) {
   // Unadorned tags should interpolate content into the template.
   Value D = Object{{"subject", "World"}};
   auto T = Template::createTemplate("Hello, {{subject}}!");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
   EXPECT_EQ("Hello, World!", Out);
 }
 
@@ -38,7 +38,7 @@ TEST(MustacheInterpolation, NoReinterpolation) {
   // Interpolated tag output should not be re-interpolated.
   Value D = Object{{"template", "{{planet}}"}, {"planet", "Earth"}};
   auto T = Template::createTemplate("{{template}}: {{planet}}");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
   EXPECT_EQ("{{planet}}: Earth", Out);
 }
 
@@ -49,7 +49,7 @@ TEST(MustacheInterpolation, HTMLEscaping) {
   };
   auto T = Template::createTemplate(
       "These characters should be HTML escaped: {{forbidden}}\n");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
   EXPECT_EQ("These characters should be HTML escaped: & " < >\n",
             Out);
 }
@@ -61,47 +61,78 @@ TEST(MustacheInterpolation, Ampersand) {
   };
   auto T = Template::createTemplate(
       "These characters should not be HTML escaped: {{&forbidden}}\n");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
   EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
 }
 
 TEST(MustacheInterpolation, BasicIntegerInterpolation) {
+  // Integers should interpolate seamlessly.
   Value D = Object{{"mph", 85}};
   auto T = Template::createTemplate("{{mph}} miles an hour!");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
+  EXPECT_EQ("85 miles an hour!", Out);
+}
+
+TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {
+  // Integers should interpolate seamlessly.
+  Value D = Object{{"mph", 85}};
+  auto T = Template::createTemplate("{{&mph}} miles an hour!");
+  auto Out = T.render(D);
   EXPECT_EQ("85 miles an hour!", Out);
 }
 
 TEST(MustacheInterpolation, BasicDecimalInterpolation) {
+  // Decimals should interpolate seamlessly with proper significance.
   Value D = Object{{"power", 1.21}};
   auto T = Template::createTemplate("{{power}} jiggawatts!");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
   EXPECT_EQ("1.21 jiggawatts!", Out);
 }
 
 TEST(MustacheInterpolation, BasicNullInterpolation) {
+  // Nulls should interpolate as the empty string.
   Value D = Object{{"cannot", nullptr}};
   auto T = Template::createTemplate("I ({{cannot}}) be seen!");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
+  EXPECT_EQ("I () be seen!", Out);
+}
+
+TEST(MustacheInterpolation, AmpersandNullInterpolation) {
+  // Nulls should interpolate as the empty string.
+  Value D = Object{{"cannot", nullptr}};
+  auto T = Template::createTemplate("I ({{&cannot}}) be seen!");
+  auto Out = T.render(D);
   EXPECT_EQ("I () be seen!", Out);
 }
 
 TEST(MustacheInterpolation, BasicContextMissInterpolation) {
+  // Failed context lookups should default to empty strings.
   Value D = Object{};
   auto T = Template::createTemplate("I ({{cannot}}) be seen!");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
   EXPECT_EQ("I () be seen!", Out);
 }
 
 TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
+  // Dotted names should be considered a form of shorthand for sections.
   Value D = Object{{"person", Object{{"name", "Joe"}}}};
   auto T = Template::createTemplate(
       "{{person.name}} == {{#person}}{{name}}{{/person}}");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
+  EXPECT_EQ("Joe == Joe", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesAmpersandInterpolation) {
+  // Dotted names should be considered a form of shorthand for sections.
+  Value D = Object{{"person", Object{{"name", "Joe"}}}};
+  auto T = Template::createTemplate(
+      "{{&person.name}} == {{#person}}{{&name}}{{/person}}");
+  auto Out = T.render(D);
   EXPECT_EQ("Joe == Joe", Out);
 }
 
 TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
+  // Dotted names should be functional to any level of nesting.
   Value D = Object{
       {"a",
        Object{{"b",
@@ -109,27 +140,845 @@ TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
                        Object{{"d",
                                Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
   auto T = Template::createTemplate("{{a.b.c.d.e.name}} == Phil");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
   EXPECT_EQ("Phil == Phil", Out);
 }
 
+TEST(MustacheInterpolation, DottedNamesBrokenChains) {
+  // Any falsey value prior to the last part of the name should yield ''.
+  Value D = Object{{"a", Object{}}};
+  auto T = Template::createTemplate("{{a.b.c}} == ");
+  auto Out = T.render(D);
+  EXPECT_EQ(" == ", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesBrokenChainResolution) {
+  // Each part of a dotted name should resolve only against its parent.
+  Value D =
+      Object{{"a", Object{{"b", Object{}}}}, {"c", Object{{"name", "Jim"}}}};
+  auto T = Template::createTemplate("{{a.b.c.name}} == ");
+  auto Out = T.render(D);
+  EXPECT_EQ(" == ", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesInitialResolution) {
+  // The first part of a dotted name should resolve as any other name.
+  Value D = Object{
+      {"a",
+       Object{
+           {"b",
+            Object{{"c",
+                    Object{{"d", Object{{"e", Object{{"name", "Phil"}}}}}}}}}}},
+      {"b",
+       Object{{"c", Object{{"d", Object{{"e", Object{{"name", "Wrong"}}}}}}}}}};
+  auto T = Template::createTemplate("{{#a}}{{b.c.d.e.name}}{{/a}} == Phil");
+  auto Out = T.render(D);
+  EXPECT_EQ("Phil == Phil", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesContextPrecedence) {
+  // Dotted names should be resolved against former resolutions.
+  Value D =
+      Object{{"a", Object{{"b", Object{}}}}, {"b", Object{{"c", "ERROR"}}}};
+  auto T = Template::createTemplate("{{#a}}{{b.c}}{{/a}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesAreNotSingleKeys) {
+  // Dotted names shall not be parsed as single, atomic keys
+  Value D = Object{{"a.b", "c"}};
+  auto T = Template::createTemplate("{{a.b}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("", Out);
+}
+
+TEST(MustacheInterpolation, DottedNamesNoMasking) {
+  // Dotted Names in a given context are unavailable due to dot splitting
+  Value D = Object{{"a.b", "c"}, {"a", Object{{"b", "d"}}}};
+  auto T = Template::createTemplate("{{a.b}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("d", Out);
+}
+
 TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
+  // Unadorned tags should interpolate content into the template.
   Value D = "world";
   auto T = Template::createTemplate("Hello, {{.}}!\n");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
   EXPECT_EQ("Hello, world!\n", Out);
 }
 
+TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {
+  // Basic interpolation should be HTML escaped.
+  Value D = "& \" < >";
+  auto T = Template::createTemplate(
+      "These characters should not be HTML escaped: {{&.}}\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
+}
+
+TEST(MustacheInterpolation, ImplicitIteratorsInteger) {
+  // Integers should interpolate seamlessly.
+  Value D = 85;
+  auto T = Template::createTemplate("{{.}} miles an hour!\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("85 miles an hour!\n", Out);
+}
+
 TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
+  // Interpolation should not alter surrounding whitespace.
   Value D = Object{{"string", "---"}};
   auto T = Template::createTemplate("| {{string}} |");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
   EXPECT_EQ("| --- |", Out);
 }
 
+TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {
+  // Interpolation should not alter surrounding whitespace.
+  Value D = Object{{"string", "---"}};
+  auto T = Template::createTemplate("| {{&string}} |");
+  auto Out = T.render(D);
+  EXPECT_EQ("| --- |", Out);
+}
+
+TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {
+  // Standalone interpolation should not alter surrounding whitespace.
+  Value D = Object{{"string", "---"}};
+  auto T = Template::createTemplate("  {{string}}\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("  ---\n", Out);
+}
+
+TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {
+  // Standalone interpolation should not alter surrounding whitespace.
+  Value D = Object{{"string", "---"}};
+  auto T = Template::createTemplate("  {{&string}}\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("  ---\n", Out);
+}
+
 TEST(MustacheInterpolation, InterpolationWithPadding) {
+  // Superfluous in-tag whitespace should be ignored.
   Value D = Object{{"string", "---"}};
   auto T = Template::createTemplate("|{{ string }}|");
-  auto Out = T.get().render(D);
+  auto Out = T.render(D);
+  EXPECT_EQ("|---|", Out);
+}
+
+TEST(MustacheInterpolation, AmpersandWithPadding) {
+  // Superfluous in-tag whitespace should be ignored.
+  Value D = Object{{"string", "---"}};
+  auto T = Template::createTemplate("|{{& string }}|");
+  auto Out = T.render(D);
+  EXPECT_EQ("|---|", Out);
+}
+
+TEST(MustacheInterpolation, InterpolationWithPaddingAndNewlines) {
+  // Superfluous in-tag whitespace should be ignored.
+  Value D = Object{{"string", "---"}};
+  auto T = Template::createTemplate("|{{ string \n\n\n }}|");
+  auto Out = T.render(D);
   EXPECT_EQ("|---|", Out);
 }
+
+TEST(MustacheSections, Truthy) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate(
+      "{{#boolean}}This should be rendered.{{/boolean}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("This should be rendered.", Out);
+}
+
+TEST(MustacheSections, Falsey) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate(
+      "{{#boolean}}This should not be rendered.{{/boolean}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("", Out);
+}
+
+TEST(MustacheSections, NullIsFalsey) {
+  Value D = Object{{"null", nullptr}};
+  auto T = Template::createTemplate(
+      "{{#null}}This should not be rendered.{{/null}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("", Out);
+}
+
+TEST(MustacheSections, Context) {
+  Value D = Object{{"context", Object{{"name", "Joe"}}}};
+  auto T = Template::createTemplate("{{#context}}Hi {{name}}.{{/context}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("Hi Joe.", Out);
+}
+
+TEST(MustacheSections, ParentContexts) {
+  Value D = Object{{"a", "foo"},
+                   {"b", "wrong"},
+                   {"sec", Object{{"b", "bar"}}},
+                   {"c", Object{{"d", "baz"}}}};
+  auto T = Template::createTemplate("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("foo, bar, baz", Out);
+}
+
+TEST(MustacheSections, VariableTest) {
+  Value D = Object{{"foo", "bar"}};
+  auto T = Template::createTemplate("{{#foo}}{{.}} is {{foo}}{{/foo}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("bar is bar", Out);
+}
+
+TEST(MustacheSections, ListContexts) {
+  Value D = Object{
+      {"tops",
+       Array{Object{
+           {"tname", Object{{"upper", "A"}, {"lower", "a"}}},
+           {"middles",
+            Array{Object{{"mname", "1"},
+                         {"bottoms", Array{Object{{"bname", "x"}},
+                                           Object{{"bname", "y"}}}}}}}}}}};
+  auto T = Template::createTemplate("{{#tops}}"
+                                    "{{#middles}}"
+                                    "{{tname.lower}}{{mname}}."
+                                    "{{#bottoms}}"
+                                    "{{tname.upper}}{{mname}}{{bname}}."
+                                    "{{/bottoms}}"
+                                    "{{/middles}}"
+                                    "{{/tops}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("a1.A1x.A1y.", Out);
+}
+
+TEST(MustacheSections, DeeplyNestedContexts) {
+  Value D = Object{
+      {"a", Object{{"one", 1}}},
+      {"b", Object{{"two", 2}}},
+      {"c", Object{{"three", 3}, {"d", Object{{"four", 4}, {"five", 5}}}}}};
+  auto T = Template::createTemplate(
+      "{{#a}}\n{{one}}\n{{#b}}\n{{one}}{{two}}{{one}}\n{{#c}}\n{{one}}{{two}}{{"
+      "three}}{{two}}{{one}}\n{{#d}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{"
+      "{two}}{{one}}\n{{#five}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{four}"
+      "}{{three}}{{two}}{{one}}\n{{one}}{{two}}{{three}}{{four}}{{.}}6{{.}}{{"
+      "four}}{{three}}{{two}}{{one}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{"
+      "four}}{{three}}{{two}}{{one}}\n{{/"
+      "five}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{/"
+      "d}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{/"
+      "c}}\n{{one}}{{two}}{{one}}\n{{/b}}\n{{one}}\n{{/a}}\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("1\n121\n12321\n1234321\n123454321\n12345654321\n123454321\n1234321"
+            "\n12321\n121\n1\n",
+            Out);
+}
+
+TEST(MustacheSections, List) {
+  Value D = Object{{"list", Array{Object{{"item", 1}}, Object{{"item", 2}},
+                                  Object{{"item", 3}}}}};
+  auto T = Template::createTemplate("{{#list}}{{item}}{{/list}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("123", Out);
+}
+
+TEST(MustacheSections, EmptyList) {
+  Value D = Object{{"list", Array{}}};
+  auto T = Template::createTemplate("{{#list}}Yay lists!{{/list}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("", Out);
+}
+
+TEST(MustacheSections, Doubled) {
+  Value D = Object{{"bool", true}, {"two", "second"}};
+  auto T = Template::createTemplate("{{#bool}}\n* first\n{{/bool}}\n* "
+                                    "{{two}}\n{{#bool}}\n* third\n{{/bool}}\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("* first\n* second\n* third\n", Out);
+}
+
+TEST(MustacheSections, NestedTruthy) {
+  Value D = Object{{"bool", true}};
+  auto T = Template::createTemplate(
+      "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
+  auto Out = T.render(D);
+  EXPECT_EQ("| A B C D E |", Out);
+}
+
+TEST(MustacheSections, NestedFalsey) {
+  Value D = Object{{"bool", false}};
+  auto T = Template::createTemplate(
+      "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
+  auto Out = T.render(D);
+  EXPECT_EQ("| A  E |", Out);
+}
+
+TEST(MustacheSections, ContextMisses) {
+  Value D = Object{};
+  auto T = Template::createTemplate(
+      "[{{#missing}}Found key 'missing'!{{/missing}}]");
+  auto Out = T.render(D);
+  EXPECT_EQ("[]", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorString) {
+  Value D = Object{{"list", Array{"a", "b", "c", "d", "e"}}};
+  auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("(a)(b)(c)(d)(e)", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorInteger) {
+  Value D = Object{{"list", Array{1, 2, 3, 4, 5}}};
+  auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("(1)(2)(3)(4)(5)", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorArray) {
+  Value D = Object{{"list", Array{Array{1, 2, 3}, Array{"a", "b", "c"}}}};
+  auto T = Template::createTemplate("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("(123)(abc)", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorHTMLEscaping) {
+  Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
+  auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("(&)(")(<)(>)", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorAmpersand) {
+  Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
+  auto T = Template::createTemplate("{{#list}}({{&.}}){{/list}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("(&)(\")(<)(>)", Out);
+}
+
+TEST(MustacheSections, ImplicitIteratorRootLevel) {
+  Value D = Array{Object{{"value", "a"}}, Object{{"value", "b"}}};
+  auto T = Template::createTemplate("{{#.}}({{value}}){{/.}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("(a)(b)", Out);
+}
+
+TEST(MustacheSections, DottedNamesTruthy) {
+  Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
+  auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == Here");
+  auto Out = T.render(D);
+  EXPECT_EQ("Here == Here", Out);
+}
+
+TEST(MustacheSections, DottedNamesFalsey) {
+  Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
+  auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == ");
+  auto Out = T.render(D);
+  EXPECT_EQ(" == ", Out);
+}
+
+TEST(MustacheSections, DottedNamesBrokenChains) {
+  Value D = Object{{"a", Object{}}};
+  auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == ");
+  auto Out = T.render(D);
+  EXPECT_EQ(" == ", Out);
+}
+
+TEST(MustacheSections, SurroundingWhitespace) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate(" | {{#boolean}}\t|\t{{/boolean}} | \n");
+  auto Out = T.render(D);
+  EXPECT_EQ(" | \t|\t | \n", Out);
+}
+
+TEST(MustacheSections, InternalWhitespace) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate(
+      " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
+  auto Out = T.render(D);
+  EXPECT_EQ(" |  \n  | \n", Out);
+}
+
+TEST(MustacheSections, IndentedInlineSections) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate(
+      " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n");
+  auto Out = T.render(D);
+  EXPECT_EQ(" YES\n GOOD\n", Out);
+}
+
+TEST(MustacheSections, StandaloneLines) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate(
+      "| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
+}
+
+TEST(MustacheSections, IndentedStandaloneLines) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate(
+      "| This Is\n  {{#boolean}}\n|\n  {{/boolean}}\n| A Line\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
+}
+
+TEST(MustacheSections, StandaloneLineEndings) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|");
+  auto Out = T.render(D);
+  EXPECT_EQ("|\r\n|", Out);
+}
+
+TEST(MustacheSections, StandaloneWithoutPreviousLine) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate("  {{#boolean}}\n#{{/boolean}}\n/");
+  auto Out = T.render(D);
+  EXPECT_EQ("#\n/", Out);
+}
+
+TEST(MustacheSections, StandaloneWithoutNewline) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate("#{{#boolean}}\n/\n  {{/boolean}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("#\n/\n", Out);
+}
+
+TEST(MustacheSections, Padding) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate("|{{# boolean }}={{/ boolean }}|");
+  auto Out = T.render(D);
+  EXPECT_EQ("|=|", Out);
+}
+
+TEST(MustacheInvertedSections, Falsey) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate(
+      "{{^boolean}}This should be rendered.{{/boolean}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("This should be rendered.", Out);
+}
+
+TEST(MustacheInvertedSections, Truthy) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate(
+      "{{^boolean}}This should not be rendered.{{/boolean}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("", Out);
+}
+
+TEST(MustacheInvertedSections, NullIsFalsey) {
+  Value D = Object{{"null", nullptr}};
+  auto T =
+      Template::createTemplate("{{^null}}This should be rendered.{{/null}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("This should be rendered.", Out);
+}
+
+TEST(MustacheInvertedSections, Context) {
+  Value D = Object{{"context", Object{{"name", "Joe"}}}};
+  auto T = Template::createTemplate("{{^context}}Hi {{name}}.{{/context}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("", Out);
+}
+
+TEST(MustacheInvertedSections, List) {
+  Value D = Object{
+      {"list", Array{Object{{"n", 1}}, Object{{"n", 2}}, Object{{"n", 3}}}}};
+  auto T = Template::createTemplate("{{^list}}{{n}}{{/list}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("", Out);
+}
+
+TEST(MustacheInvertedSections, EmptyList) {
+  Value D = Object{{"list", Array{}}};
+  auto T = Template::createTemplate("{{^list}}Yay lists!{{/list}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("Yay lists!", Out);
+}
+
+TEST(MustacheInvertedSections, Doubled) {
+  Value D = Object{{"bool", false}, {"two", "second"}};
+  auto T = Template::createTemplate("{{^bool}}\n* first\n{{/bool}}\n* "
+                                    "{{two}}\n{{^bool}}\n* third\n{{/bool}}\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("* first\n* second\n* third\n", Out);
+}
+
+TEST(MustacheInvertedSections, NestedFalsey) {
+  Value D = Object{{"bool", false}};
+  auto T = Template::createTemplate(
+      "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
+  auto Out = T.render(D);
+  EXPECT_EQ("| A B C D E |", Out);
+}
+
+TEST(MustacheInvertedSections, NestedTruthy) {
+  Value D = Object{{"bool", true}};
+  auto T = Template::createTemplate(
+      "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
+  auto Out = T.render(D);
+  EXPECT_EQ("| A  E |", Out);
+}
+
+TEST(MustacheInvertedSections, ContextMisses) {
+  Value D = Object{};
+  auto T = Template::createTemplate(
+      "[{{^missing}}Cannot find key 'missing'!{{/missing}}]");
+  auto Out = T.render(D);
+  EXPECT_EQ("[Cannot find key 'missing'!]", Out);
+}
+
+TEST(MustacheInvertedSections, DottedNamesTruthy) {
+  Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
+  auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == ");
+  auto Out = T.render(D);
+  EXPECT_EQ(" == ", Out);
+}
+
+TEST(MustacheInvertedSections, DottedNamesFalsey) {
+  Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
+  auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
+  auto Out = T.render(D);
+  EXPECT_EQ("Not Here == Not Here", Out);
+}
+
+TEST(MustacheInvertedSections, DottedNamesBrokenChains) {
+  Value D = Object{{"a", Object{}}};
+  auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
+  auto Out = T.render(D);
+  EXPECT_EQ("Not Here == Not Here", Out);
+}
+
+TEST(MustacheInvertedSections, SurroundingWhitespace) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate(" | {{^boolean}}\t|\t{{/boolean}} | \n");
+  auto Out = T.render(D);
+  EXPECT_EQ(" | \t|\t | \n", Out);
+}
+
+TEST(MustacheInvertedSections, InternalWhitespace) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate(
+      " | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
+  auto Out = T.render(D);
+  EXPECT_EQ(" |  \n  | \n", Out);
+}
+
+TEST(MustacheInvertedSections, IndentedInlineSections) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate(
+      " {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n");
+  auto Out = T.render(D);
+  EXPECT_EQ(" NO\n WAY\n", Out);
+}
+
+TEST(MustacheInvertedSections, StandaloneLines) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate(
+      "| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
+}
+
+TEST(MustacheInvertedSections, StandaloneIndentedLines) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate(
+      "| This Is\n  {{^boolean}}\n|\n  {{/boolean}}\n| A Line\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
+}
+
+TEST(MustacheInvertedSections, StandaloneLineEndings) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|");
+  auto Out = T.render(D);
+  EXPECT_EQ("|\r\n|", Out);
+}
+
+TEST(MustacheInvertedSections, StandaloneWithoutPreviousLine) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate("  {{^boolean}}\n^{{/boolean}}\n/");
+  auto Out = T.render(D);
+  EXPECT_EQ("^\n/", Out);
+}
+
+TEST(MustacheInvertedSections, StandaloneWithoutNewline) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate("^{{^boolean}}\n/\n  {{/boolean}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("^\n/\n", Out);
+}
+
+TEST(MustacheInvertedSections, Padding) {
+  Value D = Object{{"boolean", false}};
+  auto T = Template::createTemplate("|{{^ boolean }}={{/ boolean }}|");
+  auto Out = T.render(D);
+  EXPECT_EQ("|=|", Out);
+}
+
+TEST(MustachePartials, BasicBehavior) {
+  Value D = Object{};
+  auto T = Template::createTemplate("{{>text}}");
+  T.registerPartial("text", "from partial");
+  auto Out = T.render(D);
+  EXPECT_EQ("from partial", Out);
+}
+
+TEST(MustachePartials, FailedLookup) {
+  Value D = Object{};
+  auto T = Template::createTemplate("{{>text}}");
+  auto Out = T.render(D);
+  EXPECT_EQ("", Out);
+}
+
+TEST(MustachePartials, Context) {
+  Value D = Object{{"text", "content"}};
+  auto T = Template::createTemplate("{{>partial}}");
+  T.registerPartial("partial", "*{{text}}*");
+  auto Out = T.render(D);
+  EXPECT_EQ("*content*", Out);
+}
+
+TEST(MustachePartials, Recursion) {
+  Value D =
+      Object{{"content", "X"},
+             {"nodes", Array{Object{{"content", "Y"}, {"nodes", Array{}}}}}};
+  auto T = Template::createTemplate("{{>node}}");
+  T.registerPartial("node", "{{content}}({{#nodes}}{{>node}}{{/nodes}})");
+  auto Out = T.render(D);
+  EXPECT_EQ("X(Y())", Out);
+}
+
+TEST(MustachePartials, Nested) {
+  Value D = Object{{"a", "hello"}, {"b", "world"}};
+  auto T = Template::createTemplate("{{>outer}}");
+  T.registerPartial("outer", "*{{a}} {{>inner}}*");
+  T.registerPartial("inner", "{{b}}!");
+  auto Out = T.render(D);
+  EXPECT_EQ("*hello world!*", Out);
+}
+
+TEST(MustachePartials, SurroundingWhitespace) {
+  Value D = Object{};
+  auto T = Template::createTemplate("| {{>partial}} |");
+  T.registerPartial("partial", "\t|\t");
+  auto Out = T.render(D);
+  EXPECT_EQ("| \t|\t |", Out);
+}
+
+TEST(MustachePartials, InlineIndentation) {
+  Value D = Object{{"data", "|"}};
+  auto T = Template::createTemplate("  {{data}}  {{> partial}}\n");
+  T.registerPartial("partial", "(\n(");
+  auto Out = T.render(D);
+  EXPECT_EQ("  |  (\n(\n", Out);
+}
+
+TEST(MustachePartials, PaddingWhitespace) {
+  Value D = Object{{"boolean", true}};
+  auto T = Template::createTemplate("|{{> partial }}|");
+  T.registerPartial("partial", "[]");
+  auto Out = T.render(D);
+  EXPECT_EQ("|[]|", Out);
+}
+
+TEST(MustacheLambdas, BasicInterpolation) {
+  Value D = Object{};
+  auto T = Template::createTemplate("Hello, {{lambda}}!");
+  Lambda L = []() -> llvm::SmallString<128> {
+    llvm::SmallString<128> Result("World");
+    return Result;
+  };
+  T.registerLambda("lambda", L);
+  auto Out = T.render(D);
+  EXPECT_EQ("Hello, World!", Out);
+}
+
+TEST(MustacheLambdas, InterpolationExpansion) {
+  Value D = Object{{"planet", "World"}};
+  auto T = Template::createTemplate("Hello, {{lambda}}!");
+  Lambda L = []() -> llvm::SmallString<128> {
+    return llvm::SmallString<128>("{{planet}}");
+  };
+  T.registerLambda("lambda", L);
+  auto Out = T.render(D);
+  EXPECT_EQ("Hello, World!", Out);
+}
+
+TEST(MustacheLambdas, BasicMultipleCalls) {
+  Value D = Object{};
+  auto T = Template::createTemplate("{{lambda}} == {{lambda}} == {{lambda}}");
+  int I = 0;
+  Lambda L = [&I]() -> llvm::json::Value {
+    I += 1;
+    return I;
+  };
+  T.registerLambda("lambda", L);
+  auto Out = T.render(D);
+  EXPECT_EQ("1 == 2 == 3", Out);
+}
+
+TEST(MustacheLambdas, Escaping) {
+  Value D = Object{};
+  auto T = Template::createTemplate("<{{lambda}}{{&lambda}}");
+  Lambda L = []() -> llvm::json::Value { return ">"; };
+  T.registerLambda("lambda", L);
+  auto Out = T.render(D);
+  EXPECT_EQ("<>>", Out);
+}
+
+TEST(MustacheLambdas, Sections) {
+  Value D = Object{};
+  auto T = Template::createTemplate("<{{#lambda}}{{x}}{{/lambda}}>");
+  SectionLambda L = [](StringRef Text) -> llvm::json::Value {
+    if (Text == "{{x}}") {
+      return "yes";
+    }
+    return "no";
+  };
+  T.registerLambda("lambda", L);
+  auto Out = T.render(D);
+  EXPECT_EQ("<yes>", Out);
+}
+
+TEST(MustacheLambdas, SectionExpansion) {
+  Value D = Object{
+      {"planet", "Earth"},
+  };
+  auto T = Template::createTemplate("<{{#lambda}}-{{/lambda}}>");
+  SectionLambda L = [](StringRef Text) -> llvm::json::Value {
+    SmallString<128> Result;
+    Result += Text;
+    Result += "{{planet}}";
+    Result += Text;
+    return Result;
+  };
+  T.registerLambda("lambda", L);
+  auto Out = T.render(D);
+  EXPECT_EQ("<-Earth->", Out);
+}
+
+TEST(MustacheLambdas, SectionsMultipleCalls) {
+  Value D = Object{};
+  auto T = Template::createTemplate(
+      "{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}");
+  SectionLambda L = [](StringRef Text) -> llvm::json::Value {
+    SmallString<128> Result;
+    Result += "__";
+    Result += Text;
+    Result += "__";
+    return Result;
+  };
+  T.registerLambda("lambda", L);
+  auto Out = T.render(D);
+  EXPECT_EQ("__FILE__ != __LINE__", Out);
+}
+
+TEST(MustacheLambdas, InvertedSections) {
+  Value D = Object{{"static", "static"}};
+  auto T = Template::createTemplate("<{{^lambda}}{{static}}{{/lambda}}>");
+  SectionLambda L = [](StringRef Text) -> llvm::json::Value { return false; };
+  T.registerLambda("lambda", L);
+  auto Out = T.render(D);
+  EXPECT_EQ("<>", Out);
+}
+
+TEST(MustacheComments, Inline) {
+  // Comment blocks should be removed from the template.
+  Value D = {};
+  auto T = Template::createTemplate("12345{{! Comment Block! }}67890");
+  auto Out = T.render(D);
+  EXPECT_EQ("1234567890", Out);
+}
+
+TEST(MustacheComments, Multiline) {
+  // Multiline comments should be permitted.
+  Value D = {};
+  auto T = Template::createTemplate(
+      "12345{{!\n  This is a\n  multi-line comment...\n}}67890\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("1234567890\n", Out);
+}
+
+TEST(MustacheComments, Standalone) {
+  // All standalone comment lines should be removed.
+  Value D = {};
+  auto T = Template::createTemplate("Begin.\n{{! Comment Block! }}\nEnd.\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("Begin.\nEnd.\n", Out);
+}
+
+TEST(MustacheComments, IndentedStandalone) {
+  // All standalone comment lines should be removed.
+  Value D = {};
+  auto T = Template::createTemplate(
+      "Begin.\n  {{! Indented Comment Block! }}\nEnd.\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("Begin.\nEnd.\n", Out);
+}
+
+TEST(MustacheComments, StandaloneLineEndings) {
+  // "\r\n" should be considered a newline for standalone tags.
+  Value D = {};
+  auto T = Template::createTemplate("|\r\n{{! Standalone Comment }}\r\n|");
+  auto Out = T.render(D);
+  EXPECT_EQ("|\r\n|", Out);
+}
+
+TEST(MustacheComments, StandaloneWithoutPreviousLine) {
+  // Standalone tags should not require a newline to precede them.
+  Value D = {};
+  auto T = Template::createTemplate("  {{! I'm Still Standalone }}\n!");
+  auto Out = T.render(D);
+  EXPECT_EQ("!", Out);
+}
+
+TEST(MustacheComments, StandaloneWithoutNewline) {
+  // Standalone tags should not require a newline to follow them.
+  Value D = {};
+  auto T = Template::createTemplate("!\n  {{! I'm Still Standalone }}");
+  auto Out = T.render(D);
+  EXPECT_EQ("!\n", Out);
+}
+
+TEST(MustacheComments, MultilineStandalone) {
+  // All standalone comment lines should be removed.
+  Value D = {};
+  auto T = Template::createTemplate(
+      "Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("Begin.\nEnd.\n", Out);
+}
+
+TEST(MustacheComments, IndentedMultilineStandalone) {
+  // All standalone comment lines should be removed.
+  Value D = {};
+  auto T = Template::createTemplate(
+      "Begin.\n  {{!\n    Something's going on here...\n  }}\nEnd.\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("Begin.\nEnd.\n", Out);
+}
+
+TEST(MustacheComments, IndentedInline) {
+  // Inline comments should not strip whitespace.
+  Value D = {};
+  auto T = Template::createTemplate("  12 {{! 34 }}\n");
+  auto Out = T.render(D);
+  EXPECT_EQ("  12 \n", Out);
+}
+
+TEST(MustacheComments, SurroundingWhitespace) {
+  // Comment removal should preserve surrounding whitespace.
+  Value D = {};
+  auto T = Template::createTemplate("12345 {{! Comment Block! }} 67890");
+  auto Out = T.render(D);
+  EXPECT_EQ("12345  67890", Out);
+}
+
+TEST(MustacheComments, VariableNameCollision) {
+  // Comments must never render, even if a variable with the same name exists.
+  Value D = Object{
+      {"! comment", 1}, {"! comment ", 2}, {"!comment", 3}, {"comment", 4}};
+  auto T = Template::createTemplate("comments never show: >{{! comment }}<");
+  auto Out = T.render(D);
+  EXPECT_EQ("comments never show: ><", Out);
+}
\ No newline at end of file

>From 6e893c07ca49773825ddcb6d99107b96b37d5540 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 15:51:33 -0400
Subject: [PATCH 12/62] [llvm][support] fix mustache test

---
 llvm/lib/Support/Mustache.cpp           | 11 ++++++-----
 llvm/unittests/Support/MustacheTest.cpp |  4 ++--
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 80b3d8bc53651b..09adba4d6239ee 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -137,16 +137,16 @@ std::vector<Token> tokenize(StringRef Template) {
     if (I > 0 && Tokens[I - 1].getType() == Token::Type::Text &&
         RequiresCleanUp) {
       Token &PrevToken = Tokens[I - 1];
-      StringRef TokenBody = PrevToken.getTokenBody().rtrim(" \t\v\t");
+      StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
       if (TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
-          TokenBody.empty()) {
+          (TokenBody.empty() && I == 1)) {
         NoTextBehind = true;
       }
     }
     if (I < Tokens.size() - 1 && Tokens[I + 1].getType() == Token::Type::Text &&
         RequiresCleanUp) {
       Token &NextToken = Tokens[I + 1];
-      StringRef TokenBody = NextToken.getTokenBody().ltrim(" ");
+      StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
       if (TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n")) {
         NoTextAhead = true;
       }
@@ -363,8 +363,9 @@ ASTNode::render(Value Data,
       StringRef LambdaStr = printJson(LambdaResult);
       Parser P = Parser(LambdaStr);
       std::shared_ptr<ASTNode> LambdaNode = P.parse();
-      return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
-                                Escapes);
+      SmallString<128> RenderStr =
+          LambdaNode->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+      return escapeString(RenderStr, Escapes);
     }
     return escapeString(printJson(Context), Escapes);
   }
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index ceee8942f88f4e..dfd28b0b5b2377 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -768,9 +768,9 @@ TEST(MustachePartials, SurroundingWhitespace) {
 TEST(MustachePartials, InlineIndentation) {
   Value D = Object{{"data", "|"}};
   auto T = Template::createTemplate("  {{data}}  {{> partial}}\n");
-  T.registerPartial("partial", "(\n(");
+  T.registerPartial("partial", "<\n<");
   auto Out = T.render(D);
-  EXPECT_EQ("  |  (\n(\n", Out);
+  EXPECT_EQ("  |  <\n<\n", Out);
 }
 
 TEST(MustachePartials, PaddingWhitespace) {

>From 976593eff073b8741b6e3d1e4d7bcf648f6983d8 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 16:39:30 -0400
Subject: [PATCH 13/62] [llvm][support] add comments

---
 llvm/include/llvm/Support/Mustache.h | 51 +++++++++++++++++--
 llvm/lib/Support/Mustache.cpp        | 75 +++++++++-------------------
 2 files changed, 71 insertions(+), 55 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index cb7d80e47809e8..f5714c19089348 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -7,8 +7,50 @@
 //===----------------------------------------------------------------------===//
 //
 // Implementation of the Mustache templating language supports version 1.4.2
+// currently relies on llvm::json::Value for data input
+// see the Mustache spec for more information
 // (https://mustache.github.io/mustache.5.html).
 //
+// Current Features Supported:
+// - Variables
+// - Sections
+// - Inverted Sections
+// - Partials
+// - Comments
+// - Lambdas
+// - Unescaped Variables
+//
+// Features Not Supported:
+// - Set Delimiter
+// - Blocks
+// - Parents
+// - Dynamic Names
+//
+// Usage:
+// - Creating a simple template and rendering it:
+// \code
+//   auto Template = Template::createTemplate("Hello, {{name}}!");
+//   Value Data = {{"name", "World"}};
+//   StringRef Rendered = Template.render(Data);
+//   // Rendered == "Hello, World!"
+// \endcode
+// - Creating a template with a partial and rendering it:
+// \code
+//   auto Template = Template::createTemplate("{{>partial}}");
+//   Template.registerPartial("partial", "Hello, {{name}}!");
+//   Value Data = {{"name", "World"}};
+//   StringRef Rendered = Template.render(Data);
+//   // Rendered == "Hello, World!"
+// \endcode
+// - Creating a template with a lambda and rendering it:
+// \code
+//   auto Template = Template::createTemplate("{{#lambda}}Hello,
+//                                             {{name}}!{{/lambda}}");
+//   Template.registerLambda("lambda", []() { return true; });
+//   Value Data = {{"name", "World"}};
+//   StringRef Rendered = Template.render(Data);
+//   // Rendered == "Hello, World!"
+// \endcode
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_SUPPORT_MUSTACHE
@@ -17,8 +59,6 @@
 #include "Error.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/Support/JSON.h"
-#include <string>
-#include <variant>
 #include <vector>
 
 namespace llvm {
@@ -96,8 +136,6 @@ class ASTNode {
 
   void setRawBody(StringRef NewBody) { RawBody = NewBody; };
 
-  SmallString<128> getRawBody() const { return RawBody; };
-
   std::shared_ptr<ASTNode> getLastChild() const {
     return Children.empty() ? nullptr : Children.back();
   };
@@ -120,6 +158,8 @@ class ASTNode {
   llvm::json::Value LocalContext;
 };
 
+// A Template represents the container for the AST and the partials
+// and Lambdas that are registered with it.
 class Template {
 public:
   static Template createTemplate(StringRef TemplateStr);
@@ -132,6 +172,9 @@ class Template {
 
   void registerLambda(StringRef Name, SectionLambda Lambda);
 
+  // By default the Mustache Spec Specifies that HTML special characters
+  // should be escaped. This function allows the user to specify which
+  // characters should be escaped.
   void registerEscape(DenseMap<char, StringRef> Escapes);
 
 private:
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 09adba4d6239ee..18a94f9f241c4d 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -8,9 +8,6 @@
 
 #include "llvm/Support/Mustache.h"
 #include "llvm/Support/Error.h"
-#include <iostream>
-#include <regex>
-#include <sstream>
 
 using namespace llvm;
 using namespace llvm::json;
@@ -20,11 +17,10 @@ SmallString<128> escapeString(StringRef Input,
                               DenseMap<char, StringRef> &Escape) {
   SmallString<128> EscapedString("");
   for (char C : Input) {
-    if (Escape.find(C) != Escape.end()) {
+    if (Escape.find(C) != Escape.end())
       EscapedString += Escape[C];
-    } else {
+    else
       EscapedString += C;
-    }
   }
   return EscapedString;
 }
@@ -52,9 +48,9 @@ Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
     return;
 
   StringRef AccessorStr = InnerBody;
-  if (TokenType != Type::Variable) {
+  if (TokenType != Type::Variable)
     AccessorStr = InnerBody.substr(1);
-  }
+
   Accessor = split(AccessorStr.trim(), '.');
 }
 
@@ -118,9 +114,8 @@ std::vector<Token> tokenize(StringRef Template) {
     DelimiterStart = Template.find(Open, Start);
   }
 
-  if (Start < Template.size()) {
+  if (Start < Template.size())
     Tokens.push_back(Token(Template.substr(Start)));
-  }
 
   // fix up white spaces for
   // open sections/inverted sections/close section/comment
@@ -139,38 +134,27 @@ std::vector<Token> tokenize(StringRef Template) {
       Token &PrevToken = Tokens[I - 1];
       StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
       if (TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
-          (TokenBody.empty() && I == 1)) {
+          (TokenBody.empty() && I == 1))
         NoTextBehind = true;
-      }
     }
     if (I < Tokens.size() - 1 && Tokens[I + 1].getType() == Token::Type::Text &&
         RequiresCleanUp) {
       Token &NextToken = Tokens[I + 1];
       StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
-      if (TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n")) {
+      if (TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n"))
         NoTextAhead = true;
-      }
     }
 
-    if (NoTextBehind && NoTextAhead) {
-      Token &PrevToken = Tokens[I - 1];
-      Token &NextToken = Tokens[I + 1];
-      StringRef NextTokenBody = NextToken.getTokenBody();
-      PrevToken.setTokenBody(PrevToken.getTokenBody().rtrim(" \t\v\t"));
-      if (NextTokenBody.starts_with("\r\n")) {
-        NextToken.setTokenBody(NextTokenBody.substr(2));
-      } else if (NextToken.getTokenBody().starts_with("\n")) {
-        NextToken.setTokenBody(NextTokenBody.substr(1));
-      }
-    } else if (NoTextAhead && I == 0) {
+    if ((NoTextBehind && NoTextAhead) || (NoTextAhead && I == 0)) {
       Token &NextToken = Tokens[I + 1];
       StringRef NextTokenBody = NextToken.getTokenBody();
-      if (NextTokenBody.starts_with("\r\n")) {
+      if (NextTokenBody.starts_with("\r\n"))
         NextToken.setTokenBody(NextTokenBody.substr(2));
-      } else if (NextToken.getTokenBody().starts_with("\n")) {
+      else if (NextToken.getTokenBody().starts_with("\n"))
         NextToken.setTokenBody(NextTokenBody.substr(1));
-      }
-    } else if (NoTextBehind && I == Tokens.size() - 1) {
+    }
+    if ((NoTextBehind && NoTextAhead) ||
+        (NoTextBehind && I == Tokens.size() - 1)) {
       Token &PrevToken = Tokens[I - 1];
       StringRef PrevTokenBody = PrevToken.getTokenBody();
       PrevToken.setTokenBody(PrevTokenBody.rtrim(" \t\v\t"));
@@ -239,9 +223,8 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
       std::size_t End = CurrentPtr;
       SmallString<128> RawBody;
       if (Start + 1 < End - 1)
-        for (std::size_t I = Start + 1; I < End - 1; I++) {
+        for (std::size_t I = Start + 1; I < End - 1; I++)
           RawBody += Tokens[I].getRawBody();
-        }
       else if (Start + 1 == End - 1)
         RawBody = Tokens[Start].getRawBody();
       CurrentNode->setRawBody(RawBody);
@@ -256,18 +239,16 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
       std::size_t End = CurrentPtr;
       SmallString<128> RawBody;
       if (Start + 1 < End - 1)
-        for (std::size_t I = Start + 1; I < End - 1; I++) {
+        for (std::size_t I = Start + 1; I < End - 1; I++)
           RawBody += Tokens[I].getRawBody();
-        }
       else if (Start + 1 == End - 1)
         RawBody = Tokens[Start].getRawBody();
       CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
       break;
     }
-    case Token::Type::SectionClose: {
+    case Token::Type::SectionClose:
       return;
-    }
     default:
       break;
     }
@@ -309,14 +290,11 @@ void Template::registerEscape(DenseMap<char, StringRef> E) { Escapes = E; }
 SmallString<128> printJson(Value &Data) {
 
   SmallString<128> Result;
-  if (Data.getAsNull()) {
+  if (Data.getAsNull())
     return Result;
-  }
-  if (auto *Arr = Data.getAsArray()) {
-    if (Arr->empty()) {
+  if (auto *Arr = Data.getAsArray())
+    if (Arr->empty())
       return Result;
-    }
-  }
   if (Data.getAsString()) {
     Result += Data.getAsString()->str();
     return Result;
@@ -436,12 +414,10 @@ Value ASTNode::findContext() {
   // We attempt to find the JSON context in the current node if it is not found
   // we traverse the parent nodes to find the context until we reach the root
   // node or the context is found
-  if (Accessor.empty()) {
+  if (Accessor.empty())
     return nullptr;
-  }
-  if (Accessor[0] == ".") {
+  if (Accessor[0] == ".")
     return LocalContext;
-  }
   json::Object *CurrentContext = LocalContext.getAsObject();
   SmallString<128> CurrentAccessor = Accessor[0];
   std::weak_ptr<ASTNode> CurrentParent = Parent;
@@ -458,17 +434,14 @@ Value ASTNode::findContext() {
   for (std::size_t I = 0; I < Accessor.size(); I++) {
     CurrentAccessor = Accessor[I];
     Value *CurrentValue = CurrentContext->get(CurrentAccessor);
-    if (!CurrentValue) {
+    if (!CurrentValue)
       return nullptr;
-    }
     if (I < Accessor.size() - 1) {
       CurrentContext = CurrentValue->getAsObject();
-      if (!CurrentContext) {
+      if (!CurrentContext)
         return nullptr;
-      }
-    } else {
+    } else
       Context = *CurrentValue;
-    }
   }
   return Context;
 }

>From 6a60eabf0e28e1a43d0943b5952f2eb5865cb5b7 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 17:21:22 -0400
Subject: [PATCH 14/62] [llvm][support] add comments

---
 llvm/include/llvm/Support/Mustache.h | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index f5714c19089348..03892ea5679ca0 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -27,23 +27,21 @@
 // - Dynamic Names
 //
 // Usage:
-// - Creating a simple template and rendering it:
 // \code
+//   // Creating a simple template and rendering it
 //   auto Template = Template::createTemplate("Hello, {{name}}!");
 //   Value Data = {{"name", "World"}};
 //   StringRef Rendered = Template.render(Data);
 //   // Rendered == "Hello, World!"
-// \endcode
-// - Creating a template with a partial and rendering it:
-// \code
+//
+//   // Creating a template with a partial and rendering it
 //   auto Template = Template::createTemplate("{{>partial}}");
 //   Template.registerPartial("partial", "Hello, {{name}}!");
 //   Value Data = {{"name", "World"}};
 //   StringRef Rendered = Template.render(Data);
 //   // Rendered == "Hello, World!"
-// \endcode
-// - Creating a template with a lambda and rendering it:
-// \code
+//
+//   // Creating a template with a lambda and rendering it
 //   auto Template = Template::createTemplate("{{#lambda}}Hello,
 //                                             {{name}}!{{/lambda}}");
 //   Template.registerLambda("lambda", []() { return true; });

>From 6a1fcc8f0f4c40a35d4afff7ebd66c2c99b356c1 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 17:25:06 -0400
Subject: [PATCH 15/62] [llvm][support] use enumerate for loop

---
 llvm/lib/Support/Mustache.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 18a94f9f241c4d..ad5d30d335f626 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -431,8 +431,7 @@ Value ASTNode::findContext() {
     return nullptr;
   }
   Value Context = nullptr;
-  for (std::size_t I = 0; I < Accessor.size(); I++) {
-    CurrentAccessor = Accessor[I];
+  for (auto CurrentAccessor : Accessor) {
     Value *CurrentValue = CurrentContext->get(CurrentAccessor);
     if (!CurrentValue)
       return nullptr;

>From eb1e1a66382551ed2d65c37a2a6fc307051904a2 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 17:34:43 -0400
Subject: [PATCH 16/62] [llvm][support] clang-format

---
 llvm/include/llvm/Support/Mustache.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 03892ea5679ca0..db09089707167d 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -114,15 +114,15 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), LocalContext(nullptr){};
+  ASTNode() : T(Type::Root), LocalContext(nullptr) {};
 
   ASTNode(StringRef Body, std::shared_ptr<ASTNode> Parent)
-      : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr){};
+      : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr) {};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, std::shared_ptr<ASTNode> Parent)
       : T(T), Accessor(Accessor), Parent(Parent), LocalContext(nullptr),
-        Children({}){};
+        Children({}) {};
 
   void addChild(std::shared_ptr<ASTNode> Child) {
     Children.emplace_back(Child);

>From f4b052084bf41dd109213af38622fc5df581691f Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 17:43:01 -0400
Subject: [PATCH 17/62] [llvm][support] fix mustache test

---
 llvm/unittests/Support/MustacheTest.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index dfd28b0b5b2377..c38a2ed2823379 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -139,9 +139,9 @@ TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
                Object{{"c",
                        Object{{"d",
                                Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
-  auto T = Template::createTemplate("{{a.b.c.d.e.name}} == Phil");
+  auto T = Template::createTemplate("{{a.b.c.d.e.name}}");
   auto Out = T.render(D);
-  EXPECT_EQ("Phil == Phil", Out);
+  EXPECT_EQ("Phil", Out);
 }
 
 TEST(MustacheInterpolation, DottedNamesBrokenChains) {
@@ -171,9 +171,9 @@ TEST(MustacheInterpolation, DottedNamesInitialResolution) {
                     Object{{"d", Object{{"e", Object{{"name", "Phil"}}}}}}}}}}},
       {"b",
        Object{{"c", Object{{"d", Object{{"e", Object{{"name", "Wrong"}}}}}}}}}};
-  auto T = Template::createTemplate("{{#a}}{{b.c.d.e.name}}{{/a}} == Phil");
+  auto T = Template::createTemplate("{{#a}}{{b.c.d.e.name}}{{/a}}");
   auto Out = T.render(D);
-  EXPECT_EQ("Phil == Phil", Out);
+  EXPECT_EQ("Phil", Out);
 }
 
 TEST(MustacheInterpolation, DottedNamesContextPrecedence) {

>From 7ffaeec649728e21687e81fc3c479def997b1d83 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 17:50:37 -0400
Subject: [PATCH 18/62] [llvm][support] clang-format

---
 llvm/include/llvm/Support/Mustache.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index db09089707167d..afde8d995cc147 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -176,7 +176,7 @@ class Template {
   void registerEscape(DenseMap<char, StringRef> Escapes);
 
 private:
-  Template(std::shared_ptr<ASTNode> Tree) : Tree(Tree){};
+  Template(std::shared_ptr<ASTNode> Tree) : Tree(Tree) {};
   DenseMap<StringRef, std::shared_ptr<ASTNode>> Partials;
   DenseMap<StringRef, Lambda> Lambdas;
   DenseMap<StringRef, SectionLambda> SectionLambdas;

>From 746fb97f94d3533b84ff674f9f7140fdc1ef5522 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 6 Sep 2024 18:22:52 -0400
Subject: [PATCH 19/62] [llvm][support] use llvm enumerate

---
 llvm/lib/Support/Mustache.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index ad5d30d335f626..f0fb6767f6fcd5 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -431,11 +431,11 @@ Value ASTNode::findContext() {
     return nullptr;
   }
   Value Context = nullptr;
-  for (auto CurrentAccessor : Accessor) {
-    Value *CurrentValue = CurrentContext->get(CurrentAccessor);
+  for (auto E : enumerate(Accessor)) {
+    Value *CurrentValue = CurrentContext->get(E.value());
     if (!CurrentValue)
       return nullptr;
-    if (I < Accessor.size() - 1) {
+    if (E.index() < Accessor.size() - 1) {
       CurrentContext = CurrentValue->getAsObject();
       if (!CurrentContext)
         return nullptr;

>From bcc86fe0ad6f55a27c3fe50231da8d152517ac17 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Wed, 11 Sep 2024 18:41:38 -0400
Subject: [PATCH 20/62] [llvm][support] fix unittest

---
 llvm/include/llvm/Support/Mustache.h    |  4 ++--
 llvm/lib/Support/Mustache.cpp           |  7 ++++---
 llvm/unittests/Support/MustacheTest.cpp | 10 +++++-----
 3 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index afde8d995cc147..6243e29f52282d 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -121,8 +121,8 @@ class ASTNode {
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, std::shared_ptr<ASTNode> Parent)
-      : T(T), Accessor(Accessor), Parent(Parent), LocalContext(nullptr),
-        Children({}) {};
+      : T(T), Parent(Parent), Children({}), Accessor(Accessor),
+        LocalContext(nullptr) {};
 
   void addChild(std::shared_ptr<ASTNode> Child) {
     Children.emplace_back(Child);
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index f0fb6767f6fcd5..7372a248d209bc 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -55,7 +55,7 @@ Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
 }
 
 Token::Token(StringRef Str)
-    : RawBody(Str), TokenType(Type::Text), TokenBody(Str), Accessor({}) {}
+    : TokenType(Type::Text), RawBody(Str), Accessor({}), TokenBody(Str) {}
 
 Token::Type Token::getTokenType(char Identifier) {
   switch (Identifier) {
@@ -256,7 +256,7 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
 }
 
 Template Template::createTemplate(StringRef TemplateStr) {
-  Parser P = Parser(TemplateStr);
+  Parser P = Parser(TemplateStr.str());
   std::shared_ptr<ASTNode> MustacheTree = P.parse();
   Template T = Template(MustacheTree);
   // the default behaviour is to escape html entities
@@ -333,6 +333,7 @@ ASTNode::render(Value Data,
           Partial->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
       return Result;
     }
+    return Result;
   }
   case Variable: {
     if (Lambdas.find(Accessor[0]) != Lambdas.end()) {
@@ -373,7 +374,7 @@ ASTNode::render(Value Data,
       if (isFalsey(Return))
         return Result;
       StringRef LambdaStr = printJson(Return);
-      Parser P = Parser(LambdaStr);
+      Parser P = Parser(LambdaStr.str());
       std::shared_ptr<ASTNode> LambdaNode = P.parse();
       return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
                                 Escapes);
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index c38a2ed2823379..33cbad56240faf 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/Mustache.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 
 using namespace llvm;
@@ -784,9 +785,8 @@ TEST(MustachePartials, PaddingWhitespace) {
 TEST(MustacheLambdas, BasicInterpolation) {
   Value D = Object{};
   auto T = Template::createTemplate("Hello, {{lambda}}!");
-  Lambda L = []() -> llvm::SmallString<128> {
-    llvm::SmallString<128> Result("World");
-    return Result;
+  Lambda L = []() -> llvm::json::Value {
+    return "World";
   };
   T.registerLambda("lambda", L);
   auto Out = T.render(D);
@@ -796,8 +796,8 @@ TEST(MustacheLambdas, BasicInterpolation) {
 TEST(MustacheLambdas, InterpolationExpansion) {
   Value D = Object{{"planet", "World"}};
   auto T = Template::createTemplate("Hello, {{lambda}}!");
-  Lambda L = []() -> llvm::SmallString<128> {
-    return llvm::SmallString<128>("{{planet}}");
+  Lambda L = []() -> llvm::json::Value {
+    return "{{planet}}";
   };
   T.registerLambda("lambda", L);
   auto Out = T.render(D);

>From 95ad7a6a0b128128b608606bd055f386d8162a5b Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Wed, 11 Sep 2024 18:50:01 -0400
Subject: [PATCH 21/62] [llvm][support] clang-format

---
 llvm/unittests/Support/MustacheTest.cpp | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index 33cbad56240faf..3f3f04a9b55b13 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -785,9 +785,7 @@ TEST(MustachePartials, PaddingWhitespace) {
 TEST(MustacheLambdas, BasicInterpolation) {
   Value D = Object{};
   auto T = Template::createTemplate("Hello, {{lambda}}!");
-  Lambda L = []() -> llvm::json::Value {
-    return "World";
-  };
+  Lambda L = []() -> llvm::json::Value { return "World"; };
   T.registerLambda("lambda", L);
   auto Out = T.render(D);
   EXPECT_EQ("Hello, World!", Out);
@@ -796,9 +794,7 @@ TEST(MustacheLambdas, BasicInterpolation) {
 TEST(MustacheLambdas, InterpolationExpansion) {
   Value D = Object{{"planet", "World"}};
   auto T = Template::createTemplate("Hello, {{lambda}}!");
-  Lambda L = []() -> llvm::json::Value {
-    return "{{planet}}";
-  };
+  Lambda L = []() -> llvm::json::Value { return "{{planet}}"; };
   T.registerLambda("lambda", L);
   auto Out = T.render(D);
   EXPECT_EQ("Hello, World!", Out);

>From bb3b1acfedc0776e922c36ddec1ed38b7d9af5cf Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 12 Sep 2024 06:02:49 -0400
Subject: [PATCH 22/62] [llvm][support] address mustache comments

---
 llvm/include/llvm/Support/Mustache.h    |  59 ++---
 llvm/lib/Support/Mustache.cpp           | 304 +++++++++++++-----------
 llvm/unittests/Support/MustacheTest.cpp | 275 ++++++++++-----------
 3 files changed, 316 insertions(+), 322 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 6243e29f52282d..607e33b4ec2c9b 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -26,24 +26,30 @@
 // - Parents
 // - Dynamic Names
 //
+// The Template class is container class outputs the Mustache template string
+// and is main class for users. It stores all the lambdas and the ASTNode Tree.
+// When the Template is instantiated it calls tokenize the Template String into 
+// the Token class and calls a basic recursive descent parser to construct the 
+// ASTNode Tree. The ASTNodes are all stored in an arena allocator which is 
+// freed once the template class goes out of scope
+//
 // Usage:
 // \code
 //   // Creating a simple template and rendering it
-//   auto Template = Template::createTemplate("Hello, {{name}}!");
+//   auto Template = Template("Hello, {{name}}!");
 //   Value Data = {{"name", "World"}};
 //   StringRef Rendered = Template.render(Data);
 //   // Rendered == "Hello, World!"
 //
 //   // Creating a template with a partial and rendering it
-//   auto Template = Template::createTemplate("{{>partial}}");
+//   auto Template = Template("{{>partial}}");
 //   Template.registerPartial("partial", "Hello, {{name}}!");
 //   Value Data = {{"name", "World"}};
 //   StringRef Rendered = Template.render(Data);
 //   // Rendered == "Hello, World!"
 //
 //   // Creating a template with a lambda and rendering it
-//   auto Template = Template::createTemplate("{{#lambda}}Hello,
-//                                             {{name}}!{{/lambda}}");
+//   auto Template = Template("{{#lambda}}Hello, {{name}}!{{/lambda}}");
 //   Template.registerLambda("lambda", []() { return true; });
 //   Value Data = {{"name", "World"}};
 //   StringRef Rendered = Template.render(Data);
@@ -56,13 +62,14 @@
 
 #include "Error.h"
 #include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Allocator.h"
 #include "llvm/Support/JSON.h"
 #include <vector>
 
 namespace llvm {
 namespace mustache {
 
-using Accessor = std::vector<SmallString<128>>;
+using Accessor = SmallVector<SmallString<128>>;
 using Lambda = std::function<llvm::json::Value()>;
 using SectionLambda = std::function<llvm::json::Value(StringRef)>;
 
@@ -114,19 +121,17 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), LocalContext(nullptr) {};
+  ASTNode() : T(Type::Root), LocalContext(nullptr){};
 
-  ASTNode(StringRef Body, std::shared_ptr<ASTNode> Parent)
-      : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr) {};
+  ASTNode(StringRef Body, ASTNode *Parent)
+      : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr){};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
-  ASTNode(Type T, Accessor Accessor, std::shared_ptr<ASTNode> Parent)
+  ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        LocalContext(nullptr) {};
+        LocalContext(nullptr){};
 
-  void addChild(std::shared_ptr<ASTNode> Child) {
-    Children.emplace_back(Child);
-  };
+  void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 
   SmallString<128> getBody() const { return Body; };
 
@@ -134,24 +139,20 @@ class ASTNode {
 
   void setRawBody(StringRef NewBody) { RawBody = NewBody; };
 
-  std::shared_ptr<ASTNode> getLastChild() const {
-    return Children.empty() ? nullptr : Children.back();
-  };
-
-  SmallString<128>
-  render(llvm::json::Value Data,
-         DenseMap<StringRef, std::shared_ptr<ASTNode>> &Partials,
-         DenseMap<StringRef, Lambda> &Lambdas,
-         DenseMap<StringRef, SectionLambda> &SectionLambdas,
-         DenseMap<char, StringRef> &Escapes);
+  SmallString<128> render(llvm::json::Value Data,
+                          llvm::BumpPtrAllocator &Allocator,
+                          DenseMap<StringRef, ASTNode *> &Partials,
+                          DenseMap<StringRef, Lambda> &Lambdas,
+                          DenseMap<StringRef, SectionLambda> &SectionLambdas,
+                          DenseMap<char, StringRef> &Escapes);
 
 private:
   llvm::json::Value findContext();
   Type T;
   SmallString<128> RawBody;
   SmallString<128> Body;
-  std::weak_ptr<ASTNode> Parent;
-  std::vector<std::shared_ptr<ASTNode>> Children;
+  ASTNode *Parent;
+  std::vector<ASTNode *> Children;
   const Accessor Accessor;
   llvm::json::Value LocalContext;
 };
@@ -160,7 +161,7 @@ class ASTNode {
 // and Lambdas that are registered with it.
 class Template {
 public:
-  static Template createTemplate(StringRef TemplateStr);
+  Template(StringRef TemplateStr);
 
   SmallString<128> render(llvm::json::Value Data);
 
@@ -176,12 +177,12 @@ class Template {
   void registerEscape(DenseMap<char, StringRef> Escapes);
 
 private:
-  Template(std::shared_ptr<ASTNode> Tree) : Tree(Tree) {};
-  DenseMap<StringRef, std::shared_ptr<ASTNode>> Partials;
+  DenseMap<StringRef, ASTNode *> Partials;
   DenseMap<StringRef, Lambda> Lambdas;
   DenseMap<StringRef, SectionLambda> SectionLambdas;
   DenseMap<char, StringRef> Escapes;
-  std::shared_ptr<ASTNode> Tree;
+  llvm::BumpPtrAllocator Allocator;
+  ASTNode *Tree;
 };
 
 } // namespace mustache
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 7372a248d209bc..dd6e5ebb19209c 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -25,31 +25,29 @@ SmallString<128> escapeString(StringRef Input,
   return EscapedString;
 }
 
-std::vector<SmallString<128>> split(StringRef Str, char Delimiter) {
-  std::vector<SmallString<128>> Tokens;
+Accessor split(StringRef Str, char Delimiter) {
+  Accessor Tokens;
   if (Str == ".") {
-    Tokens.push_back(Str);
+    Tokens.emplace_back(Str);
     return Tokens;
   }
   StringRef Ref(Str);
   while (!Ref.empty()) {
-    llvm::StringRef Part;
+    StringRef Part;
     std::tie(Part, Ref) = Ref.split(Delimiter);
-    Tokens.push_back(Part.trim());
+    Tokens.emplace_back(Part.trim());
   }
   return Tokens;
 }
 
 Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
     : RawBody(RawBody), TokenBody(InnerBody) {
-
   TokenType = getTokenType(Identifier);
   if (TokenType == Type::Comment)
     return;
 
-  StringRef AccessorStr = InnerBody;
-  if (TokenType != Type::Variable)
-    AccessorStr = InnerBody.substr(1);
+  StringRef AccessorStr =
+      TokenType == Type::Variable ? InnerBody : InnerBody.substr(1);
 
   Accessor = split(AccessorStr.trim(), '.');
 }
@@ -76,86 +74,106 @@ Token::Type Token::getTokenType(char Identifier) {
   }
 }
 
-std::vector<Token> tokenize(StringRef Template) {
-  // Simple tokenizer that splits the template into tokens
-  // the mustache spec allows {{{ }}} to unescape variables
-  // but we don't support that here unescape variable
-  // is represented only by {{& variable}}
-  std::vector<Token> Tokens;
-  SmallString<128> Open("{{");
-  SmallString<128> Close("}}");
+// Function to check if there's no meaningful text behind
+bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
+  if (Idx == 0 || Tokens[Idx - 1].getType() != Token::Type::Text)
+    return false;
+  const Token &PrevToken = Tokens[Idx - 1];
+  StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
+  return TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
+         (TokenBody.empty() && Idx == 1);
+}
+// Function to check if there's no meaningful text ahead
+bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
+  if (Idx >= Tokens.size() - 1 ||
+      Tokens[Idx + 1].getType() != Token::Type::Text)
+    return false;
+
+  const Token &NextToken = Tokens[Idx + 1];
+  StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
+  return TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n");
+}
+
+// Simple tokenizer that splits the template into tokens
+// the mustache spec allows {{{ }}} to unescape variables
+// but we don't support that here unescape variable
+// is represented only by {{& variable}}
+SmallVector<Token, 0> tokenize(StringRef Template) {
+  SmallVector<Token, 0> Tokens;
+  StringRef Open("{{");
+  StringRef Close("}}");
   std::size_t Start = 0;
   std::size_t DelimiterStart = Template.find(Open);
   if (DelimiterStart == StringRef::npos) {
-    Tokens.push_back(Token(Template));
+    Tokens.emplace_back(Template);
     return Tokens;
   }
   while (DelimiterStart != StringRef::npos) {
     if (DelimiterStart != Start) {
-      Token TextToken = Token(Template.substr(Start, DelimiterStart - Start));
-      Tokens.push_back(TextToken);
+      Tokens.emplace_back(Template.substr(Start, DelimiterStart - Start));
     }
 
-    std::size_t DelimiterEnd = Template.find(Close, DelimiterStart);
+    size_t DelimiterEnd = Template.find(Close, DelimiterStart);
     if (DelimiterEnd == StringRef::npos) {
       break;
     }
 
+    // Extract the Interpolated variable without {{ and }}
+    size_t InterpolatedStart = DelimiterStart + Open.size();
+    size_t InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size();
     SmallString<128> Interpolated =
-        Template.substr(DelimiterStart + Open.size(),
-                        DelimiterEnd - DelimiterStart - Close.size());
+        Template.substr(InterpolatedStart, InterpolatedEnd);
     SmallString<128> RawBody;
     RawBody += Open;
     RawBody += Interpolated;
     RawBody += Close;
 
-    Tokens.push_back(Token(RawBody, Interpolated, Interpolated[0]));
+    Tokens.emplace_back(RawBody, Interpolated, Interpolated[0]);
     Start = DelimiterEnd + Close.size();
     DelimiterStart = Template.find(Open, Start);
   }
 
   if (Start < Template.size())
-    Tokens.push_back(Token(Template.substr(Start)));
+    Tokens.emplace_back(Template.substr(Start));
 
   // fix up white spaces for
   // open sections/inverted sections/close section/comment
-  for (std::size_t I = 0; I < Tokens.size(); I++) {
-    Token::Type CurrentType = Tokens[I].getType();
-    bool RequiresCleanUp = CurrentType == Token::Type::SectionOpen ||
-                           CurrentType == Token::Type::InvertSectionOpen ||
-                           CurrentType == Token::Type::SectionClose ||
-                           CurrentType == Token::Type::Comment ||
-                           CurrentType == Token::Type::Partial;
-
-    bool NoTextBehind = false;
-    bool NoTextAhead = false;
-    if (I > 0 && Tokens[I - 1].getType() == Token::Type::Text &&
-        RequiresCleanUp) {
-      Token &PrevToken = Tokens[I - 1];
-      StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
-      if (TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
-          (TokenBody.empty() && I == 1))
-        NoTextBehind = true;
-    }
-    if (I < Tokens.size() - 1 && Tokens[I + 1].getType() == Token::Type::Text &&
-        RequiresCleanUp) {
-      Token &NextToken = Tokens[I + 1];
-      StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
-      if (TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n"))
-        NoTextAhead = true;
-    }
+  // This loop attempts to find standalone tokens and tries to trim out
+  // the whitespace around them
+  // for example:
+  // if you have the template string
+  //  "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
+  // The output would be
+  //  "Line 1\n Line 2\n Line 3"
+  size_t LastIdx = Tokens.size() - 1;
+  for (size_t Idx = 0, End = Tokens.size(); Idx < End; ++Idx) {
+    Token::Type CurrentType = Tokens[Idx].getType();
+    // Check if token type requires cleanup
+    bool RequiresCleanUp = (CurrentType == Token::Type::SectionOpen ||
+                            CurrentType == Token::Type::InvertSectionOpen ||
+                            CurrentType == Token::Type::SectionClose ||
+                            CurrentType == Token::Type::Comment ||
+                            CurrentType == Token::Type::Partial);
+
+    if (!RequiresCleanUp)
+      continue;
 
-    if ((NoTextBehind && NoTextAhead) || (NoTextAhead && I == 0)) {
-      Token &NextToken = Tokens[I + 1];
+    bool NoTextBehind = noTextBehind(Idx, Tokens);
+    bool NoTextAhead = noTextAhead(Idx, Tokens);
+
+    // Adjust next token body if no text ahead
+    if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0)) {
+      Token &NextToken = Tokens[Idx + 1];
       StringRef NextTokenBody = NextToken.getTokenBody();
-      if (NextTokenBody.starts_with("\r\n"))
+      if (NextTokenBody.starts_with("\r\n")) {
         NextToken.setTokenBody(NextTokenBody.substr(2));
-      else if (NextToken.getTokenBody().starts_with("\n"))
+      } else if (NextTokenBody.starts_with("\n")) {
         NextToken.setTokenBody(NextTokenBody.substr(1));
+      }
     }
-    if ((NoTextBehind && NoTextAhead) ||
-        (NoTextBehind && I == Tokens.size() - 1)) {
-      Token &PrevToken = Tokens[I - 1];
+    // Adjust previous token body if no text behind
+    if ((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx)) {
+      Token &PrevToken = Tokens[Idx - 1];
       StringRef PrevTokenBody = PrevToken.getTokenBody();
       PrevToken.setTokenBody(PrevTokenBody.rtrim(" \t\v\t"));
     }
@@ -165,84 +183,87 @@ std::vector<Token> tokenize(StringRef Template) {
 
 class Parser {
 public:
-  Parser(StringRef TemplateStr) : TemplateStr(TemplateStr) {}
+  Parser(StringRef TemplateStr, BumpPtrAllocator &Allocator)
+      : Allocator(Allocator), TemplateStr(TemplateStr) {}
 
-  std::shared_ptr<ASTNode> parse();
+  ASTNode *parse();
 
 private:
-  void parseMustache(std::shared_ptr<ASTNode> Parent);
+  void parseMustache(ASTNode *Parent);
 
-  std::vector<Token> Tokens;
-  std::size_t CurrentPtr;
+  BumpPtrAllocator &Allocator;
+  SmallVector<Token, 0> Tokens;
+  size_t CurrentPtr;
   StringRef TemplateStr;
 };
 
-std::shared_ptr<ASTNode> Parser::parse() {
+ASTNode *Parser::parse() {
   Tokens = tokenize(TemplateStr);
   CurrentPtr = 0;
-  std::shared_ptr<ASTNode> Root = std::make_shared<ASTNode>();
-  parseMustache(Root);
-  return Root;
+  void *Root = Allocator.Allocate(sizeof(ASTNode), alignof(ASTNode));
+  ASTNode *RootNode = new (Root) ASTNode();
+  parseMustache(RootNode);
+  return RootNode;
 }
 
-void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
+void Parser::parseMustache(ASTNode *Parent) {
 
   while (CurrentPtr < Tokens.size()) {
     Token CurrentToken = Tokens[CurrentPtr];
     CurrentPtr++;
     Accessor A = CurrentToken.getAccessor();
-    std::shared_ptr<ASTNode> CurrentNode;
+    ASTNode *CurrentNode;
+    void *Node = Allocator.Allocate(sizeof(ASTNode), alignof(ASTNode));
 
     switch (CurrentToken.getType()) {
     case Token::Type::Text: {
-      CurrentNode =
-          std::make_shared<ASTNode>(CurrentToken.getTokenBody(), Parent);
+      CurrentNode = new (Node) ASTNode(CurrentToken.getTokenBody(), Parent);
       Parent->addChild(CurrentNode);
       break;
     }
     case Token::Type::Variable: {
-      CurrentNode = std::make_shared<ASTNode>(ASTNode::Variable, A, Parent);
+      CurrentNode = new (Node) ASTNode(ASTNode::Variable, A, Parent);
       Parent->addChild(CurrentNode);
       break;
     }
     case Token::Type::UnescapeVariable: {
-      CurrentNode =
-          std::make_shared<ASTNode>(ASTNode::UnescapeVariable, A, Parent);
+      CurrentNode = new (Node) ASTNode(ASTNode::UnescapeVariable, A, Parent);
       Parent->addChild(CurrentNode);
       break;
     }
     case Token::Type::Partial: {
-      CurrentNode = std::make_shared<ASTNode>(ASTNode::Partial, A, Parent);
+      CurrentNode = new (Node) ASTNode(ASTNode::Partial, A, Parent);
       Parent->addChild(CurrentNode);
       break;
     }
     case Token::Type::SectionOpen: {
-      CurrentNode = std::make_shared<ASTNode>(ASTNode::Section, A, Parent);
-      std::size_t Start = CurrentPtr;
+      CurrentNode = new (Node) ASTNode(ASTNode::Section, A, Parent);
+      size_t Start = CurrentPtr;
       parseMustache(CurrentNode);
-      std::size_t End = CurrentPtr;
+      size_t End = CurrentPtr;
       SmallString<128> RawBody;
-      if (Start + 1 < End - 1)
+      if (Start + 1 < End - 1) {
         for (std::size_t I = Start + 1; I < End - 1; I++)
           RawBody += Tokens[I].getRawBody();
-      else if (Start + 1 == End - 1)
+      } else if (Start + 1 == End - 1) {
         RawBody = Tokens[Start].getRawBody();
+      }
       CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
       break;
     }
     case Token::Type::InvertSectionOpen: {
-      CurrentNode =
-          std::make_shared<ASTNode>(ASTNode::InvertSection, A, Parent);
-      std::size_t Start = CurrentPtr;
+      CurrentNode = new (Node) ASTNode(ASTNode::InvertSection, A, Parent);
+      size_t Start = CurrentPtr;
       parseMustache(CurrentNode);
-      std::size_t End = CurrentPtr;
+      size_t End = CurrentPtr;
       SmallString<128> RawBody;
-      if (Start + 1 < End - 1)
-        for (std::size_t I = Start + 1; I < End - 1; I++)
-          RawBody += Tokens[I].getRawBody();
-      else if (Start + 1 == End - 1)
+      if (Start + 1 < End - 1) {
+        for (size_t Idx = Start + 1; Idx < End - 1; Idx++)
+          RawBody += Tokens[Idx].getRawBody();
+      } else if (Start + 1 == End - 1) {
         RawBody = Tokens[Start].getRawBody();
+      }
       CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
       break;
@@ -255,27 +276,15 @@ void Parser::parseMustache(std::shared_ptr<ASTNode> Parent) {
   }
 }
 
-Template Template::createTemplate(StringRef TemplateStr) {
-  Parser P = Parser(TemplateStr.str());
-  std::shared_ptr<ASTNode> MustacheTree = P.parse();
-  Template T = Template(MustacheTree);
-  // the default behaviour is to escape html entities
-  DenseMap<char, StringRef> HtmlEntities = {{'&', "&"},
-                                            {'<', "<"},
-                                            {'>', ">"},
-                                            {'"', """},
-                                            {'\'', "'"}};
-  T.registerEscape(HtmlEntities);
-  return T;
-}
-
 SmallString<128> Template::render(Value Data) {
-  return Tree->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+  BumpPtrAllocator LocalAllocator;
+  return Tree->render(Data, LocalAllocator, Partials, Lambdas, SectionLambdas,
+                      Escapes);
 }
 
 void Template::registerPartial(StringRef Name, StringRef Partial) {
-  Parser P = Parser(Partial);
-  std::shared_ptr<ASTNode> PartialTree = P.parse();
+  Parser P = Parser(Partial, Allocator);
+  ASTNode *PartialTree = P.parse();
   Partials[Name] = PartialTree;
 }
 
@@ -287,6 +296,18 @@ void Template::registerLambda(StringRef Name, SectionLambda L) {
 
 void Template::registerEscape(DenseMap<char, StringRef> E) { Escapes = E; }
 
+Template::Template(StringRef TemplateStr) {
+  Parser P = Parser(TemplateStr, Allocator);
+  Tree = P.parse();
+  // the default behaviour is to escape html entities
+  DenseMap<char, StringRef> HtmlEntities = {{'&', "&"},
+                                            {'<', "<"},
+                                            {'>', ">"},
+                                            {'"', """},
+                                            {'\'', "'"}};
+  registerEscape(HtmlEntities);
+}
+
 SmallString<128> printJson(Value &Data) {
 
   SmallString<128> Result;
@@ -299,7 +320,7 @@ SmallString<128> printJson(Value &Data) {
     Result += Data.getAsString()->str();
     return Result;
   }
-  return llvm::formatv("{0:2}", Data);
+  return formatv("{0:2}", Data);
 }
 
 bool isFalsey(Value &V) {
@@ -309,8 +330,8 @@ bool isFalsey(Value &V) {
 }
 
 SmallString<128>
-ASTNode::render(Value Data,
-                DenseMap<StringRef, std::shared_ptr<ASTNode>> &Partials,
+ASTNode::render(Value Data, BumpPtrAllocator &Allocator,
+                DenseMap<StringRef, ASTNode *> &Partials,
                 DenseMap<StringRef, Lambda> &Lambdas,
                 DenseMap<StringRef, SectionLambda> &SectionLambdas,
                 DenseMap<char, StringRef> &Escapes) {
@@ -319,18 +340,18 @@ ASTNode::render(Value Data,
   SmallString<128> Result;
   switch (T) {
   case Root: {
-    for (std::shared_ptr<ASTNode> Child : Children)
-      Result +=
-          Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
+    for (ASTNode *Child : Children)
+      Result += Child->render(Context, Allocator, Partials, Lambdas,
+                              SectionLambdas, Escapes);
     return Result;
   }
   case Text:
     return Body;
   case Partial: {
     if (Partials.find(Accessor[0]) != Partials.end()) {
-      std::shared_ptr<ASTNode> Partial = Partials[Accessor[0]];
-      Result +=
-          Partial->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+      ASTNode *Partial = Partials[Accessor[0]];
+      Result += Partial->render(Data, Allocator, Partials, Lambdas,
+                                SectionLambdas, Escapes);
       return Result;
     }
     return Result;
@@ -340,10 +361,10 @@ ASTNode::render(Value Data,
       Lambda &L = Lambdas[Accessor[0]];
       Value LambdaResult = L();
       StringRef LambdaStr = printJson(LambdaResult);
-      Parser P = Parser(LambdaStr);
-      std::shared_ptr<ASTNode> LambdaNode = P.parse();
-      SmallString<128> RenderStr =
-          LambdaNode->render(Data, Partials, Lambdas, SectionLambdas, Escapes);
+      Parser P = Parser(LambdaStr, Allocator);
+      ASTNode *LambdaNode = P.parse();
+      SmallString<128> RenderStr = LambdaNode->render(
+          Data, Allocator, Partials, Lambdas, SectionLambdas, Escapes);
       return escapeString(RenderStr, Escapes);
     }
     return escapeString(printJson(Context), Escapes);
@@ -353,56 +374,51 @@ ASTNode::render(Value Data,
       Lambda &L = Lambdas[Accessor[0]];
       Value LambdaResult = L();
       StringRef LambdaStr = printJson(LambdaResult);
-      Parser P = Parser(LambdaStr);
-      std::shared_ptr<ASTNode> LambdaNode = P.parse();
+      Parser P = Parser(LambdaStr, Allocator);
+      ASTNode *LambdaNode = P.parse();
       DenseMap<char, StringRef> EmptyEscapes;
-      return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
-                                EmptyEscapes);
+      return LambdaNode->render(Data, Allocator, Partials, Lambdas,
+                                SectionLambdas, EmptyEscapes);
     }
     return printJson(Context);
   }
   case Section: {
     // Sections are not rendered if the context is falsey
     bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
-
     if (isFalsey(Context) && !IsLambda)
       return Result;
-
     if (IsLambda) {
       SectionLambda &Lambda = SectionLambdas[Accessor[0]];
       Value Return = Lambda(RawBody);
       if (isFalsey(Return))
         return Result;
       StringRef LambdaStr = printJson(Return);
-      Parser P = Parser(LambdaStr.str());
-      std::shared_ptr<ASTNode> LambdaNode = P.parse();
-      return LambdaNode->render(Data, Partials, Lambdas, SectionLambdas,
-                                Escapes);
+      Parser P = Parser(LambdaStr.str(), Allocator);
+      ASTNode *LambdaNode = P.parse();
+      return LambdaNode->render(Data, Allocator, Partials, Lambdas,
+                                SectionLambdas, Escapes);
     }
-
     if (Context.getAsArray()) {
       json::Array *Arr = Context.getAsArray();
       for (Value &V : *Arr) {
-        for (std::shared_ptr<ASTNode> Child : Children)
-          Result +=
-              Child->render(V, Partials, Lambdas, SectionLambdas, Escapes);
+        for (ASTNode *Child : Children)
+          Result += Child->render(V, Allocator, Partials, Lambdas,
+                                  SectionLambdas, Escapes);
       }
       return Result;
     }
-
-    for (std::shared_ptr<ASTNode> Child : Children)
-      Result +=
-          Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
-
+    for (ASTNode *Child : Children)
+      Result += Child->render(Context, Allocator, Partials, Lambdas,
+                              SectionLambdas, Escapes);
     return Result;
   }
   case InvertSection: {
     bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
     if (!isFalsey(Context) || IsLambda)
       return Result;
-    for (std::shared_ptr<ASTNode> Child : Children)
-      Result +=
-          Child->render(Context, Partials, Lambdas, SectionLambdas, Escapes);
+    for (ASTNode *Child : Children)
+      Result += Child->render(Context, Allocator, Partials, Lambdas,
+                              SectionLambdas, Escapes);
     return Result;
   }
   }
@@ -420,13 +436,13 @@ Value ASTNode::findContext() {
   if (Accessor[0] == ".")
     return LocalContext;
   json::Object *CurrentContext = LocalContext.getAsObject();
-  SmallString<128> CurrentAccessor = Accessor[0];
-  std::weak_ptr<ASTNode> CurrentParent = Parent;
+  StringRef CurrentAccessor = Accessor[0];
+  ASTNode *CurrentParent = Parent;
 
   while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
-    if (auto Ptr = CurrentParent.lock()) {
-      CurrentContext = Ptr->LocalContext.getAsObject();
-      CurrentParent = Ptr->Parent;
+    if (CurrentParent->T != Root) {
+      CurrentContext = CurrentParent->LocalContext.getAsObject();
+      CurrentParent = CurrentParent->Parent;
       continue;
     }
     return nullptr;
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index 3f3f04a9b55b13..d120736be40856 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -22,7 +22,7 @@ using namespace llvm::json;
 TEST(MustacheInterpolation, NoInterpolation) {
   // Mustache-free templates should render as-is.
   Value D = {};
-  auto T = Template::createTemplate("Hello from {Mustache}!\n");
+  auto T = Template("Hello from {Mustache}!\n");
   auto Out = T.render(D);
   EXPECT_EQ("Hello from {Mustache}!\n", Out);
 }
@@ -30,7 +30,7 @@ TEST(MustacheInterpolation, NoInterpolation) {
 TEST(MustacheInterpolation, BasicInterpolation) {
   // Unadorned tags should interpolate content into the template.
   Value D = Object{{"subject", "World"}};
-  auto T = Template::createTemplate("Hello, {{subject}}!");
+  auto T = Template("Hello, {{subject}}!");
   auto Out = T.render(D);
   EXPECT_EQ("Hello, World!", Out);
 }
@@ -38,7 +38,7 @@ TEST(MustacheInterpolation, BasicInterpolation) {
 TEST(MustacheInterpolation, NoReinterpolation) {
   // Interpolated tag output should not be re-interpolated.
   Value D = Object{{"template", "{{planet}}"}, {"planet", "Earth"}};
-  auto T = Template::createTemplate("{{template}}: {{planet}}");
+  auto T = Template("{{template}}: {{planet}}");
   auto Out = T.render(D);
   EXPECT_EQ("{{planet}}: Earth", Out);
 }
@@ -48,8 +48,7 @@ TEST(MustacheInterpolation, HTMLEscaping) {
   Value D = Object{
       {"forbidden", "& \" < >"},
   };
-  auto T = Template::createTemplate(
-      "These characters should be HTML escaped: {{forbidden}}\n");
+  auto T = Template("These characters should be HTML escaped: {{forbidden}}\n");
   auto Out = T.render(D);
   EXPECT_EQ("These characters should be HTML escaped: & " < >\n",
             Out);
@@ -60,8 +59,8 @@ TEST(MustacheInterpolation, Ampersand) {
   Value D = Object{
       {"forbidden", "& \" < >"},
   };
-  auto T = Template::createTemplate(
-      "These characters should not be HTML escaped: {{&forbidden}}\n");
+  auto T =
+      Template("These characters should not be HTML escaped: {{&forbidden}}\n");
   auto Out = T.render(D);
   EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
 }
@@ -69,7 +68,7 @@ TEST(MustacheInterpolation, Ampersand) {
 TEST(MustacheInterpolation, BasicIntegerInterpolation) {
   // Integers should interpolate seamlessly.
   Value D = Object{{"mph", 85}};
-  auto T = Template::createTemplate("{{mph}} miles an hour!");
+  auto T = Template("{{mph}} miles an hour!");
   auto Out = T.render(D);
   EXPECT_EQ("85 miles an hour!", Out);
 }
@@ -77,7 +76,7 @@ TEST(MustacheInterpolation, BasicIntegerInterpolation) {
 TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {
   // Integers should interpolate seamlessly.
   Value D = Object{{"mph", 85}};
-  auto T = Template::createTemplate("{{&mph}} miles an hour!");
+  auto T = Template("{{&mph}} miles an hour!");
   auto Out = T.render(D);
   EXPECT_EQ("85 miles an hour!", Out);
 }
@@ -85,7 +84,7 @@ TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {
 TEST(MustacheInterpolation, BasicDecimalInterpolation) {
   // Decimals should interpolate seamlessly with proper significance.
   Value D = Object{{"power", 1.21}};
-  auto T = Template::createTemplate("{{power}} jiggawatts!");
+  auto T = Template("{{power}} jiggawatts!");
   auto Out = T.render(D);
   EXPECT_EQ("1.21 jiggawatts!", Out);
 }
@@ -93,7 +92,7 @@ TEST(MustacheInterpolation, BasicDecimalInterpolation) {
 TEST(MustacheInterpolation, BasicNullInterpolation) {
   // Nulls should interpolate as the empty string.
   Value D = Object{{"cannot", nullptr}};
-  auto T = Template::createTemplate("I ({{cannot}}) be seen!");
+  auto T = Template("I ({{cannot}}) be seen!");
   auto Out = T.render(D);
   EXPECT_EQ("I () be seen!", Out);
 }
@@ -101,7 +100,7 @@ TEST(MustacheInterpolation, BasicNullInterpolation) {
 TEST(MustacheInterpolation, AmpersandNullInterpolation) {
   // Nulls should interpolate as the empty string.
   Value D = Object{{"cannot", nullptr}};
-  auto T = Template::createTemplate("I ({{&cannot}}) be seen!");
+  auto T = Template("I ({{&cannot}}) be seen!");
   auto Out = T.render(D);
   EXPECT_EQ("I () be seen!", Out);
 }
@@ -109,7 +108,7 @@ TEST(MustacheInterpolation, AmpersandNullInterpolation) {
 TEST(MustacheInterpolation, BasicContextMissInterpolation) {
   // Failed context lookups should default to empty strings.
   Value D = Object{};
-  auto T = Template::createTemplate("I ({{cannot}}) be seen!");
+  auto T = Template("I ({{cannot}}) be seen!");
   auto Out = T.render(D);
   EXPECT_EQ("I () be seen!", Out);
 }
@@ -117,8 +116,7 @@ TEST(MustacheInterpolation, BasicContextMissInterpolation) {
 TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
   // Dotted names should be considered a form of shorthand for sections.
   Value D = Object{{"person", Object{{"name", "Joe"}}}};
-  auto T = Template::createTemplate(
-      "{{person.name}} == {{#person}}{{name}}{{/person}}");
+  auto T = Template("{{person.name}} == {{#person}}{{name}}{{/person}}");
   auto Out = T.render(D);
   EXPECT_EQ("Joe == Joe", Out);
 }
@@ -126,8 +124,7 @@ TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
 TEST(MustacheInterpolation, DottedNamesAmpersandInterpolation) {
   // Dotted names should be considered a form of shorthand for sections.
   Value D = Object{{"person", Object{{"name", "Joe"}}}};
-  auto T = Template::createTemplate(
-      "{{&person.name}} == {{#person}}{{&name}}{{/person}}");
+  auto T = Template("{{&person.name}} == {{#person}}{{&name}}{{/person}}");
   auto Out = T.render(D);
   EXPECT_EQ("Joe == Joe", Out);
 }
@@ -140,7 +137,7 @@ TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
                Object{{"c",
                        Object{{"d",
                                Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
-  auto T = Template::createTemplate("{{a.b.c.d.e.name}}");
+  auto T = Template("{{a.b.c.d.e.name}}");
   auto Out = T.render(D);
   EXPECT_EQ("Phil", Out);
 }
@@ -148,7 +145,7 @@ TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
 TEST(MustacheInterpolation, DottedNamesBrokenChains) {
   // Any falsey value prior to the last part of the name should yield ''.
   Value D = Object{{"a", Object{}}};
-  auto T = Template::createTemplate("{{a.b.c}} == ");
+  auto T = Template("{{a.b.c}} == ");
   auto Out = T.render(D);
   EXPECT_EQ(" == ", Out);
 }
@@ -157,7 +154,7 @@ TEST(MustacheInterpolation, DottedNamesBrokenChainResolution) {
   // Each part of a dotted name should resolve only against its parent.
   Value D =
       Object{{"a", Object{{"b", Object{}}}}, {"c", Object{{"name", "Jim"}}}};
-  auto T = Template::createTemplate("{{a.b.c.name}} == ");
+  auto T = Template("{{a.b.c.name}} == ");
   auto Out = T.render(D);
   EXPECT_EQ(" == ", Out);
 }
@@ -172,7 +169,7 @@ TEST(MustacheInterpolation, DottedNamesInitialResolution) {
                     Object{{"d", Object{{"e", Object{{"name", "Phil"}}}}}}}}}}},
       {"b",
        Object{{"c", Object{{"d", Object{{"e", Object{{"name", "Wrong"}}}}}}}}}};
-  auto T = Template::createTemplate("{{#a}}{{b.c.d.e.name}}{{/a}}");
+  auto T = Template("{{#a}}{{b.c.d.e.name}}{{/a}}");
   auto Out = T.render(D);
   EXPECT_EQ("Phil", Out);
 }
@@ -181,7 +178,7 @@ TEST(MustacheInterpolation, DottedNamesContextPrecedence) {
   // Dotted names should be resolved against former resolutions.
   Value D =
       Object{{"a", Object{{"b", Object{}}}}, {"b", Object{{"c", "ERROR"}}}};
-  auto T = Template::createTemplate("{{#a}}{{b.c}}{{/a}}");
+  auto T = Template("{{#a}}{{b.c}}{{/a}}");
   auto Out = T.render(D);
   EXPECT_EQ("", Out);
 }
@@ -189,7 +186,7 @@ TEST(MustacheInterpolation, DottedNamesContextPrecedence) {
 TEST(MustacheInterpolation, DottedNamesAreNotSingleKeys) {
   // Dotted names shall not be parsed as single, atomic keys
   Value D = Object{{"a.b", "c"}};
-  auto T = Template::createTemplate("{{a.b}}");
+  auto T = Template("{{a.b}}");
   auto Out = T.render(D);
   EXPECT_EQ("", Out);
 }
@@ -197,7 +194,7 @@ TEST(MustacheInterpolation, DottedNamesAreNotSingleKeys) {
 TEST(MustacheInterpolation, DottedNamesNoMasking) {
   // Dotted Names in a given context are unavailable due to dot splitting
   Value D = Object{{"a.b", "c"}, {"a", Object{{"b", "d"}}}};
-  auto T = Template::createTemplate("{{a.b}}");
+  auto T = Template("{{a.b}}");
   auto Out = T.render(D);
   EXPECT_EQ("d", Out);
 }
@@ -205,7 +202,7 @@ TEST(MustacheInterpolation, DottedNamesNoMasking) {
 TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
   // Unadorned tags should interpolate content into the template.
   Value D = "world";
-  auto T = Template::createTemplate("Hello, {{.}}!\n");
+  auto T = Template("Hello, {{.}}!\n");
   auto Out = T.render(D);
   EXPECT_EQ("Hello, world!\n", Out);
 }
@@ -213,8 +210,7 @@ TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
 TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {
   // Basic interpolation should be HTML escaped.
   Value D = "& \" < >";
-  auto T = Template::createTemplate(
-      "These characters should not be HTML escaped: {{&.}}\n");
+  auto T = Template("These characters should not be HTML escaped: {{&.}}\n");
   auto Out = T.render(D);
   EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
 }
@@ -222,7 +218,7 @@ TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {
 TEST(MustacheInterpolation, ImplicitIteratorsInteger) {
   // Integers should interpolate seamlessly.
   Value D = 85;
-  auto T = Template::createTemplate("{{.}} miles an hour!\n");
+  auto T = Template("{{.}} miles an hour!\n");
   auto Out = T.render(D);
   EXPECT_EQ("85 miles an hour!\n", Out);
 }
@@ -230,7 +226,7 @@ TEST(MustacheInterpolation, ImplicitIteratorsInteger) {
 TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
   // Interpolation should not alter surrounding whitespace.
   Value D = Object{{"string", "---"}};
-  auto T = Template::createTemplate("| {{string}} |");
+  auto T = Template("| {{string}} |");
   auto Out = T.render(D);
   EXPECT_EQ("| --- |", Out);
 }
@@ -238,7 +234,7 @@ TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
 TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {
   // Interpolation should not alter surrounding whitespace.
   Value D = Object{{"string", "---"}};
-  auto T = Template::createTemplate("| {{&string}} |");
+  auto T = Template("| {{&string}} |");
   auto Out = T.render(D);
   EXPECT_EQ("| --- |", Out);
 }
@@ -246,7 +242,7 @@ TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {
 TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {
   // Standalone interpolation should not alter surrounding whitespace.
   Value D = Object{{"string", "---"}};
-  auto T = Template::createTemplate("  {{string}}\n");
+  auto T = Template("  {{string}}\n");
   auto Out = T.render(D);
   EXPECT_EQ("  ---\n", Out);
 }
@@ -254,7 +250,7 @@ TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {
 TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {
   // Standalone interpolation should not alter surrounding whitespace.
   Value D = Object{{"string", "---"}};
-  auto T = Template::createTemplate("  {{&string}}\n");
+  auto T = Template("  {{&string}}\n");
   auto Out = T.render(D);
   EXPECT_EQ("  ---\n", Out);
 }
@@ -262,7 +258,7 @@ TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {
 TEST(MustacheInterpolation, InterpolationWithPadding) {
   // Superfluous in-tag whitespace should be ignored.
   Value D = Object{{"string", "---"}};
-  auto T = Template::createTemplate("|{{ string }}|");
+  auto T = Template("|{{ string }}|");
   auto Out = T.render(D);
   EXPECT_EQ("|---|", Out);
 }
@@ -270,7 +266,7 @@ TEST(MustacheInterpolation, InterpolationWithPadding) {
 TEST(MustacheInterpolation, AmpersandWithPadding) {
   // Superfluous in-tag whitespace should be ignored.
   Value D = Object{{"string", "---"}};
-  auto T = Template::createTemplate("|{{& string }}|");
+  auto T = Template("|{{& string }}|");
   auto Out = T.render(D);
   EXPECT_EQ("|---|", Out);
 }
@@ -278,38 +274,35 @@ TEST(MustacheInterpolation, AmpersandWithPadding) {
 TEST(MustacheInterpolation, InterpolationWithPaddingAndNewlines) {
   // Superfluous in-tag whitespace should be ignored.
   Value D = Object{{"string", "---"}};
-  auto T = Template::createTemplate("|{{ string \n\n\n }}|");
+  auto T = Template("|{{ string \n\n\n }}|");
   auto Out = T.render(D);
   EXPECT_EQ("|---|", Out);
 }
 
 TEST(MustacheSections, Truthy) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate(
-      "{{#boolean}}This should be rendered.{{/boolean}}");
+  auto T = Template("{{#boolean}}This should be rendered.{{/boolean}}");
   auto Out = T.render(D);
   EXPECT_EQ("This should be rendered.", Out);
 }
 
 TEST(MustacheSections, Falsey) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate(
-      "{{#boolean}}This should not be rendered.{{/boolean}}");
+  auto T = Template("{{#boolean}}This should not be rendered.{{/boolean}}");
   auto Out = T.render(D);
   EXPECT_EQ("", Out);
 }
 
 TEST(MustacheSections, NullIsFalsey) {
   Value D = Object{{"null", nullptr}};
-  auto T = Template::createTemplate(
-      "{{#null}}This should not be rendered.{{/null}}");
+  auto T = Template("{{#null}}This should not be rendered.{{/null}}");
   auto Out = T.render(D);
   EXPECT_EQ("", Out);
 }
 
 TEST(MustacheSections, Context) {
   Value D = Object{{"context", Object{{"name", "Joe"}}}};
-  auto T = Template::createTemplate("{{#context}}Hi {{name}}.{{/context}}");
+  auto T = Template("{{#context}}Hi {{name}}.{{/context}}");
   auto Out = T.render(D);
   EXPECT_EQ("Hi Joe.", Out);
 }
@@ -319,14 +312,14 @@ TEST(MustacheSections, ParentContexts) {
                    {"b", "wrong"},
                    {"sec", Object{{"b", "bar"}}},
                    {"c", Object{{"d", "baz"}}}};
-  auto T = Template::createTemplate("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}");
+  auto T = Template("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}");
   auto Out = T.render(D);
   EXPECT_EQ("foo, bar, baz", Out);
 }
 
 TEST(MustacheSections, VariableTest) {
   Value D = Object{{"foo", "bar"}};
-  auto T = Template::createTemplate("{{#foo}}{{.}} is {{foo}}{{/foo}}");
+  auto T = Template("{{#foo}}{{.}} is {{foo}}{{/foo}}");
   auto Out = T.render(D);
   EXPECT_EQ("bar is bar", Out);
 }
@@ -340,14 +333,14 @@ TEST(MustacheSections, ListContexts) {
             Array{Object{{"mname", "1"},
                          {"bottoms", Array{Object{{"bname", "x"}},
                                            Object{{"bname", "y"}}}}}}}}}}};
-  auto T = Template::createTemplate("{{#tops}}"
-                                    "{{#middles}}"
-                                    "{{tname.lower}}{{mname}}."
-                                    "{{#bottoms}}"
-                                    "{{tname.upper}}{{mname}}{{bname}}."
-                                    "{{/bottoms}}"
-                                    "{{/middles}}"
-                                    "{{/tops}}");
+  auto T = Template("{{#tops}}"
+                    "{{#middles}}"
+                    "{{tname.lower}}{{mname}}."
+                    "{{#bottoms}}"
+                    "{{tname.upper}}{{mname}}{{bname}}."
+                    "{{/bottoms}}"
+                    "{{/middles}}"
+                    "{{/tops}}");
   auto Out = T.render(D);
   EXPECT_EQ("a1.A1x.A1y.", Out);
 }
@@ -357,7 +350,7 @@ TEST(MustacheSections, DeeplyNestedContexts) {
       {"a", Object{{"one", 1}}},
       {"b", Object{{"two", 2}}},
       {"c", Object{{"three", 3}, {"d", Object{{"four", 4}, {"five", 5}}}}}};
-  auto T = Template::createTemplate(
+  auto T = Template(
       "{{#a}}\n{{one}}\n{{#b}}\n{{one}}{{two}}{{one}}\n{{#c}}\n{{one}}{{two}}{{"
       "three}}{{two}}{{one}}\n{{#d}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{"
       "{two}}{{one}}\n{{#five}}\n{{one}}{{two}}{{three}}{{four}}{{five}}{{four}"
@@ -376,123 +369,120 @@ TEST(MustacheSections, DeeplyNestedContexts) {
 TEST(MustacheSections, List) {
   Value D = Object{{"list", Array{Object{{"item", 1}}, Object{{"item", 2}},
                                   Object{{"item", 3}}}}};
-  auto T = Template::createTemplate("{{#list}}{{item}}{{/list}}");
+  auto T = Template("{{#list}}{{item}}{{/list}}");
   auto Out = T.render(D);
   EXPECT_EQ("123", Out);
 }
 
 TEST(MustacheSections, EmptyList) {
   Value D = Object{{"list", Array{}}};
-  auto T = Template::createTemplate("{{#list}}Yay lists!{{/list}}");
+  auto T = Template("{{#list}}Yay lists!{{/list}}");
   auto Out = T.render(D);
   EXPECT_EQ("", Out);
 }
 
 TEST(MustacheSections, Doubled) {
   Value D = Object{{"bool", true}, {"two", "second"}};
-  auto T = Template::createTemplate("{{#bool}}\n* first\n{{/bool}}\n* "
-                                    "{{two}}\n{{#bool}}\n* third\n{{/bool}}\n");
+  auto T = Template("{{#bool}}\n* first\n{{/bool}}\n* "
+                    "{{two}}\n{{#bool}}\n* third\n{{/bool}}\n");
   auto Out = T.render(D);
   EXPECT_EQ("* first\n* second\n* third\n", Out);
 }
 
 TEST(MustacheSections, NestedTruthy) {
   Value D = Object{{"bool", true}};
-  auto T = Template::createTemplate(
-      "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
+  auto T = Template("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
   auto Out = T.render(D);
   EXPECT_EQ("| A B C D E |", Out);
 }
 
 TEST(MustacheSections, NestedFalsey) {
   Value D = Object{{"bool", false}};
-  auto T = Template::createTemplate(
-      "| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
+  auto T = Template("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
   auto Out = T.render(D);
   EXPECT_EQ("| A  E |", Out);
 }
 
 TEST(MustacheSections, ContextMisses) {
   Value D = Object{};
-  auto T = Template::createTemplate(
-      "[{{#missing}}Found key 'missing'!{{/missing}}]");
+  auto T = Template("[{{#missing}}Found key 'missing'!{{/missing}}]");
   auto Out = T.render(D);
   EXPECT_EQ("[]", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorString) {
   Value D = Object{{"list", Array{"a", "b", "c", "d", "e"}}};
-  auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+  auto T = Template("{{#list}}({{.}}){{/list}}");
   auto Out = T.render(D);
   EXPECT_EQ("(a)(b)(c)(d)(e)", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorInteger) {
   Value D = Object{{"list", Array{1, 2, 3, 4, 5}}};
-  auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+  auto T = Template("{{#list}}({{.}}){{/list}}");
   auto Out = T.render(D);
   EXPECT_EQ("(1)(2)(3)(4)(5)", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorArray) {
   Value D = Object{{"list", Array{Array{1, 2, 3}, Array{"a", "b", "c"}}}};
-  auto T = Template::createTemplate("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}");
+  auto T = Template("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}");
   auto Out = T.render(D);
   EXPECT_EQ("(123)(abc)", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorHTMLEscaping) {
   Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
-  auto T = Template::createTemplate("{{#list}}({{.}}){{/list}}");
+  auto T = Template("{{#list}}({{.}}){{/list}}");
   auto Out = T.render(D);
   EXPECT_EQ("(&)(")(<)(>)", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorAmpersand) {
   Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
-  auto T = Template::createTemplate("{{#list}}({{&.}}){{/list}}");
+  auto T = Template("{{#list}}({{&.}}){{/list}}");
   auto Out = T.render(D);
   EXPECT_EQ("(&)(\")(<)(>)", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorRootLevel) {
   Value D = Array{Object{{"value", "a"}}, Object{{"value", "b"}}};
-  auto T = Template::createTemplate("{{#.}}({{value}}){{/.}}");
+  auto T = Template("{{#.}}({{value}}){{/.}}");
   auto Out = T.render(D);
   EXPECT_EQ("(a)(b)", Out);
 }
 
 TEST(MustacheSections, DottedNamesTruthy) {
   Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
-  auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == Here");
+  auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == Here");
   auto Out = T.render(D);
   EXPECT_EQ("Here == Here", Out);
 }
 
 TEST(MustacheSections, DottedNamesFalsey) {
   Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
-  auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == ");
+  auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == ");
   auto Out = T.render(D);
   EXPECT_EQ(" == ", Out);
 }
 
 TEST(MustacheSections, DottedNamesBrokenChains) {
   Value D = Object{{"a", Object{}}};
-  auto T = Template::createTemplate("{{#a.b.c}}Here{{/a.b.c}} == ");
+  auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == ");
   auto Out = T.render(D);
   EXPECT_EQ(" == ", Out);
 }
 
 TEST(MustacheSections, SurroundingWhitespace) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate(" | {{#boolean}}\t|\t{{/boolean}} | \n");
+  auto T = Template(" | {{#boolean}}\t|\t{{/boolean}} | \n");
   auto Out = T.render(D);
   EXPECT_EQ(" | \t|\t | \n", Out);
 }
 
 TEST(MustacheSections, InternalWhitespace) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate(
+  auto T = Template(
       " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
   auto Out = T.render(D);
   EXPECT_EQ(" |  \n  | \n", Out);
@@ -500,83 +490,78 @@ TEST(MustacheSections, InternalWhitespace) {
 
 TEST(MustacheSections, IndentedInlineSections) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate(
-      " {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n");
+  auto T =
+      Template(" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n");
   auto Out = T.render(D);
   EXPECT_EQ(" YES\n GOOD\n", Out);
 }
 
 TEST(MustacheSections, StandaloneLines) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate(
-      "| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n");
+  auto T = Template("| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n");
   auto Out = T.render(D);
   EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
 }
 
 TEST(MustacheSections, IndentedStandaloneLines) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate(
-      "| This Is\n  {{#boolean}}\n|\n  {{/boolean}}\n| A Line\n");
+  auto T = Template("| This Is\n  {{#boolean}}\n|\n  {{/boolean}}\n| A Line\n");
   auto Out = T.render(D);
   EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
 }
 
 TEST(MustacheSections, StandaloneLineEndings) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|");
+  auto T = Template("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|");
   auto Out = T.render(D);
   EXPECT_EQ("|\r\n|", Out);
 }
 
 TEST(MustacheSections, StandaloneWithoutPreviousLine) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate("  {{#boolean}}\n#{{/boolean}}\n/");
+  auto T = Template("  {{#boolean}}\n#{{/boolean}}\n/");
   auto Out = T.render(D);
   EXPECT_EQ("#\n/", Out);
 }
 
 TEST(MustacheSections, StandaloneWithoutNewline) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate("#{{#boolean}}\n/\n  {{/boolean}}");
+  auto T = Template("#{{#boolean}}\n/\n  {{/boolean}}");
   auto Out = T.render(D);
   EXPECT_EQ("#\n/\n", Out);
 }
 
 TEST(MustacheSections, Padding) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate("|{{# boolean }}={{/ boolean }}|");
+  auto T = Template("|{{# boolean }}={{/ boolean }}|");
   auto Out = T.render(D);
   EXPECT_EQ("|=|", Out);
 }
 
 TEST(MustacheInvertedSections, Falsey) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate(
-      "{{^boolean}}This should be rendered.{{/boolean}}");
+  auto T = Template("{{^boolean}}This should be rendered.{{/boolean}}");
   auto Out = T.render(D);
   EXPECT_EQ("This should be rendered.", Out);
 }
 
 TEST(MustacheInvertedSections, Truthy) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate(
-      "{{^boolean}}This should not be rendered.{{/boolean}}");
+  auto T = Template("{{^boolean}}This should not be rendered.{{/boolean}}");
   auto Out = T.render(D);
   EXPECT_EQ("", Out);
 }
 
 TEST(MustacheInvertedSections, NullIsFalsey) {
   Value D = Object{{"null", nullptr}};
-  auto T =
-      Template::createTemplate("{{^null}}This should be rendered.{{/null}}");
+  auto T = Template("{{^null}}This should be rendered.{{/null}}");
   auto Out = T.render(D);
   EXPECT_EQ("This should be rendered.", Out);
 }
 
 TEST(MustacheInvertedSections, Context) {
   Value D = Object{{"context", Object{{"name", "Joe"}}}};
-  auto T = Template::createTemplate("{{^context}}Hi {{name}}.{{/context}}");
+  auto T = Template("{{^context}}Hi {{name}}.{{/context}}");
   auto Out = T.render(D);
   EXPECT_EQ("", Out);
 }
@@ -584,81 +569,78 @@ TEST(MustacheInvertedSections, Context) {
 TEST(MustacheInvertedSections, List) {
   Value D = Object{
       {"list", Array{Object{{"n", 1}}, Object{{"n", 2}}, Object{{"n", 3}}}}};
-  auto T = Template::createTemplate("{{^list}}{{n}}{{/list}}");
+  auto T = Template("{{^list}}{{n}}{{/list}}");
   auto Out = T.render(D);
   EXPECT_EQ("", Out);
 }
 
 TEST(MustacheInvertedSections, EmptyList) {
   Value D = Object{{"list", Array{}}};
-  auto T = Template::createTemplate("{{^list}}Yay lists!{{/list}}");
+  auto T = Template("{{^list}}Yay lists!{{/list}}");
   auto Out = T.render(D);
   EXPECT_EQ("Yay lists!", Out);
 }
 
 TEST(MustacheInvertedSections, Doubled) {
   Value D = Object{{"bool", false}, {"two", "second"}};
-  auto T = Template::createTemplate("{{^bool}}\n* first\n{{/bool}}\n* "
-                                    "{{two}}\n{{^bool}}\n* third\n{{/bool}}\n");
+  auto T = Template("{{^bool}}\n* first\n{{/bool}}\n* "
+                    "{{two}}\n{{^bool}}\n* third\n{{/bool}}\n");
   auto Out = T.render(D);
   EXPECT_EQ("* first\n* second\n* third\n", Out);
 }
 
 TEST(MustacheInvertedSections, NestedFalsey) {
   Value D = Object{{"bool", false}};
-  auto T = Template::createTemplate(
-      "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
+  auto T = Template("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
   auto Out = T.render(D);
   EXPECT_EQ("| A B C D E |", Out);
 }
 
 TEST(MustacheInvertedSections, NestedTruthy) {
   Value D = Object{{"bool", true}};
-  auto T = Template::createTemplate(
-      "| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
+  auto T = Template("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
   auto Out = T.render(D);
   EXPECT_EQ("| A  E |", Out);
 }
 
 TEST(MustacheInvertedSections, ContextMisses) {
   Value D = Object{};
-  auto T = Template::createTemplate(
-      "[{{^missing}}Cannot find key 'missing'!{{/missing}}]");
+  auto T = Template("[{{^missing}}Cannot find key 'missing'!{{/missing}}]");
   auto Out = T.render(D);
   EXPECT_EQ("[Cannot find key 'missing'!]", Out);
 }
 
 TEST(MustacheInvertedSections, DottedNamesTruthy) {
   Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
-  auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == ");
+  auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == ");
   auto Out = T.render(D);
   EXPECT_EQ(" == ", Out);
 }
 
 TEST(MustacheInvertedSections, DottedNamesFalsey) {
   Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
-  auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
+  auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
   auto Out = T.render(D);
   EXPECT_EQ("Not Here == Not Here", Out);
 }
 
 TEST(MustacheInvertedSections, DottedNamesBrokenChains) {
   Value D = Object{{"a", Object{}}};
-  auto T = Template::createTemplate("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
+  auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
   auto Out = T.render(D);
   EXPECT_EQ("Not Here == Not Here", Out);
 }
 
 TEST(MustacheInvertedSections, SurroundingWhitespace) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate(" | {{^boolean}}\t|\t{{/boolean}} | \n");
+  auto T = Template(" | {{^boolean}}\t|\t{{/boolean}} | \n");
   auto Out = T.render(D);
   EXPECT_EQ(" | \t|\t | \n", Out);
 }
 
 TEST(MustacheInvertedSections, InternalWhitespace) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate(
+  auto T = Template(
       " | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
   auto Out = T.render(D);
   EXPECT_EQ(" |  \n  | \n", Out);
@@ -666,59 +648,57 @@ TEST(MustacheInvertedSections, InternalWhitespace) {
 
 TEST(MustacheInvertedSections, IndentedInlineSections) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate(
-      " {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n");
+  auto T =
+      Template(" {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n");
   auto Out = T.render(D);
   EXPECT_EQ(" NO\n WAY\n", Out);
 }
 
 TEST(MustacheInvertedSections, StandaloneLines) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate(
-      "| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n");
+  auto T = Template("| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n");
   auto Out = T.render(D);
   EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
 }
 
 TEST(MustacheInvertedSections, StandaloneIndentedLines) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate(
-      "| This Is\n  {{^boolean}}\n|\n  {{/boolean}}\n| A Line\n");
+  auto T = Template("| This Is\n  {{^boolean}}\n|\n  {{/boolean}}\n| A Line\n");
   auto Out = T.render(D);
   EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
 }
 
 TEST(MustacheInvertedSections, StandaloneLineEndings) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|");
+  auto T = Template("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|");
   auto Out = T.render(D);
   EXPECT_EQ("|\r\n|", Out);
 }
 
 TEST(MustacheInvertedSections, StandaloneWithoutPreviousLine) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate("  {{^boolean}}\n^{{/boolean}}\n/");
+  auto T = Template("  {{^boolean}}\n^{{/boolean}}\n/");
   auto Out = T.render(D);
   EXPECT_EQ("^\n/", Out);
 }
 
 TEST(MustacheInvertedSections, StandaloneWithoutNewline) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate("^{{^boolean}}\n/\n  {{/boolean}}");
+  auto T = Template("^{{^boolean}}\n/\n  {{/boolean}}");
   auto Out = T.render(D);
   EXPECT_EQ("^\n/\n", Out);
 }
 
 TEST(MustacheInvertedSections, Padding) {
   Value D = Object{{"boolean", false}};
-  auto T = Template::createTemplate("|{{^ boolean }}={{/ boolean }}|");
+  auto T = Template("|{{^ boolean }}={{/ boolean }}|");
   auto Out = T.render(D);
   EXPECT_EQ("|=|", Out);
 }
 
 TEST(MustachePartials, BasicBehavior) {
   Value D = Object{};
-  auto T = Template::createTemplate("{{>text}}");
+  auto T = Template("{{>text}}");
   T.registerPartial("text", "from partial");
   auto Out = T.render(D);
   EXPECT_EQ("from partial", Out);
@@ -726,14 +706,14 @@ TEST(MustachePartials, BasicBehavior) {
 
 TEST(MustachePartials, FailedLookup) {
   Value D = Object{};
-  auto T = Template::createTemplate("{{>text}}");
+  auto T = Template("{{>text}}");
   auto Out = T.render(D);
   EXPECT_EQ("", Out);
 }
 
 TEST(MustachePartials, Context) {
   Value D = Object{{"text", "content"}};
-  auto T = Template::createTemplate("{{>partial}}");
+  auto T = Template("{{>partial}}");
   T.registerPartial("partial", "*{{text}}*");
   auto Out = T.render(D);
   EXPECT_EQ("*content*", Out);
@@ -743,7 +723,7 @@ TEST(MustachePartials, Recursion) {
   Value D =
       Object{{"content", "X"},
              {"nodes", Array{Object{{"content", "Y"}, {"nodes", Array{}}}}}};
-  auto T = Template::createTemplate("{{>node}}");
+  auto T = Template("{{>node}}");
   T.registerPartial("node", "{{content}}({{#nodes}}{{>node}}{{/nodes}})");
   auto Out = T.render(D);
   EXPECT_EQ("X(Y())", Out);
@@ -751,7 +731,7 @@ TEST(MustachePartials, Recursion) {
 
 TEST(MustachePartials, Nested) {
   Value D = Object{{"a", "hello"}, {"b", "world"}};
-  auto T = Template::createTemplate("{{>outer}}");
+  auto T = Template("{{>outer}}");
   T.registerPartial("outer", "*{{a}} {{>inner}}*");
   T.registerPartial("inner", "{{b}}!");
   auto Out = T.render(D);
@@ -760,7 +740,7 @@ TEST(MustachePartials, Nested) {
 
 TEST(MustachePartials, SurroundingWhitespace) {
   Value D = Object{};
-  auto T = Template::createTemplate("| {{>partial}} |");
+  auto T = Template("| {{>partial}} |");
   T.registerPartial("partial", "\t|\t");
   auto Out = T.render(D);
   EXPECT_EQ("| \t|\t |", Out);
@@ -768,7 +748,7 @@ TEST(MustachePartials, SurroundingWhitespace) {
 
 TEST(MustachePartials, InlineIndentation) {
   Value D = Object{{"data", "|"}};
-  auto T = Template::createTemplate("  {{data}}  {{> partial}}\n");
+  auto T = Template("  {{data}}  {{> partial}}\n");
   T.registerPartial("partial", "<\n<");
   auto Out = T.render(D);
   EXPECT_EQ("  |  <\n<\n", Out);
@@ -776,7 +756,7 @@ TEST(MustachePartials, InlineIndentation) {
 
 TEST(MustachePartials, PaddingWhitespace) {
   Value D = Object{{"boolean", true}};
-  auto T = Template::createTemplate("|{{> partial }}|");
+  auto T = Template("|{{> partial }}|");
   T.registerPartial("partial", "[]");
   auto Out = T.render(D);
   EXPECT_EQ("|[]|", Out);
@@ -784,7 +764,7 @@ TEST(MustachePartials, PaddingWhitespace) {
 
 TEST(MustacheLambdas, BasicInterpolation) {
   Value D = Object{};
-  auto T = Template::createTemplate("Hello, {{lambda}}!");
+  auto T = Template("Hello, {{lambda}}!");
   Lambda L = []() -> llvm::json::Value { return "World"; };
   T.registerLambda("lambda", L);
   auto Out = T.render(D);
@@ -793,7 +773,7 @@ TEST(MustacheLambdas, BasicInterpolation) {
 
 TEST(MustacheLambdas, InterpolationExpansion) {
   Value D = Object{{"planet", "World"}};
-  auto T = Template::createTemplate("Hello, {{lambda}}!");
+  auto T = Template("Hello, {{lambda}}!");
   Lambda L = []() -> llvm::json::Value { return "{{planet}}"; };
   T.registerLambda("lambda", L);
   auto Out = T.render(D);
@@ -802,7 +782,7 @@ TEST(MustacheLambdas, InterpolationExpansion) {
 
 TEST(MustacheLambdas, BasicMultipleCalls) {
   Value D = Object{};
-  auto T = Template::createTemplate("{{lambda}} == {{lambda}} == {{lambda}}");
+  auto T = Template("{{lambda}} == {{lambda}} == {{lambda}}");
   int I = 0;
   Lambda L = [&I]() -> llvm::json::Value {
     I += 1;
@@ -815,7 +795,7 @@ TEST(MustacheLambdas, BasicMultipleCalls) {
 
 TEST(MustacheLambdas, Escaping) {
   Value D = Object{};
-  auto T = Template::createTemplate("<{{lambda}}{{&lambda}}");
+  auto T = Template("<{{lambda}}{{&lambda}}");
   Lambda L = []() -> llvm::json::Value { return ">"; };
   T.registerLambda("lambda", L);
   auto Out = T.render(D);
@@ -824,7 +804,7 @@ TEST(MustacheLambdas, Escaping) {
 
 TEST(MustacheLambdas, Sections) {
   Value D = Object{};
-  auto T = Template::createTemplate("<{{#lambda}}{{x}}{{/lambda}}>");
+  auto T = Template("<{{#lambda}}{{x}}{{/lambda}}>");
   SectionLambda L = [](StringRef Text) -> llvm::json::Value {
     if (Text == "{{x}}") {
       return "yes";
@@ -840,7 +820,7 @@ TEST(MustacheLambdas, SectionExpansion) {
   Value D = Object{
       {"planet", "Earth"},
   };
-  auto T = Template::createTemplate("<{{#lambda}}-{{/lambda}}>");
+  auto T = Template("<{{#lambda}}-{{/lambda}}>");
   SectionLambda L = [](StringRef Text) -> llvm::json::Value {
     SmallString<128> Result;
     Result += Text;
@@ -855,8 +835,7 @@ TEST(MustacheLambdas, SectionExpansion) {
 
 TEST(MustacheLambdas, SectionsMultipleCalls) {
   Value D = Object{};
-  auto T = Template::createTemplate(
-      "{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}");
+  auto T = Template("{{#lambda}}FILE{{/lambda}} != {{#lambda}}LINE{{/lambda}}");
   SectionLambda L = [](StringRef Text) -> llvm::json::Value {
     SmallString<128> Result;
     Result += "__";
@@ -871,7 +850,7 @@ TEST(MustacheLambdas, SectionsMultipleCalls) {
 
 TEST(MustacheLambdas, InvertedSections) {
   Value D = Object{{"static", "static"}};
-  auto T = Template::createTemplate("<{{^lambda}}{{static}}{{/lambda}}>");
+  auto T = Template("<{{^lambda}}{{static}}{{/lambda}}>");
   SectionLambda L = [](StringRef Text) -> llvm::json::Value { return false; };
   T.registerLambda("lambda", L);
   auto Out = T.render(D);
@@ -881,7 +860,7 @@ TEST(MustacheLambdas, InvertedSections) {
 TEST(MustacheComments, Inline) {
   // Comment blocks should be removed from the template.
   Value D = {};
-  auto T = Template::createTemplate("12345{{! Comment Block! }}67890");
+  auto T = Template("12345{{! Comment Block! }}67890");
   auto Out = T.render(D);
   EXPECT_EQ("1234567890", Out);
 }
@@ -889,8 +868,8 @@ TEST(MustacheComments, Inline) {
 TEST(MustacheComments, Multiline) {
   // Multiline comments should be permitted.
   Value D = {};
-  auto T = Template::createTemplate(
-      "12345{{!\n  This is a\n  multi-line comment...\n}}67890\n");
+  auto T =
+      Template("12345{{!\n  This is a\n  multi-line comment...\n}}67890\n");
   auto Out = T.render(D);
   EXPECT_EQ("1234567890\n", Out);
 }
@@ -898,7 +877,7 @@ TEST(MustacheComments, Multiline) {
 TEST(MustacheComments, Standalone) {
   // All standalone comment lines should be removed.
   Value D = {};
-  auto T = Template::createTemplate("Begin.\n{{! Comment Block! }}\nEnd.\n");
+  auto T = Template("Begin.\n{{! Comment Block! }}\nEnd.\n");
   auto Out = T.render(D);
   EXPECT_EQ("Begin.\nEnd.\n", Out);
 }
@@ -906,8 +885,7 @@ TEST(MustacheComments, Standalone) {
 TEST(MustacheComments, IndentedStandalone) {
   // All standalone comment lines should be removed.
   Value D = {};
-  auto T = Template::createTemplate(
-      "Begin.\n  {{! Indented Comment Block! }}\nEnd.\n");
+  auto T = Template("Begin.\n  {{! Indented Comment Block! }}\nEnd.\n");
   auto Out = T.render(D);
   EXPECT_EQ("Begin.\nEnd.\n", Out);
 }
@@ -915,7 +893,7 @@ TEST(MustacheComments, IndentedStandalone) {
 TEST(MustacheComments, StandaloneLineEndings) {
   // "\r\n" should be considered a newline for standalone tags.
   Value D = {};
-  auto T = Template::createTemplate("|\r\n{{! Standalone Comment }}\r\n|");
+  auto T = Template("|\r\n{{! Standalone Comment }}\r\n|");
   auto Out = T.render(D);
   EXPECT_EQ("|\r\n|", Out);
 }
@@ -923,7 +901,7 @@ TEST(MustacheComments, StandaloneLineEndings) {
 TEST(MustacheComments, StandaloneWithoutPreviousLine) {
   // Standalone tags should not require a newline to precede them.
   Value D = {};
-  auto T = Template::createTemplate("  {{! I'm Still Standalone }}\n!");
+  auto T = Template("  {{! I'm Still Standalone }}\n!");
   auto Out = T.render(D);
   EXPECT_EQ("!", Out);
 }
@@ -931,7 +909,7 @@ TEST(MustacheComments, StandaloneWithoutPreviousLine) {
 TEST(MustacheComments, StandaloneWithoutNewline) {
   // Standalone tags should not require a newline to follow them.
   Value D = {};
-  auto T = Template::createTemplate("!\n  {{! I'm Still Standalone }}");
+  auto T = Template("!\n  {{! I'm Still Standalone }}");
   auto Out = T.render(D);
   EXPECT_EQ("!\n", Out);
 }
@@ -939,8 +917,7 @@ TEST(MustacheComments, StandaloneWithoutNewline) {
 TEST(MustacheComments, MultilineStandalone) {
   // All standalone comment lines should be removed.
   Value D = {};
-  auto T = Template::createTemplate(
-      "Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n");
+  auto T = Template("Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n");
   auto Out = T.render(D);
   EXPECT_EQ("Begin.\nEnd.\n", Out);
 }
@@ -948,8 +925,8 @@ TEST(MustacheComments, MultilineStandalone) {
 TEST(MustacheComments, IndentedMultilineStandalone) {
   // All standalone comment lines should be removed.
   Value D = {};
-  auto T = Template::createTemplate(
-      "Begin.\n  {{!\n    Something's going on here...\n  }}\nEnd.\n");
+  auto T =
+      Template("Begin.\n  {{!\n    Something's going on here...\n  }}\nEnd.\n");
   auto Out = T.render(D);
   EXPECT_EQ("Begin.\nEnd.\n", Out);
 }
@@ -957,7 +934,7 @@ TEST(MustacheComments, IndentedMultilineStandalone) {
 TEST(MustacheComments, IndentedInline) {
   // Inline comments should not strip whitespace.
   Value D = {};
-  auto T = Template::createTemplate("  12 {{! 34 }}\n");
+  auto T = Template("  12 {{! 34 }}\n");
   auto Out = T.render(D);
   EXPECT_EQ("  12 \n", Out);
 }
@@ -965,7 +942,7 @@ TEST(MustacheComments, IndentedInline) {
 TEST(MustacheComments, SurroundingWhitespace) {
   // Comment removal should preserve surrounding whitespace.
   Value D = {};
-  auto T = Template::createTemplate("12345 {{! Comment Block! }} 67890");
+  auto T = Template("12345 {{! Comment Block! }} 67890");
   auto Out = T.render(D);
   EXPECT_EQ("12345  67890", Out);
 }
@@ -974,7 +951,7 @@ TEST(MustacheComments, VariableNameCollision) {
   // Comments must never render, even if a variable with the same name exists.
   Value D = Object{
       {"! comment", 1}, {"! comment ", 2}, {"!comment", 3}, {"comment", 4}};
-  auto T = Template::createTemplate("comments never show: >{{! comment }}<");
+  auto T = Template("comments never show: >{{! comment }}<");
   auto Out = T.render(D);
   EXPECT_EQ("comments never show: ><", Out);
 }
\ No newline at end of file

>From d8aa85c2e87b9ae9c40ea0758969f5bc5720820e Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 12 Sep 2024 06:24:27 -0400
Subject: [PATCH 23/62] [llvm][support] clang-format

---
 llvm/include/llvm/Support/Mustache.h | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 607e33b4ec2c9b..98843897707a82 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -28,10 +28,10 @@
 //
 // The Template class is container class outputs the Mustache template string
 // and is main class for users. It stores all the lambdas and the ASTNode Tree.
-// When the Template is instantiated it calls tokenize the Template String into 
-// the Token class and calls a basic recursive descent parser to construct the 
-// ASTNode Tree. The ASTNodes are all stored in an arena allocator which is 
-// freed once the template class goes out of scope
+// When the Template is instantiated it tokenize the Template String and creates 
+// a vector of Tokens. Then it calls a basic recursive descent parser to 
+// construct the ASTNode Tree. The ASTNodes are all stored in an arena
+// allocator which is freed once the template class goes out of scope
 //
 // Usage:
 // \code
@@ -121,15 +121,15 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), LocalContext(nullptr){};
+  ASTNode() : T(Type::Root), LocalContext(nullptr) {};
 
   ASTNode(StringRef Body, ASTNode *Parent)
-      : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr){};
+      : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr) {};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        LocalContext(nullptr){};
+        LocalContext(nullptr) {};
 
   void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 

>From 0534a054574ec18980e130a1d54a98af2bc59d5d Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 12 Sep 2024 06:54:19 -0400
Subject: [PATCH 24/62] [llvm][support] clang-format

---
 llvm/include/llvm/Support/Mustache.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 98843897707a82..88a2cb0d988097 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -28,9 +28,9 @@
 //
 // The Template class is container class outputs the Mustache template string
 // and is main class for users. It stores all the lambdas and the ASTNode Tree.
-// When the Template is instantiated it tokenize the Template String and creates 
-// a vector of Tokens. Then it calls a basic recursive descent parser to 
-// construct the ASTNode Tree. The ASTNodes are all stored in an arena
+// When the Template is instantiated it tokenize the Template String and 
+// creates a vector of Tokens. Then it calls a basic recursive descent parser
+// to construct the ASTNode Tree. The ASTNodes are all stored in an arena
 // allocator which is freed once the template class goes out of scope
 //
 // Usage:

>From 06da7a5897347f2d22e406e2bec7800cbb29fd1d Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 12 Sep 2024 07:46:30 -0400
Subject: [PATCH 25/62] [llvm][support] clang-format

---
 llvm/include/llvm/Support/Mustache.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 88a2cb0d988097..073e3d0148dcc4 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -28,7 +28,7 @@
 //
 // The Template class is container class outputs the Mustache template string
 // and is main class for users. It stores all the lambdas and the ASTNode Tree.
-// When the Template is instantiated it tokenize the Template String and 
+// When the Template is instantiated it tokenize the Template String and
 // creates a vector of Tokens. Then it calls a basic recursive descent parser
 // to construct the ASTNode Tree. The ASTNodes are all stored in an arena
 // allocator which is freed once the template class goes out of scope

>From f713198d1c4dbc286fa180973ccf604f2656c287 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 02:08:00 -0400
Subject: [PATCH 26/62] [llvm] mustache address comments

---
 llvm/include/llvm/Support/Mustache.h |  67 ++++--
 llvm/lib/Support/Mustache.cpp        | 331 +++++++++++++++++----------
 2 files changed, 251 insertions(+), 147 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 073e3d0148dcc4..4cd66d08dd39fa 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -64,12 +64,13 @@
 #include "llvm/ADT/StringMap.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/JSON.h"
+#include "llvm/Support/StringSaver.h"
 #include <vector>
 
 namespace llvm {
 namespace mustache {
 
-using Accessor = SmallVector<SmallString<128>>;
+using Accessor = SmallVector<SmallString<0>>;
 using Lambda = std::function<llvm::json::Value()>;
 using SectionLambda = std::function<llvm::json::Value(StringRef)>;
 
@@ -100,13 +101,20 @@ class Token {
 
   Type getType() const { return TokenType; };
 
+  void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
+
+  size_t getIndentation() const { return Indentation; };
+
   static Type getTokenType(char Identifier);
 
 private:
+  size_t Indentation;
   Type TokenType;
+  // RawBody is the original string that was tokenized
   SmallString<128> RawBody;
   Accessor Accessor;
-  SmallString<128> TokenBody;
+  // TokenBody is the original string with the identifier removed
+  SmallString<0> TokenBody;
 };
 
 class ASTNode {
@@ -121,36 +129,55 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), LocalContext(nullptr) {};
+  ASTNode() : T(Type::Root), LocalContext(nullptr){};
 
   ASTNode(StringRef Body, ASTNode *Parent)
-      : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr) {};
+      : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr),
+        Indentation(0){};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        LocalContext(nullptr) {};
+        LocalContext(nullptr), Indentation(0){};
 
   void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 
-  SmallString<128> getBody() const { return Body; };
-
   void setBody(StringRef NewBody) { Body = NewBody; };
 
   void setRawBody(StringRef NewBody) { RawBody = NewBody; };
 
-  SmallString<128> render(llvm::json::Value Data,
-                          llvm::BumpPtrAllocator &Allocator,
-                          DenseMap<StringRef, ASTNode *> &Partials,
-                          DenseMap<StringRef, Lambda> &Lambdas,
-                          DenseMap<StringRef, SectionLambda> &SectionLambdas,
-                          DenseMap<char, StringRef> &Escapes);
+  void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
+
+  void render(llvm::json::Value Data, SmallString<0> &Output);
+
+  void setUpNode(llvm::BumpPtrAllocator &Allocator,
+                 StringMap<ASTNode *> &Partials, StringMap<Lambda> &Lambdas,
+                 StringMap<SectionLambda> &SectionLambdas,
+                 DenseMap<char, StringRef> &Escapes);
 
 private:
+  void renderLambdas(llvm::json::Value &Contexts, SmallString<0> &Output,
+                     Lambda &L);
+
+  void renderSectionLambdas(llvm::json::Value &Contexts, SmallString<0> &Output,
+                            SectionLambda &L);
+
+  void renderPartial(llvm::json::Value &Contexts, SmallString<0> &Output,
+                     ASTNode *Partial);
+
+  void renderChild(llvm::json::Value &Context, SmallString<0> &Output);
+
   llvm::json::Value findContext();
+
+  llvm::BumpPtrAllocator *Allocator;
+  StringMap<ASTNode *> *Partials;
+  StringMap<Lambda> *Lambdas;
+  StringMap<SectionLambda> *SectionLambdas;
+  DenseMap<char, StringRef> *Escapes;
   Type T;
-  SmallString<128> RawBody;
-  SmallString<128> Body;
+  size_t Indentation;
+  SmallString<0> RawBody;
+  SmallString<0> Body;
   ASTNode *Parent;
   std::vector<ASTNode *> Children;
   const Accessor Accessor;
@@ -163,7 +190,7 @@ class Template {
 public:
   Template(StringRef TemplateStr);
 
-  SmallString<128> render(llvm::json::Value Data);
+  StringRef render(llvm::json::Value Data);
 
   void registerPartial(StringRef Name, StringRef Partial);
 
@@ -177,14 +204,14 @@ class Template {
   void registerEscape(DenseMap<char, StringRef> Escapes);
 
 private:
-  DenseMap<StringRef, ASTNode *> Partials;
-  DenseMap<StringRef, Lambda> Lambdas;
-  DenseMap<StringRef, SectionLambda> SectionLambdas;
+  SmallString<0> Output;
+  StringMap<ASTNode *> Partials;
+  StringMap<Lambda> Lambdas;
+  StringMap<SectionLambda> SectionLambdas;
   DenseMap<char, StringRef> Escapes;
   llvm::BumpPtrAllocator Allocator;
   ASTNode *Tree;
 };
-
 } // namespace mustache
 } // end namespace llvm
 #endif // LLVM_SUPPORT_MUSTACHE
\ No newline at end of file
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index dd6e5ebb19209c..76532161fd18b9 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -8,21 +8,23 @@
 
 #include "llvm/Support/Mustache.h"
 #include "llvm/Support/Error.h"
+#include <sstream>
 
 using namespace llvm;
 using namespace llvm::json;
 using namespace llvm::mustache;
 
-SmallString<128> escapeString(StringRef Input,
-                              DenseMap<char, StringRef> &Escape) {
-  SmallString<128> EscapedString("");
+SmallString<0> escapeString(StringRef Input,
+                            DenseMap<char, StringRef> &Escape) {
+
+  SmallString<0> Output;
   for (char C : Input) {
     if (Escape.find(C) != Escape.end())
-      EscapedString += Escape[C];
+      Output += Escape[C];
     else
-      EscapedString += C;
+      Output += C;
   }
-  return EscapedString;
+  return Output;
 }
 
 Accessor split(StringRef Str, char Delimiter) {
@@ -40,8 +42,19 @@ Accessor split(StringRef Str, char Delimiter) {
   return Tokens;
 }
 
+void addIndentation(llvm::SmallString<0> &PartialStr, size_t IndentationSize) {
+  std::string Indent(IndentationSize, ' ');
+  llvm::SmallString<0> Result;
+  for (size_t I = 0; I < PartialStr.size(); ++I) {
+    Result.push_back(PartialStr[I]);
+    if (PartialStr[I] == '\n' && I < PartialStr.size() - 1)
+      Result.append(Indent);
+  }
+  PartialStr = Result;
+}
+
 Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
-    : RawBody(RawBody), TokenBody(InnerBody) {
+    : RawBody(RawBody), TokenBody(InnerBody), Indentation(0) {
   TokenType = getTokenType(Identifier);
   if (TokenType == Type::Comment)
     return;
@@ -53,7 +66,8 @@ Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
 }
 
 Token::Token(StringRef Str)
-    : TokenType(Type::Text), RawBody(Str), Accessor({}), TokenBody(Str) {}
+    : TokenType(Type::Text), RawBody(Str), Accessor({}), TokenBody(Str),
+      Indentation(0) {}
 
 Token::Type Token::getTokenType(char Identifier) {
   switch (Identifier) {
@@ -80,8 +94,11 @@ bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
     return false;
   const Token &PrevToken = Tokens[Idx - 1];
   StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
-  return TokenBody.ends_with("\n") || TokenBody.ends_with("\r\n") ||
-         (TokenBody.empty() && Idx == 1);
+  // Check if the token body ends with a newline
+  // or if the previous token is empty and the current token is the first token
+  // eg. "  {{#section}}A{{#section}}" would be considered as no text behind
+  // and should be render as "A" instead of "  A"
+  return TokenBody.ends_with("\n") || (TokenBody.empty() && Idx == 1);
 }
 // Function to check if there's no meaningful text ahead
 bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
@@ -94,6 +111,13 @@ bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
   return TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n");
 }
 
+bool requiresCleanUp(Token::Type T) {
+  // We must clean up all the tokens that could contain child nodes
+  return T == Token::Type::SectionOpen || T == Token::Type::InvertSectionOpen ||
+         T == Token::Type::SectionClose || T == Token::Type::Comment ||
+         T == Token::Type::Partial;
+}
+
 // Simple tokenizer that splits the template into tokens
 // the mustache spec allows {{{ }}} to unescape variables
 // but we don't support that here unescape variable
@@ -102,8 +126,8 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
   SmallVector<Token, 0> Tokens;
   StringRef Open("{{");
   StringRef Close("}}");
-  std::size_t Start = 0;
-  std::size_t DelimiterStart = Template.find(Open);
+  size_t Start = 0;
+  size_t DelimiterStart = Template.find(Open);
   if (DelimiterStart == StringRef::npos) {
     Tokens.emplace_back(Template);
     return Tokens;
@@ -121,13 +145,9 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
     // Extract the Interpolated variable without {{ and }}
     size_t InterpolatedStart = DelimiterStart + Open.size();
     size_t InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size();
-    SmallString<128> Interpolated =
+    SmallString<0> Interpolated =
         Template.substr(InterpolatedStart, InterpolatedEnd);
-    SmallString<128> RawBody;
-    RawBody += Open;
-    RawBody += Interpolated;
-    RawBody += Close;
-
+    SmallString<0> RawBody({Open, Interpolated, Close});
     Tokens.emplace_back(RawBody, Interpolated, Interpolated[0]);
     Start = DelimiterEnd + Close.size();
     DelimiterStart = Template.find(Open, Start);
@@ -136,46 +156,66 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
   if (Start < Template.size())
     Tokens.emplace_back(Template.substr(Start));
 
-  // fix up white spaces for
-  // open sections/inverted sections/close section/comment
+  // Fix up white spaces for:
+  //  open sections/inverted sections/close section/comment
   // This loop attempts to find standalone tokens and tries to trim out
-  // the whitespace around them
-  // for example:
+  // the surrounding whitespace.
+  // For example:
   // if you have the template string
   //  "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
   // The output would be
   //  "Line 1\n Line 2\n Line 3"
   size_t LastIdx = Tokens.size() - 1;
   for (size_t Idx = 0, End = Tokens.size(); Idx < End; ++Idx) {
-    Token::Type CurrentType = Tokens[Idx].getType();
+    Token &CurrentToken = Tokens[Idx];
+    Token::Type CurrentType = CurrentToken.getType();
     // Check if token type requires cleanup
-    bool RequiresCleanUp = (CurrentType == Token::Type::SectionOpen ||
-                            CurrentType == Token::Type::InvertSectionOpen ||
-                            CurrentType == Token::Type::SectionClose ||
-                            CurrentType == Token::Type::Comment ||
-                            CurrentType == Token::Type::Partial);
+    bool RequiresCleanUp = requiresCleanUp(CurrentType);
 
     if (!RequiresCleanUp)
       continue;
 
+    // We adjust the token body if there's no text behind or ahead a token is
+    // considered surrounded by no text if the right of the previous token
+    // is a newline followed by spaces or if the left of the next token
+    // is spaces followed by a newline
+    // eg.
+    //  "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
+
     bool NoTextBehind = noTextBehind(Idx, Tokens);
     bool NoTextAhead = noTextAhead(Idx, Tokens);
 
-    // Adjust next token body if no text ahead
+    // Adjust next token body if there is no text ahead
+    // eg.
+    //  The template string
+    //  "{{! Comment }} \nLine 2"
+    // would be considered as no text ahead and should be render as
+    //  " Line 2"
     if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0)) {
       Token &NextToken = Tokens[Idx + 1];
       StringRef NextTokenBody = NextToken.getTokenBody();
-      if (NextTokenBody.starts_with("\r\n")) {
+      // cut off the leading newline which could be \n or \r\n
+      if (NextTokenBody.starts_with("\r\n"))
         NextToken.setTokenBody(NextTokenBody.substr(2));
-      } else if (NextTokenBody.starts_with("\n")) {
+      else if (NextTokenBody.starts_with("\n"))
         NextToken.setTokenBody(NextTokenBody.substr(1));
-      }
     }
-    // Adjust previous token body if no text behind
-    if ((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx)) {
+    // Adjust previous token body if there no text behind
+    // eg.
+    //  The template string
+    //  " \t{{#section}}A{{/section}}"
+    // would be considered as no text ahead and should be render as
+    //  "A"
+    // The exception for this is partial tag which requires us to
+    // keep track of the indentation once it's rendered
+    if (((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx))) {
       Token &PrevToken = Tokens[Idx - 1];
       StringRef PrevTokenBody = PrevToken.getTokenBody();
-      PrevToken.setTokenBody(PrevTokenBody.rtrim(" \t\v\t"));
+      StringRef Unindented = PrevTokenBody.rtrim(" \t\v");
+      size_t Indentation = PrevTokenBody.size() - Unindented.size();
+      if (CurrentType != Token::Type::Partial)
+        PrevToken.setTokenBody(Unindented);
+      CurrentToken.setIndentation(Indentation);
     }
   }
   return Tokens;
@@ -233,6 +273,7 @@ void Parser::parseMustache(ASTNode *Parent) {
     }
     case Token::Type::Partial: {
       CurrentNode = new (Node) ASTNode(ASTNode::Partial, A, Parent);
+      CurrentNode->setIndentation(CurrentToken.getIndentation());
       Parent->addChild(CurrentNode);
       break;
     }
@@ -241,12 +282,12 @@ void Parser::parseMustache(ASTNode *Parent) {
       size_t Start = CurrentPtr;
       parseMustache(CurrentNode);
       size_t End = CurrentPtr;
-      SmallString<128> RawBody;
+      SmallString<0> RawBody;
       if (Start + 1 < End - 1) {
         for (std::size_t I = Start + 1; I < End - 1; I++)
           RawBody += Tokens[I].getRawBody();
       } else if (Start + 1 == End - 1) {
-        RawBody = Tokens[Start].getRawBody();
+        RawBody = Tokens[Start + 1].getRawBody();
       }
       CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
@@ -257,12 +298,12 @@ void Parser::parseMustache(ASTNode *Parent) {
       size_t Start = CurrentPtr;
       parseMustache(CurrentNode);
       size_t End = CurrentPtr;
-      SmallString<128> RawBody;
+      SmallString<0> RawBody;
       if (Start + 1 < End - 1) {
         for (size_t Idx = Start + 1; Idx < End - 1; Idx++)
           RawBody += Tokens[Idx].getRawBody();
       } else if (Start + 1 == End - 1) {
-        RawBody = Tokens[Start].getRawBody();
+        RawBody = Tokens[Start + 1].getRawBody();
       }
       CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
@@ -276,16 +317,17 @@ void Parser::parseMustache(ASTNode *Parent) {
   }
 }
 
-SmallString<128> Template::render(Value Data) {
+StringRef Template::render(Value Data) {
   BumpPtrAllocator LocalAllocator;
-  return Tree->render(Data, LocalAllocator, Partials, Lambdas, SectionLambdas,
-                      Escapes);
+  Tree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas, Escapes);
+  Tree->render(Data, Output);
+  return Output.str();
 }
 
 void Template::registerPartial(StringRef Name, StringRef Partial) {
   Parser P = Parser(Partial, Allocator);
   ASTNode *PartialTree = P.parse();
-  Partials[Name] = PartialTree;
+  Partials.insert(std::make_pair(Name, PartialTree));
 }
 
 void Template::registerLambda(StringRef Name, Lambda L) { Lambdas[Name] = L; }
@@ -308,19 +350,23 @@ Template::Template(StringRef TemplateStr) {
   registerEscape(HtmlEntities);
 }
 
-SmallString<128> printJson(Value &Data) {
-
-  SmallString<128> Result;
+void toJsonString(Value &Data, SmallString<0> &Output) {
   if (Data.getAsNull())
-    return Result;
+    return;
   if (auto *Arr = Data.getAsArray())
     if (Arr->empty())
-      return Result;
+      return;
   if (Data.getAsString()) {
-    Result += Data.getAsString()->str();
-    return Result;
+    Output = Data.getAsString()->str();
+    return;
+  }
+  if (auto Num = Data.getAsNumber()) {
+    std::ostringstream Oss;
+    Oss << *Num;
+    Output = Oss.str();
+    return;
   }
-  return formatv("{0:2}", Data);
+  Output = formatv("{0:2}", Data);
 }
 
 bool isFalsey(Value &V) {
@@ -329,97 +375,70 @@ bool isFalsey(Value &V) {
          (V.getAsObject() && V.getAsObject()->empty());
 }
 
-SmallString<128>
-ASTNode::render(Value Data, BumpPtrAllocator &Allocator,
-                DenseMap<StringRef, ASTNode *> &Partials,
-                DenseMap<StringRef, Lambda> &Lambdas,
-                DenseMap<StringRef, SectionLambda> &SectionLambdas,
-                DenseMap<char, StringRef> &Escapes) {
+void ASTNode::render(Value Data, SmallString<0> &Output) {
   LocalContext = Data;
   Value Context = T == Root ? Data : findContext();
-  SmallString<128> Result;
   switch (T) {
-  case Root: {
-    for (ASTNode *Child : Children)
-      Result += Child->render(Context, Allocator, Partials, Lambdas,
-                              SectionLambdas, Escapes);
-    return Result;
-  }
+  case Root:
+    renderChild(Data, Output);
+    return;
   case Text:
-    return Body;
+    Output = Body;
+    return;
   case Partial: {
-    if (Partials.find(Accessor[0]) != Partials.end()) {
-      ASTNode *Partial = Partials[Accessor[0]];
-      Result += Partial->render(Data, Allocator, Partials, Lambdas,
-                                SectionLambdas, Escapes);
-      return Result;
-    }
-    return Result;
+    auto Partial = Partials->find(Accessor[0]);
+    if (Partial != Partials->end())
+      renderPartial(Context, Output, Partial->getValue());
+    return;
   }
   case Variable: {
-    if (Lambdas.find(Accessor[0]) != Lambdas.end()) {
-      Lambda &L = Lambdas[Accessor[0]];
-      Value LambdaResult = L();
-      StringRef LambdaStr = printJson(LambdaResult);
-      Parser P = Parser(LambdaStr, Allocator);
-      ASTNode *LambdaNode = P.parse();
-      SmallString<128> RenderStr = LambdaNode->render(
-          Data, Allocator, Partials, Lambdas, SectionLambdas, Escapes);
-      return escapeString(RenderStr, Escapes);
+    auto Lambda = Lambdas->find(Accessor[0]);
+    if (Lambda != Lambdas->end())
+      renderLambdas(Data, Output, Lambda->getValue());
+    else {
+      toJsonString(Context, Output);
+      Output = escapeString(Output, *Escapes);
     }
-    return escapeString(printJson(Context), Escapes);
+    return;
   }
   case UnescapeVariable: {
-    if (Lambdas.find(Accessor[0]) != Lambdas.end()) {
-      Lambda &L = Lambdas[Accessor[0]];
-      Value LambdaResult = L();
-      StringRef LambdaStr = printJson(LambdaResult);
-      Parser P = Parser(LambdaStr, Allocator);
-      ASTNode *LambdaNode = P.parse();
-      DenseMap<char, StringRef> EmptyEscapes;
-      return LambdaNode->render(Data, Allocator, Partials, Lambdas,
-                                SectionLambdas, EmptyEscapes);
-    }
-    return printJson(Context);
+    auto Lambda = Lambdas->find(Accessor[0]);
+    if (Lambda != Lambdas->end())
+      renderLambdas(Data, Output, Lambda->getValue());
+    else
+      toJsonString(Context, Output);
+    return;
   }
   case Section: {
     // Sections are not rendered if the context is falsey
-    bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
+    auto SectionLambda = SectionLambdas->find(Accessor[0]);
+    bool IsLambda = SectionLambda != SectionLambdas->end();
     if (isFalsey(Context) && !IsLambda)
-      return Result;
+      return;
+
     if (IsLambda) {
-      SectionLambda &Lambda = SectionLambdas[Accessor[0]];
-      Value Return = Lambda(RawBody);
-      if (isFalsey(Return))
-        return Result;
-      StringRef LambdaStr = printJson(Return);
-      Parser P = Parser(LambdaStr.str(), Allocator);
-      ASTNode *LambdaNode = P.parse();
-      return LambdaNode->render(Data, Allocator, Partials, Lambdas,
-                                SectionLambdas, Escapes);
+      renderSectionLambdas(Data, Output, SectionLambda->getValue());
+      return;
     }
+
     if (Context.getAsArray()) {
+      SmallString<0> Result;
       json::Array *Arr = Context.getAsArray();
-      for (Value &V : *Arr) {
-        for (ASTNode *Child : Children)
-          Result += Child->render(V, Allocator, Partials, Lambdas,
-                                  SectionLambdas, Escapes);
-      }
-      return Result;
+      for (Value &V : *Arr)
+        renderChild(V, Result);
+      Output = Result;
+      return;
     }
-    for (ASTNode *Child : Children)
-      Result += Child->render(Context, Allocator, Partials, Lambdas,
-                              SectionLambdas, Escapes);
-    return Result;
+
+    renderChild(Context, Output);
   }
   case InvertSection: {
-    bool IsLambda = SectionLambdas.find(Accessor[0]) != SectionLambdas.end();
+    bool IsLambda = SectionLambdas->find(Accessor[0]) != SectionLambdas->end();
+
     if (!isFalsey(Context) || IsLambda)
-      return Result;
-    for (ASTNode *Child : Children)
-      Result += Child->render(Context, Allocator, Partials, Lambdas,
-                              SectionLambdas, Escapes);
-    return Result;
+      return;
+
+    renderChild(Context, Output);
   }
   }
   llvm_unreachable("Invalid ASTNode type");
@@ -427,10 +446,10 @@ ASTNode::render(Value Data, BumpPtrAllocator &Allocator,
 
 Value ASTNode::findContext() {
   // The mustache spec allows for dot notation to access nested values
-  // a single dot refers to the current context
-  // We attempt to find the JSON context in the current node if it is not found
-  // we traverse the parent nodes to find the context until we reach the root
-  // node or the context is found
+  // a single dot refers to the current context.
+  // We attempt to find the JSON context in the current node, if it is not
+  // found, then we traverse the parent nodes to find the context until we
+  // reach the root node or the context is found
   if (Accessor.empty())
     return nullptr;
   if (Accessor[0] == ".")
@@ -448,11 +467,11 @@ Value ASTNode::findContext() {
     return nullptr;
   }
   Value Context = nullptr;
-  for (auto E : enumerate(Accessor)) {
-    Value *CurrentValue = CurrentContext->get(E.value());
+  for (auto [Idx, Acc] : enumerate(Accessor)) {
+    Value *CurrentValue = CurrentContext->get(Acc);
     if (!CurrentValue)
       return nullptr;
-    if (E.index() < Accessor.size() - 1) {
+    if (Idx < Accessor.size() - 1) {
       CurrentContext = CurrentValue->getAsObject();
       if (!CurrentContext)
         return nullptr;
@@ -461,3 +480,61 @@ Value ASTNode::findContext() {
   }
   return Context;
 }
+
+void ASTNode::renderChild(Value &Context, SmallString<0> &Output) {
+  for (ASTNode *Child : Children) {
+    SmallString<0> ChildResult;
+    Child->render(Context, ChildResult);
+    Output += ChildResult;
+  }
+}
+
+void ASTNode::renderPartial(Value &Context, SmallString<0> &Output,
+                            ASTNode *Partial) {
+  Partial->render(Context, Output);
+  addIndentation(Output, Indentation);
+}
+
+void ASTNode::renderLambdas(Value &Context, SmallString<0> &Output, Lambda &L) {
+  Value LambdaResult = L();
+  SmallString<0> LambdaStr;
+  toJsonString(LambdaResult, LambdaStr);
+  Parser P = Parser(LambdaStr, *Allocator);
+  ASTNode *LambdaNode = P.parse();
+  LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
+                        *Escapes);
+  LambdaNode->render(Context, Output);
+  if (T == Variable)
+    Output = escapeString(Output, *Escapes);
+  return;
+}
+
+void ASTNode::renderSectionLambdas(Value &Contexts, SmallString<0> &Output,
+                                   SectionLambda &L) {
+  Value Return = L(RawBody);
+  if (isFalsey(Return))
+    return;
+  SmallString<0> LambdaStr;
+  toJsonString(Return, LambdaStr);
+  Parser P = Parser(LambdaStr, *Allocator);
+  ASTNode *LambdaNode = P.parse();
+  LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
+                        *Escapes);
+  LambdaNode->render(Contexts, Output);
+  return;
+}
+
+void ASTNode::setUpNode(BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Par,
+                        StringMap<Lambda> &L, StringMap<SectionLambda> &SC,
+                        DenseMap<char, StringRef> &E) {
+
+  // Passed down datastructures needed for rendering to
+  // the children nodes
+  Allocator = &Alloc;
+  Partials = &Par;
+  Lambdas = &L;
+  SectionLambdas = &SC;
+  Escapes = &E;
+  for (ASTNode *Child : Children)
+    Child->setUpNode(Alloc, Par, L, SC, E);
+}

>From 6b4f5cd29a687974f346c46bc02425854a14589b Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 02:21:47 -0400
Subject: [PATCH 27/62] [llvm] clang-format

---
 llvm/include/llvm/Support/Mustache.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 4cd66d08dd39fa..21e233eb12d25c 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -129,16 +129,16 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), LocalContext(nullptr){};
+  ASTNode() : T(Type::Root), LocalContext(nullptr) {};
 
   ASTNode(StringRef Body, ASTNode *Parent)
       : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr),
-        Indentation(0){};
+        Indentation(0) {};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        LocalContext(nullptr), Indentation(0){};
+        LocalContext(nullptr), Indentation(0) {};
 
   void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 

>From d400c29c76f05f29cc5a84c616b926876c9ed085 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 03:01:32 -0400
Subject: [PATCH 28/62] [llvm] fix errors

---
 llvm/lib/Support/Mustache.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 76532161fd18b9..d4d26245433175 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -287,7 +287,7 @@ void Parser::parseMustache(ASTNode *Parent) {
         for (std::size_t I = Start + 1; I < End - 1; I++)
           RawBody += Tokens[I].getRawBody();
       } else if (Start + 1 == End - 1) {
-        RawBody = Tokens[Start + 1].getRawBody();
+        RawBody = Tokens[Start].getRawBody();
       }
       CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
@@ -303,7 +303,7 @@ void Parser::parseMustache(ASTNode *Parent) {
         for (size_t Idx = Start + 1; Idx < End - 1; Idx++)
           RawBody += Tokens[Idx].getRawBody();
       } else if (Start + 1 == End - 1) {
-        RawBody = Tokens[Start + 1].getRawBody();
+        RawBody = Tokens[Start].getRawBody();
       }
       CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
@@ -388,7 +388,7 @@ void ASTNode::render(Value Data, SmallString<0> &Output) {
   case Partial: {
     auto Partial = Partials->find(Accessor[0]);
     if (Partial != Partials->end())
-      renderPartial(Context, Output, Partial->getValue());
+      renderPartial(Data, Output, Partial->getValue());
     return;
   }
   case Variable: {
@@ -439,6 +439,7 @@ void ASTNode::render(Value Data, SmallString<0> &Output) {
       return;
 
     renderChild(Context, Output);
+    return;
   }
   }
   llvm_unreachable("Invalid ASTNode type");
@@ -491,6 +492,8 @@ void ASTNode::renderChild(Value &Context, SmallString<0> &Output) {
 
 void ASTNode::renderPartial(Value &Context, SmallString<0> &Output,
                             ASTNode *Partial) {
+  Partial->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
+                     *Escapes);
   Partial->render(Context, Output);
   addIndentation(Output, Indentation);
 }

>From 8fa5fd78b0e870d128da0dfb0f235ae9cb62bf20 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 10:23:51 -0400
Subject: [PATCH 29/62] [llvm] fix more bugs

---
 llvm/lib/Support/Mustache.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index d4d26245433175..4bf31db15d634a 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -94,12 +94,14 @@ bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
     return false;
   const Token &PrevToken = Tokens[Idx - 1];
   StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
-  // Check if the token body ends with a newline
-  // or if the previous token is empty and the current token is the first token
-  // eg. "  {{#section}}A{{#section}}" would be considered as no text behind
-  // and should be render as "A" instead of "  A"
+  // We make a special exception for when previous token is empty 
+  // and the current token is the second token 
+  // ex.
+  //  " {{#section}}A{{/section}}"
+  // that's why we check if the token body is empty and the index is 1
   return TokenBody.ends_with("\n") || (TokenBody.empty() && Idx == 1);
 }
+
 // Function to check if there's no meaningful text ahead
 bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
   if (Idx >= Tokens.size() - 1 ||
@@ -431,6 +433,7 @@ void ASTNode::render(Value Data, SmallString<0> &Output) {
     }
 
     renderChild(Context, Output);
+    return;
   }
   case InvertSection: {
     bool IsLambda = SectionLambdas->find(Accessor[0]) != SectionLambdas->end();

>From fd7c106b30289ab5966799c4524535e6aa9393bb Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 14:40:28 -0400
Subject: [PATCH 30/62] [llvm] change mustache to pass by reference

---
 llvm/include/llvm/Support/Mustache.h |  32 ++++----
 llvm/lib/Support/Mustache.cpp        | 107 ++++++++++++++-------------
 2 files changed, 73 insertions(+), 66 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 21e233eb12d25c..433e2dfe4103b9 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -129,16 +129,16 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), LocalContext(nullptr) {};
+  ASTNode() : T(Type::Root), ParentContext(nullptr), LocalContext(nullptr){};
 
   ASTNode(StringRef Body, ASTNode *Parent)
-      : T(Type::Text), Body(Body), Parent(Parent), LocalContext(nullptr),
-        Indentation(0) {};
+      : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
+        LocalContext(nullptr), Indentation(0){};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        LocalContext(nullptr), Indentation(0) {};
+        ParentContext(nullptr), LocalContext(nullptr), Indentation(0){};
 
   void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 
@@ -148,26 +148,27 @@ class ASTNode {
 
   void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
 
-  void render(llvm::json::Value Data, SmallString<0> &Output);
+  void render(const llvm::json::Value &Data, SmallString<0> &Output);
 
-  void setUpNode(llvm::BumpPtrAllocator &Allocator,
-                 StringMap<ASTNode *> &Partials, StringMap<Lambda> &Lambdas,
+  void setUpNode(StringMap<ASTNode *> &Partials, StringMap<Lambda> &Lambdas,
                  StringMap<SectionLambda> &SectionLambdas,
                  DenseMap<char, StringRef> &Escapes);
 
+  void setUpContext(llvm::BumpPtrAllocator *Alloc);
+
 private:
-  void renderLambdas(llvm::json::Value &Contexts, SmallString<0> &Output,
+  void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
                      Lambda &L);
 
-  void renderSectionLambdas(llvm::json::Value &Contexts, SmallString<0> &Output,
-                            SectionLambda &L);
+  void renderSectionLambdas(const llvm::json::Value &Contexts,
+                            SmallString<0> &Output, SectionLambda &L);
 
-  void renderPartial(llvm::json::Value &Contexts, SmallString<0> &Output,
+  void renderPartial(const llvm::json::Value &Contexts, SmallString<0> &Output,
                      ASTNode *Partial);
 
-  void renderChild(llvm::json::Value &Context, SmallString<0> &Output);
+  void renderChild(const llvm::json::Value &Context, SmallString<0> &Output);
 
-  llvm::json::Value findContext();
+  const llvm::json::Value *findContext();
 
   llvm::BumpPtrAllocator *Allocator;
   StringMap<ASTNode *> *Partials;
@@ -181,7 +182,8 @@ class ASTNode {
   ASTNode *Parent;
   std::vector<ASTNode *> Children;
   const Accessor Accessor;
-  llvm::json::Value LocalContext;
+  const llvm::json::Value *ParentContext;
+  const llvm::json::Value *LocalContext;
 };
 
 // A Template represents the container for the AST and the partials
@@ -190,7 +192,7 @@ class Template {
 public:
   Template(StringRef TemplateStr);
 
-  StringRef render(llvm::json::Value Data);
+  StringRef render(llvm::json::Value &Data);
 
   void registerPartial(StringRef Name, StringRef Partial);
 
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 4bf31db15d634a..87cded1ff0822b 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -94,8 +94,8 @@ bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
     return false;
   const Token &PrevToken = Tokens[Idx - 1];
   StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
-  // We make a special exception for when previous token is empty 
-  // and the current token is the second token 
+  // We make a special exception for when previous token is empty
+  // and the current token is the second token
   // ex.
   //  " {{#section}}A{{/section}}"
   // that's why we check if the token body is empty and the index is 1
@@ -144,7 +144,7 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
       break;
     }
 
-    // Extract the Interpolated variable without {{ and }}
+    // Extract the Interpolated variable without delimiters {{ and }}
     size_t InterpolatedStart = DelimiterStart + Open.size();
     size_t InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size();
     SmallString<0> Interpolated =
@@ -285,12 +285,8 @@ void Parser::parseMustache(ASTNode *Parent) {
       parseMustache(CurrentNode);
       size_t End = CurrentPtr;
       SmallString<0> RawBody;
-      if (Start + 1 < End - 1) {
-        for (std::size_t I = Start + 1; I < End - 1; I++)
-          RawBody += Tokens[I].getRawBody();
-      } else if (Start + 1 == End - 1) {
-        RawBody = Tokens[Start].getRawBody();
-      }
+      for (std::size_t I = Start; I < End - 1; I++)
+        RawBody += Tokens[I].getRawBody();
       CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
       break;
@@ -301,12 +297,8 @@ void Parser::parseMustache(ASTNode *Parent) {
       parseMustache(CurrentNode);
       size_t End = CurrentPtr;
       SmallString<0> RawBody;
-      if (Start + 1 < End - 1) {
-        for (size_t Idx = Start + 1; Idx < End - 1; Idx++)
-          RawBody += Tokens[Idx].getRawBody();
-      } else if (Start + 1 == End - 1) {
-        RawBody = Tokens[Start].getRawBody();
-      }
+      for (size_t Idx = Start; Idx < End - 1; Idx++)
+        RawBody += Tokens[Idx].getRawBody();
       CurrentNode->setRawBody(RawBody);
       Parent->addChild(CurrentNode);
       break;
@@ -319,9 +311,9 @@ void Parser::parseMustache(ASTNode *Parent) {
   }
 }
 
-StringRef Template::render(Value Data) {
+StringRef Template::render(Value &Data) {
   BumpPtrAllocator LocalAllocator;
-  Tree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas, Escapes);
+  Tree->setUpContext(&LocalAllocator);
   Tree->render(Data, Output);
   return Output.str();
 }
@@ -329,6 +321,7 @@ StringRef Template::render(Value Data) {
 void Template::registerPartial(StringRef Name, StringRef Partial) {
   Parser P = Parser(Partial, Allocator);
   ASTNode *PartialTree = P.parse();
+  PartialTree->setUpNode(Partials, Lambdas, SectionLambdas, Escapes);
   Partials.insert(std::make_pair(Name, PartialTree));
 }
 
@@ -350,9 +343,10 @@ Template::Template(StringRef TemplateStr) {
                                             {'"', """},
                                             {'\'', "'"}};
   registerEscape(HtmlEntities);
+  Tree->setUpNode(Partials, Lambdas, SectionLambdas, Escapes);
 }
 
-void toJsonString(Value &Data, SmallString<0> &Output) {
+void toJsonString(const Value &Data, SmallString<0> &Output) {
   if (Data.getAsNull())
     return;
   if (auto *Arr = Data.getAsArray())
@@ -371,15 +365,18 @@ void toJsonString(Value &Data, SmallString<0> &Output) {
   Output = formatv("{0:2}", Data);
 }
 
-bool isFalsey(Value &V) {
+bool isFalsey(const Value &V) {
   return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
          (V.getAsArray() && V.getAsArray()->empty()) ||
          (V.getAsObject() && V.getAsObject()->empty());
 }
 
-void ASTNode::render(Value Data, SmallString<0> &Output) {
-  LocalContext = Data;
-  Value Context = T == Root ? Data : findContext();
+void ASTNode::render(const Value &Data, SmallString<0> &Output) {
+
+  ParentContext = &Data;
+  const Value *ContextPtr = T == Root ? ParentContext : findContext();
+  const Value &Context = ContextPtr ? *ContextPtr : nullptr;
+
   switch (T) {
   case Root:
     renderChild(Data, Output);
@@ -425,8 +422,8 @@ void ASTNode::render(Value Data, SmallString<0> &Output) {
 
     if (Context.getAsArray()) {
       SmallString<0> Result;
-      json::Array *Arr = Context.getAsArray();
-      for (Value &V : *Arr)
+      const json::Array *Arr = Context.getAsArray();
+      for (const Value &V : *Arr)
         renderChild(V, Result);
       Output = Result;
       return;
@@ -448,7 +445,7 @@ void ASTNode::render(Value Data, SmallString<0> &Output) {
   llvm_unreachable("Invalid ASTNode type");
 }
 
-Value ASTNode::findContext() {
+const Value *ASTNode::findContext() {
   // The mustache spec allows for dot notation to access nested values
   // a single dot refers to the current context.
   // We attempt to find the JSON context in the current node, if it is not
@@ -457,22 +454,23 @@ Value ASTNode::findContext() {
   if (Accessor.empty())
     return nullptr;
   if (Accessor[0] == ".")
-    return LocalContext;
-  json::Object *CurrentContext = LocalContext.getAsObject();
+    return ParentContext;
+
+  const json::Object *CurrentContext = ParentContext->getAsObject();
   StringRef CurrentAccessor = Accessor[0];
   ASTNode *CurrentParent = Parent;
 
   while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
     if (CurrentParent->T != Root) {
-      CurrentContext = CurrentParent->LocalContext.getAsObject();
+      CurrentContext = CurrentParent->ParentContext->getAsObject();
       CurrentParent = CurrentParent->Parent;
       continue;
     }
     return nullptr;
   }
-  Value Context = nullptr;
+  const Value *Context = nullptr;
   for (auto [Idx, Acc] : enumerate(Accessor)) {
-    Value *CurrentValue = CurrentContext->get(Acc);
+    const Value *CurrentValue = CurrentContext->get(Acc);
     if (!CurrentValue)
       return nullptr;
     if (Idx < Accessor.size() - 1) {
@@ -480,43 +478,43 @@ Value ASTNode::findContext() {
       if (!CurrentContext)
         return nullptr;
     } else
-      Context = *CurrentValue;
+      Context = CurrentValue;
   }
   return Context;
 }
 
-void ASTNode::renderChild(Value &Context, SmallString<0> &Output) {
+void ASTNode::renderChild(const Value &Contexts, SmallString<0> &Output) {
   for (ASTNode *Child : Children) {
     SmallString<0> ChildResult;
-    Child->render(Context, ChildResult);
+    Child->render(Contexts, ChildResult);
     Output += ChildResult;
   }
 }
 
-void ASTNode::renderPartial(Value &Context, SmallString<0> &Output,
+void ASTNode::renderPartial(const Value &Contexts, SmallString<0> &Output,
                             ASTNode *Partial) {
-  Partial->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
-                     *Escapes);
-  Partial->render(Context, Output);
+  Partial->setUpContext(Allocator);
+  Partial->render(Contexts, Output);
   addIndentation(Output, Indentation);
 }
 
-void ASTNode::renderLambdas(Value &Context, SmallString<0> &Output, Lambda &L) {
+void ASTNode::renderLambdas(const Value &Contexts, SmallString<0> &Output,
+                            Lambda &L) {
   Value LambdaResult = L();
   SmallString<0> LambdaStr;
   toJsonString(LambdaResult, LambdaStr);
   Parser P = Parser(LambdaStr, *Allocator);
   ASTNode *LambdaNode = P.parse();
-  LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
-                        *Escapes);
-  LambdaNode->render(Context, Output);
+  LambdaNode->setUpNode(*Partials, *Lambdas, *SectionLambdas, *Escapes);
+  LambdaNode->setUpContext(Allocator);
+  LambdaNode->render(Contexts, Output);
   if (T == Variable)
     Output = escapeString(Output, *Escapes);
   return;
 }
 
-void ASTNode::renderSectionLambdas(Value &Contexts, SmallString<0> &Output,
-                                   SectionLambda &L) {
+void ASTNode::renderSectionLambdas(const Value &Contexts,
+                                   SmallString<0> &Output, SectionLambda &L) {
   Value Return = L(RawBody);
   if (isFalsey(Return))
     return;
@@ -524,23 +522,30 @@ void ASTNode::renderSectionLambdas(Value &Contexts, SmallString<0> &Output,
   toJsonString(Return, LambdaStr);
   Parser P = Parser(LambdaStr, *Allocator);
   ASTNode *LambdaNode = P.parse();
-  LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
-                        *Escapes);
+  LambdaNode->setUpNode(*Partials, *Lambdas, *SectionLambdas, *Escapes);
+  LambdaNode->setUpContext(Allocator);
   LambdaNode->render(Contexts, Output);
   return;
 }
 
-void ASTNode::setUpNode(BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Par,
-                        StringMap<Lambda> &L, StringMap<SectionLambda> &SC,
+void ASTNode::setUpNode(StringMap<ASTNode *> &Par, StringMap<Lambda> &L,
+                        StringMap<SectionLambda> &SC,
                         DenseMap<char, StringRef> &E) {
-
   // Passed down datastructures needed for rendering to
-  // the children nodes
-  Allocator = &Alloc;
+  // the children nodes. This must be called before rendering
   Partials = &Par;
   Lambdas = &L;
   SectionLambdas = &SC;
   Escapes = &E;
   for (ASTNode *Child : Children)
-    Child->setUpNode(Alloc, Par, L, SC, E);
+    Child->setUpNode(Par, L, SC, E);
+}
+
+void ASTNode::setUpContext(llvm::BumpPtrAllocator *Alloc) {
+  // Passed down the allocator to the children nodes.
+  // Each render call requires a allocator for generating
+  // Partial/Section nodes
+  Allocator = Alloc;
+  for (ASTNode *Child : Children)
+    Child->setUpContext(Alloc);
 }

>From 29bba68935ef7f8cc5dff0477853ac25b8cf65bc Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 14:58:04 -0400
Subject: [PATCH 31/62] [llvm] fix failing mustache regression test

---
 llvm/include/llvm/Support/Mustache.h | 6 +++---
 llvm/lib/Support/Mustache.cpp        | 5 +++--
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 433e2dfe4103b9..6d9cc8393a2f1e 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -129,16 +129,16 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), ParentContext(nullptr), LocalContext(nullptr){};
+  ASTNode() : T(Type::Root), ParentContext(nullptr), LocalContext(nullptr) {};
 
   ASTNode(StringRef Body, ASTNode *Parent)
       : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
-        LocalContext(nullptr), Indentation(0){};
+        LocalContext(nullptr), Indentation(0) {};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        ParentContext(nullptr), LocalContext(nullptr), Indentation(0){};
+        ParentContext(nullptr), LocalContext(nullptr), Indentation(0) {};
 
   void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 87cded1ff0822b..422ab60a672e57 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -19,8 +19,9 @@ SmallString<0> escapeString(StringRef Input,
 
   SmallString<0> Output;
   for (char C : Input) {
-    if (Escape.find(C) != Escape.end())
-      Output += Escape[C];
+    auto It = Escape.find(C);
+    if (It != Escape.end())
+      Output += It->getSecond();
     else
       Output += C;
   }

>From 7eed82f343478e317b89dd9e48faeff2c9ce65f1 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 15:03:12 -0400
Subject: [PATCH 32/62] [llvm] format

---
 llvm/include/llvm/Support/Mustache.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 6d9cc8393a2f1e..1b6a8c76f5218a 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -55,6 +55,7 @@
 //   StringRef Rendered = Template.render(Data);
 //   // Rendered == "Hello, World!"
 // \endcode
+//
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_SUPPORT_MUSTACHE
@@ -157,6 +158,7 @@ class ASTNode {
   void setUpContext(llvm::BumpPtrAllocator *Alloc);
 
 private:
+  
   void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
                      Lambda &L);
 

>From e73454d198155a595efde5401ea0d344a8970765 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 15:05:24 -0400
Subject: [PATCH 33/62] [llvm] remove unused mustache member

---
 llvm/include/llvm/Support/Mustache.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 1b6a8c76f5218a..8b22fdfb74cf26 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -185,7 +185,6 @@ class ASTNode {
   std::vector<ASTNode *> Children;
   const Accessor Accessor;
   const llvm::json::Value *ParentContext;
-  const llvm::json::Value *LocalContext;
 };
 
 // A Template represents the container for the AST and the partials

>From b58fbcbbcbf93c077d63af64e67977e53f17cb9c Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 15:08:47 -0400
Subject: [PATCH 34/62] [llvm] remove unused mustache member

---
 llvm/include/llvm/Support/Mustache.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 8b22fdfb74cf26..50435d4a04cd0f 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -134,12 +134,12 @@ class ASTNode {
 
   ASTNode(StringRef Body, ASTNode *Parent)
       : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
-        LocalContext(nullptr), Indentation(0) {};
+        Indentation(0) {};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        ParentContext(nullptr), LocalContext(nullptr), Indentation(0) {};
+        ParentContext(nullptr), Indentation(0) {};
 
   void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 

>From bb4ba4096446218b33e232a87d5309a174858092 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 16:03:11 -0400
Subject: [PATCH 35/62] [llvm] address more comments

---
 llvm/include/llvm/Support/Mustache.h |  15 +--
 llvm/lib/Support/Mustache.cpp        | 153 +++++++++++++++------------
 2 files changed, 95 insertions(+), 73 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 50435d4a04cd0f..62c15e05850e60 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -130,7 +130,7 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), ParentContext(nullptr), LocalContext(nullptr) {};
+  ASTNode() : T(Type::Root), ParentContext(nullptr) {};
 
   ASTNode(StringRef Body, ASTNode *Parent)
       : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
@@ -151,12 +151,12 @@ class ASTNode {
 
   void render(const llvm::json::Value &Data, SmallString<0> &Output);
 
-  void setUpNode(StringMap<ASTNode *> &Partials, StringMap<Lambda> &Lambdas,
+  void setUpNode(llvm::BumpPtrAllocator &Alloc,
+                 StringMap<ASTNode *> &Partials, 
+                 StringMap<Lambda> &Lambdas,
                  StringMap<SectionLambda> &SectionLambdas,
                  DenseMap<char, StringRef> &Escapes);
-
-  void setUpContext(llvm::BumpPtrAllocator *Alloc);
-
+  
 private:
   
   void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
@@ -212,9 +212,12 @@ class Template {
   StringMap<Lambda> Lambdas;
   StringMap<SectionLambda> SectionLambdas;
   DenseMap<char, StringRef> Escapes;
+  // The allocator for the ASTNode Tree
   llvm::BumpPtrAllocator Allocator;
+  // Allocator for each render call resets after each render
+  llvm::BumpPtrAllocator LocalAllocator;
   ASTNode *Tree;
 };
 } // namespace mustache
 } // end namespace llvm
-#endif // LLVM_SUPPORT_MUSTACHE
\ No newline at end of file
+#endif // LLVM_SUPPORT_MUSTACHE
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 422ab60a672e57..0fa88c0896f6d3 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -16,8 +16,8 @@ using namespace llvm::mustache;
 
 SmallString<0> escapeString(StringRef Input,
                             DenseMap<char, StringRef> &Escape) {
-
   SmallString<0> Output;
+  Output.reserve(Input.size());
   for (char C : Input) {
     auto It = Escape.find(C);
     if (It != Escape.end())
@@ -91,24 +91,33 @@ Token::Type Token::getTokenType(char Identifier) {
 
 // Function to check if there's no meaningful text behind
 bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
-  if (Idx == 0 || Tokens[Idx - 1].getType() != Token::Type::Text)
+  if(Idx == 0)
+    return false;
+  
+  int PrevIdx = Idx - 1;
+  if (Tokens[PrevIdx].getType() != Token::Type::Text)
     return false;
+  
   const Token &PrevToken = Tokens[Idx - 1];
   StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
-  // We make a special exception for when previous token is empty
+  // We make an exception for when previous token is empty
   // and the current token is the second token
   // ex.
   //  " {{#section}}A{{/section}}"
-  // that's why we check if the token body is empty and the index is 1
+  // the output of this is
+  //  "A"
   return TokenBody.ends_with("\n") || (TokenBody.empty() && Idx == 1);
 }
 
 // Function to check if there's no meaningful text ahead
 bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
-  if (Idx >= Tokens.size() - 1 ||
-      Tokens[Idx + 1].getType() != Token::Type::Text)
+  if (Idx >= Tokens.size() - 1)
     return false;
-
+  
+  int PrevIdx = Idx + 1;
+  if (Tokens[Idx + 1].getType() != Token::Type::Text)
+    return false;
+  
   const Token &NextToken = Tokens[Idx + 1];
   StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
   return TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n");
@@ -121,10 +130,47 @@ bool requiresCleanUp(Token::Type T) {
          T == Token::Type::Partial;
 }
 
-// Simple tokenizer that splits the template into tokens
-// the mustache spec allows {{{ }}} to unescape variables
-// but we don't support that here unescape variable
-// is represented only by {{& variable}}
+// Adjust next token body if there is no text ahead
+// eg.
+//  The template string
+//  "{{! Comment }} \nLine 2"
+// would be considered as no text ahead and should be render as
+//  " Line 2"
+void stripTokenAhead(SmallVector<Token, 0>& Tokens, size_t Idx) {
+  Token &NextToken = Tokens[Idx + 1];
+  StringRef NextTokenBody = NextToken.getTokenBody();
+  // cut off the leading newline which could be \n or \r\n
+  if (NextTokenBody.starts_with("\r\n"))
+    NextToken.setTokenBody(NextTokenBody.substr(2));
+  else if (NextTokenBody.starts_with("\n"))
+    NextToken.setTokenBody(NextTokenBody.substr(1));
+}
+
+// Adjust previous token body if there no text behind
+// eg.
+//  The template string
+//  " \t{{#section}}A{{/section}}"
+// would be considered as no text ahead and should be render as
+//  "A"
+// The exception for this is partial tag which requires us to
+// keep track of the indentation once it's rendered
+void stripTokenBefore(SmallVector<Token, 0>& Tokens, 
+                      size_t Idx,
+                      Token& CurrentToken,
+                      Token::Type CurrentType) {
+  Token &PrevToken = Tokens[Idx - 1];
+  StringRef PrevTokenBody = PrevToken.getTokenBody();
+  StringRef Unindented = PrevTokenBody.rtrim(" \t\v");
+  size_t Indentation = PrevTokenBody.size() - Unindented.size();
+  if (CurrentType != Token::Type::Partial)
+    PrevToken.setTokenBody(Unindented);
+  CurrentToken.setIndentation(Indentation);
+}
+
+// Simple tokenizer that splits the template into tokens.
+// The mustache spec allows {{{ }}} to unescape variables
+// but we don't support that here. An unescape variable
+// is represented only by {{& variable}}.
 SmallVector<Token, 0> tokenize(StringRef Template) {
   SmallVector<Token, 0> Tokens;
   StringRef Open("{{");
@@ -160,7 +206,11 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
     Tokens.emplace_back(Template.substr(Start));
 
   // Fix up white spaces for:
-  //  open sections/inverted sections/close section/comment
+  //   - open sections
+  //   - inverted sections
+  //   - close sections
+  //   - comments
+  //
   // This loop attempts to find standalone tokens and tries to trim out
   // the surrounding whitespace.
   // For example:
@@ -178,47 +228,22 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
     if (!RequiresCleanUp)
       continue;
 
-    // We adjust the token body if there's no text behind or ahead a token is
-    // considered surrounded by no text if the right of the previous token
-    // is a newline followed by spaces or if the left of the next token
-    // is spaces followed by a newline
+    // We adjust the token body if there's no text behind or ahead. 
+    // A token is considered to have no text ahead if the right of the previous
+    // token is a newline followed by spaces.
+    // A token is considered to have no text behind if the left of the next 
+    // token is spaces followed by a newline.
     // eg.
     //  "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
-
     bool NoTextBehind = noTextBehind(Idx, Tokens);
     bool NoTextAhead = noTextAhead(Idx, Tokens);
-
-    // Adjust next token body if there is no text ahead
-    // eg.
-    //  The template string
-    //  "{{! Comment }} \nLine 2"
-    // would be considered as no text ahead and should be render as
-    //  " Line 2"
+    
     if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0)) {
-      Token &NextToken = Tokens[Idx + 1];
-      StringRef NextTokenBody = NextToken.getTokenBody();
-      // cut off the leading newline which could be \n or \r\n
-      if (NextTokenBody.starts_with("\r\n"))
-        NextToken.setTokenBody(NextTokenBody.substr(2));
-      else if (NextTokenBody.starts_with("\n"))
-        NextToken.setTokenBody(NextTokenBody.substr(1));
+      stripTokenAhead(Tokens, Idx);
     }
-    // Adjust previous token body if there no text behind
-    // eg.
-    //  The template string
-    //  " \t{{#section}}A{{/section}}"
-    // would be considered as no text ahead and should be render as
-    //  "A"
-    // The exception for this is partial tag which requires us to
-    // keep track of the indentation once it's rendered
+    
     if (((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx))) {
-      Token &PrevToken = Tokens[Idx - 1];
-      StringRef PrevTokenBody = PrevToken.getTokenBody();
-      StringRef Unindented = PrevTokenBody.rtrim(" \t\v");
-      size_t Indentation = PrevTokenBody.size() - Unindented.size();
-      if (CurrentType != Token::Type::Partial)
-        PrevToken.setTokenBody(Unindented);
-      CurrentToken.setIndentation(Indentation);
+      stripTokenBefore(Tokens, Idx, CurrentToken, CurrentType);
     }
   }
   return Tokens;
@@ -304,25 +329,25 @@ void Parser::parseMustache(ASTNode *Parent) {
       Parent->addChild(CurrentNode);
       break;
     }
+    case Token::Type::Comment:
+      break;
     case Token::Type::SectionClose:
       return;
-    default:
-      break;
     }
   }
 }
 
 StringRef Template::render(Value &Data) {
-  BumpPtrAllocator LocalAllocator;
-  Tree->setUpContext(&LocalAllocator);
   Tree->render(Data, Output);
+  LocalAllocator.Reset();
   return Output.str();
 }
 
 void Template::registerPartial(StringRef Name, StringRef Partial) {
   Parser P = Parser(Partial, Allocator);
   ASTNode *PartialTree = P.parse();
-  PartialTree->setUpNode(Partials, Lambdas, SectionLambdas, Escapes);
+  PartialTree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas, 
+                         Escapes);
   Partials.insert(std::make_pair(Name, PartialTree));
 }
 
@@ -344,7 +369,8 @@ Template::Template(StringRef TemplateStr) {
                                             {'"', """},
                                             {'\'', "'"}};
   registerEscape(HtmlEntities);
-  Tree->setUpNode(Partials, Lambdas, SectionLambdas, Escapes);
+  Tree->setUpNode(LocalAllocator,
+                  Partials, Lambdas, SectionLambdas, Escapes);
 }
 
 void toJsonString(const Value &Data, SmallString<0> &Output) {
@@ -494,7 +520,6 @@ void ASTNode::renderChild(const Value &Contexts, SmallString<0> &Output) {
 
 void ASTNode::renderPartial(const Value &Contexts, SmallString<0> &Output,
                             ASTNode *Partial) {
-  Partial->setUpContext(Allocator);
   Partial->render(Contexts, Output);
   addIndentation(Output, Indentation);
 }
@@ -506,8 +531,8 @@ void ASTNode::renderLambdas(const Value &Contexts, SmallString<0> &Output,
   toJsonString(LambdaResult, LambdaStr);
   Parser P = Parser(LambdaStr, *Allocator);
   ASTNode *LambdaNode = P.parse();
-  LambdaNode->setUpNode(*Partials, *Lambdas, *SectionLambdas, *Escapes);
-  LambdaNode->setUpContext(Allocator);
+  LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas, 
+                        *Escapes);
   LambdaNode->render(Contexts, Output);
   if (T == Variable)
     Output = escapeString(Output, *Escapes);
@@ -523,30 +548,24 @@ void ASTNode::renderSectionLambdas(const Value &Contexts,
   toJsonString(Return, LambdaStr);
   Parser P = Parser(LambdaStr, *Allocator);
   ASTNode *LambdaNode = P.parse();
-  LambdaNode->setUpNode(*Partials, *Lambdas, *SectionLambdas, *Escapes);
-  LambdaNode->setUpContext(Allocator);
+  LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas, 
+                        *Escapes);
   LambdaNode->render(Contexts, Output);
   return;
 }
 
-void ASTNode::setUpNode(StringMap<ASTNode *> &Par, StringMap<Lambda> &L,
+void ASTNode::setUpNode(llvm::BumpPtrAllocator &Alloc,
+                        StringMap<ASTNode *> &Par, StringMap<Lambda> &L,
                         StringMap<SectionLambda> &SC,
                         DenseMap<char, StringRef> &E) {
   // Passed down datastructures needed for rendering to
   // the children nodes. This must be called before rendering
+  Allocator = &Alloc;
   Partials = &Par;
   Lambdas = &L;
   SectionLambdas = &SC;
   Escapes = &E;
   for (ASTNode *Child : Children)
-    Child->setUpNode(Par, L, SC, E);
+    Child->setUpNode(Alloc, Par, L, SC, E);
 }
 
-void ASTNode::setUpContext(llvm::BumpPtrAllocator *Alloc) {
-  // Passed down the allocator to the children nodes.
-  // Each render call requires a allocator for generating
-  // Partial/Section nodes
-  Allocator = Alloc;
-  for (ASTNode *Child : Children)
-    Child->setUpContext(Alloc);
-}

>From b0ce196c46d0b488afd013861889f1eaf8cfffc6 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 16:16:49 -0400
Subject: [PATCH 36/62] [llvm] address comments

---
 llvm/include/llvm/Support/Mustache.h | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 62c15e05850e60..e475d05e108a75 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -96,7 +96,7 @@ class Token {
 
   StringRef getRawBody() const { return RawBody; };
 
-  void setTokenBody(SmallString<128> NewBody) { TokenBody = NewBody; };
+  void setTokenBody(StringRef NewBody) { TokenBody = NewBody.str(); };
 
   Accessor getAccessor() const { return Accessor; };
 
@@ -112,7 +112,7 @@ class Token {
   size_t Indentation;
   Type TokenType;
   // RawBody is the original string that was tokenized
-  SmallString<128> RawBody;
+  SmallString<0> RawBody;
   Accessor Accessor;
   // TokenBody is the original string with the identifier removed
   SmallString<0> TokenBody;
@@ -130,16 +130,16 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), ParentContext(nullptr) {};
+  ASTNode() : T(Type::Root), ParentContext(nullptr){};
 
   ASTNode(StringRef Body, ASTNode *Parent)
       : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
-        Indentation(0) {};
+        Indentation(0){};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        ParentContext(nullptr), Indentation(0) {};
+        ParentContext(nullptr), Indentation(0){};
 
   void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 
@@ -151,14 +151,12 @@ class ASTNode {
 
   void render(const llvm::json::Value &Data, SmallString<0> &Output);
 
-  void setUpNode(llvm::BumpPtrAllocator &Alloc,
-                 StringMap<ASTNode *> &Partials, 
+  void setUpNode(llvm::BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Partials,
                  StringMap<Lambda> &Lambdas,
                  StringMap<SectionLambda> &SectionLambdas,
                  DenseMap<char, StringRef> &Escapes);
-  
+
 private:
-  
   void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
                      Lambda &L);
 

>From 561c7eb095baf4648b0756d19fdd392f07a9586f Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 16:28:09 -0400
Subject: [PATCH 37/62] clang-format

---
 llvm/lib/Support/Mustache.cpp | 36 ++++++++++++++++-------------------
 1 file changed, 16 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 0fa88c0896f6d3..2af4307fae02a3 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -91,13 +91,13 @@ Token::Type Token::getTokenType(char Identifier) {
 
 // Function to check if there's no meaningful text behind
 bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
-  if(Idx == 0)
+  if (Idx == 0)
     return false;
-  
+
   int PrevIdx = Idx - 1;
   if (Tokens[PrevIdx].getType() != Token::Type::Text)
     return false;
-  
+
   const Token &PrevToken = Tokens[Idx - 1];
   StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
   // We make an exception for when previous token is empty
@@ -113,11 +113,11 @@ bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
 bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
   if (Idx >= Tokens.size() - 1)
     return false;
-  
+
   int PrevIdx = Idx + 1;
   if (Tokens[Idx + 1].getType() != Token::Type::Text)
     return false;
-  
+
   const Token &NextToken = Tokens[Idx + 1];
   StringRef TokenBody = NextToken.getRawBody().ltrim(" ");
   return TokenBody.starts_with("\r\n") || TokenBody.starts_with("\n");
@@ -136,7 +136,7 @@ bool requiresCleanUp(Token::Type T) {
 //  "{{! Comment }} \nLine 2"
 // would be considered as no text ahead and should be render as
 //  " Line 2"
-void stripTokenAhead(SmallVector<Token, 0>& Tokens, size_t Idx) {
+void stripTokenAhead(SmallVector<Token, 0> &Tokens, size_t Idx) {
   Token &NextToken = Tokens[Idx + 1];
   StringRef NextTokenBody = NextToken.getTokenBody();
   // cut off the leading newline which could be \n or \r\n
@@ -154,10 +154,8 @@ void stripTokenAhead(SmallVector<Token, 0>& Tokens, size_t Idx) {
 //  "A"
 // The exception for this is partial tag which requires us to
 // keep track of the indentation once it's rendered
-void stripTokenBefore(SmallVector<Token, 0>& Tokens, 
-                      size_t Idx,
-                      Token& CurrentToken,
-                      Token::Type CurrentType) {
+void stripTokenBefore(SmallVector<Token, 0> &Tokens, size_t Idx,
+                      Token &CurrentToken, Token::Type CurrentType) {
   Token &PrevToken = Tokens[Idx - 1];
   StringRef PrevTokenBody = PrevToken.getTokenBody();
   StringRef Unindented = PrevTokenBody.rtrim(" \t\v");
@@ -228,20 +226,20 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
     if (!RequiresCleanUp)
       continue;
 
-    // We adjust the token body if there's no text behind or ahead. 
+    // We adjust the token body if there's no text behind or ahead.
     // A token is considered to have no text ahead if the right of the previous
     // token is a newline followed by spaces.
-    // A token is considered to have no text behind if the left of the next 
+    // A token is considered to have no text behind if the left of the next
     // token is spaces followed by a newline.
     // eg.
     //  "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
     bool NoTextBehind = noTextBehind(Idx, Tokens);
     bool NoTextAhead = noTextAhead(Idx, Tokens);
-    
+
     if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0)) {
       stripTokenAhead(Tokens, Idx);
     }
-    
+
     if (((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx))) {
       stripTokenBefore(Tokens, Idx, CurrentToken, CurrentType);
     }
@@ -346,7 +344,7 @@ StringRef Template::render(Value &Data) {
 void Template::registerPartial(StringRef Name, StringRef Partial) {
   Parser P = Parser(Partial, Allocator);
   ASTNode *PartialTree = P.parse();
-  PartialTree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas, 
+  PartialTree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas,
                          Escapes);
   Partials.insert(std::make_pair(Name, PartialTree));
 }
@@ -369,8 +367,7 @@ Template::Template(StringRef TemplateStr) {
                                             {'"', """},
                                             {'\'', "'"}};
   registerEscape(HtmlEntities);
-  Tree->setUpNode(LocalAllocator,
-                  Partials, Lambdas, SectionLambdas, Escapes);
+  Tree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas, Escapes);
 }
 
 void toJsonString(const Value &Data, SmallString<0> &Output) {
@@ -531,7 +528,7 @@ void ASTNode::renderLambdas(const Value &Contexts, SmallString<0> &Output,
   toJsonString(LambdaResult, LambdaStr);
   Parser P = Parser(LambdaStr, *Allocator);
   ASTNode *LambdaNode = P.parse();
-  LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas, 
+  LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
                         *Escapes);
   LambdaNode->render(Contexts, Output);
   if (T == Variable)
@@ -548,7 +545,7 @@ void ASTNode::renderSectionLambdas(const Value &Contexts,
   toJsonString(Return, LambdaStr);
   Parser P = Parser(LambdaStr, *Allocator);
   ASTNode *LambdaNode = P.parse();
-  LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas, 
+  LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
                         *Escapes);
   LambdaNode->render(Contexts, Output);
   return;
@@ -568,4 +565,3 @@ void ASTNode::setUpNode(llvm::BumpPtrAllocator &Alloc,
   for (ASTNode *Child : Children)
     Child->setUpNode(Alloc, Par, L, SC, E);
 }
-

>From 96f69907c1be3418f617ecc10d192538e61633ec Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 16:28:56 -0400
Subject: [PATCH 38/62] clang-format

---
 llvm/include/llvm/Support/Mustache.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index e475d05e108a75..1130434a265e42 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -130,16 +130,16 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), ParentContext(nullptr){};
+  ASTNode() : T(Type::Root), ParentContext(nullptr) {};
 
   ASTNode(StringRef Body, ASTNode *Parent)
       : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
-        Indentation(0){};
+        Indentation(0) {};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        ParentContext(nullptr), Indentation(0){};
+        ParentContext(nullptr), Indentation(0) {};
 
   void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 

>From a247423a1be655c5fb432f56929361149ddbea66 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 10 Oct 2024 13:02:53 -0400
Subject: [PATCH 39/62] [llvm] factor out internal classes

---
 llvm/include/llvm/Support/Mustache.h | 110 +------------------------
 llvm/lib/Support/Mustache.cpp        | 116 ++++++++++++++++++++++++++-
 2 files changed, 116 insertions(+), 110 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index 1130434a265e42..ab8c02fa732ab8 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -75,115 +75,7 @@ using Accessor = SmallVector<SmallString<0>>;
 using Lambda = std::function<llvm::json::Value()>;
 using SectionLambda = std::function<llvm::json::Value(StringRef)>;
 
-class Token {
-public:
-  enum class Type {
-    Text,
-    Variable,
-    Partial,
-    SectionOpen,
-    SectionClose,
-    InvertSectionOpen,
-    UnescapeVariable,
-    Comment,
-  };
-
-  Token(StringRef Str);
-
-  Token(StringRef RawBody, StringRef Str, char Identifier);
-
-  StringRef getTokenBody() const { return TokenBody; };
-
-  StringRef getRawBody() const { return RawBody; };
-
-  void setTokenBody(StringRef NewBody) { TokenBody = NewBody.str(); };
-
-  Accessor getAccessor() const { return Accessor; };
-
-  Type getType() const { return TokenType; };
-
-  void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
-
-  size_t getIndentation() const { return Indentation; };
-
-  static Type getTokenType(char Identifier);
-
-private:
-  size_t Indentation;
-  Type TokenType;
-  // RawBody is the original string that was tokenized
-  SmallString<0> RawBody;
-  Accessor Accessor;
-  // TokenBody is the original string with the identifier removed
-  SmallString<0> TokenBody;
-};
-
-class ASTNode {
-public:
-  enum Type {
-    Root,
-    Text,
-    Partial,
-    Variable,
-    UnescapeVariable,
-    Section,
-    InvertSection,
-  };
-
-  ASTNode() : T(Type::Root), ParentContext(nullptr) {};
-
-  ASTNode(StringRef Body, ASTNode *Parent)
-      : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
-        Indentation(0) {};
-
-  // Constructor for Section/InvertSection/Variable/UnescapeVariable
-  ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
-      : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        ParentContext(nullptr), Indentation(0) {};
-
-  void addChild(ASTNode *Child) { Children.emplace_back(Child); };
-
-  void setBody(StringRef NewBody) { Body = NewBody; };
-
-  void setRawBody(StringRef NewBody) { RawBody = NewBody; };
-
-  void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
-
-  void render(const llvm::json::Value &Data, SmallString<0> &Output);
-
-  void setUpNode(llvm::BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Partials,
-                 StringMap<Lambda> &Lambdas,
-                 StringMap<SectionLambda> &SectionLambdas,
-                 DenseMap<char, StringRef> &Escapes);
-
-private:
-  void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
-                     Lambda &L);
-
-  void renderSectionLambdas(const llvm::json::Value &Contexts,
-                            SmallString<0> &Output, SectionLambda &L);
-
-  void renderPartial(const llvm::json::Value &Contexts, SmallString<0> &Output,
-                     ASTNode *Partial);
-
-  void renderChild(const llvm::json::Value &Context, SmallString<0> &Output);
-
-  const llvm::json::Value *findContext();
-
-  llvm::BumpPtrAllocator *Allocator;
-  StringMap<ASTNode *> *Partials;
-  StringMap<Lambda> *Lambdas;
-  StringMap<SectionLambda> *SectionLambdas;
-  DenseMap<char, StringRef> *Escapes;
-  Type T;
-  size_t Indentation;
-  SmallString<0> RawBody;
-  SmallString<0> Body;
-  ASTNode *Parent;
-  std::vector<ASTNode *> Children;
-  const Accessor Accessor;
-  const llvm::json::Value *ParentContext;
-};
+class ASTNode;
 
 // A Template represents the container for the AST and the partials
 // and Lambdas that are registered with it.
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 2af4307fae02a3..031481955feabb 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -12,7 +12,119 @@
 
 using namespace llvm;
 using namespace llvm::json;
-using namespace llvm::mustache;
+
+namespace llvm {
+namespace mustache {
+
+class Token {
+public:
+  enum class Type {
+    Text,
+    Variable,
+    Partial,
+    SectionOpen,
+    SectionClose,
+    InvertSectionOpen,
+    UnescapeVariable,
+    Comment,
+  };
+  
+  Token(StringRef Str);
+
+  Token(StringRef RawBody, StringRef Str, char Identifier);
+
+  StringRef getTokenBody() const { return TokenBody; };
+
+  StringRef getRawBody() const { return RawBody; };
+
+  void setTokenBody(StringRef NewBody) { TokenBody = NewBody.str(); };
+
+  Accessor getAccessor() const { return Accessor; };
+
+  Type getType() const { return TokenType; };
+
+  void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
+
+  size_t getIndentation() const { return Indentation; };
+
+  static Type getTokenType(char Identifier);
+
+private:
+  size_t Indentation;
+  Type TokenType;
+  // RawBody is the original string that was tokenized
+  SmallString<0> RawBody;
+  Accessor Accessor;
+  // TokenBody is the original string with the identifier removed
+  SmallString<0> TokenBody;
+};
+
+class ASTNode {
+public:
+  enum Type {
+    Root,
+    Text,
+    Partial,
+    Variable,
+    UnescapeVariable,
+    Section,
+    InvertSection,
+  };
+
+  ASTNode() : T(Type::Root), ParentContext(nullptr) {};
+
+  ASTNode(StringRef Body, ASTNode *Parent)
+      : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
+        Indentation(0) {};
+
+  // Constructor for Section/InvertSection/Variable/UnescapeVariable
+  ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
+      : T(T), Parent(Parent), Children({}), Accessor(Accessor),
+        ParentContext(nullptr), Indentation(0) {};
+
+  void addChild(ASTNode *Child) { Children.emplace_back(Child); };
+
+  void setBody(StringRef NewBody) { Body = NewBody; };
+
+  void setRawBody(StringRef NewBody) { RawBody = NewBody; };
+
+  void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
+
+  void render(const llvm::json::Value &Data, SmallString<0> &Output);
+
+  void setUpNode(llvm::BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Partials,
+                 StringMap<Lambda> &Lambdas,
+                 StringMap<SectionLambda> &SectionLambdas,
+                 DenseMap<char, StringRef> &Escapes);
+
+private:
+  void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
+                     Lambda &L);
+
+  void renderSectionLambdas(const llvm::json::Value &Contexts,
+                            SmallString<0> &Output, SectionLambda &L);
+
+  void renderPartial(const llvm::json::Value &Contexts, SmallString<0> &Output,
+                     ASTNode *Partial);
+
+  void renderChild(const llvm::json::Value &Context, SmallString<0> &Output);
+
+  const llvm::json::Value *findContext();
+
+  llvm::BumpPtrAllocator *Allocator;
+  StringMap<ASTNode *> *Partials;
+  StringMap<Lambda> *Lambdas;
+  StringMap<SectionLambda> *SectionLambdas;
+  DenseMap<char, StringRef> *Escapes;
+  Type T;
+  size_t Indentation;
+  SmallString<0> RawBody;
+  SmallString<0> Body;
+  ASTNode *Parent;
+  std::vector<ASTNode *> Children;
+  const Accessor Accessor;
+  const llvm::json::Value *ParentContext;
+};
 
 SmallString<0> escapeString(StringRef Input,
                             DenseMap<char, StringRef> &Escape) {
@@ -565,3 +677,5 @@ void ASTNode::setUpNode(llvm::BumpPtrAllocator &Alloc,
   for (ASTNode *Child : Children)
     Child->setUpNode(Alloc, Par, L, SC, E);
 }
+} // namespace mustache
+} // namespace llvm

>From a0e7d48e3247f596ab0103d52fe207bc59cb827e Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 10 Oct 2024 13:17:12 -0400
Subject: [PATCH 40/62] [llvm] clang-format

---
 llvm/lib/Support/Mustache.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 031481955feabb..bf0c14eb6e5142 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -28,7 +28,7 @@ class Token {
     UnescapeVariable,
     Comment,
   };
-  
+
   Token(StringRef Str);
 
   Token(StringRef RawBody, StringRef Str, char Identifier);

>From 4165fec73d6b329b33b106471bffd00a2922b8a3 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 11 Oct 2024 21:29:15 -0400
Subject: [PATCH 41/62] [llvm] refactor mustache to raw_ostream

---
 llvm/include/llvm/Support/Mustache.h    |   2 +-
 llvm/lib/Support/Mustache.cpp           | 176 ++++++----
 llvm/unittests/Support/MustacheTest.cpp | 442 ++++++++++++++++++------
 3 files changed, 438 insertions(+), 182 deletions(-)

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
index ab8c02fa732ab8..110cacde22ca1e 100644
--- a/llvm/include/llvm/Support/Mustache.h
+++ b/llvm/include/llvm/Support/Mustache.h
@@ -83,7 +83,7 @@ class Template {
 public:
   Template(StringRef TemplateStr);
 
-  StringRef render(llvm::json::Value &Data);
+  void render(llvm::json::Value &Data, llvm::raw_ostream &OS);
 
   void registerPartial(StringRef Name, StringRef Partial);
 
diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index bf0c14eb6e5142..2d7a707cc50b4f 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -8,6 +8,7 @@
 
 #include "llvm/Support/Mustache.h"
 #include "llvm/Support/Error.h"
+#include "llvm/Support/raw_ostream.h"
 #include <sstream>
 
 using namespace llvm;
@@ -90,7 +91,7 @@ class ASTNode {
 
   void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
 
-  void render(const llvm::json::Value &Data, SmallString<0> &Output);
+  void render(const llvm::json::Value &Data, llvm::raw_ostream &OS);
 
   void setUpNode(llvm::BumpPtrAllocator &Alloc, StringMap<ASTNode *> &Partials,
                  StringMap<Lambda> &Lambdas,
@@ -98,16 +99,19 @@ class ASTNode {
                  DenseMap<char, StringRef> &Escapes);
 
 private:
-  void renderLambdas(const llvm::json::Value &Contexts, SmallString<0> &Output,
+  void renderLambdas(const llvm::json::Value &Contexts,
+                     llvm::raw_ostream &OS,
                      Lambda &L);
 
   void renderSectionLambdas(const llvm::json::Value &Contexts,
-                            SmallString<0> &Output, SectionLambda &L);
+                            llvm::raw_ostream &OS, SectionLambda &L);
 
-  void renderPartial(const llvm::json::Value &Contexts, SmallString<0> &Output,
+  void renderPartial(const llvm::json::Value &Contexts, 
+                     llvm::raw_ostream &OS,
                      ASTNode *Partial);
 
-  void renderChild(const llvm::json::Value &Context, SmallString<0> &Output);
+  void renderChild(const llvm::json::Value &Context, 
+                   llvm::raw_ostream &OS);
 
   const llvm::json::Value *findContext();
 
@@ -126,19 +130,62 @@ class ASTNode {
   const llvm::json::Value *ParentContext;
 };
 
-SmallString<0> escapeString(StringRef Input,
-                            DenseMap<char, StringRef> &Escape) {
-  SmallString<0> Output;
-  Output.reserve(Input.size());
-  for (char C : Input) {
-    auto It = Escape.find(C);
-    if (It != Escape.end())
-      Output += It->getSecond();
-    else
-      Output += C;
+// Custom stream to escape strings
+class EscapeStringStream : public raw_ostream {
+public:
+  explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
+                              DenseMap<char, StringRef> &Escape)
+      : Escape(Escape), WrappedStream(WrappedStream) {}
+  
+protected:
+  // This is where data gets written
+  void write_impl(const char *Ptr, size_t Size) override {
+    llvm::StringRef Data(Ptr, Size);
+    for (char C : Data) {
+      auto It = Escape.find(C);
+      if (It != Escape.end())
+        WrappedStream << It->getSecond();
+      else
+        WrappedStream << C;
+    }
   }
-  return Output;
-}
+
+  uint64_t current_pos() const override {
+    return WrappedStream.tell();
+  }
+
+private:
+  DenseMap<char, StringRef> &Escape;
+  llvm::raw_ostream &WrappedStream;
+};
+
+
+class AddIndentationStringStream : public raw_ostream {
+public:
+  explicit AddIndentationStringStream(llvm::raw_ostream &WrappedStream,
+                                      size_t Indentation)
+      : Indentation(Indentation), WrappedStream(WrappedStream) {}
+  
+protected:
+  // This is where data gets written
+  void write_impl(const char *Ptr, size_t Size) override {
+    llvm::StringRef Data(Ptr, Size);
+    std::string Indent(Indentation, ' ');
+    for (char C : Data) {
+      WrappedStream << C;
+      if (C == '\n')
+        WrappedStream << Indent;
+    }
+  }
+  
+  uint64_t current_pos() const override {
+    return WrappedStream.tell();
+  }
+
+private:
+  size_t Indentation;
+  llvm::raw_ostream &WrappedStream;
+};
 
 Accessor split(StringRef Str, char Delimiter) {
   Accessor Tokens;
@@ -155,17 +202,6 @@ Accessor split(StringRef Str, char Delimiter) {
   return Tokens;
 }
 
-void addIndentation(llvm::SmallString<0> &PartialStr, size_t IndentationSize) {
-  std::string Indent(IndentationSize, ' ');
-  llvm::SmallString<0> Result;
-  for (size_t I = 0; I < PartialStr.size(); ++I) {
-    Result.push_back(PartialStr[I]);
-    if (PartialStr[I] == '\n' && I < PartialStr.size() - 1)
-      Result.append(Indent);
-  }
-  PartialStr = Result;
-}
-
 Token::Token(StringRef RawBody, StringRef InnerBody, char Identifier)
     : RawBody(RawBody), TokenBody(InnerBody), Indentation(0) {
   TokenType = getTokenType(Identifier);
@@ -226,8 +262,8 @@ bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
   if (Idx >= Tokens.size() - 1)
     return false;
 
-  int PrevIdx = Idx + 1;
-  if (Tokens[Idx + 1].getType() != Token::Type::Text)
+  int NextIdx = Idx + 1;
+  if (Tokens[NextIdx].getType() != Token::Type::Text)
     return false;
 
   const Token &NextToken = Tokens[Idx + 1];
@@ -447,10 +483,9 @@ void Parser::parseMustache(ASTNode *Parent) {
   }
 }
 
-StringRef Template::render(Value &Data) {
-  Tree->render(Data, Output);
+void Template::render(Value &Data, llvm::raw_ostream &OS) {
+  Tree->render(Data, OS);
   LocalAllocator.Reset();
-  return Output.str();
 }
 
 void Template::registerPartial(StringRef Name, StringRef Partial) {
@@ -482,23 +517,23 @@ Template::Template(StringRef TemplateStr) {
   Tree->setUpNode(LocalAllocator, Partials, Lambdas, SectionLambdas, Escapes);
 }
 
-void toJsonString(const Value &Data, SmallString<0> &Output) {
+void toJsonString(const Value &Data, raw_ostream &OS) {
   if (Data.getAsNull())
     return;
   if (auto *Arr = Data.getAsArray())
     if (Arr->empty())
       return;
   if (Data.getAsString()) {
-    Output = Data.getAsString()->str();
+    OS << Data.getAsString()->str();
     return;
   }
   if (auto Num = Data.getAsNumber()) {
     std::ostringstream Oss;
     Oss << *Num;
-    Output = Oss.str();
+    OS << Oss.str();
     return;
   }
-  Output = formatv("{0:2}", Data);
+  OS << formatv("{0:2}", Data);
 }
 
 bool isFalsey(const Value &V) {
@@ -507,7 +542,7 @@ bool isFalsey(const Value &V) {
          (V.getAsObject() && V.getAsObject()->empty());
 }
 
-void ASTNode::render(const Value &Data, SmallString<0> &Output) {
+void ASTNode::render(const Value &Data, raw_ostream &OS) {
 
   ParentContext = &Data;
   const Value *ContextPtr = T == Root ? ParentContext : findContext();
@@ -515,33 +550,33 @@ void ASTNode::render(const Value &Data, SmallString<0> &Output) {
 
   switch (T) {
   case Root:
-    renderChild(Data, Output);
+    renderChild(Data, OS);
     return;
   case Text:
-    Output = Body;
+    OS << Body;
     return;
   case Partial: {
     auto Partial = Partials->find(Accessor[0]);
     if (Partial != Partials->end())
-      renderPartial(Data, Output, Partial->getValue());
+      renderPartial(Data, OS, Partial->getValue());
     return;
   }
   case Variable: {
     auto Lambda = Lambdas->find(Accessor[0]);
     if (Lambda != Lambdas->end())
-      renderLambdas(Data, Output, Lambda->getValue());
+      renderLambdas(Data, OS, Lambda->getValue());
     else {
-      toJsonString(Context, Output);
-      Output = escapeString(Output, *Escapes);
+      EscapeStringStream ES(OS, *Escapes);
+      toJsonString(Context, ES);
     }
     return;
   }
   case UnescapeVariable: {
     auto Lambda = Lambdas->find(Accessor[0]);
     if (Lambda != Lambdas->end())
-      renderLambdas(Data, Output, Lambda->getValue());
+      renderLambdas(Data, OS, Lambda->getValue());
     else
-      toJsonString(Context, Output);
+      toJsonString(Context, OS);
     return;
   }
   case Section: {
@@ -552,29 +587,24 @@ void ASTNode::render(const Value &Data, SmallString<0> &Output) {
       return;
 
     if (IsLambda) {
-      renderSectionLambdas(Data, Output, SectionLambda->getValue());
+      renderSectionLambdas(Data, OS, SectionLambda->getValue());
       return;
     }
 
     if (Context.getAsArray()) {
-      SmallString<0> Result;
       const json::Array *Arr = Context.getAsArray();
       for (const Value &V : *Arr)
-        renderChild(V, Result);
-      Output = Result;
+        renderChild(V, OS);
       return;
     }
-
-    renderChild(Context, Output);
+    renderChild(Context, OS);
     return;
   }
   case InvertSection: {
     bool IsLambda = SectionLambdas->find(Accessor[0]) != SectionLambdas->end();
-
     if (!isFalsey(Context) || IsLambda)
       return;
-
-    renderChild(Context, Output);
+    renderChild(Context, OS);
     return;
   }
   }
@@ -619,47 +649,51 @@ const Value *ASTNode::findContext() {
   return Context;
 }
 
-void ASTNode::renderChild(const Value &Contexts, SmallString<0> &Output) {
+void ASTNode::renderChild(const Value &Contexts, llvm::raw_ostream &OS) {
   for (ASTNode *Child : Children) {
-    SmallString<0> ChildResult;
-    Child->render(Contexts, ChildResult);
-    Output += ChildResult;
+    Child->render(Contexts, OS);
   }
 }
 
-void ASTNode::renderPartial(const Value &Contexts, SmallString<0> &Output,
+void ASTNode::renderPartial(const Value &Contexts, llvm::raw_ostream &OS,
                             ASTNode *Partial) {
-  Partial->render(Contexts, Output);
-  addIndentation(Output, Indentation);
+  AddIndentationStringStream IS(OS, Partial->Indentation);
+  Partial->render(Contexts, IS);
 }
 
-void ASTNode::renderLambdas(const Value &Contexts, SmallString<0> &Output,
+void ASTNode::renderLambdas(const Value &Contexts, llvm::raw_ostream &OS,
                             Lambda &L) {
   Value LambdaResult = L();
-  SmallString<0> LambdaStr;
-  toJsonString(LambdaResult, LambdaStr);
+  std::string LambdaStr;
+  raw_string_ostream Output(LambdaStr);
+  toJsonString(LambdaResult, Output);
   Parser P = Parser(LambdaStr, *Allocator);
   ASTNode *LambdaNode = P.parse();
   LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
                         *Escapes);
-  LambdaNode->render(Contexts, Output);
+  
+  EscapeStringStream ES(OS, *Escapes);
   if (T == Variable)
-    Output = escapeString(Output, *Escapes);
+    LambdaNode->render(Contexts, ES);
+  else
+    LambdaNode->render(Contexts, OS);
+  
   return;
 }
 
 void ASTNode::renderSectionLambdas(const Value &Contexts,
-                                   SmallString<0> &Output, SectionLambda &L) {
+                                   llvm::raw_ostream &OS, SectionLambda &L) {
   Value Return = L(RawBody);
   if (isFalsey(Return))
     return;
-  SmallString<0> LambdaStr;
-  toJsonString(Return, LambdaStr);
+  std::string LambdaStr;
+  raw_string_ostream Output(LambdaStr);
+  toJsonString(Return, Output);
   Parser P = Parser(LambdaStr, *Allocator);
   ASTNode *LambdaNode = P.parse();
   LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
                         *Escapes);
-  LambdaNode->render(Contexts, Output);
+  LambdaNode->render(Contexts, OS);
   return;
 }
 
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index d120736be40856..afa47562c5b3d6 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -23,7 +23,9 @@ TEST(MustacheInterpolation, NoInterpolation) {
   // Mustache-free templates should render as-is.
   Value D = {};
   auto T = Template("Hello from {Mustache}!\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Hello from {Mustache}!\n", Out);
 }
 
@@ -31,7 +33,9 @@ TEST(MustacheInterpolation, BasicInterpolation) {
   // Unadorned tags should interpolate content into the template.
   Value D = Object{{"subject", "World"}};
   auto T = Template("Hello, {{subject}}!");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Hello, World!", Out);
 }
 
@@ -39,7 +43,9 @@ TEST(MustacheInterpolation, NoReinterpolation) {
   // Interpolated tag output should not be re-interpolated.
   Value D = Object{{"template", "{{planet}}"}, {"planet", "Earth"}};
   auto T = Template("{{template}}: {{planet}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("{{planet}}: Earth", Out);
 }
 
@@ -49,7 +55,9 @@ TEST(MustacheInterpolation, HTMLEscaping) {
       {"forbidden", "& \" < >"},
   };
   auto T = Template("These characters should be HTML escaped: {{forbidden}}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("These characters should be HTML escaped: & " < >\n",
             Out);
 }
@@ -61,7 +69,9 @@ TEST(MustacheInterpolation, Ampersand) {
   };
   auto T =
       Template("These characters should not be HTML escaped: {{&forbidden}}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
 }
 
@@ -69,7 +79,9 @@ TEST(MustacheInterpolation, BasicIntegerInterpolation) {
   // Integers should interpolate seamlessly.
   Value D = Object{{"mph", 85}};
   auto T = Template("{{mph}} miles an hour!");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("85 miles an hour!", Out);
 }
 
@@ -77,7 +89,9 @@ TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {
   // Integers should interpolate seamlessly.
   Value D = Object{{"mph", 85}};
   auto T = Template("{{&mph}} miles an hour!");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("85 miles an hour!", Out);
 }
 
@@ -85,7 +99,9 @@ TEST(MustacheInterpolation, BasicDecimalInterpolation) {
   // Decimals should interpolate seamlessly with proper significance.
   Value D = Object{{"power", 1.21}};
   auto T = Template("{{power}} jiggawatts!");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("1.21 jiggawatts!", Out);
 }
 
@@ -93,7 +109,9 @@ TEST(MustacheInterpolation, BasicNullInterpolation) {
   // Nulls should interpolate as the empty string.
   Value D = Object{{"cannot", nullptr}};
   auto T = Template("I ({{cannot}}) be seen!");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("I () be seen!", Out);
 }
 
@@ -101,7 +119,9 @@ TEST(MustacheInterpolation, AmpersandNullInterpolation) {
   // Nulls should interpolate as the empty string.
   Value D = Object{{"cannot", nullptr}};
   auto T = Template("I ({{&cannot}}) be seen!");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("I () be seen!", Out);
 }
 
@@ -109,7 +129,9 @@ TEST(MustacheInterpolation, BasicContextMissInterpolation) {
   // Failed context lookups should default to empty strings.
   Value D = Object{};
   auto T = Template("I ({{cannot}}) be seen!");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("I () be seen!", Out);
 }
 
@@ -117,7 +139,9 @@ TEST(MustacheInterpolation, DottedNamesBasicInterpolation) {
   // Dotted names should be considered a form of shorthand for sections.
   Value D = Object{{"person", Object{{"name", "Joe"}}}};
   auto T = Template("{{person.name}} == {{#person}}{{name}}{{/person}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Joe == Joe", Out);
 }
 
@@ -125,7 +149,9 @@ TEST(MustacheInterpolation, DottedNamesAmpersandInterpolation) {
   // Dotted names should be considered a form of shorthand for sections.
   Value D = Object{{"person", Object{{"name", "Joe"}}}};
   auto T = Template("{{&person.name}} == {{#person}}{{&name}}{{/person}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Joe == Joe", Out);
 }
 
@@ -138,7 +164,9 @@ TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
                        Object{{"d",
                                Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
   auto T = Template("{{a.b.c.d.e.name}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Phil", Out);
 }
 
@@ -146,7 +174,9 @@ TEST(MustacheInterpolation, DottedNamesBrokenChains) {
   // Any falsey value prior to the last part of the name should yield ''.
   Value D = Object{{"a", Object{}}};
   auto T = Template("{{a.b.c}} == ");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" == ", Out);
 }
 
@@ -155,7 +185,9 @@ TEST(MustacheInterpolation, DottedNamesBrokenChainResolution) {
   Value D =
       Object{{"a", Object{{"b", Object{}}}}, {"c", Object{{"name", "Jim"}}}};
   auto T = Template("{{a.b.c.name}} == ");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" == ", Out);
 }
 
@@ -170,7 +202,9 @@ TEST(MustacheInterpolation, DottedNamesInitialResolution) {
       {"b",
        Object{{"c", Object{{"d", Object{{"e", Object{{"name", "Wrong"}}}}}}}}}};
   auto T = Template("{{#a}}{{b.c.d.e.name}}{{/a}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Phil", Out);
 }
 
@@ -179,7 +213,9 @@ TEST(MustacheInterpolation, DottedNamesContextPrecedence) {
   Value D =
       Object{{"a", Object{{"b", Object{}}}}, {"b", Object{{"c", "ERROR"}}}};
   auto T = Template("{{#a}}{{b.c}}{{/a}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("", Out);
 }
 
@@ -187,7 +223,9 @@ TEST(MustacheInterpolation, DottedNamesAreNotSingleKeys) {
   // Dotted names shall not be parsed as single, atomic keys
   Value D = Object{{"a.b", "c"}};
   auto T = Template("{{a.b}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("", Out);
 }
 
@@ -195,7 +233,9 @@ TEST(MustacheInterpolation, DottedNamesNoMasking) {
   // Dotted Names in a given context are unavailable due to dot splitting
   Value D = Object{{"a.b", "c"}, {"a", Object{{"b", "d"}}}};
   auto T = Template("{{a.b}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("d", Out);
 }
 
@@ -203,7 +243,9 @@ TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
   // Unadorned tags should interpolate content into the template.
   Value D = "world";
   auto T = Template("Hello, {{.}}!\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Hello, world!\n", Out);
 }
 
@@ -211,7 +253,9 @@ TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {
   // Basic interpolation should be HTML escaped.
   Value D = "& \" < >";
   auto T = Template("These characters should not be HTML escaped: {{&.}}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
 }
 
@@ -219,7 +263,9 @@ TEST(MustacheInterpolation, ImplicitIteratorsInteger) {
   // Integers should interpolate seamlessly.
   Value D = 85;
   auto T = Template("{{.}} miles an hour!\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("85 miles an hour!\n", Out);
 }
 
@@ -227,7 +273,9 @@ TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
   // Interpolation should not alter surrounding whitespace.
   Value D = Object{{"string", "---"}};
   auto T = Template("| {{string}} |");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| --- |", Out);
 }
 
@@ -235,7 +283,9 @@ TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {
   // Interpolation should not alter surrounding whitespace.
   Value D = Object{{"string", "---"}};
   auto T = Template("| {{&string}} |");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| --- |", Out);
 }
 
@@ -243,7 +293,9 @@ TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {
   // Standalone interpolation should not alter surrounding whitespace.
   Value D = Object{{"string", "---"}};
   auto T = Template("  {{string}}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("  ---\n", Out);
 }
 
@@ -251,7 +303,9 @@ TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {
   // Standalone interpolation should not alter surrounding whitespace.
   Value D = Object{{"string", "---"}};
   auto T = Template("  {{&string}}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("  ---\n", Out);
 }
 
@@ -259,7 +313,9 @@ TEST(MustacheInterpolation, InterpolationWithPadding) {
   // Superfluous in-tag whitespace should be ignored.
   Value D = Object{{"string", "---"}};
   auto T = Template("|{{ string }}|");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("|---|", Out);
 }
 
@@ -267,7 +323,9 @@ TEST(MustacheInterpolation, AmpersandWithPadding) {
   // Superfluous in-tag whitespace should be ignored.
   Value D = Object{{"string", "---"}};
   auto T = Template("|{{& string }}|");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("|---|", Out);
 }
 
@@ -275,35 +333,45 @@ TEST(MustacheInterpolation, InterpolationWithPaddingAndNewlines) {
   // Superfluous in-tag whitespace should be ignored.
   Value D = Object{{"string", "---"}};
   auto T = Template("|{{ string \n\n\n }}|");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("|---|", Out);
 }
 
 TEST(MustacheSections, Truthy) {
   Value D = Object{{"boolean", true}};
   auto T = Template("{{#boolean}}This should be rendered.{{/boolean}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("This should be rendered.", Out);
 }
 
 TEST(MustacheSections, Falsey) {
   Value D = Object{{"boolean", false}};
   auto T = Template("{{#boolean}}This should not be rendered.{{/boolean}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("", Out);
 }
 
 TEST(MustacheSections, NullIsFalsey) {
   Value D = Object{{"null", nullptr}};
   auto T = Template("{{#null}}This should not be rendered.{{/null}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("", Out);
 }
 
 TEST(MustacheSections, Context) {
   Value D = Object{{"context", Object{{"name", "Joe"}}}};
   auto T = Template("{{#context}}Hi {{name}}.{{/context}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Hi Joe.", Out);
 }
 
@@ -313,14 +381,18 @@ TEST(MustacheSections, ParentContexts) {
                    {"sec", Object{{"b", "bar"}}},
                    {"c", Object{{"d", "baz"}}}};
   auto T = Template("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("foo, bar, baz", Out);
 }
 
 TEST(MustacheSections, VariableTest) {
   Value D = Object{{"foo", "bar"}};
   auto T = Template("{{#foo}}{{.}} is {{foo}}{{/foo}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("bar is bar", Out);
 }
 
@@ -341,7 +413,9 @@ TEST(MustacheSections, ListContexts) {
                     "{{/bottoms}}"
                     "{{/middles}}"
                     "{{/tops}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("a1.A1x.A1y.", Out);
 }
 
@@ -370,14 +444,18 @@ TEST(MustacheSections, List) {
   Value D = Object{{"list", Array{Object{{"item", 1}}, Object{{"item", 2}},
                                   Object{{"item", 3}}}}};
   auto T = Template("{{#list}}{{item}}{{/list}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("123", Out);
 }
 
 TEST(MustacheSections, EmptyList) {
   Value D = Object{{"list", Array{}}};
   auto T = Template("{{#list}}Yay lists!{{/list}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("", Out);
 }
 
@@ -385,98 +463,126 @@ TEST(MustacheSections, Doubled) {
   Value D = Object{{"bool", true}, {"two", "second"}};
   auto T = Template("{{#bool}}\n* first\n{{/bool}}\n* "
                     "{{two}}\n{{#bool}}\n* third\n{{/bool}}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("* first\n* second\n* third\n", Out);
 }
 
 TEST(MustacheSections, NestedTruthy) {
   Value D = Object{{"bool", true}};
   auto T = Template("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| A B C D E |", Out);
 }
 
 TEST(MustacheSections, NestedFalsey) {
   Value D = Object{{"bool", false}};
   auto T = Template("| A {{#bool}}B {{#bool}}C{{/bool}} D{{/bool}} E |");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| A  E |", Out);
 }
 
 TEST(MustacheSections, ContextMisses) {
   Value D = Object{};
   auto T = Template("[{{#missing}}Found key 'missing'!{{/missing}}]");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("[]", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorString) {
   Value D = Object{{"list", Array{"a", "b", "c", "d", "e"}}};
   auto T = Template("{{#list}}({{.}}){{/list}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("(a)(b)(c)(d)(e)", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorInteger) {
   Value D = Object{{"list", Array{1, 2, 3, 4, 5}}};
   auto T = Template("{{#list}}({{.}}){{/list}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("(1)(2)(3)(4)(5)", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorArray) {
   Value D = Object{{"list", Array{Array{1, 2, 3}, Array{"a", "b", "c"}}}};
   auto T = Template("{{#list}}({{#.}}{{.}}{{/.}}){{/list}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("(123)(abc)", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorHTMLEscaping) {
   Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
   auto T = Template("{{#list}}({{.}}){{/list}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("(&)(")(<)(>)", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorAmpersand) {
   Value D = Object{{"list", Array{"&", "\"", "<", ">"}}};
   auto T = Template("{{#list}}({{&.}}){{/list}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("(&)(\")(<)(>)", Out);
 }
 
 TEST(MustacheSections, ImplicitIteratorRootLevel) {
   Value D = Array{Object{{"value", "a"}}, Object{{"value", "b"}}};
   auto T = Template("{{#.}}({{value}}){{/.}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("(a)(b)", Out);
 }
 
 TEST(MustacheSections, DottedNamesTruthy) {
   Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
   auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == Here");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Here == Here", Out);
 }
 
 TEST(MustacheSections, DottedNamesFalsey) {
   Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
   auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == ");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" == ", Out);
 }
 
 TEST(MustacheSections, DottedNamesBrokenChains) {
   Value D = Object{{"a", Object{}}};
   auto T = Template("{{#a.b.c}}Here{{/a.b.c}} == ");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" == ", Out);
 }
 
 TEST(MustacheSections, SurroundingWhitespace) {
   Value D = Object{{"boolean", true}};
   auto T = Template(" | {{#boolean}}\t|\t{{/boolean}} | \n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" | \t|\t | \n", Out);
 }
 
@@ -492,77 +598,99 @@ TEST(MustacheSections, IndentedInlineSections) {
   Value D = Object{{"boolean", true}};
   auto T =
       Template(" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" YES\n GOOD\n", Out);
 }
 
 TEST(MustacheSections, StandaloneLines) {
   Value D = Object{{"boolean", true}};
   auto T = Template("| This Is\n{{#boolean}}\n|\n{{/boolean}}\n| A Line\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
 }
 
 TEST(MustacheSections, IndentedStandaloneLines) {
   Value D = Object{{"boolean", true}};
   auto T = Template("| This Is\n  {{#boolean}}\n|\n  {{/boolean}}\n| A Line\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
 }
 
 TEST(MustacheSections, StandaloneLineEndings) {
   Value D = Object{{"boolean", true}};
   auto T = Template("|\r\n{{#boolean}}\r\n{{/boolean}}\r\n|");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("|\r\n|", Out);
 }
 
 TEST(MustacheSections, StandaloneWithoutPreviousLine) {
   Value D = Object{{"boolean", true}};
   auto T = Template("  {{#boolean}}\n#{{/boolean}}\n/");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("#\n/", Out);
 }
 
 TEST(MustacheSections, StandaloneWithoutNewline) {
   Value D = Object{{"boolean", true}};
   auto T = Template("#{{#boolean}}\n/\n  {{/boolean}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("#\n/\n", Out);
 }
 
 TEST(MustacheSections, Padding) {
   Value D = Object{{"boolean", true}};
   auto T = Template("|{{# boolean }}={{/ boolean }}|");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("|=|", Out);
 }
 
 TEST(MustacheInvertedSections, Falsey) {
   Value D = Object{{"boolean", false}};
   auto T = Template("{{^boolean}}This should be rendered.{{/boolean}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("This should be rendered.", Out);
 }
 
 TEST(MustacheInvertedSections, Truthy) {
   Value D = Object{{"boolean", true}};
   auto T = Template("{{^boolean}}This should not be rendered.{{/boolean}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("", Out);
 }
 
 TEST(MustacheInvertedSections, NullIsFalsey) {
   Value D = Object{{"null", nullptr}};
   auto T = Template("{{^null}}This should be rendered.{{/null}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("This should be rendered.", Out);
 }
 
 TEST(MustacheInvertedSections, Context) {
   Value D = Object{{"context", Object{{"name", "Joe"}}}};
   auto T = Template("{{^context}}Hi {{name}}.{{/context}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("", Out);
 }
 
@@ -570,14 +698,18 @@ TEST(MustacheInvertedSections, List) {
   Value D = Object{
       {"list", Array{Object{{"n", 1}}, Object{{"n", 2}}, Object{{"n", 3}}}}};
   auto T = Template("{{^list}}{{n}}{{/list}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("", Out);
 }
 
 TEST(MustacheInvertedSections, EmptyList) {
   Value D = Object{{"list", Array{}}};
   auto T = Template("{{^list}}Yay lists!{{/list}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Yay lists!", Out);
 }
 
@@ -585,56 +717,72 @@ TEST(MustacheInvertedSections, Doubled) {
   Value D = Object{{"bool", false}, {"two", "second"}};
   auto T = Template("{{^bool}}\n* first\n{{/bool}}\n* "
                     "{{two}}\n{{^bool}}\n* third\n{{/bool}}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("* first\n* second\n* third\n", Out);
 }
 
 TEST(MustacheInvertedSections, NestedFalsey) {
   Value D = Object{{"bool", false}};
   auto T = Template("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| A B C D E |", Out);
 }
 
 TEST(MustacheInvertedSections, NestedTruthy) {
   Value D = Object{{"bool", true}};
   auto T = Template("| A {{^bool}}B {{^bool}}C{{/bool}} D{{/bool}} E |");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| A  E |", Out);
 }
 
 TEST(MustacheInvertedSections, ContextMisses) {
   Value D = Object{};
   auto T = Template("[{{^missing}}Cannot find key 'missing'!{{/missing}}]");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("[Cannot find key 'missing'!]", Out);
 }
 
 TEST(MustacheInvertedSections, DottedNamesTruthy) {
   Value D = Object{{"a", Object{{"b", Object{{"c", true}}}}}};
   auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == ");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" == ", Out);
 }
 
 TEST(MustacheInvertedSections, DottedNamesFalsey) {
   Value D = Object{{"a", Object{{"b", Object{{"c", false}}}}}};
   auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Not Here == Not Here", Out);
 }
 
 TEST(MustacheInvertedSections, DottedNamesBrokenChains) {
   Value D = Object{{"a", Object{}}};
   auto T = Template("{{^a.b.c}}Not Here{{/a.b.c}} == Not Here");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Not Here == Not Here", Out);
 }
 
 TEST(MustacheInvertedSections, SurroundingWhitespace) {
   Value D = Object{{"boolean", false}};
   auto T = Template(" | {{^boolean}}\t|\t{{/boolean}} | \n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" | \t|\t | \n", Out);
 }
 
@@ -642,7 +790,9 @@ TEST(MustacheInvertedSections, InternalWhitespace) {
   Value D = Object{{"boolean", false}};
   auto T = Template(
       " | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" |  \n  | \n", Out);
 }
 
@@ -650,49 +800,63 @@ TEST(MustacheInvertedSections, IndentedInlineSections) {
   Value D = Object{{"boolean", false}};
   auto T =
       Template(" {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" NO\n WAY\n", Out);
 }
 
 TEST(MustacheInvertedSections, StandaloneLines) {
   Value D = Object{{"boolean", false}};
   auto T = Template("| This Is\n{{^boolean}}\n|\n{{/boolean}}\n| A Line\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
 }
 
 TEST(MustacheInvertedSections, StandaloneIndentedLines) {
   Value D = Object{{"boolean", false}};
   auto T = Template("| This Is\n  {{^boolean}}\n|\n  {{/boolean}}\n| A Line\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| This Is\n|\n| A Line\n", Out);
 }
 
 TEST(MustacheInvertedSections, StandaloneLineEndings) {
   Value D = Object{{"boolean", false}};
   auto T = Template("|\r\n{{^boolean}}\r\n{{/boolean}}\r\n|");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("|\r\n|", Out);
 }
 
 TEST(MustacheInvertedSections, StandaloneWithoutPreviousLine) {
   Value D = Object{{"boolean", false}};
   auto T = Template("  {{^boolean}}\n^{{/boolean}}\n/");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("^\n/", Out);
 }
 
 TEST(MustacheInvertedSections, StandaloneWithoutNewline) {
   Value D = Object{{"boolean", false}};
   auto T = Template("^{{^boolean}}\n/\n  {{/boolean}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("^\n/\n", Out);
 }
 
 TEST(MustacheInvertedSections, Padding) {
   Value D = Object{{"boolean", false}};
   auto T = Template("|{{^ boolean }}={{/ boolean }}|");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("|=|", Out);
 }
 
@@ -700,14 +864,18 @@ TEST(MustachePartials, BasicBehavior) {
   Value D = Object{};
   auto T = Template("{{>text}}");
   T.registerPartial("text", "from partial");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("from partial", Out);
 }
 
 TEST(MustachePartials, FailedLookup) {
   Value D = Object{};
   auto T = Template("{{>text}}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("", Out);
 }
 
@@ -715,7 +883,9 @@ TEST(MustachePartials, Context) {
   Value D = Object{{"text", "content"}};
   auto T = Template("{{>partial}}");
   T.registerPartial("partial", "*{{text}}*");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("*content*", Out);
 }
 
@@ -725,7 +895,9 @@ TEST(MustachePartials, Recursion) {
              {"nodes", Array{Object{{"content", "Y"}, {"nodes", Array{}}}}}};
   auto T = Template("{{>node}}");
   T.registerPartial("node", "{{content}}({{#nodes}}{{>node}}{{/nodes}})");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("X(Y())", Out);
 }
 
@@ -734,7 +906,9 @@ TEST(MustachePartials, Nested) {
   auto T = Template("{{>outer}}");
   T.registerPartial("outer", "*{{a}} {{>inner}}*");
   T.registerPartial("inner", "{{b}}!");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("*hello world!*", Out);
 }
 
@@ -742,7 +916,9 @@ TEST(MustachePartials, SurroundingWhitespace) {
   Value D = Object{};
   auto T = Template("| {{>partial}} |");
   T.registerPartial("partial", "\t|\t");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("| \t|\t |", Out);
 }
 
@@ -750,7 +926,9 @@ TEST(MustachePartials, InlineIndentation) {
   Value D = Object{{"data", "|"}};
   auto T = Template("  {{data}}  {{> partial}}\n");
   T.registerPartial("partial", "<\n<");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("  |  <\n<\n", Out);
 }
 
@@ -758,7 +936,9 @@ TEST(MustachePartials, PaddingWhitespace) {
   Value D = Object{{"boolean", true}};
   auto T = Template("|{{> partial }}|");
   T.registerPartial("partial", "[]");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("|[]|", Out);
 }
 
@@ -767,7 +947,9 @@ TEST(MustacheLambdas, BasicInterpolation) {
   auto T = Template("Hello, {{lambda}}!");
   Lambda L = []() -> llvm::json::Value { return "World"; };
   T.registerLambda("lambda", L);
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Hello, World!", Out);
 }
 
@@ -776,7 +958,9 @@ TEST(MustacheLambdas, InterpolationExpansion) {
   auto T = Template("Hello, {{lambda}}!");
   Lambda L = []() -> llvm::json::Value { return "{{planet}}"; };
   T.registerLambda("lambda", L);
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Hello, World!", Out);
 }
 
@@ -789,7 +973,9 @@ TEST(MustacheLambdas, BasicMultipleCalls) {
     return I;
   };
   T.registerLambda("lambda", L);
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("1 == 2 == 3", Out);
 }
 
@@ -798,7 +984,9 @@ TEST(MustacheLambdas, Escaping) {
   auto T = Template("<{{lambda}}{{&lambda}}");
   Lambda L = []() -> llvm::json::Value { return ">"; };
   T.registerLambda("lambda", L);
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("<>>", Out);
 }
 
@@ -812,7 +1000,9 @@ TEST(MustacheLambdas, Sections) {
     return "no";
   };
   T.registerLambda("lambda", L);
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("<yes>", Out);
 }
 
@@ -829,7 +1019,9 @@ TEST(MustacheLambdas, SectionExpansion) {
     return Result;
   };
   T.registerLambda("lambda", L);
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("<-Earth->", Out);
 }
 
@@ -844,7 +1036,9 @@ TEST(MustacheLambdas, SectionsMultipleCalls) {
     return Result;
   };
   T.registerLambda("lambda", L);
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("__FILE__ != __LINE__", Out);
 }
 
@@ -853,7 +1047,9 @@ TEST(MustacheLambdas, InvertedSections) {
   auto T = Template("<{{^lambda}}{{static}}{{/lambda}}>");
   SectionLambda L = [](StringRef Text) -> llvm::json::Value { return false; };
   T.registerLambda("lambda", L);
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("<>", Out);
 }
 
@@ -861,7 +1057,9 @@ TEST(MustacheComments, Inline) {
   // Comment blocks should be removed from the template.
   Value D = {};
   auto T = Template("12345{{! Comment Block! }}67890");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("1234567890", Out);
 }
 
@@ -870,7 +1068,9 @@ TEST(MustacheComments, Multiline) {
   Value D = {};
   auto T =
       Template("12345{{!\n  This is a\n  multi-line comment...\n}}67890\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("1234567890\n", Out);
 }
 
@@ -878,7 +1078,9 @@ TEST(MustacheComments, Standalone) {
   // All standalone comment lines should be removed.
   Value D = {};
   auto T = Template("Begin.\n{{! Comment Block! }}\nEnd.\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Begin.\nEnd.\n", Out);
 }
 
@@ -886,7 +1088,9 @@ TEST(MustacheComments, IndentedStandalone) {
   // All standalone comment lines should be removed.
   Value D = {};
   auto T = Template("Begin.\n  {{! Indented Comment Block! }}\nEnd.\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Begin.\nEnd.\n", Out);
 }
 
@@ -894,7 +1098,9 @@ TEST(MustacheComments, StandaloneLineEndings) {
   // "\r\n" should be considered a newline for standalone tags.
   Value D = {};
   auto T = Template("|\r\n{{! Standalone Comment }}\r\n|");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("|\r\n|", Out);
 }
 
@@ -902,7 +1108,9 @@ TEST(MustacheComments, StandaloneWithoutPreviousLine) {
   // Standalone tags should not require a newline to precede them.
   Value D = {};
   auto T = Template("  {{! I'm Still Standalone }}\n!");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("!", Out);
 }
 
@@ -910,7 +1118,9 @@ TEST(MustacheComments, StandaloneWithoutNewline) {
   // Standalone tags should not require a newline to follow them.
   Value D = {};
   auto T = Template("!\n  {{! I'm Still Standalone }}");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("!\n", Out);
 }
 
@@ -918,7 +1128,9 @@ TEST(MustacheComments, MultilineStandalone) {
   // All standalone comment lines should be removed.
   Value D = {};
   auto T = Template("Begin.\n{{!\nSomething's going on here...\n}}\nEnd.\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Begin.\nEnd.\n", Out);
 }
 
@@ -927,7 +1139,9 @@ TEST(MustacheComments, IndentedMultilineStandalone) {
   Value D = {};
   auto T =
       Template("Begin.\n  {{!\n    Something's going on here...\n  }}\nEnd.\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("Begin.\nEnd.\n", Out);
 }
 
@@ -935,7 +1149,9 @@ TEST(MustacheComments, IndentedInline) {
   // Inline comments should not strip whitespace.
   Value D = {};
   auto T = Template("  12 {{! 34 }}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("  12 \n", Out);
 }
 
@@ -943,7 +1159,11 @@ TEST(MustacheComments, SurroundingWhitespace) {
   // Comment removal should preserve surrounding whitespace.
   Value D = {};
   auto T = Template("12345 {{! Comment Block! }} 67890");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("12345  67890", Out);
 }
 
@@ -952,6 +1172,8 @@ TEST(MustacheComments, VariableNameCollision) {
   Value D = Object{
       {"! comment", 1}, {"! comment ", 2}, {"!comment", 3}, {"comment", 4}};
   auto T = Template("comments never show: >{{! comment }}<");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("comments never show: ><", Out);
 }
\ No newline at end of file

>From 5b0a20ab9ef08882e9d83c6ab944c58bce2738cb Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Sat, 12 Oct 2024 01:47:32 -0400
Subject: [PATCH 42/62] [llvm] refactor to use os_stream

---
 llvm/lib/Support/Mustache.cpp           | 98 ++++++++++++-------------
 llvm/unittests/Support/MustacheTest.cpp | 10 ++-
 2 files changed, 54 insertions(+), 54 deletions(-)

diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 2d7a707cc50b4f..0d989c74dbac66 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -72,16 +72,15 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), ParentContext(nullptr) {};
+  ASTNode() : T(Type::Root), ParentContext(nullptr){};
 
   ASTNode(StringRef Body, ASTNode *Parent)
-      : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr),
-        Indentation(0) {};
+      : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr){};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        ParentContext(nullptr), Indentation(0) {};
+        ParentContext(nullptr){};
 
   void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 
@@ -99,19 +98,16 @@ class ASTNode {
                  DenseMap<char, StringRef> &Escapes);
 
 private:
-  void renderLambdas(const llvm::json::Value &Contexts,
-                     llvm::raw_ostream &OS,
+  void renderLambdas(const llvm::json::Value &Contexts, llvm::raw_ostream &OS,
                      Lambda &L);
 
   void renderSectionLambdas(const llvm::json::Value &Contexts,
                             llvm::raw_ostream &OS, SectionLambda &L);
 
-  void renderPartial(const llvm::json::Value &Contexts, 
-                     llvm::raw_ostream &OS,
+  void renderPartial(const llvm::json::Value &Contexts, llvm::raw_ostream &OS,
                      ASTNode *Partial);
 
-  void renderChild(const llvm::json::Value &Context, 
-                   llvm::raw_ostream &OS);
+  void renderChild(const llvm::json::Value &Context, llvm::raw_ostream &OS);
 
   const llvm::json::Value *findContext();
 
@@ -121,7 +117,7 @@ class ASTNode {
   StringMap<SectionLambda> *SectionLambdas;
   DenseMap<char, StringRef> *Escapes;
   Type T;
-  size_t Indentation;
+  size_t Indentation = 0;
   SmallString<0> RawBody;
   SmallString<0> Body;
   ASTNode *Parent;
@@ -135,10 +131,11 @@ class EscapeStringStream : public raw_ostream {
 public:
   explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
                               DenseMap<char, StringRef> &Escape)
-      : Escape(Escape), WrappedStream(WrappedStream) {}
-  
+      : Escape(Escape), WrappedStream(WrappedStream) {
+    SetUnbuffered();
+  }
+
 protected:
-  // This is where data gets written
   void write_impl(const char *Ptr, size_t Size) override {
     llvm::StringRef Data(Ptr, Size);
     for (char C : Data) {
@@ -150,24 +147,23 @@ class EscapeStringStream : public raw_ostream {
     }
   }
 
-  uint64_t current_pos() const override {
-    return WrappedStream.tell();
-  }
+  uint64_t current_pos() const override { return WrappedStream.tell(); }
 
 private:
   DenseMap<char, StringRef> &Escape;
   llvm::raw_ostream &WrappedStream;
 };
 
-
+// Custom stream to add indentation used to for rendering partials
 class AddIndentationStringStream : public raw_ostream {
 public:
   explicit AddIndentationStringStream(llvm::raw_ostream &WrappedStream,
                                       size_t Indentation)
-      : Indentation(Indentation), WrappedStream(WrappedStream) {}
-  
+      : Indentation(Indentation), WrappedStream(WrappedStream) {
+    SetUnbuffered();
+  }
+
 protected:
-  // This is where data gets written
   void write_impl(const char *Ptr, size_t Size) override {
     llvm::StringRef Data(Ptr, Size);
     std::string Indent(Indentation, ' ');
@@ -177,10 +173,8 @@ class AddIndentationStringStream : public raw_ostream {
         WrappedStream << Indent;
     }
   }
-  
-  uint64_t current_pos() const override {
-    return WrappedStream.tell();
-  }
+
+  uint64_t current_pos() const override { return WrappedStream.tell(); }
 
 private:
   size_t Indentation;
@@ -237,7 +231,14 @@ Token::Type Token::getTokenType(char Identifier) {
   }
 }
 
-// Function to check if there's no meaningful text behind
+// Function to check if there's no meaningful text behind.
+// We determine if a token has no meaningful text behind
+// if the right of previous token is empty spaces or tabs followed
+// by a newline
+// eg. "Other Stuff\n {{#Section}}"
+// We make an exception for when previous token is empty
+// and the current token is the second token
+// eg. " {{#Section}}"
 bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
   if (Idx == 0)
     return false;
@@ -247,17 +248,15 @@ bool noTextBehind(size_t Idx, const SmallVector<Token, 0> &Tokens) {
     return false;
 
   const Token &PrevToken = Tokens[Idx - 1];
-  StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v\t");
-  // We make an exception for when previous token is empty
-  // and the current token is the second token
-  // ex.
-  //  " {{#section}}A{{/section}}"
-  // the output of this is
-  //  "A"
+  StringRef TokenBody = PrevToken.getRawBody().rtrim(" \t\v");
   return TokenBody.ends_with("\n") || (TokenBody.empty() && Idx == 1);
 }
 
 // Function to check if there's no meaningful text ahead
+// We determine if a token has no meaningful text behind
+// if the left of previous token is empty spaces or tabs followed
+// by a newline
+// eg. "{{#Section}}  \n"
 bool noTextAhead(size_t Idx, const SmallVector<Token, 0> &Tokens) {
   if (Idx >= Tokens.size() - 1)
     return false;
@@ -298,7 +297,7 @@ void stripTokenAhead(SmallVector<Token, 0> &Tokens, size_t Idx) {
 // eg.
 //  The template string
 //  " \t{{#section}}A{{/section}}"
-// would be considered as no text ahead and should be render as
+// would be considered as having no text ahead and would be render as
 //  "A"
 // The exception for this is partial tag which requires us to
 // keep track of the indentation once it's rendered
@@ -361,9 +360,11 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
   // the surrounding whitespace.
   // For example:
   // if you have the template string
-  //  "Line 1\n {{#section}} \n Line 2 \n {{/section}} \n Line 3"
-  // The output would be
-  //  "Line 1\n Line 2\n Line 3"
+  //  {{#section}} \n Example \n{{/section}}
+  // The output should would be
+  //  Example
+  // Not:
+  //  \n Example \n
   size_t LastIdx = Tokens.size() - 1;
   for (size_t Idx = 0, End = Tokens.size(); Idx < End; ++Idx) {
     Token &CurrentToken = Tokens[Idx];
@@ -384,13 +385,11 @@ SmallVector<Token, 0> tokenize(StringRef Template) {
     bool NoTextBehind = noTextBehind(Idx, Tokens);
     bool NoTextAhead = noTextAhead(Idx, Tokens);
 
-    if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0)) {
+    if ((NoTextBehind && NoTextAhead) || (NoTextAhead && Idx == 0))
       stripTokenAhead(Tokens, Idx);
-    }
 
-    if (((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx))) {
+    if (((NoTextBehind && NoTextAhead) || (NoTextBehind && Idx == LastIdx)))
       stripTokenBefore(Tokens, Idx, CurrentToken, CurrentType);
-    }
   }
   return Tokens;
 }
@@ -650,9 +649,8 @@ const Value *ASTNode::findContext() {
 }
 
 void ASTNode::renderChild(const Value &Contexts, llvm::raw_ostream &OS) {
-  for (ASTNode *Child : Children) {
+  for (ASTNode *Child : Children)
     Child->render(Contexts, OS);
-  }
 }
 
 void ASTNode::renderPartial(const Value &Contexts, llvm::raw_ostream &OS,
@@ -671,18 +669,18 @@ void ASTNode::renderLambdas(const Value &Contexts, llvm::raw_ostream &OS,
   ASTNode *LambdaNode = P.parse();
   LambdaNode->setUpNode(*Allocator, *Partials, *Lambdas, *SectionLambdas,
                         *Escapes);
-  
+
   EscapeStringStream ES(OS, *Escapes);
-  if (T == Variable)
+  if (T == Variable) {
     LambdaNode->render(Contexts, ES);
-  else
-    LambdaNode->render(Contexts, OS);
-  
+    return;
+  }
+  LambdaNode->render(Contexts, OS);
   return;
 }
 
-void ASTNode::renderSectionLambdas(const Value &Contexts,
-                                   llvm::raw_ostream &OS, SectionLambda &L) {
+void ASTNode::renderSectionLambdas(const Value &Contexts, llvm::raw_ostream &OS,
+                                   SectionLambda &L) {
   Value Return = L(RawBody);
   if (isFalsey(Return))
     return;
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
index afa47562c5b3d6..1883bd5b401fcc 100644
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ b/llvm/unittests/Support/MustacheTest.cpp
@@ -434,7 +434,9 @@ TEST(MustacheSections, DeeplyNestedContexts) {
       "five}}\n{{one}}{{two}}{{three}}{{four}}{{three}}{{two}}{{one}}\n{{/"
       "d}}\n{{one}}{{two}}{{three}}{{two}}{{one}}\n{{/"
       "c}}\n{{one}}{{two}}{{one}}\n{{/b}}\n{{one}}\n{{/a}}\n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ("1\n121\n12321\n1234321\n123454321\n12345654321\n123454321\n1234321"
             "\n12321\n121\n1\n",
             Out);
@@ -590,7 +592,9 @@ TEST(MustacheSections, InternalWhitespace) {
   Value D = Object{{"boolean", true}};
   auto T = Template(
       " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
-  auto Out = T.render(D);
+  std::string Out;
+  raw_string_ostream OS(Out);
+  T.render(D, OS);
   EXPECT_EQ(" |  \n  | \n", Out);
 }
 
@@ -1161,8 +1165,6 @@ TEST(MustacheComments, SurroundingWhitespace) {
   auto T = Template("12345 {{! Comment Block! }} 67890");
   std::string Out;
   raw_string_ostream OS(Out);
-  std::string Out;
-  raw_string_ostream OS(Out);
   T.render(D, OS);
   EXPECT_EQ("12345  67890", Out);
 }

>From ba4a6db09fb38f393d10775fd2e9163d2519a98b Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Sat, 12 Oct 2024 01:56:35 -0400
Subject: [PATCH 43/62] [llvm] clang-format

---
 llvm/lib/Support/Mustache.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index 0d989c74dbac66..f74877b695a3a2 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -72,15 +72,15 @@ class ASTNode {
     InvertSection,
   };
 
-  ASTNode() : T(Type::Root), ParentContext(nullptr){};
+  ASTNode() : T(Type::Root), ParentContext(nullptr) {};
 
   ASTNode(StringRef Body, ASTNode *Parent)
-      : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr){};
+      : T(Type::Text), Body(Body), Parent(Parent), ParentContext(nullptr) {};
 
   // Constructor for Section/InvertSection/Variable/UnescapeVariable
   ASTNode(Type T, Accessor Accessor, ASTNode *Parent)
       : T(T), Parent(Parent), Children({}), Accessor(Accessor),
-        ParentContext(nullptr){};
+        ParentContext(nullptr) {};
 
   void addChild(ASTNode *Child) { Children.emplace_back(Child); };
 

>From 362728f8b029a9a404ecff2f7c3e6932f7da5424 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Sat, 12 Oct 2024 03:45:32 -0400
Subject: [PATCH 44/62] [llvm] fix indentation bug

---
 llvm/lib/Support/Mustache.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Support/Mustache.cpp b/llvm/lib/Support/Mustache.cpp
index f74877b695a3a2..29b40dd1e1f7b0 100644
--- a/llvm/lib/Support/Mustache.cpp
+++ b/llvm/lib/Support/Mustache.cpp
@@ -655,7 +655,7 @@ void ASTNode::renderChild(const Value &Contexts, llvm::raw_ostream &OS) {
 
 void ASTNode::renderPartial(const Value &Contexts, llvm::raw_ostream &OS,
                             ASTNode *Partial) {
-  AddIndentationStringStream IS(OS, Partial->Indentation);
+  AddIndentationStringStream IS(OS, Indentation);
   Partial->render(Contexts, IS);
 }
 

>From dca40c5e1f1c4361ecad64338be7cd316171c12d Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 02:10:26 -0400
Subject: [PATCH 45/62] [llvm] add mustache verification tool

---
 llvm/tools/mustache/CMakeLists.txt |   3 +
 llvm/tools/mustache/mustache.cpp   | 102 +++++++++++++++++++++++++++++
 2 files changed, 105 insertions(+)
 create mode 100644 llvm/tools/mustache/CMakeLists.txt
 create mode 100644 llvm/tools/mustache/mustache.cpp

diff --git a/llvm/tools/mustache/CMakeLists.txt b/llvm/tools/mustache/CMakeLists.txt
new file mode 100644
index 00000000000000..721b553741d05b
--- /dev/null
+++ b/llvm/tools/mustache/CMakeLists.txt
@@ -0,0 +1,3 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+    add_llvm_tool(mustache mustache.cpp)
\ No newline at end of file
diff --git a/llvm/tools/mustache/mustache.cpp b/llvm/tools/mustache/mustache.cpp
new file mode 100644
index 00000000000000..07b7c0dc528696
--- /dev/null
+++ b/llvm/tools/mustache/mustache.cpp
@@ -0,0 +1,102 @@
+//===- mustache.cpp - The LLVM Modular Optimizer
+//-------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Simple drivers to test the mustache spec found here
+// https://github.com/mustache/
+// It is used to verify that the current implementation conforms to the spec
+// simply download the spec and pass the test files to the driver
+//
+// Currently Triple Mustache is not supported we expect the following spec
+// test to fail:
+//    Triple Mustache
+//    Triple Mustache Integer Interpolation
+//    Triple Mustache Decimal Interpolation
+//    Triple Mustache Null Interpolation
+//    Triple Mustache Context Miss Interpolation
+//    Dotted Names - Triple Mustache Interpolation
+//    Implicit Iterators - Triple Mustache
+//    Triple Mustache - Surrounding Whitespace
+//    Triple Mustache - Standalone
+//    Triple Mustache With Padding
+//    Standalone Indentation
+//    Implicit Iterator - Triple mustache
+//
+// Usage:
+//  mustache path/to/test/file/test.json path/to/test/file/test2.json ...
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Mustache.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <string>
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace llvm::mustache;
+
+cl::list<std::string> InputFiles(cl::Positional, cl::desc("<input files>"),
+                                 cl::OneOrMore);
+
+void runThroughTest(StringRef InputFile) {
+  llvm::outs() << "Running Tests: " << InputFile << "\n";
+  ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferOrError =
+      MemoryBuffer::getFile(InputFile);
+
+  if (auto EC = BufferOrError.getError()) {
+    return;
+  }
+  std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
+  llvm::StringRef FileContent = Buffer->getBuffer();
+  Expected<Value> Json = parse(FileContent);
+
+  if (auto E = Json.takeError()) {
+    errs() << "Parsing error: " << toString(std::move(E)) << "\n";
+    return;
+  }
+  // Get test
+  Array *Obj = (*Json).getAsObject()->getArray("tests");
+  size_t Total = 0;
+  size_t Success = 0;
+  for (Value V : *Obj) {
+    Object *TestCase = V.getAsObject();
+    StringRef TemplateStr = TestCase->getString("template").value();
+    StringRef ExpectedStr = TestCase->getString("expected").value();
+    StringRef Name = TestCase->getString("name").value();
+    Value *Data = TestCase->get("data");
+    Value *Partials = TestCase->get("partials");
+
+    if (!Data)
+      continue;
+
+    Template T = Template(TemplateStr);
+    if (Partials) {
+      for (auto PartialPairs : *Partials->getAsObject()) {
+        StringRef Partial = PartialPairs.getSecond().getAsString().value();
+        StringRef Str = llvm::StringRef(PartialPairs.getFirst());
+        T.registerPartial(Str, Partial);
+      }
+    }
+    StringRef ActualStr = T.render(*Data);
+    if (ExpectedStr == ActualStr) {
+      Success++;
+    } else {
+      llvm::outs() << "Test Failed: " << Name << "\n";
+    }
+    Total++;
+  }
+
+  llvm::outs() << "Result " << Success << "/" << Total << " succeeded\n";
+}
+int main(int argc, char **argv) {
+  llvm::cl::ParseCommandLineOptions(argc, argv);
+  for (const auto &FileName : InputFiles) {
+    runThroughTest(FileName);
+  }
+  return 0;
+}
\ No newline at end of file

>From 6fffeb23fc5fb0a153f51f02d05574751cb66590 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Sat, 12 Oct 2024 03:41:22 -0400
Subject: [PATCH 46/62] [llvm] modify mustache tool

---
 llvm/tools/mustache/CMakeLists.txt |  2 +-
 llvm/tools/mustache/mustache.cpp   | 10 ++++++----
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/llvm/tools/mustache/CMakeLists.txt b/llvm/tools/mustache/CMakeLists.txt
index 721b553741d05b..81e42264d48308 100644
--- a/llvm/tools/mustache/CMakeLists.txt
+++ b/llvm/tools/mustache/CMakeLists.txt
@@ -1,3 +1,3 @@
 set(LLVM_LINK_COMPONENTS Support)
 
-    add_llvm_tool(mustache mustache.cpp)
\ No newline at end of file
+add_llvm_tool(mustache mustache.cpp)
\ No newline at end of file
diff --git a/llvm/tools/mustache/mustache.cpp b/llvm/tools/mustache/mustache.cpp
index 07b7c0dc528696..1fd106418d9cc4 100644
--- a/llvm/tools/mustache/mustache.cpp
+++ b/llvm/tools/mustache/mustache.cpp
@@ -82,12 +82,14 @@ void runThroughTest(StringRef InputFile) {
         T.registerPartial(Str, Partial);
       }
     }
-    StringRef ActualStr = T.render(*Data);
-    if (ExpectedStr == ActualStr) {
+    std::string ActualStr;
+    llvm::raw_string_ostream Stream(ActualStr);
+    T.render(*Data, Stream);
+    if (ExpectedStr == ActualStr)
       Success++;
-    } else {
+    else
       llvm::outs() << "Test Failed: " << Name << "\n";
-    }
+    
     Total++;
   }
 

>From 7337a99ce8027fd220451df96c1095958d91b5a4 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 02:10:26 -0400
Subject: [PATCH 47/62] [llvm] add mustache verification tool

---
 llvm/tools/mustache/mustache.cpp | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/llvm/tools/mustache/mustache.cpp b/llvm/tools/mustache/mustache.cpp
index 1fd106418d9cc4..4c893437419e0b 100644
--- a/llvm/tools/mustache/mustache.cpp
+++ b/llvm/tools/mustache/mustache.cpp
@@ -83,22 +83,19 @@ void runThroughTest(StringRef InputFile) {
       }
     }
     std::string ActualStr;
-    llvm::raw_string_ostream Stream(ActualStr);
-    T.render(*Data, Stream);
+    llvm::raw_string_ostream OS(ActualStr);
+    T.render(*Data, OS);
     if (ExpectedStr == ActualStr)
       Success++;
     else
       llvm::outs() << "Test Failed: " << Name << "\n";
-    
     Total++;
   }
-
   llvm::outs() << "Result " << Success << "/" << Total << " succeeded\n";
 }
 int main(int argc, char **argv) {
   llvm::cl::ParseCommandLineOptions(argc, argv);
-  for (const auto &FileName : InputFiles) {
+  for (const auto &FileName : InputFiles)
     runThroughTest(FileName);
-  }
   return 0;
 }
\ No newline at end of file

>From 439955b5fecd3666a80b3f1a71ae63bafd101079 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 8 Oct 2024 02:10:26 -0400
Subject: [PATCH 48/62] [llvm] add mustache verification tool

---
 llvm/tools/mustache/mustache.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/llvm/tools/mustache/mustache.cpp b/llvm/tools/mustache/mustache.cpp
index 4c893437419e0b..6d01d6b88ec9e6 100644
--- a/llvm/tools/mustache/mustache.cpp
+++ b/llvm/tools/mustache/mustache.cpp
@@ -85,17 +85,20 @@ void runThroughTest(StringRef InputFile) {
     std::string ActualStr;
     llvm::raw_string_ostream OS(ActualStr);
     T.render(*Data, OS);
-    if (ExpectedStr == ActualStr)
+    if (ExpectedStr == ActualStr) {
       Success++;
-    else
+    } else {
       llvm::outs() << "Test Failed: " << Name << "\n";
+    }
     Total++;
   }
+
   llvm::outs() << "Result " << Success << "/" << Total << " succeeded\n";
 }
 int main(int argc, char **argv) {
   llvm::cl::ParseCommandLineOptions(argc, argv);
-  for (const auto &FileName : InputFiles)
+  for (const auto &FileName : InputFiles) {
     runThroughTest(FileName);
+  }
   return 0;
 }
\ No newline at end of file

>From 20045a90a3c3b758e63593fd3d08e04c673f28d0 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 02:26:22 -0400
Subject: [PATCH 49/62] [clang-doc] init mustache implementation

---
 clang-tools-extra/clang-doc/CMakeLists.txt    |   1 +
 clang-tools-extra/clang-doc/HTMLGenerator.cpp |   3 +-
 .../clang-doc/HTMLMustacheGenerator.cpp       | 349 ++++++++++++++++++
 clang-tools-extra/clang-doc/Representation.h  |   4 +-
 .../clang-doc/assets/template.mustache        |  23 ++
 .../clang-doc/tool/CMakeLists.txt             |   1 +
 .../clang-doc/tool/ClangDocMain.cpp           |  31 ++
 7 files changed, 410 insertions(+), 2 deletions(-)
 create mode 100644 clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
 create mode 100644 clang-tools-extra/clang-doc/assets/template.mustache

diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 975ad8e242e490..82aded940222db 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -15,6 +15,7 @@ add_clang_library(clangDoc
   Representation.cpp
   Serialize.cpp
   YAMLGenerator.cpp
+  HTMLMustacheGenerator.cpp
 
   DEPENDS
   omp_gen
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index e3532559a32fcc..ed65ac965114b5 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -1133,7 +1133,8 @@ static llvm::Error genIndex(const ClangDocContext &CDCtx) {
   return llvm::Error::success();
 }
 
-static llvm::Error copyFile(StringRef FilePath, StringRef OutDirectory) {
+static llvm::Error 
+copyFile(StringRef FilePath, StringRef OutDirectory) {
   llvm::SmallString<128> PathWrite;
   llvm::sys::path::native(OutDirectory, PathWrite);
   llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
new file mode 100644
index 00000000000000..63def07c5fa804
--- /dev/null
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -0,0 +1,349 @@
+//===-- HTMLMustacheGenerator.cpp - HTML Mustache Generator -----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "Generators.h"
+#include "Representation.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Mustache.h"
+
+using namespace llvm;
+using namespace llvm::json;
+using namespace llvm::mustache;
+
+namespace clang {
+namespace doc {
+
+
+class MustacheHTMLGenerator : public Generator {
+public:
+  static const char *Format;
+  llvm::Error generateDocs(StringRef RootDir,
+                           llvm::StringMap<std::unique_ptr<doc::Info>> Infos,
+                           const ClangDocContext &CDCtx) override;
+  llvm::Error createResources(ClangDocContext &CDCtx) override;
+  llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS,
+                                 const ClangDocContext &CDCtx) override;
+  
+};
+
+class MustacheTemplateFile : public Template {
+public:
+  static ErrorOr<std::unique_ptr<MustacheTemplateFile>> createMustacheFile
+      (StringRef FileName) {
+    ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
+        MemoryBuffer::getFile(FileName);
+    
+    if (auto EC = BufferOrError.getError()) {
+      return EC;
+    }
+    std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
+    llvm::StringRef FileContent = Buffer->getBuffer();
+    return std::make_unique<MustacheTemplateFile>(FileContent);
+  }
+  
+  Error registerPartialFile(StringRef Name, StringRef FileName) {
+    ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
+        MemoryBuffer::getFile(FileName);
+    if (auto EC = BufferOrError.getError()) {
+      return llvm::createFileError("cannot open file", EC);
+    }
+    std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
+    llvm::StringRef FileContent = Buffer->getBuffer();
+    registerPartial(Name, FileContent);
+  }
+
+  MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
+};
+
+static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
+
+static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
+
+
+llvm::Error 
+setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
+  auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
+  auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
+  if (auto EC = Template.getError()) {
+    return llvm::createFileError("cannot open file", EC);
+  }
+  NamespaceTemplate = std::move(Template.get());
+}
+
+llvm::Error 
+MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir, 
+                                    llvm::StringMap<std::unique_ptr<doc::Info>> Infos, 
+                                    const clang::doc::ClangDocContext &CDCtx) {
+  if (auto Err = setupTemplateFiles(CDCtx)) {
+    return Err;
+  }
+  // Track which directories we already tried to create.
+  llvm::StringSet<> CreatedDirs;
+  // Collect all output by file name and create the necessary directories.
+  llvm::StringMap<std::vector<doc::Info *>> FileToInfos;
+  for (const auto &Group : Infos) {
+    doc::Info *Info = Group.getValue().get();
+    
+    llvm::SmallString<128> Path;
+    llvm::sys::path::native(RootDir, Path);
+    llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
+    if (!CreatedDirs.contains(Path)) {
+      if (std::error_code Err = llvm::sys::fs::create_directories(Path);
+          Err != std::error_code()) {
+        return llvm::createStringError(Err, "Failed to create directory '%s'.",
+                                       Path.c_str());
+      }
+      CreatedDirs.insert(Path);
+    }
+
+    llvm::sys::path::append(Path, Info->getFileBaseName() + ".html");
+    FileToInfos[Path].push_back(Info);
+  }
+  
+  for (const auto &Group : FileToInfos) {
+    std::error_code FileErr;
+    llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
+                                llvm::sys::fs::OF_None);
+    if (FileErr) {
+      return llvm::createStringError(FileErr, "Error opening file '%s'",
+                                     Group.getKey().str().c_str());
+    }
+    for (const auto &Info : Group.getValue()) {
+      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
+        return Err;
+      }
+    }
+  }
+}
+
+Value extractValue(const Location &L, 
+                   std::optional<StringRef> RepositoryUrl = std::nullopt) {
+  Object Obj = Object();
+  if (!L.IsFileInRootDir || !RepositoryUrl) {
+    Obj.insert({"LineNumber", L.LineNumber});
+    Obj.insert({"Filename", L.Filename});
+  }
+  SmallString<128> FileURL(*RepositoryUrl);
+  llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+  Obj.insert({"FileURL", FileURL});
+}
+
+Value extractValue(const Reference &I, StringRef CurrentDirectory) {
+  llvm::SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
+  llvm::sys::path::append(Path, I.getFileBaseName() + ".html");
+  llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
+  Object Obj = Object();
+  Obj.insert({"Link", Path});
+  Obj.insert({"Name", I.Name});
+  return Obj;
+}
+
+
+Value extractValue(const TypedefInfo &I) {
+  // Not Supported
+  return nullptr;
+}
+
+Value extractValue(const CommentInfo &I) {
+  Object Obj = Object();
+  Value Child = Object();
+  
+  if (I.Kind == "FullComment") {
+    Value ChildArr = Array();
+    for (const auto& C: I.Children)
+      ChildArr.getAsArray()->emplace_back(extractValue(*C));
+    Child.getAsObject()->insert({"Children", ChildArr});
+    Obj.insert({"FullComment", Child});
+  }
+  if (I.Kind == "ParagraphComment") {
+    Value ChildArr = Array();
+    for (const auto& C: I.Children)
+      ChildArr.getAsArray()->emplace_back(extractValue(*C));
+    Child.getAsObject()->insert({"Children", ChildArr});
+    Obj.insert({"ParagraphComment", Child});
+  }
+  if (I.Kind == "BlockCommandComment") {
+    Child.getAsObject()->insert({"Command", I.Name});
+    Value ChildArr = Array();
+    for (const auto& C: I.Children)
+      ChildArr.getAsArray()->emplace_back(extractValue(*C));
+    Child.getAsObject()->insert({"Children", ChildArr});
+    Obj.insert({"BlockCommandComment", Child});
+  }
+  if (I.Kind == "TextComment")
+    Obj.insert({"TextComment", I.Text});
+  
+  return Obj;
+}
+
+Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
+                   const ClangDocContext &CDCtx) {
+  Object Obj = Object();
+  Obj.insert({"Name", I.Name});
+  Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+  Obj.insert({"Access", getAccessSpelling(I.Access).str()});
+  Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
+  
+  Value ParamArr = Array();
+  for (const auto &P : I.Params) {
+    ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
+  }
+  Obj.insert({"Params", ParamArr});
+  
+  if (!I.Description.empty()) {
+    Value ArrDesc = Array();
+    for (const CommentInfo& Child : I.Description) 
+      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+    Obj.insert({"FunctionComments", ArrDesc});
+  }
+  if (I.DefLoc) {
+    if (!CDCtx.RepositoryUrl)
+      Obj.insert({"Location", extractValue(*I.DefLoc)});
+    else
+      Obj.insert({"Location", extractValue(*I.DefLoc, 
+                                           StringRef{*CDCtx.RepositoryUrl})});  
+  }
+  return Obj;
+}
+
+Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
+  Object Obj = Object();
+  std::string EnumType = I.Scoped ? "enum class " : "enum ";
+  EnumType += I.Name;
+  bool HasComment = std::any_of(
+      I.Members.begin(), I.Members.end(),
+      [](const EnumValueInfo &M) { return !M.Description.empty(); });
+  Obj.insert({"EnumName", EnumType});
+  Obj.insert({"HasComment", HasComment});
+  Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+  
+  Value Arr = Array();
+  for (const EnumValueInfo& M: I.Members) {
+    Value EnumValue = Object();
+    EnumValue.getAsObject()->insert({"Name", M.Name});
+    if (!M.ValueExpr.empty())
+      EnumValue.getAsObject()->insert({"ValueExpr", M.ValueExpr});
+    else
+      EnumValue.getAsObject()->insert({"Value", M.Value});
+    
+    if (!M.Description.empty()) {
+      Value ArrDesc = Array();
+      for (const CommentInfo& Child : M.Description) 
+        ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+      EnumValue.getAsObject()->insert({"EnumValueComments", ArrDesc});
+    }
+    Arr.getAsArray()->emplace_back(EnumValue);
+  }
+  Obj.insert({"EnumValues", Arr});
+  
+  if (!I.Description.empty()) {
+    Value ArrDesc = Array();
+    for (const CommentInfo& Child : I.Description) 
+      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+    Obj.insert({"EnumComments", ArrDesc});
+  }
+  
+  if (I.DefLoc) {
+    if (!CDCtx.RepositoryUrl)
+      Obj.insert({"Location", extractValue(*I.DefLoc)});
+    else
+      Obj.insert({"Location", extractValue(*I.DefLoc, 
+                                           StringRef{*CDCtx.RepositoryUrl})});  
+  }
+  
+  return Obj;
+}
+
+Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
+  Object NamespaceValue = Object();
+  std::string InfoTitle;
+  if (I.Name.str() == "")
+    InfoTitle = "Global Namespace";
+  else
+    InfoTitle = ("namespace " + I.Name).str();  
+  
+  StringRef BasePath = I.getRelativeFilePath("");
+  NamespaceValue.insert({"NamespaceTitle", InfoTitle});
+  NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
+  
+  if (!I.Description.empty()) {
+    Value ArrDesc = Array();
+    for (const CommentInfo& Child : I.Description) 
+      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+    NamespaceValue.insert({"NamespaceComments", ArrDesc });
+  }
+
+  Value ArrNamespace = Array();
+  for (const Reference& Child : I.Children.Namespaces)
+    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  NamespaceValue.insert({"Namespace", ArrNamespace});
+  
+  Value ArrRecord = Array();
+  for (const Reference& Child : I.Children.Records)
+    ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  NamespaceValue.insert({"Record", ArrRecord});
+  
+  Value ArrFunction = Array();
+  for (const FunctionInfo& Child : I.Children.Functions)
+    ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
+                                                        CDCtx));
+  NamespaceValue.insert({"Function", ArrRecord});
+  
+  Value ArrEnum = Array();
+  for (const EnumInfo& Child : I.Children.Enums)
+    ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
+  NamespaceValue.insert({"Enums", ArrEnum });
+  
+  Value ArrTypedefs = Array();
+  for (const TypedefInfo& Child : I.Children.Typedefs) 
+    ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
+  NamespaceValue.insert({"Typedefs", ArrTypedefs });
+  
+}
+
+
+
+llvm::Error
+MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
+                                          const ClangDocContext &CDCtx) {
+  switch (I->IT) {
+  case InfoType::IT_namespace: {
+    Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
+    OS << NamespaceTemplate->render(V);
+    break;
+  }
+  case InfoType::IT_record:
+    break;
+  case InfoType::IT_enum:
+    break;
+  case InfoType::IT_function:
+    break;
+  case InfoType::IT_typedef:
+    break;
+  case InfoType::IT_default:
+    return createStringError(llvm::inconvertibleErrorCode(),
+                             "unexpected InfoType");
+  }
+  return llvm::Error::success();
+}
+
+llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
+  
+}
+
+const char *MustacheHTMLGenerator::Format = "mustache";
+
+
+static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
+                                                  "Generator for mustache HTML output.");
+
+// This anchor is used to force the linker to link in the generated object
+// file and thus register the generator.
+volatile int HTMLGeneratorAnchorSource = 0;
+
+} // namespace doc
+} // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index bb0c534af7b740..3175b8209e3be3 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -521,8 +521,10 @@ struct ClangDocContext {
   // Path of CSS stylesheets that will be copied to OutDirectory and used to
   // style all HTML files.
   std::vector<std::string> UserStylesheets;
-  // JavaScript files that will be imported in allHTML file.
+  // JavaScript files that will be imported in all HTML file.
   std::vector<std::string> JsScripts;
+  // Mustache Template files
+  llvm::StringMap<std::string> MustacheTemplates;
   Index Idx;
 };
 
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
new file mode 100644
index 00000000000000..af4c60182ae52c
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -0,0 +1,23 @@
+{{! 
+    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
+    
+    This file defines the template
+}}
+<!DOCTYPE html>
+<html lang="en-US">
+    <head>
+        <meta charset="utf-8"/>
+        <title>{{NamespaceTitle}}</title>
+    </head>
+    {{#NamespaceComments}}
+    <p>Namespace Comment Present!</p>
+    {{/NamespaceComments}}
+    {{#Namespace}}
+    <p>Namespace Present!</p>
+    {{/Namespace}}
+    {{#Record}}
+    <p>Record Present!</p>
+    {{/Record}}
+</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index 601a0460d76b38..8eb067dbe6de87 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,6 +21,7 @@ target_link_libraries(clang-doc
 
 set(assets
   index.js
+  template.mustache
   clang-doc-default-stylesheet.css
 )
 
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 04773fb8a6e721..fc5a9529111b16 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -205,6 +205,30 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
   return getDefaultAssetFiles(Argv0, CDCtx);
 }
 
+llvm::Error getMustacheHtmlFiles(const char *Argv0,
+                                 clang::doc::ClangDocContext &CDCtx) {
+  if (!UserAssetPath.empty() &&
+      !llvm::sys::fs::is_directory(std::string(UserAssetPath)))
+    llvm::outs() << "Asset path supply is not a directory: " << UserAssetPath
+                 << " falling back to default\n";
+  if (llvm::sys::fs::is_directory(std::string(UserAssetPath)))
+    return getAssetFiles(CDCtx);
+  
+  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");
+  
+  llvm::SmallString<128> MustacheTemplate;
+  llvm::sys::path::native(AssetsPath, MustacheTemplate);
+  llvm::sys::path::append(MustacheTemplate, "template.mustache");
+  CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+}
+
 /// Make the output of clang-doc deterministic by sorting the children of
 /// namespaces and records.
 void sortUsrToInfo(llvm::StringMap<std::unique_ptr<doc::Info>> &USRToInfo) {
@@ -277,6 +301,13 @@ Example usage for a project using a compile commands database:
       return 1;
     }
   }
+  
+  if (Format == "mhtml") {
+    if (auto Err = getMustacheHtmlFiles(argv[0], CDCtx)) {
+      llvm::errs() << toString(std::move(Err)) << "\n";
+      return 1;
+    }
+  }
 
   // Mapping phase
   llvm::outs() << "Mapping decls...\n";

>From adda08b4b88889d9ec975ce94c54923985e41d7e Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 17:50:03 -0400
Subject: [PATCH 50/62] [clang-doc] add a mustache backend init implementation

---
 clang-tools-extra/clang-doc/Generators.cpp    |  4 +-
 .../clang-doc/HTMLMustacheGenerator.cpp       | 32 ++++++++++++----
 .../clang-doc/assets/template.mustache        | 37 +++++++++++++++++--
 .../clang-doc/tool/ClangDocMain.cpp           |  9 ++++-
 .../Inputs/basic-project/include/Shape.h      |  2 -
 5 files changed, 68 insertions(+), 16 deletions(-)

diff --git a/clang-tools-extra/clang-doc/Generators.cpp b/clang-tools-extra/clang-doc/Generators.cpp
index a3986b66f3c742..ae5b556d17063b 100644
--- a/clang-tools-extra/clang-doc/Generators.cpp
+++ b/clang-tools-extra/clang-doc/Generators.cpp
@@ -100,12 +100,14 @@ void Generator::addInfoToIndex(Index &Idx, const doc::Info *Info) {
 extern volatile int YAMLGeneratorAnchorSource;
 extern volatile int MDGeneratorAnchorSource;
 extern volatile int HTMLGeneratorAnchorSource;
+extern volatile int MHTMLGeneratorAnchorSource;
 static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest =
     YAMLGeneratorAnchorSource;
 static int LLVM_ATTRIBUTE_UNUSED MDGeneratorAnchorDest =
     MDGeneratorAnchorSource;
 static int LLVM_ATTRIBUTE_UNUSED HTMLGeneratorAnchorDest =
     HTMLGeneratorAnchorSource;
-
+static int LLVM_ATTRIBUTE_UNUSED MHTMLGeneratorAnchorDest =
+    MHTMLGeneratorAnchorSource;
 } // namespace doc
 } // namespace clang
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 63def07c5fa804..bfa563ebdc7b37 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -54,6 +54,7 @@ class MustacheTemplateFile : public Template {
     std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
     llvm::StringRef FileContent = Buffer->getBuffer();
     registerPartial(Name, FileContent);
+    return llvm::Error::success();
   }
 
   MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
@@ -72,6 +73,7 @@ setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
     return llvm::createFileError("cannot open file", EC);
   }
   NamespaceTemplate = std::move(Template.get());
+  return llvm::Error::success();
 }
 
 llvm::Error 
@@ -118,6 +120,7 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
       }
     }
   }
+  return llvm::Error::success();
 }
 
 Value extractValue(const Location &L, 
@@ -130,6 +133,8 @@ Value extractValue(const Location &L,
   SmallString<128> FileURL(*RepositoryUrl);
   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
   Obj.insert({"FileURL", FileURL});
+  
+  return Obj;
 }
 
 Value extractValue(const Reference &I, StringRef CurrentDirectory) {
@@ -280,29 +285,39 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
   Value ArrNamespace = Array();
   for (const Reference& Child : I.Children.Namespaces)
     ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
-  NamespaceValue.insert({"Namespace", ArrNamespace});
+  
+  if (!ArrNamespace.getAsArray()->empty())
+    NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
   
   Value ArrRecord = Array();
   for (const Reference& Child : I.Children.Records)
     ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
-  NamespaceValue.insert({"Record", ArrRecord});
+  
+  if (!ArrRecord.getAsArray()->empty())
+    NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
   
   Value ArrFunction = Array();
   for (const FunctionInfo& Child : I.Children.Functions)
     ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
                                                         CDCtx));
-  NamespaceValue.insert({"Function", ArrRecord});
+  if (!ArrFunction.getAsArray()->empty())
+    NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
   
   Value ArrEnum = Array();
   for (const EnumInfo& Child : I.Children.Enums)
     ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
-  NamespaceValue.insert({"Enums", ArrEnum });
+  
+  if (!ArrEnum.getAsArray()->empty())
+    NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
   
   Value ArrTypedefs = Array();
   for (const TypedefInfo& Child : I.Children.Typedefs) 
     ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
-  NamespaceValue.insert({"Typedefs", ArrTypedefs });
   
+  if (!ArrTypedefs.getAsArray()->empty())
+    NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+  
+  return NamespaceValue;
 }
 
 
@@ -313,6 +328,7 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
+    llvm::outs() << V << "\n";
     OS << NamespaceTemplate->render(V);
     break;
   }
@@ -332,10 +348,10 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
 }
 
 llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
-  
+  return llvm::Error::success();
 }
 
-const char *MustacheHTMLGenerator::Format = "mustache";
+const char *MustacheHTMLGenerator::Format = "mhtml";
 
 
 static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
@@ -343,7 +359,7 @@ static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator
 
 // This anchor is used to force the linker to link in the generated object
 // file and thus register the generator.
-volatile int HTMLGeneratorAnchorSource = 0;
+volatile int MHTMLGeneratorAnchorSource = 0;
 
 } // namespace doc
 } // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
index af4c60182ae52c..1d3407f8b52920 100644
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -3,7 +3,7 @@
     See https://llvm.org/LICENSE.txt for license information.
     SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
     
-    This file defines the template
+    This file defines the template for generating Namespaces
 }}
 <!DOCTYPE html>
 <html lang="en-US">
@@ -11,13 +11,42 @@
         <meta charset="utf-8"/>
         <title>{{NamespaceTitle}}</title>
     </head>
+    <h1>{{NamespaceTitle}}</h1>
     {{#NamespaceComments}}
-    <p>Namespace Comment Present!</p>
+    <p>Namespace Comment</p>
     {{/NamespaceComments}}
     {{#Namespace}}
-    <p>Namespace Present!</p>
+    <h2 id="Namespace">Namespace</h2>
+    <ul>
+        {{#Links}}
+        <li>
+            <a href="{{Link}}">{{Name}}</a>
+        </li>
+        {{/Links}}
+    </ul>
     {{/Namespace}}
     {{#Record}}
-    <p>Record Present!</p>
+    <h2 id="Class">Class</h2>
+    <ul>
+        {{#Links}}
+        <li>
+            <a href="{{Link}}">{{Name}}</a>
+        </li>
+        {{/Links}}
+    </ul>
     {{/Record}}
+    {{#Function}}
+    <h2 id="Function">Function</h2>
+    <div>
+        {{#Obj}}
+        {{/Obj}}
+    </div>
+    {{/Function}}
+    {{#Enums}}
+    <h2 id="Enums">Enums</h2>
+    <div>
+        {{#Obj}}
+        {{/Obj}}
+    </div>
+    {{/Enums}}
 </html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index fc5a9529111b16..eee8dbf093cc53 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -103,6 +103,7 @@ enum OutputFormatTy {
   md,
   yaml,
   html,
+  mhtml
 };
 
 static llvm::cl::opt<OutputFormatTy>
@@ -112,7 +113,9 @@ static llvm::cl::opt<OutputFormatTy>
                                 clEnumValN(OutputFormatTy::md, "md",
                                            "Documentation in MD format."),
                                 clEnumValN(OutputFormatTy::html, "html",
-                                           "Documentation in HTML format.")),
+                                           "Documentation in HTML format."),
+                                clEnumValN(OutputFormatTy::mhtml, "mhtml",
+                                           "Documentation in mHTML format")),
                llvm::cl::init(OutputFormatTy::yaml),
                llvm::cl::cat(ClangDocCategory));
 
@@ -124,6 +127,8 @@ std::string getFormatString() {
     return "md";
   case OutputFormatTy::html:
     return "html";
+  case OutputFormatTy::mhtml:
+    return "mhtml";
   }
   llvm_unreachable("Unknown OutputFormatTy");
 }
@@ -227,6 +232,8 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
   llvm::sys::path::native(AssetsPath, MustacheTemplate);
   llvm::sys::path::append(MustacheTemplate, "template.mustache");
   CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+  
+  return llvm::Error::success();
 }
 
 /// Make the output of clang-doc deterministic by sorting the children of
diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
index e5c5d4c9e4412b..5354032f4d8323 100644
--- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
+++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
@@ -26,5 +26,3 @@ class Shape {
      */
     virtual double perimeter() const = 0;
 };
-
-

>From 587bb02d1e1c675c2970e91249d579e623e951ea Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 24 Sep 2024 16:48:29 -0400
Subject: [PATCH 51/62] [clang-doc] update

---
 .../clang-doc/HTMLMustacheGenerator.cpp       | 16 ++++++++---
 .../clang-doc/assets/comments.mustache        | 27 +++++++++++++++++++
 .../clang-doc/assets/template.mustache        |  7 +++--
 .../Inputs/basic-project/include/Shape.h      |  2 ++
 4 files changed, 46 insertions(+), 6 deletions(-)
 create mode 100644 clang-tools-extra/clang-doc/assets/comments.mustache

diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index bfa563ebdc7b37..96a685dbe2ae87 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -194,8 +194,12 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
   Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
   
   Value ParamArr = Array();
-  for (const auto &P : I.Params) {
-    ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
+  for (const auto Val : llvm::enumerate(I.Params)) {
+    Value V = Object();
+    V.getAsObject()->insert({"Name", Val.value().Name});
+    V.getAsObject()->insert({"Type", Val.value().Type.Name});
+    V.getAsObject()->insert({"End",  Val.index() + 1 == I.Params.size()});
+    ParamArr.getAsArray()->emplace_back(V);
   }
   Obj.insert({"Params", ParamArr});
   
@@ -328,17 +332,21 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
-    llvm::outs() << V << "\n";
-    OS << NamespaceTemplate->render(V);
+    llvm::raw_ostream &OS = llvm::outs();
+    llvm::json::OStream J(OS, /*IndentSize=*/2);
+    J.value(V);
     break;
   }
   case InfoType::IT_record:
     break;
   case InfoType::IT_enum:
+    llvm::outs() << "IT_enum\n";
     break;
   case InfoType::IT_function:
+    llvm::outs() << "IT_Function\n";
     break;
   case InfoType::IT_typedef:
+    llvm::outs() << "IT_typedef\n";
     break;
   case InfoType::IT_default:
     return createStringError(llvm::inconvertibleErrorCode(),
diff --git a/clang-tools-extra/clang-doc/assets/comments.mustache b/clang-tools-extra/clang-doc/assets/comments.mustache
new file mode 100644
index 00000000000000..1eac4de91836ab
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/comments.mustache
@@ -0,0 +1,27 @@
+{{!
+    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
+    
+    This file defines templates for generating
+}}
+
+{{#FullComments}}
+    {{#Children}}
+    {{>Comment}}    
+    {{/Children}}
+{{/FullComments}}
+{{#ParagraphComment}}
+    {{#Children}}
+    {{>Comment}}
+    {{/Children}}
+{{/ParagraphComment}}
+{{#BlockCommandComment}}
+    <div>{{Command}}</div>
+    {{#Children}}
+    {{>Comment}}
+    {{/Children}}
+{{/BlockCommandComment}}
+{{#TextComment}}
+    <p>{{TextComment}}</p>
+{{/TextComment}}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
index 1d3407f8b52920..3b77e0189f4e22 100644
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -3,7 +3,7 @@
     See https://llvm.org/LICENSE.txt for license information.
     SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
     
-    This file defines the template for generating Namespaces
+    This file defines the template for generating namespaces
 }}
 <!DOCTYPE html>
 <html lang="en-US">
@@ -13,7 +13,9 @@
     </head>
     <h1>{{NamespaceTitle}}</h1>
     {{#NamespaceComments}}
-    <p>Namespace Comment</p>
+    <div>
+        {{>Comments}}
+    </div>
     {{/NamespaceComments}}
     {{#Namespace}}
     <h2 id="Namespace">Namespace</h2>
@@ -39,6 +41,7 @@
     <h2 id="Function">Function</h2>
     <div>
         {{#Obj}}
+            
         {{/Obj}}
     </div>
     {{/Function}}
diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
index 5354032f4d8323..e5c5d4c9e4412b 100644
--- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
+++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
@@ -26,3 +26,5 @@ class Shape {
      */
     virtual double perimeter() const = 0;
 };
+
+

>From ab1c3a6ff20c645d2cf938d6d5572f7fd3c948e0 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 7 Oct 2024 15:07:41 -0400
Subject: [PATCH 52/62] [clang-doc] add more templates

---
 clang-tools-extra/clang-doc/CMakeLists.txt    |   5 +-
 .../clang-doc/FileHelpersClangDoc.cpp         |  75 +++
 .../clang-doc/FileHelpersClangDoc.h           |  26 +
 clang-tools-extra/clang-doc/HTMLGenerator.cpp |  59 +--
 .../clang-doc/HTMLMustacheGenerator.cpp       | 281 ++++++++---
 .../clang-doc/assets/clang-doc-mustache.css   | 476 ++++++++++++++++++
 .../clang-doc/assets/class-template.mustache  | 227 +++++++++
 .../assets/comments-template.mustache         |  34 ++
 .../clang-doc/assets/comments.mustache        |  27 -
 .../clang-doc/assets/enum-template.mustache   |  47 ++
 .../assets/function-template.mustache         |  23 +
 .../clang-doc/assets/mustache-index.js        |  33 ++
 .../assets/namespace-template.mustache        |  87 ++++
 .../clang-doc/assets/template.mustache        |  55 --
 .../clang-doc/tool/CMakeLists.txt             |   8 +-
 .../clang-doc/tool/ClangDocMain.cpp           |  53 +-
 16 files changed, 1296 insertions(+), 220 deletions(-)
 create mode 100644 clang-tools-extra/clang-doc/FileHelpersClangDoc.cpp
 create mode 100644 clang-tools-extra/clang-doc/FileHelpersClangDoc.h
 create mode 100644 clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
 create mode 100644 clang-tools-extra/clang-doc/assets/class-template.mustache
 create mode 100644 clang-tools-extra/clang-doc/assets/comments-template.mustache
 delete mode 100644 clang-tools-extra/clang-doc/assets/comments.mustache
 create mode 100644 clang-tools-extra/clang-doc/assets/enum-template.mustache
 create mode 100644 clang-tools-extra/clang-doc/assets/function-template.mustache
 create mode 100644 clang-tools-extra/clang-doc/assets/mustache-index.js
 create mode 100644 clang-tools-extra/clang-doc/assets/namespace-template.mustache
 delete mode 100644 clang-tools-extra/clang-doc/assets/template.mustache

diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 82aded940222db..0793c343305184 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -8,15 +8,16 @@ add_clang_library(clangDoc
   BitcodeReader.cpp
   BitcodeWriter.cpp
   ClangDoc.cpp
+  FileHelpersClangDoc.cpp
   Generators.cpp
   HTMLGenerator.cpp
+  HTMLMustacheGenerator.cpp      
   Mapper.cpp
   MDGenerator.cpp
   Representation.cpp
   Serialize.cpp
   YAMLGenerator.cpp
-  HTMLMustacheGenerator.cpp
-
+        
   DEPENDS
   omp_gen
   ClangDriverOptions
diff --git a/clang-tools-extra/clang-doc/FileHelpersClangDoc.cpp b/clang-tools-extra/clang-doc/FileHelpersClangDoc.cpp
new file mode 100644
index 00000000000000..50209cfac1ca3d
--- /dev/null
+++ b/clang-tools-extra/clang-doc/FileHelpersClangDoc.cpp
@@ -0,0 +1,75 @@
+//===-- FileHelpersClangDoc.cpp - File Helpers -------------------*- C++-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "FileHelpersClangDoc.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace doc {
+
+llvm::Error
+copyFile(llvm::StringRef FilePath, llvm::StringRef OutDirectory) {
+  llvm::SmallString<128> PathWrite;
+  llvm::sys::path::native(OutDirectory, PathWrite);
+  llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
+  llvm::SmallString<128> PathRead;
+  llvm::sys::path::native(FilePath, PathRead);
+  std::error_code OK;
+  std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
+  if (FileErr != OK) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "error creating file " +
+                                       llvm::sys::path::filename(FilePath) +
+                                       ": " + FileErr.message() + "\n");
+  }
+  return llvm::Error::success();
+}
+
+
+llvm::SmallString<128> computeRelativePath(llvm::StringRef Destination,
+                                                  llvm::StringRef Origin) {
+  // If Origin is empty, the relative path to the Destination is its complete
+  // path.
+  if (Origin.empty())
+    return Destination;
+  
+  // The relative path is an empty path if both directories are the same.
+  if (Destination == Origin)
+    return {};
+
+  // These iterators iterate through each of their parent directories
+  llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
+  llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
+  llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
+  llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
+  // Advance both iterators until the paths differ. Example:
+  //    Destination = A/B/C/D
+  //    Origin      = A/B/E/F
+  // FileI will point to C and DirI to E. The directories behind them is the
+  // directory they share (A/B).
+  while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
+    ++FileI;
+    ++DirI;
+  }
+  llvm::SmallString<128> Result; // This will hold the resulting path.
+  // Result has to go up one directory for each of the remaining directories in
+  // Origin
+  while (DirI != DirE) {
+    llvm::sys::path::append(Result, "..");
+    ++DirI;
+  }
+  // Result has to append each of the remaining directories in Destination
+  while (FileI != FileE) {
+    llvm::sys::path::append(Result, *FileI);
+    ++FileI;
+  }
+  return Result;
+}
+
+} // namespace doc
+} // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/FileHelpersClangDoc.h b/clang-tools-extra/clang-doc/FileHelpersClangDoc.h
new file mode 100644
index 00000000000000..9072a7bd08a4f8
--- /dev/null
+++ b/clang-tools-extra/clang-doc/FileHelpersClangDoc.h
@@ -0,0 +1,26 @@
+//===-- FileHelpersClangDoc.h --- File Helpers -------------------*- C++-*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_FILEHELPER_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_FILEHELPER_H
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Error.h"
+
+namespace clang {
+namespace doc {
+
+llvm::Error 
+copyFile(llvm::StringRef FilePath, llvm::StringRef OutDirectory);
+
+llvm::SmallString<128> 
+computeRelativePath(llvm::StringRef Destination,llvm::StringRef Origin);
+
+} // namespace doc
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_DOC_FILEHELPER_H
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index ed65ac965114b5..aa76d17d9411d6 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -8,6 +8,7 @@
 
 #include "Generators.h"
 #include "Representation.h"
+#include "FileHelpersClangDoc.h"
 #include "clang/Basic/Version.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -251,46 +252,6 @@ static void appendVector(std::vector<Derived> &&New,
   std::move(New.begin(), New.end(), std::back_inserter(Original));
 }
 
-// Compute the relative path from an Origin directory to a Destination directory
-static SmallString<128> computeRelativePath(StringRef Destination,
-                                            StringRef Origin) {
-  // If Origin is empty, the relative path to the Destination is its complete
-  // path.
-  if (Origin.empty())
-    return Destination;
-
-  // The relative path is an empty path if both directories are the same.
-  if (Destination == Origin)
-    return {};
-
-  // These iterators iterate through each of their parent directories
-  llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
-  llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
-  llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
-  llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
-  // Advance both iterators until the paths differ. Example:
-  //    Destination = A/B/C/D
-  //    Origin      = A/B/E/F
-  // FileI will point to C and DirI to E. The directories behind them is the
-  // directory they share (A/B).
-  while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
-    ++FileI;
-    ++DirI;
-  }
-  SmallString<128> Result; // This will hold the resulting path.
-  // Result has to go up one directory for each of the remaining directories in
-  // Origin
-  while (DirI != DirE) {
-    llvm::sys::path::append(Result, "..");
-    ++DirI;
-  }
-  // Result has to append each of the remaining directories in Destination
-  while (FileI != FileE) {
-    llvm::sys::path::append(Result, *FileI);
-    ++FileI;
-  }
-  return Result;
-}
 
 // HTML generation
 
@@ -1133,24 +1094,6 @@ static llvm::Error genIndex(const ClangDocContext &CDCtx) {
   return llvm::Error::success();
 }
 
-static llvm::Error 
-copyFile(StringRef FilePath, StringRef OutDirectory) {
-  llvm::SmallString<128> PathWrite;
-  llvm::sys::path::native(OutDirectory, PathWrite);
-  llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
-  llvm::SmallString<128> PathRead;
-  llvm::sys::path::native(FilePath, PathRead);
-  std::error_code OK;
-  std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
-  if (FileErr != OK) {
-    return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                   "error creating file " +
-                                       llvm::sys::path::filename(FilePath) +
-                                       ": " + FileErr.message() + "\n");
-  }
-  return llvm::Error::success();
-}
-
 llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
   auto Err = serializeIndex(CDCtx);
   if (Err)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 96a685dbe2ae87..6be4c795a68654 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 #include "Generators.h"
 #include "Representation.h"
+#include "FileHelpersClangDoc.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Mustache.h"
 
@@ -48,9 +49,8 @@ class MustacheTemplateFile : public Template {
   Error registerPartialFile(StringRef Name, StringRef FileName) {
     ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
         MemoryBuffer::getFile(FileName);
-    if (auto EC = BufferOrError.getError()) {
+    if (auto EC = BufferOrError.getError())
       return llvm::createFileError("cannot open file", EC);
-    }
     std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
     llvm::StringRef FileContent = Buffer->getBuffer();
     registerPartial(Name, FileContent);
@@ -65,24 +65,54 @@ static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
 static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
 
 
+llvm::Error setupTemplate(
+                   std::unique_ptr<MustacheTemplateFile> &Template,
+                   StringRef TemplatePath,
+                   std::vector<std::pair<StringRef, StringRef>> Partials) {
+  auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
+  if (auto EC = T.getError())
+     return llvm::createFileError("cannot open file", EC);
+  Template = std::move(T.get());
+  for (const auto &P : Partials) {
+    auto Err = Template->registerPartialFile(P.first, P.second);
+    if (Err)
+      return Err;
+  }
+  return llvm::Error::success();
+}
+
 llvm::Error 
 setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
-  auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
-  auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
-  if (auto EC = Template.getError()) {
-    return llvm::createFileError("cannot open file", EC);
-  }
-  NamespaceTemplate = std::move(Template.get());
+  auto NamespaceFilePath = CDCtx.MustacheTemplates.lookup("namespace-template");
+  auto ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
+  auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
+  auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
+  auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
+  std::vector<std::pair<StringRef, StringRef>> Partials = {
+    {"Comments", CommentFilePath},
+    {"FunctionPartial", FunctionFilePath},
+    {"EnumPartial", EnumFilePath}
+  };
+  
+  auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
+  if (Err)
+    return Err;
+  
+  Err = setupTemplate(RecordTemplate, ClassFilePath, Partials);
+  
+  if (Err)
+    return Err;
+  
   return llvm::Error::success();
 }
 
+
 llvm::Error 
 MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir, 
                                     llvm::StringMap<std::unique_ptr<doc::Info>> Infos, 
                                     const clang::doc::ClangDocContext &CDCtx) {
-  if (auto Err = setupTemplateFiles(CDCtx)) {
+  if (auto Err = setupTemplateFiles(CDCtx))
     return Err;
-  }
   // Track which directories we already tried to create.
   llvm::StringSet<> CreatedDirs;
   // Collect all output by file name and create the necessary directories.
@@ -95,10 +125,9 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
     llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
     if (!CreatedDirs.contains(Path)) {
       if (std::error_code Err = llvm::sys::fs::create_directories(Path);
-          Err != std::error_code()) {
+          Err != std::error_code())
         return llvm::createStringError(Err, "Failed to create directory '%s'.",
                                        Path.c_str());
-      }
       CreatedDirs.insert(Path);
     }
 
@@ -110,14 +139,13 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
     std::error_code FileErr;
     llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
                                 llvm::sys::fs::OF_None);
-    if (FileErr) {
+    if (FileErr)
       return llvm::createStringError(FileErr, "Error opening file '%s'",
                                      Group.getKey().str().c_str());
-    }
+    
     for (const auto &Info : Group.getValue()) {
-      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
+      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
         return Err;
-      }
     }
   }
   return llvm::Error::success();
@@ -126,12 +154,15 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
 Value extractValue(const Location &L, 
                    std::optional<StringRef> RepositoryUrl = std::nullopt) {
   Object Obj = Object();
+  Obj.insert({"LineNumber", L.LineNumber});
+  Obj.insert({"Filename", L.Filename});
+  
   if (!L.IsFileInRootDir || !RepositoryUrl) {
-    Obj.insert({"LineNumber", L.LineNumber});
-    Obj.insert({"Filename", L.Filename});
+    return Obj;
   }
   SmallString<128> FileURL(*RepositoryUrl);
   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+  FileURL += "#" + std::to_string(L.LineNumber);
   Obj.insert({"FileURL", FileURL});
   
   return Obj;
@@ -144,6 +175,8 @@ Value extractValue(const Reference &I, StringRef CurrentDirectory) {
   Object Obj = Object();
   Obj.insert({"Link", Path});
   Obj.insert({"Name", I.Name});
+  Obj.insert({"QualName", I.QualName});
+  Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
   return Obj;
 }
 
@@ -209,12 +242,13 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
       ArrDesc.getAsArray()->emplace_back(extractValue(Child));
     Obj.insert({"FunctionComments", ArrDesc});
   }
-  if (I.DefLoc) {
-    if (!CDCtx.RepositoryUrl)
-      Obj.insert({"Location", extractValue(*I.DefLoc)});
+  if (I.DefLoc.has_value()) {
+    Location L = *I.DefLoc;
+    if (CDCtx.RepositoryUrl.has_value())
+      Obj.insert({"Location", extractValue(L,
+                                           StringRef{*CDCtx.RepositoryUrl})});
     else
-      Obj.insert({"Location", extractValue(*I.DefLoc, 
-                                           StringRef{*CDCtx.RepositoryUrl})});  
+      Obj.insert({"Location", extractValue(L)});  
   }
   return Obj;
 }
@@ -229,7 +263,6 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
   Obj.insert({"EnumName", EnumType});
   Obj.insert({"HasComment", HasComment});
   Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
-  
   Value Arr = Array();
   for (const EnumValueInfo& M: I.Members) {
     Value EnumValue = Object();
@@ -256,75 +289,175 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
     Obj.insert({"EnumComments", ArrDesc});
   }
   
-  if (I.DefLoc) {
-    if (!CDCtx.RepositoryUrl)
-      Obj.insert({"Location", extractValue(*I.DefLoc)});
+  if (I.DefLoc.has_value()) {
+    Location L = *I.DefLoc;
+    if (CDCtx.RepositoryUrl.has_value())
+      Obj.insert({"Location", extractValue(L,
+                                           StringRef{*CDCtx.RepositoryUrl})});
     else
-      Obj.insert({"Location", extractValue(*I.DefLoc, 
-                                           StringRef{*CDCtx.RepositoryUrl})});  
+      Obj.insert({"Location", extractValue(L)});  
   }
   
   return Obj;
 }
 
-Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
-  Object NamespaceValue = Object();
-  std::string InfoTitle;
-  if (I.Name.str() == "")
-    InfoTitle = "Global Namespace";
-  else
-    InfoTitle = ("namespace " + I.Name).str();  
-  
-  StringRef BasePath = I.getRelativeFilePath("");
-  NamespaceValue.insert({"NamespaceTitle", InfoTitle});
-  NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
-  
-  if (!I.Description.empty()) {
-    Value ArrDesc = Array();
-    for (const CommentInfo& Child : I.Description) 
-      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
-    NamespaceValue.insert({"NamespaceComments", ArrDesc });
-  }
-
+void extractScopeChildren(const ScopeChildren &S, Object &Obj, 
+                          StringRef ParentInfoDir,
+                          const ClangDocContext &CDCtx) {
   Value ArrNamespace = Array();
-  for (const Reference& Child : I.Children.Namespaces)
-    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  for (const Reference& Child : S.Namespaces)
+    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
   
   if (!ArrNamespace.getAsArray()->empty())
-    NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
+    Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
   
   Value ArrRecord = Array();
-  for (const Reference& Child : I.Children.Records)
-    ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  for (const Reference& Child : S.Records)
+    ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
   
   if (!ArrRecord.getAsArray()->empty())
-    NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
+    Obj.insert({"Record", Object{{"Links", ArrRecord}}});
   
   Value ArrFunction = Array();
-  for (const FunctionInfo& Child : I.Children.Functions)
-    ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
-                                                        CDCtx));
+  Value PublicFunction = Array();
+  Value ProtectedFunction = Array();
+  Value PrivateFunction = Array();
+  
+  for (const FunctionInfo& Child : S.Functions) {
+    Value F = extractValue(Child, ParentInfoDir, CDCtx);
+    AccessSpecifier Access = Child.Access;
+    if (Access == AccessSpecifier::AS_public)
+      PublicFunction.getAsArray()->emplace_back(F);
+    else if (Access == AccessSpecifier::AS_protected)
+      ProtectedFunction.getAsArray()->emplace_back(F);
+    else
+      ArrFunction.getAsArray()->emplace_back(F);
+  }  
   if (!ArrFunction.getAsArray()->empty())
-    NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
+    Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
+  
+  if (!PublicFunction.getAsArray()->empty())
+    Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
+  
+  if (!ProtectedFunction.getAsArray()->empty())
+    Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
+  
   
   Value ArrEnum = Array();
-  for (const EnumInfo& Child : I.Children.Enums)
+  for (const EnumInfo& Child : S.Enums)
     ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
   
   if (!ArrEnum.getAsArray()->empty())
-    NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
+    Obj.insert({"Enums", Object{{"Obj", ArrEnum }}});
   
   Value ArrTypedefs = Array();
-  for (const TypedefInfo& Child : I.Children.Typedefs) 
+  for (const TypedefInfo& Child : S.Typedefs) 
     ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
   
   if (!ArrTypedefs.getAsArray()->empty())
-    NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+    Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+}
+
+Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
+  Object NamespaceValue = Object();
+  std::string InfoTitle;
+  if (I.Name.str() == "")
+    InfoTitle = "Global Namespace";
+  else
+    InfoTitle = ("namespace " + I.Name).str();  
+  
+  StringRef BasePath = I.getRelativeFilePath("");
+  NamespaceValue.insert({"NamespaceTitle", InfoTitle});
+  NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
   
+  if (!I.Description.empty()) {
+    Value ArrDesc = Array();
+    for (const CommentInfo& Child : I.Description) 
+      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+    NamespaceValue.insert({"NamespaceComments", ArrDesc });
+  }
+  extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
   return NamespaceValue;
 }
 
+Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
+  Object RecordValue = Object();
+  
+  if (!I.Description.empty()) {
+    Value ArrDesc = Array();
+    for (const CommentInfo& Child : I.Description) 
+      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+    RecordValue.insert({"RecordComments", ArrDesc });
+  }
+  RecordValue.insert({"Name", I.Name});
+  RecordValue.insert({"FullName", I.FullName});
+  RecordValue.insert({"RecordType", getTagType(I.TagType)});
+  
+  if (I.DefLoc.has_value()) {
+    Location L = *I.DefLoc;
+    if (CDCtx.RepositoryUrl.has_value())
+      RecordValue.insert({"Location", extractValue(L,
+                                           StringRef{*CDCtx.RepositoryUrl})});
+    else
+      RecordValue.insert({"Location", extractValue(L)});  
+  }
+  
+  StringRef BasePath = I.getRelativeFilePath("");
+  extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
+  Value PublicMembers = Array();
+  Value ProtectedMembers = Array();
+  Value PrivateMembers = Array();
+  for (const MemberTypeInfo &Member : I.Members ) {
+    Value MemberValue = Object();
+    MemberValue.getAsObject()->insert({"Name", Member.Name});
+    MemberValue.getAsObject()->insert({"Type", Member.Type.Name});
+    if (!Member.Description.empty()) {
+      Value ArrDesc = Array();
+      for (const CommentInfo& Child : Member.Description) 
+            ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+      MemberValue.getAsObject()->insert({"MemberComments", ArrDesc });
+    }
+    
+    if (Member.Access == AccessSpecifier::AS_public)
+      PublicMembers.getAsArray()->emplace_back(MemberValue);
+    else if (Member.Access == AccessSpecifier::AS_protected)
+      ProtectedMembers.getAsArray()->emplace_back(MemberValue);
+    else if (Member.Access == AccessSpecifier::AS_private)
+      PrivateMembers.getAsArray()->emplace_back(MemberValue);
+  }
+  if (!PublicMembers.getAsArray()->empty())
+    RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
+  if (!ProtectedMembers.getAsArray()->empty())
+    RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
+  if (!PrivateMembers.getAsArray()->empty())
+    RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+  
+  return RecordValue;
+}
 
+void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
+  V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+  Value StylesheetArr = Array();
+  auto InfoPath = I->getRelativeFilePath("");
+  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+  for (const auto &FilePath  : CDCtx.UserStylesheets) {
+    SmallString<128> StylesheetPath = RelativePath;
+    llvm::sys::path::append(StylesheetPath,
+                            llvm::sys::path::filename(FilePath));
+    llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
+    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+  }
+  V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+  
+  Value ScriptArr = Array();
+  for (auto Script : CDCtx.JsScripts) {
+    SmallString<128> JsPath = RelativePath;
+    llvm::sys::path::append(JsPath, llvm::sys::path::filename(Script));
+    ScriptArr.getAsArray()->emplace_back(JsPath);
+  }
+  V.getAsObject()->insert({"Scripts", ScriptArr});
+}
+ 
 
 llvm::Error
 MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -332,13 +465,18 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
-    llvm::raw_ostream &OS = llvm::outs();
-    llvm::json::OStream J(OS, /*IndentSize=*/2);
-    J.value(V);
+    setupTemplateValue(CDCtx, V, I);
+    OS << NamespaceTemplate->render(V);
     break;
   }
-  case InfoType::IT_record:
+  case InfoType::IT_record: {
+    Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
+    setupTemplateValue(CDCtx, V, I);
+    // Serialize the JSON value to the output stream in a readable format.
+    llvm::outs() << llvm::formatv("{0:2}", V);
+    OS << RecordTemplate->render(V);
     break;
+  }  
   case InfoType::IT_enum:
     llvm::outs() << "IT_enum\n";
     break;
@@ -356,6 +494,17 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
 }
 
 llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
+  llvm::Error Err = llvm::Error::success();
+  for (const auto &FilePath : CDCtx.UserStylesheets) {
+    Err = copyFile(FilePath, CDCtx.OutDirectory);
+    if (Err)
+      return Err;
+  }
+  for (const auto &FilePath : CDCtx.JsScripts) {
+    Err = copyFile(FilePath, CDCtx.OutDirectory);
+    if (Err)
+      return Err;
+  }
   return llvm::Error::success();
 }
 
diff --git a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
new file mode 100644
index 00000000000000..2f07baa7ca0a1a
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -0,0 +1,476 @@
+ at import "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap";
+
+*,*::before *::after {
+    box-sizing:border-box
+}
+* {
+    margin:0;
+    padding:0
+}
+ol,
+ul {
+    list-style:none
+}
+img,
+picture,
+svg,
+video {
+    display:block;
+    max-width:100%
+}
+* {
+    --brand-light:#ce6300;
+    --text1-light:#000000;
+    --text2-light:#333333;
+    --surface1-light:#ffffff;
+    --surface2-light:#f5f5f5;
+    --brand-dark:#de9853;
+    --text1-dark:#ffffff;
+    --text2-dark:#cccccc;
+    --surface1-dark:#161212;
+    --surface2-dark:#272424
+}
+:root {
+    color-scheme:light;
+    --brand:var(--brand-light);
+    --text1:var(--text1-light);
+    --text2:var(--text2-light);
+    --text1-inverse:var(--text1-dark);
+    --text2-inverse:var(--text2-dark);
+    --surface1:var(--surface1-light);
+    --surface2:var(--surface2-light)
+}
+ at media(prefers-color-scheme:dark) {
+    :root {
+        color-scheme:dark;
+        --brand:var(--brand-dark);
+        --text1:var(--text1-dark);
+        --text2:var(--text2-dark);
+        --text1-inverse:var(--text1-light);
+        --text2-inverse:var(--text2-light);
+        --surface1:var(--surface1-dark);
+        --surface2:var(--surface2-dark)
+    }
+}
+[color-scheme=light] {
+    color-scheme:light;
+    --brand:var(--brand-light);
+    --text1:var(--text1-light);
+    --text2:var(--text2-light);
+    --text1-inverse:var(--text1-dark);
+    --text2-inverse:var(--text2-dark);
+    --surface1:var(--surface1-light);
+    --surface2:var(--surface2-light)
+}
+[color-scheme=dark] {
+    color-scheme:dark;
+    --brand:var(--brand-dark);
+    --text1:var(--text1-dark);
+    --text2:var(--text2-dark);
+    --text1-inverse:var(--text1-light);
+    --text2-inverse:var(--text2-light);
+    --surface1:var(--surface1-dark);
+    --surface2:var(--surface2-dark)
+}
+html {
+    background-color:var(--surface1)
+}
+
+html, body {
+    min-height: 100vh;
+    margin: 0;
+    padding: 0;
+    width: 100%;
+}
+
+.container {
+    display: flex;
+    margin-top: 60px;
+    height: calc(100% - 60px);
+    box-sizing: border-box;
+}
+
+body, html {
+    font-family:Inter,sans-serif;
+    margin: 0;
+    padding: 0;
+    height: 100%;
+}
+
+/* Navbar Styles */
+.navbar {
+    background-color: var(--surface2);
+    border-bottom: 1px solid var(--text2);
+    position: fixed;
+    width: 100%;
+    top: 0;
+    left: 0;
+    height: 60px; /* Adjust as needed */
+    color: white;
+    display: flex;
+    align-items: center;
+    padding: 0 20px;
+    box-sizing: border-box;
+    z-index: 1000;
+}
+
+
+.navbar__container {
+    display:flex;
+    justify-content:space-between;
+    align-items:center;
+    padding:1rem;
+    color:var(--text1);
+    max-width:2048px;
+    margin:auto
+}
+.navbar__logo {
+    display:flex;
+    align-items:center;
+    height:40px
+}
+.navbar__logo a {
+    display:flex;
+    align-items:center;
+    text-decoration:none;
+    height:100%
+}
+.navbar__logo img {
+    height:100%;
+    width:auto
+}
+.navbar__toggle {
+    background:0 0;
+    color:var(--text2);
+    border:none;
+    cursor:pointer;
+    font-size:1.5rem;
+    width:2.5rem;
+    height:2.5rem;
+    margin-left:auto
+}
+.navbar__toggle:hover {
+    color:var(--text1)
+}
+ at media(min-width:769px) {
+    .navbar__toggle {
+        display:none
+    }
+}
+.navbar__menu {
+    display:flex;
+    justify-content:space-between;
+    align-items:center;
+    list-style:none;
+    margin:0;
+    padding:0;
+    gap:.25rem;
+    margin-left:auto
+}
+
+ at media(max-width:768px) {
+    .navbar__menu {
+        flex-direction:column;
+        justify-content:flex-start;
+        width:100%;
+        background-color:var(--surface2);
+        position:fixed;
+        top:0;
+        left:0;
+        right:0;
+        bottom:0;
+        padding:1.5rem;
+        transform:translateX(100%);
+        transition:transform .5s ease-in-out
+    }
+}
+ at media(max-width:768px) {
+    .navbar__menu.active {
+        transform:translateX(0)
+    }
+}
+.navbar__close {
+    background:0 0;
+    border:none;
+    cursor:pointer;
+    font-size:1.5rem;
+    color:var(--text2);
+    margin-left:auto
+}
+.navbar__close:hover {
+    color:var(--text1)
+}
+ at media(min-width:769px) {
+    .navbar__close {
+        display:none
+    }
+}
+.navbar__links {
+    display:flex;
+    gap:1rem;
+    align-items:center;
+    margin:0;
+    padding:0
+}
+ at media(max-width:768px) {
+    .navbar__links {
+        flex-direction:column
+    }
+}
+.navbar__item {
+    list-style-type:none
+}
+.navbar__link {
+    color:var(--text2);
+    text-decoration:none;
+    padding:.5rem
+}
+.navbar__link:hover {
+    color:var(--text1)
+}
+.navbar__theme-toggle-button {
+    background:0 0;
+    color:var(--text2);
+    border:none;
+    cursor:pointer;
+    font-size:1.5rem;
+    width:2.5rem;
+    height:2.5rem
+}
+.navbar__theme-toggle-button:hover {
+    color:var(--text1)
+}
+
+.hero__container {
+    margin-top:1rem;
+    display:flex;
+    justify-content:center;
+    align-items:center;
+    gap:2rem
+}
+.hero__title {
+    font-size:2.5rem;
+    margin-bottom:.5rem
+}
+.hero__title-large {
+    font-size:3rem
+}
+ at media(max-width:768px) {
+    .hero__title-large {
+        font-size:2.5rem
+    }
+}
+ at media(max-width:480px) {
+    .hero__title-large {
+        font-size:2rem
+    }
+}
+ at media(max-width:768px) {
+    .hero__title {
+        font-size:2rem
+    }
+}
+ at media(max-width:480px) {
+    .hero__title {
+        font-size:1.75rem
+    }
+}
+.hero__subtitle {
+    font-size:1.25rem;
+    font-weight:500
+}
+ at media(max-width:768px) {
+    .hero__subtitle {
+        font-size:1rem
+    }
+}
+ at media(max-width:480px) {
+    .hero__subtitle {
+        font-size:.875rem
+    }
+}
+
+.section-container {
+    max-width: 2048px;
+    margin-left:auto;
+    margin-right:auto;
+    margin-top:0;
+    margin-bottom: 1rem;
+    padding:1rem 2rem
+}
+
+
+ at media(max-width:768px) {
+    .section-container {
+        padding:1rem
+    }
+}
+.section-container h2 {
+    font-size:1.5rem;
+    margin-bottom:1rem;
+    color:var(--brand);
+    border-bottom: 1px solid var(--text2);
+}
+ at media(max-width:768px) {
+    .section-container h2 {
+        font-size:1.25rem
+    }
+}
+.section-container p {
+    font-size:1rem;
+    line-height:1.5
+}
+ at media(max-width:768px) {
+    .section-container p {
+        font-size:.875rem
+    }
+}
+.home__row {
+    display:grid;
+    grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
+    gap:2rem
+}
+
+
+.links-wrapper {
+    display:grid;
+    gap:1rem;
+    grid-template-columns:1fr 1fr 1fr 1fr
+}
+ at media(max-width:768px) {
+    .links-wrapper {
+        grid-template-columns:1fr 1fr
+    }
+}
+ at media(max-width:480px) {
+    .links-wrapper {
+        grid-template-columns:1fr
+    }
+}
+.links-wrapper .link {
+    display:flex;
+    flex-direction:column;
+    align-items:center;
+    padding:1rem;
+    border:1px solid var(--text1);
+    border-radius:.25rem
+}
+.links-wrapper .link__icon {
+    font-size:2rem
+}
+.links-wrapper .link__title {
+    font-size:1rem
+}
+
+.table-wrapper {
+    display:flex;
+    flex-direction:column;
+    padding:1rem;
+    border-collapse: collapse; /* Ensures there are no gaps between cells */
+}
+
+.table-wrapper th, .table-wrapper td {
+    padding: 0.5rem 1rem; /* Adds padding inside the cells */
+    border:1px solid var(--text1);
+    text-align: left;
+}
+
+
+.block-command-command {
+    font-weight: bold;
+}
+
+.code-clang-doc {
+    font-size: 1.1rem;
+}
+
+.delimiter-container {
+    padding: 0.5rem 1rem;
+    margin-bottom:1rem; 
+}
+
+.resizer {
+    width: 5px;
+    cursor: col-resize;
+    background-color: var(--text2);
+}
+
+.resizer:hover {
+    background-color: var(--text2-inverse);
+}
+
+.sidebar {
+    width: 250px;
+    top: 0;
+    left: 0;
+    height: 100%;
+    position: fixed;
+    background-color: var(--surface1);
+    display: flex;
+    border-left: 1px solid var(--text2);
+    flex-direction: column;
+    overflow-y: auto;
+    scrollbar-width: thin;
+}
+
+.sidebar h2 {
+    margin-top: 0;
+    margin-bottom: 20px;
+    padding: 10px;
+}
+
+.sidebar ul {
+    width: 100%;
+    padding: 0;
+    list-style-type: none;
+}
+
+.sidebar ul li {
+    padding-right: 1rem;
+    padding-left: 2rem;
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+}
+
+.sidebar-section {
+    font-size:1.5rem;
+    font-weight: bold;
+    margin-bottom: 1rem;
+    padding: 3rem;
+}
+.sidebar-section a {
+    color: var(--brand)
+}
+
+
+/* Content */
+.content {
+    background-color: var(--text1-inverse);
+    padding: 20px;
+    left: 250px;
+    position: relative;
+    width: calc(100% - 250px);
+    height: 100vh;
+}
+.sidebar-item {
+    color: var(--text1);
+}
+
+.sidebar-item-container:hover {
+    width: 100%;
+    background-color: grey;
+}
+
+.sidebar-item-container:hover a {
+    width: 100%;
+    color: var(--text1-inverse);
+}
+
+.class-container {
+    padding: 0.5rem 1rem;
+}
+
+a, a:visited, a:hover, a:active {
+    text-decoration: none;
+    color: inherit;
+}
diff --git a/clang-tools-extra/clang-doc/assets/class-template.mustache b/clang-tools-extra/clang-doc/assets/class-template.mustache
new file mode 100644
index 00000000000000..7ce51c6e162110
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/class-template.mustache
@@ -0,0 +1,227 @@
+{{! 
+    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
+    
+    This file defines the template for classes/struct
+}}
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+    <meta charset="utf-8"/>
+    <title>{{Name}}</title>
+    {{#Stylesheets}}
+        <link rel="stylesheet" type="text/css" href="{{.}}"/>
+    {{/Stylesheets}}
+    {{#Scripts}}
+        <script src="{{.}}"></script>
+    {{/Scripts}}
+    {{! Highlight.js dependency for syntax highlighting }}
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js"></script>
+</head>
+<body>
+<nav class="navbar">
+    <div class="navbar__container">
+        {{#ProjectName}}
+            <div class="navbar__logo">
+                {{ProjectName}}
+            </div>
+        {{/ProjectName}}
+        <div class="navbar__menu">
+            <ul class="navbar__links">
+                <li class="navbar__item">
+                    <a href="/" class="navbar__link">Namespace</a>
+                </li>
+                <li class="navbar__item">
+                    <a href="/" class="navbar__link">Class</a>
+                </li>
+            </ul>
+        </div>
+    </div>
+</nav>
+<main>
+    <div class="container">
+        <div class="sidebar">
+            <h2>{{RecordType}} {{Name}}</h2>
+            <ul>
+                {{#PublicMembers}}
+                <li class="sidebar-section">
+                    <a class="sidebar-item" href="#PublicMethods">Public Members</a>
+                </li>
+                <ul>
+                    {{#Obj}}
+                    <li class="sidebar-item-container">
+                        <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+                    </li>
+                    {{/Obj}}
+                </ul>
+                {{/PublicMembers}}
+                {{#ProtectedMembers}}
+                    <li class="sidebar-section">
+                        <a class="sidebar-item" href="#PublicMethods">Protected Members</a>
+                    </li>
+                    <ul>
+                        {{#Obj}}
+                            <li class="sidebar-item-container">
+                                <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+                            </li>
+                        {{/Obj}}
+                    </ul>
+                {{/ProtectedMembers}}
+                {{#PublicFunction}}
+                <li class="sidebar-section">
+                    <a class="sidebar-item" href="#PublicMethods">Public Method</a>
+                </li>
+                <ul>
+                    {{#Obj}}
+                    <li class="sidebar-item-container">
+                        <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                    </li>
+                    {{/Obj}}
+                </ul>
+                {{/PublicFunction}}
+                {{#ProtectedFunction}}
+                <li class="sidebar-section">
+                    <a class="sidebar-item" href="#ProtectedFunction">Protected Method</a>
+                </li>
+                <ul>
+                    {{#Obj}}
+                    <li class="sidebar-item-container">
+                        <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                    </li>
+                    {{/Obj}}
+                </ul>
+                {{/ProtectedFunction}}
+                {{#Enums}}
+                <li class="sidebar-section">
+                    <a class="sidebar-item" href="#Enums">Enums</a>
+                </li>
+                <ul>
+                    {{#Obj}}
+                    <li class="sidebar-item-container">
+                        <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
+                    </li>
+                    {{/Obj}}
+                </ul>
+                {{/Enums}}
+                {{#Typedef}}
+                <li class="sidebar-section">Typedef</li>
+                {{/Typedef}}
+                {{#Record}}
+                <li class="sidebar-section">
+                    <a class="sidebar-item" href="#Classes">Inner Classes</a>
+                </li>
+                <ul>
+                    {{#Links}}
+                    <li class="sidebar-item-container">
+                        <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                    </li>
+                    {{/Links}}
+                </ul>
+                {{/Record}}
+            </ul>
+        </div>
+        <div class="resizer" id="resizer"></div>
+        <div class="content">
+            <section class="hero section-container">
+                <div class="hero__title">
+                    <h1 class="hero__title-large">{{RecordType}} {{Name}}</h1>
+                    {{#RecordComments}}
+                    <div class="hero__subtitle">
+                        {{>Comments}}
+                    </div>
+                    {{/RecordComments}}
+                </div>
+            </section>
+            {{#PublicMembers}}
+            <section id="PublicMembers" class="section-container">
+                <h2>Public Members</h2>
+                <div>
+                    {{#Obj}}
+                    <div id="{{Name}}" class="delimiter-container">
+                        <pre>
+<code class="language-cpp code-clang-doc" >{{Type}} {{Name}}</code>
+                        </pre>
+                        {{#MemberComments}}
+                        <div>
+                            {{>Comments}}
+                        </div>
+                        {{/MemberComments}}
+                    </div>
+                    {{/Obj}}
+                </div>
+            </section>    
+            {{/PublicMembers}}
+            {{#ProtectedMembers}}
+            <section id="ProtectedMembers" class="section-container">
+                <h2>Protected Members</h2>
+                <div>
+                    {{#Obj}}
+                    <div id="{{Name}}" class="delimiter-container">
+                        <pre>
+<code class="language-cpp code-clang-doc" >{{Type}} {{Name}}</code>
+                        </pre>
+                        {{#MemberComments}}
+                        <div>
+                            {{>Comments}}
+                        </div>
+                        {{/MemberComments}}
+                    </div>
+                    {{/Obj}}
+                </div>
+            </section>
+            {{/ProtectedMembers}}
+            {{#PublicFunction}}
+            <section id="PublicMethods" class="section-container">
+                <h2>Public Methods</h2>
+                <div>
+                    {{#Obj}}
+{{>FunctionPartial}}
+                    {{/Obj}}
+                </div>
+            </section>
+            {{/PublicFunction}}
+            {{#ProtectedFunction}}
+            <section id="ProtectedFunction" class="section-container">
+                <h2>Protected Methods</h2>
+                <div>
+                    {{#Obj}}
+{{>FunctionPartial}}
+                    {{/Obj}}
+                </div>
+            </section>
+            {{/ProtectedFunction}}
+            {{#Enums}}
+            <section id="Enums" class="section-container">
+                <h2>Enumerations</h2>
+                <div>
+                    {{#Obj}}
+{{>EnumPartial}}
+                    {{/Obj}}
+                </div>
+            </section>
+            {{/Enums}}
+            {{#Record}}
+            <section id="Classes" class="section-container">
+                <h2>Inner Classes</h2>
+                <ul class="class-container">
+                    {{#Links}}
+                    <li id="{{ID}}" style="max-height: 40px;">
+<a href="{{Link}}"><pre><code class="language-cpp code-clang-doc" >class {{Name}}</code></pre></a>
+                    </li>
+                    {{/Links}}
+                </ul>
+            </section>
+            {{/Record}}
+            {{#Typedef}}
+            <section class="section-container">
+                <h2 id="Enums">Enums</h2>
+            </section>
+            {{/Typedef}}
+        </div>
+    </div>
+</main>
+</body>
+</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/comments-template.mustache b/clang-tools-extra/clang-doc/assets/comments-template.mustache
new file mode 100644
index 00000000000000..f6b62b4407b9c4
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/comments-template.mustache
@@ -0,0 +1,34 @@
+{{!
+    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
+    
+    This file defines templates for generating comments
+}}
+{{#FullComment}}
+    {{#Children}}
+    {{>Comments}}    
+    {{/Children}}
+{{/FullComment}}
+{{#ParagraphComment}}
+    {{#Children}}
+    {{>Comments}}
+    {{/Children}}
+{{/ParagraphComment}}
+{{#BlockCommandComment}}
+    <div class="block-command-comment__command">
+        <div class="block-command-command">
+            {{Command}}
+        </div>
+        <div>
+            {{#Children}}
+                {{>Comments}}
+            {{/Children}}
+        </div>
+    </div>
+{{/BlockCommandComment}}
+{{#TextComment}}
+    <div>
+        <p>{{TextComment}}</p>
+    </div>
+{{/TextComment}}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/comments.mustache b/clang-tools-extra/clang-doc/assets/comments.mustache
deleted file mode 100644
index 1eac4de91836ab..00000000000000
--- a/clang-tools-extra/clang-doc/assets/comments.mustache
+++ /dev/null
@@ -1,27 +0,0 @@
-{{!
-    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
-    
-    This file defines templates for generating
-}}
-
-{{#FullComments}}
-    {{#Children}}
-    {{>Comment}}    
-    {{/Children}}
-{{/FullComments}}
-{{#ParagraphComment}}
-    {{#Children}}
-    {{>Comment}}
-    {{/Children}}
-{{/ParagraphComment}}
-{{#BlockCommandComment}}
-    <div>{{Command}}</div>
-    {{#Children}}
-    {{>Comment}}
-    {{/Children}}
-{{/BlockCommandComment}}
-{{#TextComment}}
-    <p>{{TextComment}}</p>
-{{/TextComment}}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/enum-template.mustache b/clang-tools-extra/clang-doc/assets/enum-template.mustache
new file mode 100644
index 00000000000000..d63bf258f8f0fb
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/enum-template.mustache
@@ -0,0 +1,47 @@
+{{! 
+    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
+    
+    This file defines the template for enums
+}}
+<div id="{{ID}}" class="delimiter-container">
+    <div>
+        <pre>
+            <code class="language-cpp code-clang-doc">
+{{EnumName}}
+            </code>
+        </pre>
+    </div>
+    {{! Enum Values }}
+    <table class="table-wrapper">
+        <tbody>
+        <tr>
+            <th>Name</th>
+            <th>Value</th>
+            {{#HasComment}}
+                <th>Comment</th>
+            {{/HasComment}}
+        </tr>
+        {{#EnumValues}}
+            <tr>
+                <td>{{Name}}</td>
+                <td>{{Value}}</td>
+                {{#EnumValueComments}}
+                    <td>{{>Comments}}</td>
+                {{/EnumValueComments}}
+            </tr>
+        {{/EnumValues}}
+        </tbody>
+    </table>
+    {{#EnumComments}}
+    <div>
+        {{>Comments}}
+    </div>
+    {{/EnumComments}}
+    {{#Location}}
+    <div>
+        Defined at line {{LineNumber}} of file {{Filename}}
+    </div>
+    {{/Location}}
+</div>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/function-template.mustache b/clang-tools-extra/clang-doc/assets/function-template.mustache
new file mode 100644
index 00000000000000..0564647467aa60
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/function-template.mustache
@@ -0,0 +1,23 @@
+{{! 
+    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
+    
+    This file defines the template for functions/methods
+}}
+<div class="delimiter-container">
+    <div id="{{ID}}">
+        {{! Function Prototype }}
+        <pre>
+            <code class="language-cpp code-clang-doc">
+{{ReturnType.Name}} {{Name}} ({{#Params}}{{^End}}{{Type}} {{Name}}, {{/End}}{{#End}}{{Type}} {{Name}}{{/End}}{{/Params}})
+            </code>
+        </pre>
+        {{! Function Comments }}
+        {{#FunctionComments}}
+        <div>
+            {{>Comments}}
+        </div>
+        {{/FunctionComments}}
+    </div>
+</div>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/mustache-index.js b/clang-tools-extra/clang-doc/assets/mustache-index.js
new file mode 100644
index 00000000000000..db320cd8ae403c
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/mustache-index.js
@@ -0,0 +1,33 @@
+document.addEventListener("DOMContentLoaded", function() {
+    const resizer = document.getElementById('resizer');
+    const sidebar = document.querySelector('.sidebar');
+    
+    let isResizing = false;
+    resizer.addEventListener('mousedown', (e) => {
+        isResizing = true;
+    });
+
+    document.addEventListener('mousemove', (e) => {
+        if (!isResizing) return;
+        const newWidth = e.clientX;
+        if (newWidth > 100 && newWidth < window.innerWidth - 100) {
+            sidebar.style.width = `${newWidth}px`;
+        }
+    });
+
+    document.addEventListener('mouseup', () => {
+        isResizing = false;
+    });
+
+    document.querySelectorAll('pre code').forEach((el) => {
+        hljs.highlightElement(el);
+        el.classList.remove("hljs");
+    });
+
+    document.querySelectorAll('.sidebar-item-container').forEach(item => {
+        item.addEventListener('click', function() {
+            const anchor = item.getElementsByTagName("a");
+            window.location.hash = anchor[0].getAttribute('href');
+        });
+    });
+})
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
new file mode 100644
index 00000000000000..4061fd026886e4
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
@@ -0,0 +1,87 @@
+{{! 
+    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
+    
+    This file defines the template for generating namespaces
+}}
+<!DOCTYPE html>
+<html lang="en-US">
+    <head>
+        <meta charset="utf-8"/>
+        <title>{{NamespaceTitle}}</title>
+        {{#Stylesheets}}
+        <link rel="stylesheet" type="text/css" href="{{.}}"/>
+        {{/Stylesheets}}
+        {{#Scripts}}
+        <script src="{{.}}"></script>
+        {{/Scripts}}
+        {{! Highlight.js dependency for syntax highlighting }}
+        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
+        <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
+        <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js"></script>
+    </head>
+    <body>
+        <nav class="navbar">
+            Navbar
+        </nav>
+        <main>
+            <div class="container">
+                <div class="sidebar">
+                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
+                        sed do eiusmod tempor incididunt ut labore et dolore magna 
+                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco 
+                        laboris nisi ut aliquip ex ea commodo consequat. 
+                        Duis aute irure dolor in reprehenderit in voluptate velit esse 
+                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat 
+                        cupidatat non proident, sunt in culpa qui officia deserunt mollit 
+                        anim id est laborum.
+                        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+                        sed do eiusmod tempor incididunt ut labore et dolore magna
+                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
+                        laboris nisi ut aliquip ex ea commodo consequat.
+                        Duis aute irure dolor in reprehenderit in voluptate velit esse
+                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+                        cupidatat non proident, sunt in culpa qui officia deserunt mollit
+                        anim id est laborum.
+                        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+                        sed do eiusmod tempor incididunt ut labore et dolore magna
+                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
+                        laboris nisi ut aliquip ex ea commodo consequat.
+                        Duis aute irure dolor in reprehenderit in voluptate velit esse
+                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+                        cupidatat non proident, sunt in culpa qui officia deserunt mollit
+                        anim id est laborum.
+                        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+                        sed do eiusmod tempor incididunt ut labore et dolore magna
+                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
+                        laboris nisi ut aliquip ex ea commodo consequat.
+                        Duis aute irure dolor in reprehenderit in voluptate velit esse
+                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+                        cupidatat non proident, sunt in culpa qui officia deserunt mollit
+                        anim id est laborum.
+                        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+                        sed do eiusmod tempor incididunt ut labore et dolore magna
+                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
+                        laboris nisi ut aliquip ex ea commodo consequat.
+                        Duis aute irure dolor in reprehenderit in voluptate velit esse
+                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+                        cupidatat non proident, sunt in culpa qui officia deserunt mollit
+                        anim id est laborum.
+                        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+                        sed do eiusmod tempor incididunt ut labore et dolore magna
+                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
+                        laboris nisi ut aliquip ex ea commodo consequat.
+                        Duis aute irure dolor in reprehenderit in voluptate velit esse
+                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+                        cupidatat non proident, sunt in culpa qui officia deserunt mollit
+                        anim id est laborum.
+                </div>
+                <div class="resizer" id="resizer"></div>
+                <div class="content">
+                    Content
+                </div>
+            </div>
+        </main>
+    </body>
+</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
deleted file mode 100644
index 3b77e0189f4e22..00000000000000
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ /dev/null
@@ -1,55 +0,0 @@
-{{! 
-    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
-    
-    This file defines the template for generating namespaces
-}}
-<!DOCTYPE html>
-<html lang="en-US">
-    <head>
-        <meta charset="utf-8"/>
-        <title>{{NamespaceTitle}}</title>
-    </head>
-    <h1>{{NamespaceTitle}}</h1>
-    {{#NamespaceComments}}
-    <div>
-        {{>Comments}}
-    </div>
-    {{/NamespaceComments}}
-    {{#Namespace}}
-    <h2 id="Namespace">Namespace</h2>
-    <ul>
-        {{#Links}}
-        <li>
-            <a href="{{Link}}">{{Name}}</a>
-        </li>
-        {{/Links}}
-    </ul>
-    {{/Namespace}}
-    {{#Record}}
-    <h2 id="Class">Class</h2>
-    <ul>
-        {{#Links}}
-        <li>
-            <a href="{{Link}}">{{Name}}</a>
-        </li>
-        {{/Links}}
-    </ul>
-    {{/Record}}
-    {{#Function}}
-    <h2 id="Function">Function</h2>
-    <div>
-        {{#Obj}}
-            
-        {{/Obj}}
-    </div>
-    {{/Function}}
-    {{#Enums}}
-    <h2 id="Enums">Enums</h2>
-    <div>
-        {{#Obj}}
-        {{/Obj}}
-    </div>
-    {{/Enums}}
-</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index 8eb067dbe6de87..eccbc99a7ecc4b 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,7 +21,13 @@ target_link_libraries(clang-doc
 
 set(assets
   index.js
-  template.mustache
+  mustache-index.js
+  class-template.mustache
+  comments-template.mustache
+  enum-template.mustache      
+  function-template.mustache
+  namespace-template.mustache      
+  clang-doc-mustache.css
   clang-doc-default-stylesheet.css
 )
 
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index eee8dbf093cc53..256e29c3c9f894 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -163,6 +163,15 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
   return llvm::Error::success();
 }
 
+llvm::SmallString<128> appendPathNative(llvm::SmallString<128> Path, 
+                                    llvm::StringRef Asset) {
+  llvm::SmallString<128> Default;
+  llvm::sys::path::native(Path, Default);
+  llvm::sys::path::append(Default, Asset);
+  return Default;
+}
+
+
 llvm::Error getDefaultAssetFiles(const char *Argv0,
                                  clang::doc::ClangDocContext &CDCtx) {
   void *MainAddr = (void *)(intptr_t)getExecutablePath;
@@ -173,13 +182,10 @@ llvm::Error getDefaultAssetFiles(const char *Argv0,
   llvm::SmallString<128> AssetsPath;
   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
-  llvm::SmallString<128> DefaultStylesheet;
-  llvm::sys::path::native(AssetsPath, DefaultStylesheet);
-  llvm::sys::path::append(DefaultStylesheet,
-                          "clang-doc-default-stylesheet.css");
-  llvm::SmallString<128> IndexJS;
-  llvm::sys::path::native(AssetsPath, IndexJS);
-  llvm::sys::path::append(IndexJS, "index.js");
+  llvm::SmallString<128> DefaultStylesheet =
+      appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css");
+  llvm::SmallString<128> IndexJS =
+      appendPathNative(AssetsPath, "index.js");
 
   if (!llvm::sys::fs::is_regular_file(IndexJS))
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -210,6 +216,7 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
   return getDefaultAssetFiles(Argv0, CDCtx);
 }
 
+
 llvm::Error getMustacheHtmlFiles(const char *Argv0,
                                  clang::doc::ClangDocContext &CDCtx) {
   if (!UserAssetPath.empty() &&
@@ -228,10 +235,34 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
   
-  llvm::SmallString<128> MustacheTemplate;
-  llvm::sys::path::native(AssetsPath, MustacheTemplate);
-  llvm::sys::path::append(MustacheTemplate, "template.mustache");
-  CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+  llvm::SmallString<128> DefaultStylesheet
+      = appendPathNative(AssetsPath, "clang-doc-mustache.css");
+  llvm::SmallString<128> NamespaceTemplate 
+      = appendPathNative(AssetsPath, "namespace-template.mustache");
+  llvm::SmallString<128> ClassTemplate
+      = appendPathNative(AssetsPath, "class-template.mustache");
+  llvm::SmallString<128> EnumTemplate
+      = appendPathNative(AssetsPath, "enum-template.mustache");
+  llvm::SmallString<128> FunctionTemplate
+      = appendPathNative(AssetsPath, "function-template.mustache");
+  llvm::SmallString<128> CommentTemplate
+      = appendPathNative(AssetsPath, "comments-template.mustache");
+  llvm::SmallString<128> IndexJS
+      = appendPathNative(AssetsPath, "mustache-index.js");
+  
+  CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
+  CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
+                               std::string(DefaultStylesheet));
+  CDCtx.MustacheTemplates.insert({"namespace-template", 
+                                  NamespaceTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"class-template",
+                                  ClassTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"enum-template",
+                                  EnumTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"function-template",
+                                  FunctionTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"comments-template", 
+                                  CommentTemplate.c_str()});
   
   return llvm::Error::success();
 }

>From 47c2d15a0015ea1137b494af9ce712ad23b6d987 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 02:26:22 -0400
Subject: [PATCH 53/62] [clang-doc] init mustache implementation

---
 clang-tools-extra/clang-doc/CMakeLists.txt    |   5 +-
 clang-tools-extra/clang-doc/HTMLGenerator.cpp |  59 +++-
 .../clang-doc/HTMLMustacheGenerator.cpp       | 289 ++++--------------
 .../clang-doc/assets/template.mustache        |  23 ++
 .../clang-doc/tool/CMakeLists.txt             |   8 +-
 .../clang-doc/tool/ClangDocMain.cpp           |  62 +---
 6 files changed, 154 insertions(+), 292 deletions(-)
 create mode 100644 clang-tools-extra/clang-doc/assets/template.mustache

diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 0793c343305184..82aded940222db 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -8,16 +8,15 @@ add_clang_library(clangDoc
   BitcodeReader.cpp
   BitcodeWriter.cpp
   ClangDoc.cpp
-  FileHelpersClangDoc.cpp
   Generators.cpp
   HTMLGenerator.cpp
-  HTMLMustacheGenerator.cpp      
   Mapper.cpp
   MDGenerator.cpp
   Representation.cpp
   Serialize.cpp
   YAMLGenerator.cpp
-        
+  HTMLMustacheGenerator.cpp
+
   DEPENDS
   omp_gen
   ClangDriverOptions
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index aa76d17d9411d6..ed65ac965114b5 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -8,7 +8,6 @@
 
 #include "Generators.h"
 #include "Representation.h"
-#include "FileHelpersClangDoc.h"
 #include "clang/Basic/Version.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -252,6 +251,46 @@ static void appendVector(std::vector<Derived> &&New,
   std::move(New.begin(), New.end(), std::back_inserter(Original));
 }
 
+// Compute the relative path from an Origin directory to a Destination directory
+static SmallString<128> computeRelativePath(StringRef Destination,
+                                            StringRef Origin) {
+  // If Origin is empty, the relative path to the Destination is its complete
+  // path.
+  if (Origin.empty())
+    return Destination;
+
+  // The relative path is an empty path if both directories are the same.
+  if (Destination == Origin)
+    return {};
+
+  // These iterators iterate through each of their parent directories
+  llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
+  llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
+  llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
+  llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
+  // Advance both iterators until the paths differ. Example:
+  //    Destination = A/B/C/D
+  //    Origin      = A/B/E/F
+  // FileI will point to C and DirI to E. The directories behind them is the
+  // directory they share (A/B).
+  while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
+    ++FileI;
+    ++DirI;
+  }
+  SmallString<128> Result; // This will hold the resulting path.
+  // Result has to go up one directory for each of the remaining directories in
+  // Origin
+  while (DirI != DirE) {
+    llvm::sys::path::append(Result, "..");
+    ++DirI;
+  }
+  // Result has to append each of the remaining directories in Destination
+  while (FileI != FileE) {
+    llvm::sys::path::append(Result, *FileI);
+    ++FileI;
+  }
+  return Result;
+}
 
 // HTML generation
 
@@ -1094,6 +1133,24 @@ static llvm::Error genIndex(const ClangDocContext &CDCtx) {
   return llvm::Error::success();
 }
 
+static llvm::Error 
+copyFile(StringRef FilePath, StringRef OutDirectory) {
+  llvm::SmallString<128> PathWrite;
+  llvm::sys::path::native(OutDirectory, PathWrite);
+  llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
+  llvm::SmallString<128> PathRead;
+  llvm::sys::path::native(FilePath, PathRead);
+  std::error_code OK;
+  std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
+  if (FileErr != OK) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "error creating file " +
+                                       llvm::sys::path::filename(FilePath) +
+                                       ": " + FileErr.message() + "\n");
+  }
+  return llvm::Error::success();
+}
+
 llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
   auto Err = serializeIndex(CDCtx);
   if (Err)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 6be4c795a68654..63def07c5fa804 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 #include "Generators.h"
 #include "Representation.h"
-#include "FileHelpersClangDoc.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Mustache.h"
 
@@ -49,12 +48,12 @@ class MustacheTemplateFile : public Template {
   Error registerPartialFile(StringRef Name, StringRef FileName) {
     ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
         MemoryBuffer::getFile(FileName);
-    if (auto EC = BufferOrError.getError())
+    if (auto EC = BufferOrError.getError()) {
       return llvm::createFileError("cannot open file", EC);
+    }
     std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
     llvm::StringRef FileContent = Buffer->getBuffer();
     registerPartial(Name, FileContent);
-    return llvm::Error::success();
   }
 
   MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
@@ -65,54 +64,23 @@ static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
 static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
 
 
-llvm::Error setupTemplate(
-                   std::unique_ptr<MustacheTemplateFile> &Template,
-                   StringRef TemplatePath,
-                   std::vector<std::pair<StringRef, StringRef>> Partials) {
-  auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
-  if (auto EC = T.getError())
-     return llvm::createFileError("cannot open file", EC);
-  Template = std::move(T.get());
-  for (const auto &P : Partials) {
-    auto Err = Template->registerPartialFile(P.first, P.second);
-    if (Err)
-      return Err;
-  }
-  return llvm::Error::success();
-}
-
 llvm::Error 
 setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
-  auto NamespaceFilePath = CDCtx.MustacheTemplates.lookup("namespace-template");
-  auto ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
-  auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
-  auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
-  auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
-  std::vector<std::pair<StringRef, StringRef>> Partials = {
-    {"Comments", CommentFilePath},
-    {"FunctionPartial", FunctionFilePath},
-    {"EnumPartial", EnumFilePath}
-  };
-  
-  auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
-  if (Err)
-    return Err;
-  
-  Err = setupTemplate(RecordTemplate, ClassFilePath, Partials);
-  
-  if (Err)
-    return Err;
-  
-  return llvm::Error::success();
+  auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
+  auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
+  if (auto EC = Template.getError()) {
+    return llvm::createFileError("cannot open file", EC);
+  }
+  NamespaceTemplate = std::move(Template.get());
 }
 
-
 llvm::Error 
 MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir, 
                                     llvm::StringMap<std::unique_ptr<doc::Info>> Infos, 
                                     const clang::doc::ClangDocContext &CDCtx) {
-  if (auto Err = setupTemplateFiles(CDCtx))
+  if (auto Err = setupTemplateFiles(CDCtx)) {
     return Err;
+  }
   // Track which directories we already tried to create.
   llvm::StringSet<> CreatedDirs;
   // Collect all output by file name and create the necessary directories.
@@ -125,9 +93,10 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
     llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
     if (!CreatedDirs.contains(Path)) {
       if (std::error_code Err = llvm::sys::fs::create_directories(Path);
-          Err != std::error_code())
+          Err != std::error_code()) {
         return llvm::createStringError(Err, "Failed to create directory '%s'.",
                                        Path.c_str());
+      }
       CreatedDirs.insert(Path);
     }
 
@@ -139,33 +108,28 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
     std::error_code FileErr;
     llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
                                 llvm::sys::fs::OF_None);
-    if (FileErr)
+    if (FileErr) {
       return llvm::createStringError(FileErr, "Error opening file '%s'",
                                      Group.getKey().str().c_str());
-    
+    }
     for (const auto &Info : Group.getValue()) {
-      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
+      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
         return Err;
+      }
     }
   }
-  return llvm::Error::success();
 }
 
 Value extractValue(const Location &L, 
                    std::optional<StringRef> RepositoryUrl = std::nullopt) {
   Object Obj = Object();
-  Obj.insert({"LineNumber", L.LineNumber});
-  Obj.insert({"Filename", L.Filename});
-  
   if (!L.IsFileInRootDir || !RepositoryUrl) {
-    return Obj;
+    Obj.insert({"LineNumber", L.LineNumber});
+    Obj.insert({"Filename", L.Filename});
   }
   SmallString<128> FileURL(*RepositoryUrl);
   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
-  FileURL += "#" + std::to_string(L.LineNumber);
   Obj.insert({"FileURL", FileURL});
-  
-  return Obj;
 }
 
 Value extractValue(const Reference &I, StringRef CurrentDirectory) {
@@ -175,8 +139,6 @@ Value extractValue(const Reference &I, StringRef CurrentDirectory) {
   Object Obj = Object();
   Obj.insert({"Link", Path});
   Obj.insert({"Name", I.Name});
-  Obj.insert({"QualName", I.QualName});
-  Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
   return Obj;
 }
 
@@ -227,12 +189,8 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
   Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
   
   Value ParamArr = Array();
-  for (const auto Val : llvm::enumerate(I.Params)) {
-    Value V = Object();
-    V.getAsObject()->insert({"Name", Val.value().Name});
-    V.getAsObject()->insert({"Type", Val.value().Type.Name});
-    V.getAsObject()->insert({"End",  Val.index() + 1 == I.Params.size()});
-    ParamArr.getAsArray()->emplace_back(V);
+  for (const auto &P : I.Params) {
+    ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
   }
   Obj.insert({"Params", ParamArr});
   
@@ -242,13 +200,12 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
       ArrDesc.getAsArray()->emplace_back(extractValue(Child));
     Obj.insert({"FunctionComments", ArrDesc});
   }
-  if (I.DefLoc.has_value()) {
-    Location L = *I.DefLoc;
-    if (CDCtx.RepositoryUrl.has_value())
-      Obj.insert({"Location", extractValue(L,
-                                           StringRef{*CDCtx.RepositoryUrl})});
+  if (I.DefLoc) {
+    if (!CDCtx.RepositoryUrl)
+      Obj.insert({"Location", extractValue(*I.DefLoc)});
     else
-      Obj.insert({"Location", extractValue(L)});  
+      Obj.insert({"Location", extractValue(*I.DefLoc, 
+                                           StringRef{*CDCtx.RepositoryUrl})});  
   }
   return Obj;
 }
@@ -263,6 +220,7 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
   Obj.insert({"EnumName", EnumType});
   Obj.insert({"HasComment", HasComment});
   Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+  
   Value Arr = Array();
   for (const EnumValueInfo& M: I.Members) {
     Value EnumValue = Object();
@@ -289,75 +247,17 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
     Obj.insert({"EnumComments", ArrDesc});
   }
   
-  if (I.DefLoc.has_value()) {
-    Location L = *I.DefLoc;
-    if (CDCtx.RepositoryUrl.has_value())
-      Obj.insert({"Location", extractValue(L,
-                                           StringRef{*CDCtx.RepositoryUrl})});
+  if (I.DefLoc) {
+    if (!CDCtx.RepositoryUrl)
+      Obj.insert({"Location", extractValue(*I.DefLoc)});
     else
-      Obj.insert({"Location", extractValue(L)});  
+      Obj.insert({"Location", extractValue(*I.DefLoc, 
+                                           StringRef{*CDCtx.RepositoryUrl})});  
   }
   
   return Obj;
 }
 
-void extractScopeChildren(const ScopeChildren &S, Object &Obj, 
-                          StringRef ParentInfoDir,
-                          const ClangDocContext &CDCtx) {
-  Value ArrNamespace = Array();
-  for (const Reference& Child : S.Namespaces)
-    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
-  
-  if (!ArrNamespace.getAsArray()->empty())
-    Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
-  
-  Value ArrRecord = Array();
-  for (const Reference& Child : S.Records)
-    ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
-  
-  if (!ArrRecord.getAsArray()->empty())
-    Obj.insert({"Record", Object{{"Links", ArrRecord}}});
-  
-  Value ArrFunction = Array();
-  Value PublicFunction = Array();
-  Value ProtectedFunction = Array();
-  Value PrivateFunction = Array();
-  
-  for (const FunctionInfo& Child : S.Functions) {
-    Value F = extractValue(Child, ParentInfoDir, CDCtx);
-    AccessSpecifier Access = Child.Access;
-    if (Access == AccessSpecifier::AS_public)
-      PublicFunction.getAsArray()->emplace_back(F);
-    else if (Access == AccessSpecifier::AS_protected)
-      ProtectedFunction.getAsArray()->emplace_back(F);
-    else
-      ArrFunction.getAsArray()->emplace_back(F);
-  }  
-  if (!ArrFunction.getAsArray()->empty())
-    Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
-  
-  if (!PublicFunction.getAsArray()->empty())
-    Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
-  
-  if (!ProtectedFunction.getAsArray()->empty())
-    Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
-  
-  
-  Value ArrEnum = Array();
-  for (const EnumInfo& Child : S.Enums)
-    ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
-  
-  if (!ArrEnum.getAsArray()->empty())
-    Obj.insert({"Enums", Object{{"Obj", ArrEnum }}});
-  
-  Value ArrTypedefs = Array();
-  for (const TypedefInfo& Child : S.Typedefs) 
-    ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
-  
-  if (!ArrTypedefs.getAsArray()->empty())
-    Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
-}
-
 Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
   Object NamespaceValue = Object();
   std::string InfoTitle;
@@ -376,88 +276,36 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
       ArrDesc.getAsArray()->emplace_back(extractValue(Child));
     NamespaceValue.insert({"NamespaceComments", ArrDesc });
   }
-  extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
-  return NamespaceValue;
-}
 
-Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
-  Object RecordValue = Object();
+  Value ArrNamespace = Array();
+  for (const Reference& Child : I.Children.Namespaces)
+    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  NamespaceValue.insert({"Namespace", ArrNamespace});
   
-  if (!I.Description.empty()) {
-    Value ArrDesc = Array();
-    for (const CommentInfo& Child : I.Description) 
-      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
-    RecordValue.insert({"RecordComments", ArrDesc });
-  }
-  RecordValue.insert({"Name", I.Name});
-  RecordValue.insert({"FullName", I.FullName});
-  RecordValue.insert({"RecordType", getTagType(I.TagType)});
+  Value ArrRecord = Array();
+  for (const Reference& Child : I.Children.Records)
+    ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  NamespaceValue.insert({"Record", ArrRecord});
   
-  if (I.DefLoc.has_value()) {
-    Location L = *I.DefLoc;
-    if (CDCtx.RepositoryUrl.has_value())
-      RecordValue.insert({"Location", extractValue(L,
-                                           StringRef{*CDCtx.RepositoryUrl})});
-    else
-      RecordValue.insert({"Location", extractValue(L)});  
-  }
+  Value ArrFunction = Array();
+  for (const FunctionInfo& Child : I.Children.Functions)
+    ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
+                                                        CDCtx));
+  NamespaceValue.insert({"Function", ArrRecord});
   
-  StringRef BasePath = I.getRelativeFilePath("");
-  extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
-  Value PublicMembers = Array();
-  Value ProtectedMembers = Array();
-  Value PrivateMembers = Array();
-  for (const MemberTypeInfo &Member : I.Members ) {
-    Value MemberValue = Object();
-    MemberValue.getAsObject()->insert({"Name", Member.Name});
-    MemberValue.getAsObject()->insert({"Type", Member.Type.Name});
-    if (!Member.Description.empty()) {
-      Value ArrDesc = Array();
-      for (const CommentInfo& Child : Member.Description) 
-            ArrDesc.getAsArray()->emplace_back(extractValue(Child));
-      MemberValue.getAsObject()->insert({"MemberComments", ArrDesc });
-    }
-    
-    if (Member.Access == AccessSpecifier::AS_public)
-      PublicMembers.getAsArray()->emplace_back(MemberValue);
-    else if (Member.Access == AccessSpecifier::AS_protected)
-      ProtectedMembers.getAsArray()->emplace_back(MemberValue);
-    else if (Member.Access == AccessSpecifier::AS_private)
-      PrivateMembers.getAsArray()->emplace_back(MemberValue);
-  }
-  if (!PublicMembers.getAsArray()->empty())
-    RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
-  if (!ProtectedMembers.getAsArray()->empty())
-    RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
-  if (!PrivateMembers.getAsArray()->empty())
-    RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+  Value ArrEnum = Array();
+  for (const EnumInfo& Child : I.Children.Enums)
+    ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
+  NamespaceValue.insert({"Enums", ArrEnum });
   
-  return RecordValue;
-}
-
-void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
-  V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
-  Value StylesheetArr = Array();
-  auto InfoPath = I->getRelativeFilePath("");
-  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
-  for (const auto &FilePath  : CDCtx.UserStylesheets) {
-    SmallString<128> StylesheetPath = RelativePath;
-    llvm::sys::path::append(StylesheetPath,
-                            llvm::sys::path::filename(FilePath));
-    llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
-    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
-  }
-  V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+  Value ArrTypedefs = Array();
+  for (const TypedefInfo& Child : I.Children.Typedefs) 
+    ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
+  NamespaceValue.insert({"Typedefs", ArrTypedefs });
   
-  Value ScriptArr = Array();
-  for (auto Script : CDCtx.JsScripts) {
-    SmallString<128> JsPath = RelativePath;
-    llvm::sys::path::append(JsPath, llvm::sys::path::filename(Script));
-    ScriptArr.getAsArray()->emplace_back(JsPath);
-  }
-  V.getAsObject()->insert({"Scripts", ScriptArr});
 }
- 
+
+
 
 llvm::Error
 MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -465,26 +313,16 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
-    setupTemplateValue(CDCtx, V, I);
     OS << NamespaceTemplate->render(V);
     break;
   }
-  case InfoType::IT_record: {
-    Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
-    setupTemplateValue(CDCtx, V, I);
-    // Serialize the JSON value to the output stream in a readable format.
-    llvm::outs() << llvm::formatv("{0:2}", V);
-    OS << RecordTemplate->render(V);
+  case InfoType::IT_record:
     break;
-  }  
   case InfoType::IT_enum:
-    llvm::outs() << "IT_enum\n";
     break;
   case InfoType::IT_function:
-    llvm::outs() << "IT_Function\n";
     break;
   case InfoType::IT_typedef:
-    llvm::outs() << "IT_typedef\n";
     break;
   case InfoType::IT_default:
     return createStringError(llvm::inconvertibleErrorCode(),
@@ -494,21 +332,10 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
 }
 
 llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
-  llvm::Error Err = llvm::Error::success();
-  for (const auto &FilePath : CDCtx.UserStylesheets) {
-    Err = copyFile(FilePath, CDCtx.OutDirectory);
-    if (Err)
-      return Err;
-  }
-  for (const auto &FilePath : CDCtx.JsScripts) {
-    Err = copyFile(FilePath, CDCtx.OutDirectory);
-    if (Err)
-      return Err;
-  }
-  return llvm::Error::success();
+  
 }
 
-const char *MustacheHTMLGenerator::Format = "mhtml";
+const char *MustacheHTMLGenerator::Format = "mustache";
 
 
 static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
@@ -516,7 +343,7 @@ static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator
 
 // This anchor is used to force the linker to link in the generated object
 // file and thus register the generator.
-volatile int MHTMLGeneratorAnchorSource = 0;
+volatile int HTMLGeneratorAnchorSource = 0;
 
 } // namespace doc
 } // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
new file mode 100644
index 00000000000000..af4c60182ae52c
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -0,0 +1,23 @@
+{{! 
+    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
+    
+    This file defines the template
+}}
+<!DOCTYPE html>
+<html lang="en-US">
+    <head>
+        <meta charset="utf-8"/>
+        <title>{{NamespaceTitle}}</title>
+    </head>
+    {{#NamespaceComments}}
+    <p>Namespace Comment Present!</p>
+    {{/NamespaceComments}}
+    {{#Namespace}}
+    <p>Namespace Present!</p>
+    {{/Namespace}}
+    {{#Record}}
+    <p>Record Present!</p>
+    {{/Record}}
+</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index eccbc99a7ecc4b..8eb067dbe6de87 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,13 +21,7 @@ target_link_libraries(clang-doc
 
 set(assets
   index.js
-  mustache-index.js
-  class-template.mustache
-  comments-template.mustache
-  enum-template.mustache      
-  function-template.mustache
-  namespace-template.mustache      
-  clang-doc-mustache.css
+  template.mustache
   clang-doc-default-stylesheet.css
 )
 
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 256e29c3c9f894..fc5a9529111b16 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -103,7 +103,6 @@ enum OutputFormatTy {
   md,
   yaml,
   html,
-  mhtml
 };
 
 static llvm::cl::opt<OutputFormatTy>
@@ -113,9 +112,7 @@ static llvm::cl::opt<OutputFormatTy>
                                 clEnumValN(OutputFormatTy::md, "md",
                                            "Documentation in MD format."),
                                 clEnumValN(OutputFormatTy::html, "html",
-                                           "Documentation in HTML format."),
-                                clEnumValN(OutputFormatTy::mhtml, "mhtml",
-                                           "Documentation in mHTML format")),
+                                           "Documentation in HTML format.")),
                llvm::cl::init(OutputFormatTy::yaml),
                llvm::cl::cat(ClangDocCategory));
 
@@ -127,8 +124,6 @@ std::string getFormatString() {
     return "md";
   case OutputFormatTy::html:
     return "html";
-  case OutputFormatTy::mhtml:
-    return "mhtml";
   }
   llvm_unreachable("Unknown OutputFormatTy");
 }
@@ -163,15 +158,6 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
   return llvm::Error::success();
 }
 
-llvm::SmallString<128> appendPathNative(llvm::SmallString<128> Path, 
-                                    llvm::StringRef Asset) {
-  llvm::SmallString<128> Default;
-  llvm::sys::path::native(Path, Default);
-  llvm::sys::path::append(Default, Asset);
-  return Default;
-}
-
-
 llvm::Error getDefaultAssetFiles(const char *Argv0,
                                  clang::doc::ClangDocContext &CDCtx) {
   void *MainAddr = (void *)(intptr_t)getExecutablePath;
@@ -182,10 +168,13 @@ llvm::Error getDefaultAssetFiles(const char *Argv0,
   llvm::SmallString<128> AssetsPath;
   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
-  llvm::SmallString<128> DefaultStylesheet =
-      appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css");
-  llvm::SmallString<128> IndexJS =
-      appendPathNative(AssetsPath, "index.js");
+  llvm::SmallString<128> DefaultStylesheet;
+  llvm::sys::path::native(AssetsPath, DefaultStylesheet);
+  llvm::sys::path::append(DefaultStylesheet,
+                          "clang-doc-default-stylesheet.css");
+  llvm::SmallString<128> IndexJS;
+  llvm::sys::path::native(AssetsPath, IndexJS);
+  llvm::sys::path::append(IndexJS, "index.js");
 
   if (!llvm::sys::fs::is_regular_file(IndexJS))
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -216,7 +205,6 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
   return getDefaultAssetFiles(Argv0, CDCtx);
 }
 
-
 llvm::Error getMustacheHtmlFiles(const char *Argv0,
                                  clang::doc::ClangDocContext &CDCtx) {
   if (!UserAssetPath.empty() &&
@@ -235,36 +223,10 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
   
-  llvm::SmallString<128> DefaultStylesheet
-      = appendPathNative(AssetsPath, "clang-doc-mustache.css");
-  llvm::SmallString<128> NamespaceTemplate 
-      = appendPathNative(AssetsPath, "namespace-template.mustache");
-  llvm::SmallString<128> ClassTemplate
-      = appendPathNative(AssetsPath, "class-template.mustache");
-  llvm::SmallString<128> EnumTemplate
-      = appendPathNative(AssetsPath, "enum-template.mustache");
-  llvm::SmallString<128> FunctionTemplate
-      = appendPathNative(AssetsPath, "function-template.mustache");
-  llvm::SmallString<128> CommentTemplate
-      = appendPathNative(AssetsPath, "comments-template.mustache");
-  llvm::SmallString<128> IndexJS
-      = appendPathNative(AssetsPath, "mustache-index.js");
-  
-  CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
-  CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
-                               std::string(DefaultStylesheet));
-  CDCtx.MustacheTemplates.insert({"namespace-template", 
-                                  NamespaceTemplate.c_str()});
-  CDCtx.MustacheTemplates.insert({"class-template",
-                                  ClassTemplate.c_str()});
-  CDCtx.MustacheTemplates.insert({"enum-template",
-                                  EnumTemplate.c_str()});
-  CDCtx.MustacheTemplates.insert({"function-template",
-                                  FunctionTemplate.c_str()});
-  CDCtx.MustacheTemplates.insert({"comments-template", 
-                                  CommentTemplate.c_str()});
-  
-  return llvm::Error::success();
+  llvm::SmallString<128> MustacheTemplate;
+  llvm::sys::path::native(AssetsPath, MustacheTemplate);
+  llvm::sys::path::append(MustacheTemplate, "template.mustache");
+  CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
 }
 
 /// Make the output of clang-doc deterministic by sorting the children of

>From 279ceee3cbf6ba98f4879999193d67f1c7c4c1e8 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 17:50:03 -0400
Subject: [PATCH 54/62] [clang-doc] add a mustache backend init implementation

---
 .../clang-doc/HTMLMustacheGenerator.cpp       | 32 ++++++++++++----
 .../clang-doc/assets/template.mustache        | 37 +++++++++++++++++--
 .../clang-doc/tool/ClangDocMain.cpp           |  9 ++++-
 .../Inputs/basic-project/include/Shape.h      |  2 -
 4 files changed, 65 insertions(+), 15 deletions(-)

diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 63def07c5fa804..bfa563ebdc7b37 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -54,6 +54,7 @@ class MustacheTemplateFile : public Template {
     std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
     llvm::StringRef FileContent = Buffer->getBuffer();
     registerPartial(Name, FileContent);
+    return llvm::Error::success();
   }
 
   MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
@@ -72,6 +73,7 @@ setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
     return llvm::createFileError("cannot open file", EC);
   }
   NamespaceTemplate = std::move(Template.get());
+  return llvm::Error::success();
 }
 
 llvm::Error 
@@ -118,6 +120,7 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
       }
     }
   }
+  return llvm::Error::success();
 }
 
 Value extractValue(const Location &L, 
@@ -130,6 +133,8 @@ Value extractValue(const Location &L,
   SmallString<128> FileURL(*RepositoryUrl);
   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
   Obj.insert({"FileURL", FileURL});
+  
+  return Obj;
 }
 
 Value extractValue(const Reference &I, StringRef CurrentDirectory) {
@@ -280,29 +285,39 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
   Value ArrNamespace = Array();
   for (const Reference& Child : I.Children.Namespaces)
     ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
-  NamespaceValue.insert({"Namespace", ArrNamespace});
+  
+  if (!ArrNamespace.getAsArray()->empty())
+    NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
   
   Value ArrRecord = Array();
   for (const Reference& Child : I.Children.Records)
     ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
-  NamespaceValue.insert({"Record", ArrRecord});
+  
+  if (!ArrRecord.getAsArray()->empty())
+    NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
   
   Value ArrFunction = Array();
   for (const FunctionInfo& Child : I.Children.Functions)
     ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
                                                         CDCtx));
-  NamespaceValue.insert({"Function", ArrRecord});
+  if (!ArrFunction.getAsArray()->empty())
+    NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
   
   Value ArrEnum = Array();
   for (const EnumInfo& Child : I.Children.Enums)
     ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
-  NamespaceValue.insert({"Enums", ArrEnum });
+  
+  if (!ArrEnum.getAsArray()->empty())
+    NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
   
   Value ArrTypedefs = Array();
   for (const TypedefInfo& Child : I.Children.Typedefs) 
     ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
-  NamespaceValue.insert({"Typedefs", ArrTypedefs });
   
+  if (!ArrTypedefs.getAsArray()->empty())
+    NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+  
+  return NamespaceValue;
 }
 
 
@@ -313,6 +328,7 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
+    llvm::outs() << V << "\n";
     OS << NamespaceTemplate->render(V);
     break;
   }
@@ -332,10 +348,10 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
 }
 
 llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
-  
+  return llvm::Error::success();
 }
 
-const char *MustacheHTMLGenerator::Format = "mustache";
+const char *MustacheHTMLGenerator::Format = "mhtml";
 
 
 static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
@@ -343,7 +359,7 @@ static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator
 
 // This anchor is used to force the linker to link in the generated object
 // file and thus register the generator.
-volatile int HTMLGeneratorAnchorSource = 0;
+volatile int MHTMLGeneratorAnchorSource = 0;
 
 } // namespace doc
 } // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
index af4c60182ae52c..1d3407f8b52920 100644
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -3,7 +3,7 @@
     See https://llvm.org/LICENSE.txt for license information.
     SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
     
-    This file defines the template
+    This file defines the template for generating Namespaces
 }}
 <!DOCTYPE html>
 <html lang="en-US">
@@ -11,13 +11,42 @@
         <meta charset="utf-8"/>
         <title>{{NamespaceTitle}}</title>
     </head>
+    <h1>{{NamespaceTitle}}</h1>
     {{#NamespaceComments}}
-    <p>Namespace Comment Present!</p>
+    <p>Namespace Comment</p>
     {{/NamespaceComments}}
     {{#Namespace}}
-    <p>Namespace Present!</p>
+    <h2 id="Namespace">Namespace</h2>
+    <ul>
+        {{#Links}}
+        <li>
+            <a href="{{Link}}">{{Name}}</a>
+        </li>
+        {{/Links}}
+    </ul>
     {{/Namespace}}
     {{#Record}}
-    <p>Record Present!</p>
+    <h2 id="Class">Class</h2>
+    <ul>
+        {{#Links}}
+        <li>
+            <a href="{{Link}}">{{Name}}</a>
+        </li>
+        {{/Links}}
+    </ul>
     {{/Record}}
+    {{#Function}}
+    <h2 id="Function">Function</h2>
+    <div>
+        {{#Obj}}
+        {{/Obj}}
+    </div>
+    {{/Function}}
+    {{#Enums}}
+    <h2 id="Enums">Enums</h2>
+    <div>
+        {{#Obj}}
+        {{/Obj}}
+    </div>
+    {{/Enums}}
 </html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index fc5a9529111b16..eee8dbf093cc53 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -103,6 +103,7 @@ enum OutputFormatTy {
   md,
   yaml,
   html,
+  mhtml
 };
 
 static llvm::cl::opt<OutputFormatTy>
@@ -112,7 +113,9 @@ static llvm::cl::opt<OutputFormatTy>
                                 clEnumValN(OutputFormatTy::md, "md",
                                            "Documentation in MD format."),
                                 clEnumValN(OutputFormatTy::html, "html",
-                                           "Documentation in HTML format.")),
+                                           "Documentation in HTML format."),
+                                clEnumValN(OutputFormatTy::mhtml, "mhtml",
+                                           "Documentation in mHTML format")),
                llvm::cl::init(OutputFormatTy::yaml),
                llvm::cl::cat(ClangDocCategory));
 
@@ -124,6 +127,8 @@ std::string getFormatString() {
     return "md";
   case OutputFormatTy::html:
     return "html";
+  case OutputFormatTy::mhtml:
+    return "mhtml";
   }
   llvm_unreachable("Unknown OutputFormatTy");
 }
@@ -227,6 +232,8 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
   llvm::sys::path::native(AssetsPath, MustacheTemplate);
   llvm::sys::path::append(MustacheTemplate, "template.mustache");
   CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+  
+  return llvm::Error::success();
 }
 
 /// Make the output of clang-doc deterministic by sorting the children of
diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
index e5c5d4c9e4412b..5354032f4d8323 100644
--- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
+++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
@@ -26,5 +26,3 @@ class Shape {
      */
     virtual double perimeter() const = 0;
 };
-
-

>From 9ab49b250ac72fcba38638a76709c104db27b2f7 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 24 Sep 2024 16:48:29 -0400
Subject: [PATCH 55/62] [clang-doc] update

---
 .../clang-doc/HTMLMustacheGenerator.cpp       | 16 ++++++++---
 .../clang-doc/assets/comments.mustache        | 27 +++++++++++++++++++
 .../clang-doc/assets/template.mustache        |  7 +++--
 .../Inputs/basic-project/include/Shape.h      |  2 ++
 4 files changed, 46 insertions(+), 6 deletions(-)
 create mode 100644 clang-tools-extra/clang-doc/assets/comments.mustache

diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index bfa563ebdc7b37..96a685dbe2ae87 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -194,8 +194,12 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
   Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
   
   Value ParamArr = Array();
-  for (const auto &P : I.Params) {
-    ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
+  for (const auto Val : llvm::enumerate(I.Params)) {
+    Value V = Object();
+    V.getAsObject()->insert({"Name", Val.value().Name});
+    V.getAsObject()->insert({"Type", Val.value().Type.Name});
+    V.getAsObject()->insert({"End",  Val.index() + 1 == I.Params.size()});
+    ParamArr.getAsArray()->emplace_back(V);
   }
   Obj.insert({"Params", ParamArr});
   
@@ -328,17 +332,21 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
-    llvm::outs() << V << "\n";
-    OS << NamespaceTemplate->render(V);
+    llvm::raw_ostream &OS = llvm::outs();
+    llvm::json::OStream J(OS, /*IndentSize=*/2);
+    J.value(V);
     break;
   }
   case InfoType::IT_record:
     break;
   case InfoType::IT_enum:
+    llvm::outs() << "IT_enum\n";
     break;
   case InfoType::IT_function:
+    llvm::outs() << "IT_Function\n";
     break;
   case InfoType::IT_typedef:
+    llvm::outs() << "IT_typedef\n";
     break;
   case InfoType::IT_default:
     return createStringError(llvm::inconvertibleErrorCode(),
diff --git a/clang-tools-extra/clang-doc/assets/comments.mustache b/clang-tools-extra/clang-doc/assets/comments.mustache
new file mode 100644
index 00000000000000..1eac4de91836ab
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/comments.mustache
@@ -0,0 +1,27 @@
+{{!
+    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
+    
+    This file defines templates for generating
+}}
+
+{{#FullComments}}
+    {{#Children}}
+    {{>Comment}}    
+    {{/Children}}
+{{/FullComments}}
+{{#ParagraphComment}}
+    {{#Children}}
+    {{>Comment}}
+    {{/Children}}
+{{/ParagraphComment}}
+{{#BlockCommandComment}}
+    <div>{{Command}}</div>
+    {{#Children}}
+    {{>Comment}}
+    {{/Children}}
+{{/BlockCommandComment}}
+{{#TextComment}}
+    <p>{{TextComment}}</p>
+{{/TextComment}}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
index 1d3407f8b52920..3b77e0189f4e22 100644
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -3,7 +3,7 @@
     See https://llvm.org/LICENSE.txt for license information.
     SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
     
-    This file defines the template for generating Namespaces
+    This file defines the template for generating namespaces
 }}
 <!DOCTYPE html>
 <html lang="en-US">
@@ -13,7 +13,9 @@
     </head>
     <h1>{{NamespaceTitle}}</h1>
     {{#NamespaceComments}}
-    <p>Namespace Comment</p>
+    <div>
+        {{>Comments}}
+    </div>
     {{/NamespaceComments}}
     {{#Namespace}}
     <h2 id="Namespace">Namespace</h2>
@@ -39,6 +41,7 @@
     <h2 id="Function">Function</h2>
     <div>
         {{#Obj}}
+            
         {{/Obj}}
     </div>
     {{/Function}}
diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
index 5354032f4d8323..e5c5d4c9e4412b 100644
--- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
+++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
@@ -26,3 +26,5 @@ class Shape {
      */
     virtual double perimeter() const = 0;
 };
+
+

>From 00830fe6ae738ada3389aec794a47d41f9950cfe Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 7 Oct 2024 15:07:41 -0400
Subject: [PATCH 56/62] [clang-doc] add more templates

---
 clang-tools-extra/clang-doc/CMakeLists.txt    |   5 +-
 clang-tools-extra/clang-doc/HTMLGenerator.cpp |  59 +---
 .../clang-doc/HTMLMustacheGenerator.cpp       | 281 ++++++++++++++----
 .../clang-doc/assets/comments.mustache        |  27 --
 .../clang-doc/assets/template.mustache        |  55 ----
 .../clang-doc/tool/CMakeLists.txt             |   8 +-
 .../clang-doc/tool/ClangDocMain.cpp           |  53 +++-
 7 files changed, 268 insertions(+), 220 deletions(-)
 delete mode 100644 clang-tools-extra/clang-doc/assets/comments.mustache
 delete mode 100644 clang-tools-extra/clang-doc/assets/template.mustache

diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 82aded940222db..0793c343305184 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -8,15 +8,16 @@ add_clang_library(clangDoc
   BitcodeReader.cpp
   BitcodeWriter.cpp
   ClangDoc.cpp
+  FileHelpersClangDoc.cpp
   Generators.cpp
   HTMLGenerator.cpp
+  HTMLMustacheGenerator.cpp      
   Mapper.cpp
   MDGenerator.cpp
   Representation.cpp
   Serialize.cpp
   YAMLGenerator.cpp
-  HTMLMustacheGenerator.cpp
-
+        
   DEPENDS
   omp_gen
   ClangDriverOptions
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index ed65ac965114b5..aa76d17d9411d6 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -8,6 +8,7 @@
 
 #include "Generators.h"
 #include "Representation.h"
+#include "FileHelpersClangDoc.h"
 #include "clang/Basic/Version.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -251,46 +252,6 @@ static void appendVector(std::vector<Derived> &&New,
   std::move(New.begin(), New.end(), std::back_inserter(Original));
 }
 
-// Compute the relative path from an Origin directory to a Destination directory
-static SmallString<128> computeRelativePath(StringRef Destination,
-                                            StringRef Origin) {
-  // If Origin is empty, the relative path to the Destination is its complete
-  // path.
-  if (Origin.empty())
-    return Destination;
-
-  // The relative path is an empty path if both directories are the same.
-  if (Destination == Origin)
-    return {};
-
-  // These iterators iterate through each of their parent directories
-  llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
-  llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
-  llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
-  llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
-  // Advance both iterators until the paths differ. Example:
-  //    Destination = A/B/C/D
-  //    Origin      = A/B/E/F
-  // FileI will point to C and DirI to E. The directories behind them is the
-  // directory they share (A/B).
-  while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
-    ++FileI;
-    ++DirI;
-  }
-  SmallString<128> Result; // This will hold the resulting path.
-  // Result has to go up one directory for each of the remaining directories in
-  // Origin
-  while (DirI != DirE) {
-    llvm::sys::path::append(Result, "..");
-    ++DirI;
-  }
-  // Result has to append each of the remaining directories in Destination
-  while (FileI != FileE) {
-    llvm::sys::path::append(Result, *FileI);
-    ++FileI;
-  }
-  return Result;
-}
 
 // HTML generation
 
@@ -1133,24 +1094,6 @@ static llvm::Error genIndex(const ClangDocContext &CDCtx) {
   return llvm::Error::success();
 }
 
-static llvm::Error 
-copyFile(StringRef FilePath, StringRef OutDirectory) {
-  llvm::SmallString<128> PathWrite;
-  llvm::sys::path::native(OutDirectory, PathWrite);
-  llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
-  llvm::SmallString<128> PathRead;
-  llvm::sys::path::native(FilePath, PathRead);
-  std::error_code OK;
-  std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
-  if (FileErr != OK) {
-    return llvm::createStringError(llvm::inconvertibleErrorCode(),
-                                   "error creating file " +
-                                       llvm::sys::path::filename(FilePath) +
-                                       ": " + FileErr.message() + "\n");
-  }
-  return llvm::Error::success();
-}
-
 llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
   auto Err = serializeIndex(CDCtx);
   if (Err)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 96a685dbe2ae87..6be4c795a68654 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 #include "Generators.h"
 #include "Representation.h"
+#include "FileHelpersClangDoc.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Mustache.h"
 
@@ -48,9 +49,8 @@ class MustacheTemplateFile : public Template {
   Error registerPartialFile(StringRef Name, StringRef FileName) {
     ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
         MemoryBuffer::getFile(FileName);
-    if (auto EC = BufferOrError.getError()) {
+    if (auto EC = BufferOrError.getError())
       return llvm::createFileError("cannot open file", EC);
-    }
     std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
     llvm::StringRef FileContent = Buffer->getBuffer();
     registerPartial(Name, FileContent);
@@ -65,24 +65,54 @@ static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
 static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
 
 
+llvm::Error setupTemplate(
+                   std::unique_ptr<MustacheTemplateFile> &Template,
+                   StringRef TemplatePath,
+                   std::vector<std::pair<StringRef, StringRef>> Partials) {
+  auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
+  if (auto EC = T.getError())
+     return llvm::createFileError("cannot open file", EC);
+  Template = std::move(T.get());
+  for (const auto &P : Partials) {
+    auto Err = Template->registerPartialFile(P.first, P.second);
+    if (Err)
+      return Err;
+  }
+  return llvm::Error::success();
+}
+
 llvm::Error 
 setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
-  auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
-  auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
-  if (auto EC = Template.getError()) {
-    return llvm::createFileError("cannot open file", EC);
-  }
-  NamespaceTemplate = std::move(Template.get());
+  auto NamespaceFilePath = CDCtx.MustacheTemplates.lookup("namespace-template");
+  auto ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
+  auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
+  auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
+  auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
+  std::vector<std::pair<StringRef, StringRef>> Partials = {
+    {"Comments", CommentFilePath},
+    {"FunctionPartial", FunctionFilePath},
+    {"EnumPartial", EnumFilePath}
+  };
+  
+  auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
+  if (Err)
+    return Err;
+  
+  Err = setupTemplate(RecordTemplate, ClassFilePath, Partials);
+  
+  if (Err)
+    return Err;
+  
   return llvm::Error::success();
 }
 
+
 llvm::Error 
 MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir, 
                                     llvm::StringMap<std::unique_ptr<doc::Info>> Infos, 
                                     const clang::doc::ClangDocContext &CDCtx) {
-  if (auto Err = setupTemplateFiles(CDCtx)) {
+  if (auto Err = setupTemplateFiles(CDCtx))
     return Err;
-  }
   // Track which directories we already tried to create.
   llvm::StringSet<> CreatedDirs;
   // Collect all output by file name and create the necessary directories.
@@ -95,10 +125,9 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
     llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
     if (!CreatedDirs.contains(Path)) {
       if (std::error_code Err = llvm::sys::fs::create_directories(Path);
-          Err != std::error_code()) {
+          Err != std::error_code())
         return llvm::createStringError(Err, "Failed to create directory '%s'.",
                                        Path.c_str());
-      }
       CreatedDirs.insert(Path);
     }
 
@@ -110,14 +139,13 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
     std::error_code FileErr;
     llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
                                 llvm::sys::fs::OF_None);
-    if (FileErr) {
+    if (FileErr)
       return llvm::createStringError(FileErr, "Error opening file '%s'",
                                      Group.getKey().str().c_str());
-    }
+    
     for (const auto &Info : Group.getValue()) {
-      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
+      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
         return Err;
-      }
     }
   }
   return llvm::Error::success();
@@ -126,12 +154,15 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
 Value extractValue(const Location &L, 
                    std::optional<StringRef> RepositoryUrl = std::nullopt) {
   Object Obj = Object();
+  Obj.insert({"LineNumber", L.LineNumber});
+  Obj.insert({"Filename", L.Filename});
+  
   if (!L.IsFileInRootDir || !RepositoryUrl) {
-    Obj.insert({"LineNumber", L.LineNumber});
-    Obj.insert({"Filename", L.Filename});
+    return Obj;
   }
   SmallString<128> FileURL(*RepositoryUrl);
   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+  FileURL += "#" + std::to_string(L.LineNumber);
   Obj.insert({"FileURL", FileURL});
   
   return Obj;
@@ -144,6 +175,8 @@ Value extractValue(const Reference &I, StringRef CurrentDirectory) {
   Object Obj = Object();
   Obj.insert({"Link", Path});
   Obj.insert({"Name", I.Name});
+  Obj.insert({"QualName", I.QualName});
+  Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
   return Obj;
 }
 
@@ -209,12 +242,13 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
       ArrDesc.getAsArray()->emplace_back(extractValue(Child));
     Obj.insert({"FunctionComments", ArrDesc});
   }
-  if (I.DefLoc) {
-    if (!CDCtx.RepositoryUrl)
-      Obj.insert({"Location", extractValue(*I.DefLoc)});
+  if (I.DefLoc.has_value()) {
+    Location L = *I.DefLoc;
+    if (CDCtx.RepositoryUrl.has_value())
+      Obj.insert({"Location", extractValue(L,
+                                           StringRef{*CDCtx.RepositoryUrl})});
     else
-      Obj.insert({"Location", extractValue(*I.DefLoc, 
-                                           StringRef{*CDCtx.RepositoryUrl})});  
+      Obj.insert({"Location", extractValue(L)});  
   }
   return Obj;
 }
@@ -229,7 +263,6 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
   Obj.insert({"EnumName", EnumType});
   Obj.insert({"HasComment", HasComment});
   Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
-  
   Value Arr = Array();
   for (const EnumValueInfo& M: I.Members) {
     Value EnumValue = Object();
@@ -256,75 +289,175 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
     Obj.insert({"EnumComments", ArrDesc});
   }
   
-  if (I.DefLoc) {
-    if (!CDCtx.RepositoryUrl)
-      Obj.insert({"Location", extractValue(*I.DefLoc)});
+  if (I.DefLoc.has_value()) {
+    Location L = *I.DefLoc;
+    if (CDCtx.RepositoryUrl.has_value())
+      Obj.insert({"Location", extractValue(L,
+                                           StringRef{*CDCtx.RepositoryUrl})});
     else
-      Obj.insert({"Location", extractValue(*I.DefLoc, 
-                                           StringRef{*CDCtx.RepositoryUrl})});  
+      Obj.insert({"Location", extractValue(L)});  
   }
   
   return Obj;
 }
 
-Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
-  Object NamespaceValue = Object();
-  std::string InfoTitle;
-  if (I.Name.str() == "")
-    InfoTitle = "Global Namespace";
-  else
-    InfoTitle = ("namespace " + I.Name).str();  
-  
-  StringRef BasePath = I.getRelativeFilePath("");
-  NamespaceValue.insert({"NamespaceTitle", InfoTitle});
-  NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
-  
-  if (!I.Description.empty()) {
-    Value ArrDesc = Array();
-    for (const CommentInfo& Child : I.Description) 
-      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
-    NamespaceValue.insert({"NamespaceComments", ArrDesc });
-  }
-
+void extractScopeChildren(const ScopeChildren &S, Object &Obj, 
+                          StringRef ParentInfoDir,
+                          const ClangDocContext &CDCtx) {
   Value ArrNamespace = Array();
-  for (const Reference& Child : I.Children.Namespaces)
-    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  for (const Reference& Child : S.Namespaces)
+    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
   
   if (!ArrNamespace.getAsArray()->empty())
-    NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
+    Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
   
   Value ArrRecord = Array();
-  for (const Reference& Child : I.Children.Records)
-    ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  for (const Reference& Child : S.Records)
+    ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
   
   if (!ArrRecord.getAsArray()->empty())
-    NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
+    Obj.insert({"Record", Object{{"Links", ArrRecord}}});
   
   Value ArrFunction = Array();
-  for (const FunctionInfo& Child : I.Children.Functions)
-    ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
-                                                        CDCtx));
+  Value PublicFunction = Array();
+  Value ProtectedFunction = Array();
+  Value PrivateFunction = Array();
+  
+  for (const FunctionInfo& Child : S.Functions) {
+    Value F = extractValue(Child, ParentInfoDir, CDCtx);
+    AccessSpecifier Access = Child.Access;
+    if (Access == AccessSpecifier::AS_public)
+      PublicFunction.getAsArray()->emplace_back(F);
+    else if (Access == AccessSpecifier::AS_protected)
+      ProtectedFunction.getAsArray()->emplace_back(F);
+    else
+      ArrFunction.getAsArray()->emplace_back(F);
+  }  
   if (!ArrFunction.getAsArray()->empty())
-    NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
+    Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
+  
+  if (!PublicFunction.getAsArray()->empty())
+    Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
+  
+  if (!ProtectedFunction.getAsArray()->empty())
+    Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
+  
   
   Value ArrEnum = Array();
-  for (const EnumInfo& Child : I.Children.Enums)
+  for (const EnumInfo& Child : S.Enums)
     ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
   
   if (!ArrEnum.getAsArray()->empty())
-    NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
+    Obj.insert({"Enums", Object{{"Obj", ArrEnum }}});
   
   Value ArrTypedefs = Array();
-  for (const TypedefInfo& Child : I.Children.Typedefs) 
+  for (const TypedefInfo& Child : S.Typedefs) 
     ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
   
   if (!ArrTypedefs.getAsArray()->empty())
-    NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+    Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+}
+
+Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
+  Object NamespaceValue = Object();
+  std::string InfoTitle;
+  if (I.Name.str() == "")
+    InfoTitle = "Global Namespace";
+  else
+    InfoTitle = ("namespace " + I.Name).str();  
+  
+  StringRef BasePath = I.getRelativeFilePath("");
+  NamespaceValue.insert({"NamespaceTitle", InfoTitle});
+  NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
   
+  if (!I.Description.empty()) {
+    Value ArrDesc = Array();
+    for (const CommentInfo& Child : I.Description) 
+      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+    NamespaceValue.insert({"NamespaceComments", ArrDesc });
+  }
+  extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
   return NamespaceValue;
 }
 
+Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
+  Object RecordValue = Object();
+  
+  if (!I.Description.empty()) {
+    Value ArrDesc = Array();
+    for (const CommentInfo& Child : I.Description) 
+      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+    RecordValue.insert({"RecordComments", ArrDesc });
+  }
+  RecordValue.insert({"Name", I.Name});
+  RecordValue.insert({"FullName", I.FullName});
+  RecordValue.insert({"RecordType", getTagType(I.TagType)});
+  
+  if (I.DefLoc.has_value()) {
+    Location L = *I.DefLoc;
+    if (CDCtx.RepositoryUrl.has_value())
+      RecordValue.insert({"Location", extractValue(L,
+                                           StringRef{*CDCtx.RepositoryUrl})});
+    else
+      RecordValue.insert({"Location", extractValue(L)});  
+  }
+  
+  StringRef BasePath = I.getRelativeFilePath("");
+  extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
+  Value PublicMembers = Array();
+  Value ProtectedMembers = Array();
+  Value PrivateMembers = Array();
+  for (const MemberTypeInfo &Member : I.Members ) {
+    Value MemberValue = Object();
+    MemberValue.getAsObject()->insert({"Name", Member.Name});
+    MemberValue.getAsObject()->insert({"Type", Member.Type.Name});
+    if (!Member.Description.empty()) {
+      Value ArrDesc = Array();
+      for (const CommentInfo& Child : Member.Description) 
+            ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+      MemberValue.getAsObject()->insert({"MemberComments", ArrDesc });
+    }
+    
+    if (Member.Access == AccessSpecifier::AS_public)
+      PublicMembers.getAsArray()->emplace_back(MemberValue);
+    else if (Member.Access == AccessSpecifier::AS_protected)
+      ProtectedMembers.getAsArray()->emplace_back(MemberValue);
+    else if (Member.Access == AccessSpecifier::AS_private)
+      PrivateMembers.getAsArray()->emplace_back(MemberValue);
+  }
+  if (!PublicMembers.getAsArray()->empty())
+    RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
+  if (!ProtectedMembers.getAsArray()->empty())
+    RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
+  if (!PrivateMembers.getAsArray()->empty())
+    RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+  
+  return RecordValue;
+}
 
+void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
+  V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+  Value StylesheetArr = Array();
+  auto InfoPath = I->getRelativeFilePath("");
+  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+  for (const auto &FilePath  : CDCtx.UserStylesheets) {
+    SmallString<128> StylesheetPath = RelativePath;
+    llvm::sys::path::append(StylesheetPath,
+                            llvm::sys::path::filename(FilePath));
+    llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
+    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+  }
+  V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+  
+  Value ScriptArr = Array();
+  for (auto Script : CDCtx.JsScripts) {
+    SmallString<128> JsPath = RelativePath;
+    llvm::sys::path::append(JsPath, llvm::sys::path::filename(Script));
+    ScriptArr.getAsArray()->emplace_back(JsPath);
+  }
+  V.getAsObject()->insert({"Scripts", ScriptArr});
+}
+ 
 
 llvm::Error
 MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -332,13 +465,18 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
-    llvm::raw_ostream &OS = llvm::outs();
-    llvm::json::OStream J(OS, /*IndentSize=*/2);
-    J.value(V);
+    setupTemplateValue(CDCtx, V, I);
+    OS << NamespaceTemplate->render(V);
     break;
   }
-  case InfoType::IT_record:
+  case InfoType::IT_record: {
+    Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
+    setupTemplateValue(CDCtx, V, I);
+    // Serialize the JSON value to the output stream in a readable format.
+    llvm::outs() << llvm::formatv("{0:2}", V);
+    OS << RecordTemplate->render(V);
     break;
+  }  
   case InfoType::IT_enum:
     llvm::outs() << "IT_enum\n";
     break;
@@ -356,6 +494,17 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
 }
 
 llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
+  llvm::Error Err = llvm::Error::success();
+  for (const auto &FilePath : CDCtx.UserStylesheets) {
+    Err = copyFile(FilePath, CDCtx.OutDirectory);
+    if (Err)
+      return Err;
+  }
+  for (const auto &FilePath : CDCtx.JsScripts) {
+    Err = copyFile(FilePath, CDCtx.OutDirectory);
+    if (Err)
+      return Err;
+  }
   return llvm::Error::success();
 }
 
diff --git a/clang-tools-extra/clang-doc/assets/comments.mustache b/clang-tools-extra/clang-doc/assets/comments.mustache
deleted file mode 100644
index 1eac4de91836ab..00000000000000
--- a/clang-tools-extra/clang-doc/assets/comments.mustache
+++ /dev/null
@@ -1,27 +0,0 @@
-{{!
-    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
-    
-    This file defines templates for generating
-}}
-
-{{#FullComments}}
-    {{#Children}}
-    {{>Comment}}    
-    {{/Children}}
-{{/FullComments}}
-{{#ParagraphComment}}
-    {{#Children}}
-    {{>Comment}}
-    {{/Children}}
-{{/ParagraphComment}}
-{{#BlockCommandComment}}
-    <div>{{Command}}</div>
-    {{#Children}}
-    {{>Comment}}
-    {{/Children}}
-{{/BlockCommandComment}}
-{{#TextComment}}
-    <p>{{TextComment}}</p>
-{{/TextComment}}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
deleted file mode 100644
index 3b77e0189f4e22..00000000000000
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ /dev/null
@@ -1,55 +0,0 @@
-{{! 
-    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
-    
-    This file defines the template for generating namespaces
-}}
-<!DOCTYPE html>
-<html lang="en-US">
-    <head>
-        <meta charset="utf-8"/>
-        <title>{{NamespaceTitle}}</title>
-    </head>
-    <h1>{{NamespaceTitle}}</h1>
-    {{#NamespaceComments}}
-    <div>
-        {{>Comments}}
-    </div>
-    {{/NamespaceComments}}
-    {{#Namespace}}
-    <h2 id="Namespace">Namespace</h2>
-    <ul>
-        {{#Links}}
-        <li>
-            <a href="{{Link}}">{{Name}}</a>
-        </li>
-        {{/Links}}
-    </ul>
-    {{/Namespace}}
-    {{#Record}}
-    <h2 id="Class">Class</h2>
-    <ul>
-        {{#Links}}
-        <li>
-            <a href="{{Link}}">{{Name}}</a>
-        </li>
-        {{/Links}}
-    </ul>
-    {{/Record}}
-    {{#Function}}
-    <h2 id="Function">Function</h2>
-    <div>
-        {{#Obj}}
-            
-        {{/Obj}}
-    </div>
-    {{/Function}}
-    {{#Enums}}
-    <h2 id="Enums">Enums</h2>
-    <div>
-        {{#Obj}}
-        {{/Obj}}
-    </div>
-    {{/Enums}}
-</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index 8eb067dbe6de87..eccbc99a7ecc4b 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,7 +21,13 @@ target_link_libraries(clang-doc
 
 set(assets
   index.js
-  template.mustache
+  mustache-index.js
+  class-template.mustache
+  comments-template.mustache
+  enum-template.mustache      
+  function-template.mustache
+  namespace-template.mustache      
+  clang-doc-mustache.css
   clang-doc-default-stylesheet.css
 )
 
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index eee8dbf093cc53..256e29c3c9f894 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -163,6 +163,15 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
   return llvm::Error::success();
 }
 
+llvm::SmallString<128> appendPathNative(llvm::SmallString<128> Path, 
+                                    llvm::StringRef Asset) {
+  llvm::SmallString<128> Default;
+  llvm::sys::path::native(Path, Default);
+  llvm::sys::path::append(Default, Asset);
+  return Default;
+}
+
+
 llvm::Error getDefaultAssetFiles(const char *Argv0,
                                  clang::doc::ClangDocContext &CDCtx) {
   void *MainAddr = (void *)(intptr_t)getExecutablePath;
@@ -173,13 +182,10 @@ llvm::Error getDefaultAssetFiles(const char *Argv0,
   llvm::SmallString<128> AssetsPath;
   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
-  llvm::SmallString<128> DefaultStylesheet;
-  llvm::sys::path::native(AssetsPath, DefaultStylesheet);
-  llvm::sys::path::append(DefaultStylesheet,
-                          "clang-doc-default-stylesheet.css");
-  llvm::SmallString<128> IndexJS;
-  llvm::sys::path::native(AssetsPath, IndexJS);
-  llvm::sys::path::append(IndexJS, "index.js");
+  llvm::SmallString<128> DefaultStylesheet =
+      appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css");
+  llvm::SmallString<128> IndexJS =
+      appendPathNative(AssetsPath, "index.js");
 
   if (!llvm::sys::fs::is_regular_file(IndexJS))
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -210,6 +216,7 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
   return getDefaultAssetFiles(Argv0, CDCtx);
 }
 
+
 llvm::Error getMustacheHtmlFiles(const char *Argv0,
                                  clang::doc::ClangDocContext &CDCtx) {
   if (!UserAssetPath.empty() &&
@@ -228,10 +235,34 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
   
-  llvm::SmallString<128> MustacheTemplate;
-  llvm::sys::path::native(AssetsPath, MustacheTemplate);
-  llvm::sys::path::append(MustacheTemplate, "template.mustache");
-  CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+  llvm::SmallString<128> DefaultStylesheet
+      = appendPathNative(AssetsPath, "clang-doc-mustache.css");
+  llvm::SmallString<128> NamespaceTemplate 
+      = appendPathNative(AssetsPath, "namespace-template.mustache");
+  llvm::SmallString<128> ClassTemplate
+      = appendPathNative(AssetsPath, "class-template.mustache");
+  llvm::SmallString<128> EnumTemplate
+      = appendPathNative(AssetsPath, "enum-template.mustache");
+  llvm::SmallString<128> FunctionTemplate
+      = appendPathNative(AssetsPath, "function-template.mustache");
+  llvm::SmallString<128> CommentTemplate
+      = appendPathNative(AssetsPath, "comments-template.mustache");
+  llvm::SmallString<128> IndexJS
+      = appendPathNative(AssetsPath, "mustache-index.js");
+  
+  CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
+  CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
+                               std::string(DefaultStylesheet));
+  CDCtx.MustacheTemplates.insert({"namespace-template", 
+                                  NamespaceTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"class-template",
+                                  ClassTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"enum-template",
+                                  EnumTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"function-template",
+                                  FunctionTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"comments-template", 
+                                  CommentTemplate.c_str()});
   
   return llvm::Error::success();
 }

>From 08a9f9960969e040eac234b5c67aff7acf03bc2f Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 02:26:22 -0400
Subject: [PATCH 57/62] [clang-doc] init mustache implementation

---
 clang-tools-extra/clang-doc/CMakeLists.txt    |   5 +-
 clang-tools-extra/clang-doc/HTMLGenerator.cpp |  59 +++-
 .../clang-doc/HTMLMustacheGenerator.cpp       | 289 ++++--------------
 .../clang-doc/assets/template.mustache        |  23 ++
 .../clang-doc/tool/CMakeLists.txt             |   8 +-
 .../clang-doc/tool/ClangDocMain.cpp           |  62 +---
 6 files changed, 154 insertions(+), 292 deletions(-)
 create mode 100644 clang-tools-extra/clang-doc/assets/template.mustache

diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 0793c343305184..82aded940222db 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -8,16 +8,15 @@ add_clang_library(clangDoc
   BitcodeReader.cpp
   BitcodeWriter.cpp
   ClangDoc.cpp
-  FileHelpersClangDoc.cpp
   Generators.cpp
   HTMLGenerator.cpp
-  HTMLMustacheGenerator.cpp      
   Mapper.cpp
   MDGenerator.cpp
   Representation.cpp
   Serialize.cpp
   YAMLGenerator.cpp
-        
+  HTMLMustacheGenerator.cpp
+
   DEPENDS
   omp_gen
   ClangDriverOptions
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index aa76d17d9411d6..ed65ac965114b5 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -8,7 +8,6 @@
 
 #include "Generators.h"
 #include "Representation.h"
-#include "FileHelpersClangDoc.h"
 #include "clang/Basic/Version.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
@@ -252,6 +251,46 @@ static void appendVector(std::vector<Derived> &&New,
   std::move(New.begin(), New.end(), std::back_inserter(Original));
 }
 
+// Compute the relative path from an Origin directory to a Destination directory
+static SmallString<128> computeRelativePath(StringRef Destination,
+                                            StringRef Origin) {
+  // If Origin is empty, the relative path to the Destination is its complete
+  // path.
+  if (Origin.empty())
+    return Destination;
+
+  // The relative path is an empty path if both directories are the same.
+  if (Destination == Origin)
+    return {};
+
+  // These iterators iterate through each of their parent directories
+  llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination);
+  llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination);
+  llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin);
+  llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin);
+  // Advance both iterators until the paths differ. Example:
+  //    Destination = A/B/C/D
+  //    Origin      = A/B/E/F
+  // FileI will point to C and DirI to E. The directories behind them is the
+  // directory they share (A/B).
+  while (FileI != FileE && DirI != DirE && *FileI == *DirI) {
+    ++FileI;
+    ++DirI;
+  }
+  SmallString<128> Result; // This will hold the resulting path.
+  // Result has to go up one directory for each of the remaining directories in
+  // Origin
+  while (DirI != DirE) {
+    llvm::sys::path::append(Result, "..");
+    ++DirI;
+  }
+  // Result has to append each of the remaining directories in Destination
+  while (FileI != FileE) {
+    llvm::sys::path::append(Result, *FileI);
+    ++FileI;
+  }
+  return Result;
+}
 
 // HTML generation
 
@@ -1094,6 +1133,24 @@ static llvm::Error genIndex(const ClangDocContext &CDCtx) {
   return llvm::Error::success();
 }
 
+static llvm::Error 
+copyFile(StringRef FilePath, StringRef OutDirectory) {
+  llvm::SmallString<128> PathWrite;
+  llvm::sys::path::native(OutDirectory, PathWrite);
+  llvm::sys::path::append(PathWrite, llvm::sys::path::filename(FilePath));
+  llvm::SmallString<128> PathRead;
+  llvm::sys::path::native(FilePath, PathRead);
+  std::error_code OK;
+  std::error_code FileErr = llvm::sys::fs::copy_file(PathRead, PathWrite);
+  if (FileErr != OK) {
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "error creating file " +
+                                       llvm::sys::path::filename(FilePath) +
+                                       ": " + FileErr.message() + "\n");
+  }
+  return llvm::Error::success();
+}
+
 llvm::Error HTMLGenerator::createResources(ClangDocContext &CDCtx) {
   auto Err = serializeIndex(CDCtx);
   if (Err)
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 6be4c795a68654..63def07c5fa804 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 #include "Generators.h"
 #include "Representation.h"
-#include "FileHelpersClangDoc.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Mustache.h"
 
@@ -49,12 +48,12 @@ class MustacheTemplateFile : public Template {
   Error registerPartialFile(StringRef Name, StringRef FileName) {
     ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
         MemoryBuffer::getFile(FileName);
-    if (auto EC = BufferOrError.getError())
+    if (auto EC = BufferOrError.getError()) {
       return llvm::createFileError("cannot open file", EC);
+    }
     std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
     llvm::StringRef FileContent = Buffer->getBuffer();
     registerPartial(Name, FileContent);
-    return llvm::Error::success();
   }
 
   MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
@@ -65,54 +64,23 @@ static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
 static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
 
 
-llvm::Error setupTemplate(
-                   std::unique_ptr<MustacheTemplateFile> &Template,
-                   StringRef TemplatePath,
-                   std::vector<std::pair<StringRef, StringRef>> Partials) {
-  auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
-  if (auto EC = T.getError())
-     return llvm::createFileError("cannot open file", EC);
-  Template = std::move(T.get());
-  for (const auto &P : Partials) {
-    auto Err = Template->registerPartialFile(P.first, P.second);
-    if (Err)
-      return Err;
-  }
-  return llvm::Error::success();
-}
-
 llvm::Error 
 setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
-  auto NamespaceFilePath = CDCtx.MustacheTemplates.lookup("namespace-template");
-  auto ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
-  auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
-  auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
-  auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
-  std::vector<std::pair<StringRef, StringRef>> Partials = {
-    {"Comments", CommentFilePath},
-    {"FunctionPartial", FunctionFilePath},
-    {"EnumPartial", EnumFilePath}
-  };
-  
-  auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
-  if (Err)
-    return Err;
-  
-  Err = setupTemplate(RecordTemplate, ClassFilePath, Partials);
-  
-  if (Err)
-    return Err;
-  
-  return llvm::Error::success();
+  auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
+  auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
+  if (auto EC = Template.getError()) {
+    return llvm::createFileError("cannot open file", EC);
+  }
+  NamespaceTemplate = std::move(Template.get());
 }
 
-
 llvm::Error 
 MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir, 
                                     llvm::StringMap<std::unique_ptr<doc::Info>> Infos, 
                                     const clang::doc::ClangDocContext &CDCtx) {
-  if (auto Err = setupTemplateFiles(CDCtx))
+  if (auto Err = setupTemplateFiles(CDCtx)) {
     return Err;
+  }
   // Track which directories we already tried to create.
   llvm::StringSet<> CreatedDirs;
   // Collect all output by file name and create the necessary directories.
@@ -125,9 +93,10 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
     llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
     if (!CreatedDirs.contains(Path)) {
       if (std::error_code Err = llvm::sys::fs::create_directories(Path);
-          Err != std::error_code())
+          Err != std::error_code()) {
         return llvm::createStringError(Err, "Failed to create directory '%s'.",
                                        Path.c_str());
+      }
       CreatedDirs.insert(Path);
     }
 
@@ -139,33 +108,28 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
     std::error_code FileErr;
     llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
                                 llvm::sys::fs::OF_None);
-    if (FileErr)
+    if (FileErr) {
       return llvm::createStringError(FileErr, "Error opening file '%s'",
                                      Group.getKey().str().c_str());
-    
+    }
     for (const auto &Info : Group.getValue()) {
-      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
+      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
         return Err;
+      }
     }
   }
-  return llvm::Error::success();
 }
 
 Value extractValue(const Location &L, 
                    std::optional<StringRef> RepositoryUrl = std::nullopt) {
   Object Obj = Object();
-  Obj.insert({"LineNumber", L.LineNumber});
-  Obj.insert({"Filename", L.Filename});
-  
   if (!L.IsFileInRootDir || !RepositoryUrl) {
-    return Obj;
+    Obj.insert({"LineNumber", L.LineNumber});
+    Obj.insert({"Filename", L.Filename});
   }
   SmallString<128> FileURL(*RepositoryUrl);
   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
-  FileURL += "#" + std::to_string(L.LineNumber);
   Obj.insert({"FileURL", FileURL});
-  
-  return Obj;
 }
 
 Value extractValue(const Reference &I, StringRef CurrentDirectory) {
@@ -175,8 +139,6 @@ Value extractValue(const Reference &I, StringRef CurrentDirectory) {
   Object Obj = Object();
   Obj.insert({"Link", Path});
   Obj.insert({"Name", I.Name});
-  Obj.insert({"QualName", I.QualName});
-  Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
   return Obj;
 }
 
@@ -227,12 +189,8 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
   Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
   
   Value ParamArr = Array();
-  for (const auto Val : llvm::enumerate(I.Params)) {
-    Value V = Object();
-    V.getAsObject()->insert({"Name", Val.value().Name});
-    V.getAsObject()->insert({"Type", Val.value().Type.Name});
-    V.getAsObject()->insert({"End",  Val.index() + 1 == I.Params.size()});
-    ParamArr.getAsArray()->emplace_back(V);
+  for (const auto &P : I.Params) {
+    ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
   }
   Obj.insert({"Params", ParamArr});
   
@@ -242,13 +200,12 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
       ArrDesc.getAsArray()->emplace_back(extractValue(Child));
     Obj.insert({"FunctionComments", ArrDesc});
   }
-  if (I.DefLoc.has_value()) {
-    Location L = *I.DefLoc;
-    if (CDCtx.RepositoryUrl.has_value())
-      Obj.insert({"Location", extractValue(L,
-                                           StringRef{*CDCtx.RepositoryUrl})});
+  if (I.DefLoc) {
+    if (!CDCtx.RepositoryUrl)
+      Obj.insert({"Location", extractValue(*I.DefLoc)});
     else
-      Obj.insert({"Location", extractValue(L)});  
+      Obj.insert({"Location", extractValue(*I.DefLoc, 
+                                           StringRef{*CDCtx.RepositoryUrl})});  
   }
   return Obj;
 }
@@ -263,6 +220,7 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
   Obj.insert({"EnumName", EnumType});
   Obj.insert({"HasComment", HasComment});
   Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+  
   Value Arr = Array();
   for (const EnumValueInfo& M: I.Members) {
     Value EnumValue = Object();
@@ -289,75 +247,17 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
     Obj.insert({"EnumComments", ArrDesc});
   }
   
-  if (I.DefLoc.has_value()) {
-    Location L = *I.DefLoc;
-    if (CDCtx.RepositoryUrl.has_value())
-      Obj.insert({"Location", extractValue(L,
-                                           StringRef{*CDCtx.RepositoryUrl})});
+  if (I.DefLoc) {
+    if (!CDCtx.RepositoryUrl)
+      Obj.insert({"Location", extractValue(*I.DefLoc)});
     else
-      Obj.insert({"Location", extractValue(L)});  
+      Obj.insert({"Location", extractValue(*I.DefLoc, 
+                                           StringRef{*CDCtx.RepositoryUrl})});  
   }
   
   return Obj;
 }
 
-void extractScopeChildren(const ScopeChildren &S, Object &Obj, 
-                          StringRef ParentInfoDir,
-                          const ClangDocContext &CDCtx) {
-  Value ArrNamespace = Array();
-  for (const Reference& Child : S.Namespaces)
-    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
-  
-  if (!ArrNamespace.getAsArray()->empty())
-    Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
-  
-  Value ArrRecord = Array();
-  for (const Reference& Child : S.Records)
-    ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
-  
-  if (!ArrRecord.getAsArray()->empty())
-    Obj.insert({"Record", Object{{"Links", ArrRecord}}});
-  
-  Value ArrFunction = Array();
-  Value PublicFunction = Array();
-  Value ProtectedFunction = Array();
-  Value PrivateFunction = Array();
-  
-  for (const FunctionInfo& Child : S.Functions) {
-    Value F = extractValue(Child, ParentInfoDir, CDCtx);
-    AccessSpecifier Access = Child.Access;
-    if (Access == AccessSpecifier::AS_public)
-      PublicFunction.getAsArray()->emplace_back(F);
-    else if (Access == AccessSpecifier::AS_protected)
-      ProtectedFunction.getAsArray()->emplace_back(F);
-    else
-      ArrFunction.getAsArray()->emplace_back(F);
-  }  
-  if (!ArrFunction.getAsArray()->empty())
-    Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
-  
-  if (!PublicFunction.getAsArray()->empty())
-    Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
-  
-  if (!ProtectedFunction.getAsArray()->empty())
-    Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
-  
-  
-  Value ArrEnum = Array();
-  for (const EnumInfo& Child : S.Enums)
-    ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
-  
-  if (!ArrEnum.getAsArray()->empty())
-    Obj.insert({"Enums", Object{{"Obj", ArrEnum }}});
-  
-  Value ArrTypedefs = Array();
-  for (const TypedefInfo& Child : S.Typedefs) 
-    ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
-  
-  if (!ArrTypedefs.getAsArray()->empty())
-    Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
-}
-
 Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
   Object NamespaceValue = Object();
   std::string InfoTitle;
@@ -376,88 +276,36 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
       ArrDesc.getAsArray()->emplace_back(extractValue(Child));
     NamespaceValue.insert({"NamespaceComments", ArrDesc });
   }
-  extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
-  return NamespaceValue;
-}
 
-Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
-  Object RecordValue = Object();
+  Value ArrNamespace = Array();
+  for (const Reference& Child : I.Children.Namespaces)
+    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  NamespaceValue.insert({"Namespace", ArrNamespace});
   
-  if (!I.Description.empty()) {
-    Value ArrDesc = Array();
-    for (const CommentInfo& Child : I.Description) 
-      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
-    RecordValue.insert({"RecordComments", ArrDesc });
-  }
-  RecordValue.insert({"Name", I.Name});
-  RecordValue.insert({"FullName", I.FullName});
-  RecordValue.insert({"RecordType", getTagType(I.TagType)});
+  Value ArrRecord = Array();
+  for (const Reference& Child : I.Children.Records)
+    ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  NamespaceValue.insert({"Record", ArrRecord});
   
-  if (I.DefLoc.has_value()) {
-    Location L = *I.DefLoc;
-    if (CDCtx.RepositoryUrl.has_value())
-      RecordValue.insert({"Location", extractValue(L,
-                                           StringRef{*CDCtx.RepositoryUrl})});
-    else
-      RecordValue.insert({"Location", extractValue(L)});  
-  }
+  Value ArrFunction = Array();
+  for (const FunctionInfo& Child : I.Children.Functions)
+    ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
+                                                        CDCtx));
+  NamespaceValue.insert({"Function", ArrRecord});
   
-  StringRef BasePath = I.getRelativeFilePath("");
-  extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
-  Value PublicMembers = Array();
-  Value ProtectedMembers = Array();
-  Value PrivateMembers = Array();
-  for (const MemberTypeInfo &Member : I.Members ) {
-    Value MemberValue = Object();
-    MemberValue.getAsObject()->insert({"Name", Member.Name});
-    MemberValue.getAsObject()->insert({"Type", Member.Type.Name});
-    if (!Member.Description.empty()) {
-      Value ArrDesc = Array();
-      for (const CommentInfo& Child : Member.Description) 
-            ArrDesc.getAsArray()->emplace_back(extractValue(Child));
-      MemberValue.getAsObject()->insert({"MemberComments", ArrDesc });
-    }
-    
-    if (Member.Access == AccessSpecifier::AS_public)
-      PublicMembers.getAsArray()->emplace_back(MemberValue);
-    else if (Member.Access == AccessSpecifier::AS_protected)
-      ProtectedMembers.getAsArray()->emplace_back(MemberValue);
-    else if (Member.Access == AccessSpecifier::AS_private)
-      PrivateMembers.getAsArray()->emplace_back(MemberValue);
-  }
-  if (!PublicMembers.getAsArray()->empty())
-    RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
-  if (!ProtectedMembers.getAsArray()->empty())
-    RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
-  if (!PrivateMembers.getAsArray()->empty())
-    RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+  Value ArrEnum = Array();
+  for (const EnumInfo& Child : I.Children.Enums)
+    ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
+  NamespaceValue.insert({"Enums", ArrEnum });
   
-  return RecordValue;
-}
-
-void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
-  V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
-  Value StylesheetArr = Array();
-  auto InfoPath = I->getRelativeFilePath("");
-  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
-  for (const auto &FilePath  : CDCtx.UserStylesheets) {
-    SmallString<128> StylesheetPath = RelativePath;
-    llvm::sys::path::append(StylesheetPath,
-                            llvm::sys::path::filename(FilePath));
-    llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
-    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
-  }
-  V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+  Value ArrTypedefs = Array();
+  for (const TypedefInfo& Child : I.Children.Typedefs) 
+    ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
+  NamespaceValue.insert({"Typedefs", ArrTypedefs });
   
-  Value ScriptArr = Array();
-  for (auto Script : CDCtx.JsScripts) {
-    SmallString<128> JsPath = RelativePath;
-    llvm::sys::path::append(JsPath, llvm::sys::path::filename(Script));
-    ScriptArr.getAsArray()->emplace_back(JsPath);
-  }
-  V.getAsObject()->insert({"Scripts", ScriptArr});
 }
- 
+
+
 
 llvm::Error
 MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -465,26 +313,16 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
-    setupTemplateValue(CDCtx, V, I);
     OS << NamespaceTemplate->render(V);
     break;
   }
-  case InfoType::IT_record: {
-    Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
-    setupTemplateValue(CDCtx, V, I);
-    // Serialize the JSON value to the output stream in a readable format.
-    llvm::outs() << llvm::formatv("{0:2}", V);
-    OS << RecordTemplate->render(V);
+  case InfoType::IT_record:
     break;
-  }  
   case InfoType::IT_enum:
-    llvm::outs() << "IT_enum\n";
     break;
   case InfoType::IT_function:
-    llvm::outs() << "IT_Function\n";
     break;
   case InfoType::IT_typedef:
-    llvm::outs() << "IT_typedef\n";
     break;
   case InfoType::IT_default:
     return createStringError(llvm::inconvertibleErrorCode(),
@@ -494,21 +332,10 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
 }
 
 llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
-  llvm::Error Err = llvm::Error::success();
-  for (const auto &FilePath : CDCtx.UserStylesheets) {
-    Err = copyFile(FilePath, CDCtx.OutDirectory);
-    if (Err)
-      return Err;
-  }
-  for (const auto &FilePath : CDCtx.JsScripts) {
-    Err = copyFile(FilePath, CDCtx.OutDirectory);
-    if (Err)
-      return Err;
-  }
-  return llvm::Error::success();
+  
 }
 
-const char *MustacheHTMLGenerator::Format = "mhtml";
+const char *MustacheHTMLGenerator::Format = "mustache";
 
 
 static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
@@ -516,7 +343,7 @@ static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator
 
 // This anchor is used to force the linker to link in the generated object
 // file and thus register the generator.
-volatile int MHTMLGeneratorAnchorSource = 0;
+volatile int HTMLGeneratorAnchorSource = 0;
 
 } // namespace doc
 } // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
new file mode 100644
index 00000000000000..af4c60182ae52c
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -0,0 +1,23 @@
+{{! 
+    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
+    
+    This file defines the template
+}}
+<!DOCTYPE html>
+<html lang="en-US">
+    <head>
+        <meta charset="utf-8"/>
+        <title>{{NamespaceTitle}}</title>
+    </head>
+    {{#NamespaceComments}}
+    <p>Namespace Comment Present!</p>
+    {{/NamespaceComments}}
+    {{#Namespace}}
+    <p>Namespace Present!</p>
+    {{/Namespace}}
+    {{#Record}}
+    <p>Record Present!</p>
+    {{/Record}}
+</html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index eccbc99a7ecc4b..8eb067dbe6de87 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,13 +21,7 @@ target_link_libraries(clang-doc
 
 set(assets
   index.js
-  mustache-index.js
-  class-template.mustache
-  comments-template.mustache
-  enum-template.mustache      
-  function-template.mustache
-  namespace-template.mustache      
-  clang-doc-mustache.css
+  template.mustache
   clang-doc-default-stylesheet.css
 )
 
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 256e29c3c9f894..fc5a9529111b16 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -103,7 +103,6 @@ enum OutputFormatTy {
   md,
   yaml,
   html,
-  mhtml
 };
 
 static llvm::cl::opt<OutputFormatTy>
@@ -113,9 +112,7 @@ static llvm::cl::opt<OutputFormatTy>
                                 clEnumValN(OutputFormatTy::md, "md",
                                            "Documentation in MD format."),
                                 clEnumValN(OutputFormatTy::html, "html",
-                                           "Documentation in HTML format."),
-                                clEnumValN(OutputFormatTy::mhtml, "mhtml",
-                                           "Documentation in mHTML format")),
+                                           "Documentation in HTML format.")),
                llvm::cl::init(OutputFormatTy::yaml),
                llvm::cl::cat(ClangDocCategory));
 
@@ -127,8 +124,6 @@ std::string getFormatString() {
     return "md";
   case OutputFormatTy::html:
     return "html";
-  case OutputFormatTy::mhtml:
-    return "mhtml";
   }
   llvm_unreachable("Unknown OutputFormatTy");
 }
@@ -163,15 +158,6 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
   return llvm::Error::success();
 }
 
-llvm::SmallString<128> appendPathNative(llvm::SmallString<128> Path, 
-                                    llvm::StringRef Asset) {
-  llvm::SmallString<128> Default;
-  llvm::sys::path::native(Path, Default);
-  llvm::sys::path::append(Default, Asset);
-  return Default;
-}
-
-
 llvm::Error getDefaultAssetFiles(const char *Argv0,
                                  clang::doc::ClangDocContext &CDCtx) {
   void *MainAddr = (void *)(intptr_t)getExecutablePath;
@@ -182,10 +168,13 @@ llvm::Error getDefaultAssetFiles(const char *Argv0,
   llvm::SmallString<128> AssetsPath;
   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
-  llvm::SmallString<128> DefaultStylesheet =
-      appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css");
-  llvm::SmallString<128> IndexJS =
-      appendPathNative(AssetsPath, "index.js");
+  llvm::SmallString<128> DefaultStylesheet;
+  llvm::sys::path::native(AssetsPath, DefaultStylesheet);
+  llvm::sys::path::append(DefaultStylesheet,
+                          "clang-doc-default-stylesheet.css");
+  llvm::SmallString<128> IndexJS;
+  llvm::sys::path::native(AssetsPath, IndexJS);
+  llvm::sys::path::append(IndexJS, "index.js");
 
   if (!llvm::sys::fs::is_regular_file(IndexJS))
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -216,7 +205,6 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
   return getDefaultAssetFiles(Argv0, CDCtx);
 }
 
-
 llvm::Error getMustacheHtmlFiles(const char *Argv0,
                                  clang::doc::ClangDocContext &CDCtx) {
   if (!UserAssetPath.empty() &&
@@ -235,36 +223,10 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
   
-  llvm::SmallString<128> DefaultStylesheet
-      = appendPathNative(AssetsPath, "clang-doc-mustache.css");
-  llvm::SmallString<128> NamespaceTemplate 
-      = appendPathNative(AssetsPath, "namespace-template.mustache");
-  llvm::SmallString<128> ClassTemplate
-      = appendPathNative(AssetsPath, "class-template.mustache");
-  llvm::SmallString<128> EnumTemplate
-      = appendPathNative(AssetsPath, "enum-template.mustache");
-  llvm::SmallString<128> FunctionTemplate
-      = appendPathNative(AssetsPath, "function-template.mustache");
-  llvm::SmallString<128> CommentTemplate
-      = appendPathNative(AssetsPath, "comments-template.mustache");
-  llvm::SmallString<128> IndexJS
-      = appendPathNative(AssetsPath, "mustache-index.js");
-  
-  CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
-  CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
-                               std::string(DefaultStylesheet));
-  CDCtx.MustacheTemplates.insert({"namespace-template", 
-                                  NamespaceTemplate.c_str()});
-  CDCtx.MustacheTemplates.insert({"class-template",
-                                  ClassTemplate.c_str()});
-  CDCtx.MustacheTemplates.insert({"enum-template",
-                                  EnumTemplate.c_str()});
-  CDCtx.MustacheTemplates.insert({"function-template",
-                                  FunctionTemplate.c_str()});
-  CDCtx.MustacheTemplates.insert({"comments-template", 
-                                  CommentTemplate.c_str()});
-  
-  return llvm::Error::success();
+  llvm::SmallString<128> MustacheTemplate;
+  llvm::sys::path::native(AssetsPath, MustacheTemplate);
+  llvm::sys::path::append(MustacheTemplate, "template.mustache");
+  CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
 }
 
 /// Make the output of clang-doc deterministic by sorting the children of

>From 7e1399b9e73ed4fcedb661f58321f7ab48347c4a Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 13 Sep 2024 17:50:03 -0400
Subject: [PATCH 58/62] [clang-doc] add a mustache backend init implementation

---
 .../clang-doc/HTMLMustacheGenerator.cpp       | 32 ++++++++++++----
 .../clang-doc/assets/template.mustache        | 37 +++++++++++++++++--
 .../clang-doc/tool/ClangDocMain.cpp           |  9 ++++-
 .../Inputs/basic-project/include/Shape.h      |  2 -
 4 files changed, 65 insertions(+), 15 deletions(-)

diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 63def07c5fa804..bfa563ebdc7b37 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -54,6 +54,7 @@ class MustacheTemplateFile : public Template {
     std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
     llvm::StringRef FileContent = Buffer->getBuffer();
     registerPartial(Name, FileContent);
+    return llvm::Error::success();
   }
 
   MustacheTemplateFile(StringRef TemplateStr) : Template(TemplateStr) {}
@@ -72,6 +73,7 @@ setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
     return llvm::createFileError("cannot open file", EC);
   }
   NamespaceTemplate = std::move(Template.get());
+  return llvm::Error::success();
 }
 
 llvm::Error 
@@ -118,6 +120,7 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
       }
     }
   }
+  return llvm::Error::success();
 }
 
 Value extractValue(const Location &L, 
@@ -130,6 +133,8 @@ Value extractValue(const Location &L,
   SmallString<128> FileURL(*RepositoryUrl);
   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
   Obj.insert({"FileURL", FileURL});
+  
+  return Obj;
 }
 
 Value extractValue(const Reference &I, StringRef CurrentDirectory) {
@@ -280,29 +285,39 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
   Value ArrNamespace = Array();
   for (const Reference& Child : I.Children.Namespaces)
     ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
-  NamespaceValue.insert({"Namespace", ArrNamespace});
+  
+  if (!ArrNamespace.getAsArray()->empty())
+    NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
   
   Value ArrRecord = Array();
   for (const Reference& Child : I.Children.Records)
     ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
-  NamespaceValue.insert({"Record", ArrRecord});
+  
+  if (!ArrRecord.getAsArray()->empty())
+    NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
   
   Value ArrFunction = Array();
   for (const FunctionInfo& Child : I.Children.Functions)
     ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
                                                         CDCtx));
-  NamespaceValue.insert({"Function", ArrRecord});
+  if (!ArrFunction.getAsArray()->empty())
+    NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
   
   Value ArrEnum = Array();
   for (const EnumInfo& Child : I.Children.Enums)
     ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
-  NamespaceValue.insert({"Enums", ArrEnum });
+  
+  if (!ArrEnum.getAsArray()->empty())
+    NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
   
   Value ArrTypedefs = Array();
   for (const TypedefInfo& Child : I.Children.Typedefs) 
     ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
-  NamespaceValue.insert({"Typedefs", ArrTypedefs });
   
+  if (!ArrTypedefs.getAsArray()->empty())
+    NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+  
+  return NamespaceValue;
 }
 
 
@@ -313,6 +328,7 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
+    llvm::outs() << V << "\n";
     OS << NamespaceTemplate->render(V);
     break;
   }
@@ -332,10 +348,10 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
 }
 
 llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
-  
+  return llvm::Error::success();
 }
 
-const char *MustacheHTMLGenerator::Format = "mustache";
+const char *MustacheHTMLGenerator::Format = "mhtml";
 
 
 static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
@@ -343,7 +359,7 @@ static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator
 
 // This anchor is used to force the linker to link in the generated object
 // file and thus register the generator.
-volatile int HTMLGeneratorAnchorSource = 0;
+volatile int MHTMLGeneratorAnchorSource = 0;
 
 } // namespace doc
 } // namespace clang
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/template.mustache b/clang-tools-extra/clang-doc/assets/template.mustache
index af4c60182ae52c..1d3407f8b52920 100644
--- a/clang-tools-extra/clang-doc/assets/template.mustache
+++ b/clang-tools-extra/clang-doc/assets/template.mustache
@@ -3,7 +3,7 @@
     See https://llvm.org/LICENSE.txt for license information.
     SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
     
-    This file defines the template
+    This file defines the template for generating Namespaces
 }}
 <!DOCTYPE html>
 <html lang="en-US">
@@ -11,13 +11,42 @@
         <meta charset="utf-8"/>
         <title>{{NamespaceTitle}}</title>
     </head>
+    <h1>{{NamespaceTitle}}</h1>
     {{#NamespaceComments}}
-    <p>Namespace Comment Present!</p>
+    <p>Namespace Comment</p>
     {{/NamespaceComments}}
     {{#Namespace}}
-    <p>Namespace Present!</p>
+    <h2 id="Namespace">Namespace</h2>
+    <ul>
+        {{#Links}}
+        <li>
+            <a href="{{Link}}">{{Name}}</a>
+        </li>
+        {{/Links}}
+    </ul>
     {{/Namespace}}
     {{#Record}}
-    <p>Record Present!</p>
+    <h2 id="Class">Class</h2>
+    <ul>
+        {{#Links}}
+        <li>
+            <a href="{{Link}}">{{Name}}</a>
+        </li>
+        {{/Links}}
+    </ul>
     {{/Record}}
+    {{#Function}}
+    <h2 id="Function">Function</h2>
+    <div>
+        {{#Obj}}
+        {{/Obj}}
+    </div>
+    {{/Function}}
+    {{#Enums}}
+    <h2 id="Enums">Enums</h2>
+    <div>
+        {{#Obj}}
+        {{/Obj}}
+    </div>
+    {{/Enums}}
 </html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index fc5a9529111b16..eee8dbf093cc53 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -103,6 +103,7 @@ enum OutputFormatTy {
   md,
   yaml,
   html,
+  mhtml
 };
 
 static llvm::cl::opt<OutputFormatTy>
@@ -112,7 +113,9 @@ static llvm::cl::opt<OutputFormatTy>
                                 clEnumValN(OutputFormatTy::md, "md",
                                            "Documentation in MD format."),
                                 clEnumValN(OutputFormatTy::html, "html",
-                                           "Documentation in HTML format.")),
+                                           "Documentation in HTML format."),
+                                clEnumValN(OutputFormatTy::mhtml, "mhtml",
+                                           "Documentation in mHTML format")),
                llvm::cl::init(OutputFormatTy::yaml),
                llvm::cl::cat(ClangDocCategory));
 
@@ -124,6 +127,8 @@ std::string getFormatString() {
     return "md";
   case OutputFormatTy::html:
     return "html";
+  case OutputFormatTy::mhtml:
+    return "mhtml";
   }
   llvm_unreachable("Unknown OutputFormatTy");
 }
@@ -227,6 +232,8 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
   llvm::sys::path::native(AssetsPath, MustacheTemplate);
   llvm::sys::path::append(MustacheTemplate, "template.mustache");
   CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+  
+  return llvm::Error::success();
 }
 
 /// Make the output of clang-doc deterministic by sorting the children of
diff --git a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
index e5c5d4c9e4412b..5354032f4d8323 100644
--- a/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
+++ b/clang-tools-extra/test/clang-doc/Inputs/basic-project/include/Shape.h
@@ -26,5 +26,3 @@ class Shape {
      */
     virtual double perimeter() const = 0;
 };
-
-

>From 4e5109af97bf06357bf7a9de762106bf93b2852b Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 11 Oct 2024 17:58:48 -0400
Subject: [PATCH 59/62] [clang-doc] add class template

---
 clang-tools-extra/clang-doc/CMakeLists.txt    |   1 +
 .../clang-doc/HTMLMustacheGenerator.cpp       | 292 ++++++++++++++----
 .../clang-doc/assets/clang-doc-mustache.css   |  63 ++--
 .../assets/namespace-template.mustache        |  42 +--
 .../clang-doc/tool/CMakeLists.txt             |   8 +-
 .../clang-doc/tool/ClangDocMain.cpp           |  53 +++-
 6 files changed, 305 insertions(+), 154 deletions(-)

diff --git a/clang-tools-extra/clang-doc/CMakeLists.txt b/clang-tools-extra/clang-doc/CMakeLists.txt
index 82aded940222db..288a85fea9d3ba 100644
--- a/clang-tools-extra/clang-doc/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/CMakeLists.txt
@@ -16,6 +16,7 @@ add_clang_library(clangDoc
   Serialize.cpp
   YAMLGenerator.cpp
   HTMLMustacheGenerator.cpp
+  FileHelpersClangDoc.cpp
 
   DEPENDS
   omp_gen
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index bfa563ebdc7b37..1f96202f1f6de6 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 #include "Generators.h"
 #include "Representation.h"
+#include "FileHelpersClangDoc.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Mustache.h"
 
@@ -48,9 +49,8 @@ class MustacheTemplateFile : public Template {
   Error registerPartialFile(StringRef Name, StringRef FileName) {
     ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrError =
         MemoryBuffer::getFile(FileName);
-    if (auto EC = BufferOrError.getError()) {
+    if (auto EC = BufferOrError.getError())
       return llvm::createFileError("cannot open file", EC);
-    }
     std::unique_ptr<llvm::MemoryBuffer> Buffer = std::move(BufferOrError.get());
     llvm::StringRef FileContent = Buffer->getBuffer();
     registerPartial(Name, FileContent);
@@ -65,24 +65,54 @@ static std::unique_ptr<MustacheTemplateFile> NamespaceTemplate = nullptr;
 static std::unique_ptr<MustacheTemplateFile> RecordTemplate = nullptr;
 
 
-llvm::Error 
-setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
-  auto TemplateFilePath = CDCtx.MustacheTemplates.lookup("template");
-  auto Template = MustacheTemplateFile::createMustacheFile(TemplateFilePath);
-  if (auto EC = Template.getError()) {
+llvm::Error setupTemplate(
+    std::unique_ptr<MustacheTemplateFile> &Template,
+    StringRef TemplatePath,
+    std::vector<std::pair<StringRef, StringRef>> Partials) {
+  auto T = MustacheTemplateFile::createMustacheFile(TemplatePath);
+  if (auto EC = T.getError())
     return llvm::createFileError("cannot open file", EC);
+  Template = std::move(T.get());
+  for (const auto &P : Partials) {
+    auto Err = Template->registerPartialFile(P.first, P.second);
+    if (Err)
+      return Err;
   }
-  NamespaceTemplate = std::move(Template.get());
   return llvm::Error::success();
 }
 
+llvm::Error 
+setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
+  auto NamespaceFilePath = CDCtx.MustacheTemplates.lookup("namespace-template");
+  auto ClassFilePath = CDCtx.MustacheTemplates.lookup("class-template");
+  auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
+  auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
+  auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
+  std::vector<std::pair<StringRef, StringRef>> Partials = {
+      {"Comments", CommentFilePath},
+      {"FunctionPartial", FunctionFilePath},
+      {"EnumPartial", EnumFilePath}
+  };
+  
+  auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
+  if (Err)
+    return Err;
+  
+  Err = setupTemplate(RecordTemplate, ClassFilePath, Partials);
+  
+  if (Err)
+    return Err;
+  
+  return llvm::Error::success();
+}
+
+
 llvm::Error 
 MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir, 
                                     llvm::StringMap<std::unique_ptr<doc::Info>> Infos, 
                                     const clang::doc::ClangDocContext &CDCtx) {
-  if (auto Err = setupTemplateFiles(CDCtx)) {
+  if (auto Err = setupTemplateFiles(CDCtx))
     return Err;
-  }
   // Track which directories we already tried to create.
   llvm::StringSet<> CreatedDirs;
   // Collect all output by file name and create the necessary directories.
@@ -95,10 +125,9 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
     llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
     if (!CreatedDirs.contains(Path)) {
       if (std::error_code Err = llvm::sys::fs::create_directories(Path);
-          Err != std::error_code()) {
+          Err != std::error_code())
         return llvm::createStringError(Err, "Failed to create directory '%s'.",
                                        Path.c_str());
-      }
       CreatedDirs.insert(Path);
     }
 
@@ -110,14 +139,13 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
     std::error_code FileErr;
     llvm::raw_fd_ostream InfoOS(Group.getKey(), FileErr,
                                 llvm::sys::fs::OF_None);
-    if (FileErr) {
+    if (FileErr)
       return llvm::createStringError(FileErr, "Error opening file '%s'",
                                      Group.getKey().str().c_str());
-    }
+    
     for (const auto &Info : Group.getValue()) {
-      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) {
+      if (llvm::Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
         return Err;
-      }
     }
   }
   return llvm::Error::success();
@@ -126,12 +154,15 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
 Value extractValue(const Location &L, 
                    std::optional<StringRef> RepositoryUrl = std::nullopt) {
   Object Obj = Object();
+  Obj.insert({"LineNumber", L.LineNumber});
+  Obj.insert({"Filename", L.Filename});
+  
   if (!L.IsFileInRootDir || !RepositoryUrl) {
-    Obj.insert({"LineNumber", L.LineNumber});
-    Obj.insert({"Filename", L.Filename});
+    return Obj;
   }
   SmallString<128> FileURL(*RepositoryUrl);
   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+  FileURL += "#" + std::to_string(L.LineNumber);
   Obj.insert({"FileURL", FileURL});
   
   return Obj;
@@ -144,6 +175,8 @@ Value extractValue(const Reference &I, StringRef CurrentDirectory) {
   Object Obj = Object();
   Obj.insert({"Link", Path});
   Obj.insert({"Name", I.Name});
+  Obj.insert({"QualName", I.QualName});
+  Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
   return Obj;
 }
 
@@ -194,8 +227,12 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
   Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
   
   Value ParamArr = Array();
-  for (const auto &P : I.Params) {
-    ParamArr.getAsArray()->emplace_back(extractValue(P.Type, ParentInfoDir));
+  for (const auto Val : llvm::enumerate(I.Params)) {
+    Value V = Object();
+    V.getAsObject()->insert({"Name", Val.value().Name});
+    V.getAsObject()->insert({"Type", Val.value().Type.Name});
+    V.getAsObject()->insert({"End",  Val.index() + 1 == I.Params.size()});
+    ParamArr.getAsArray()->emplace_back(V);
   }
   Obj.insert({"Params", ParamArr});
   
@@ -205,12 +242,13 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
       ArrDesc.getAsArray()->emplace_back(extractValue(Child));
     Obj.insert({"FunctionComments", ArrDesc});
   }
-  if (I.DefLoc) {
-    if (!CDCtx.RepositoryUrl)
-      Obj.insert({"Location", extractValue(*I.DefLoc)});
+  if (I.DefLoc.has_value()) {
+    Location L = *I.DefLoc;
+    if (CDCtx.RepositoryUrl.has_value())
+      Obj.insert({"Location", extractValue(L,
+                                           StringRef{*CDCtx.RepositoryUrl})});
     else
-      Obj.insert({"Location", extractValue(*I.DefLoc, 
-                                           StringRef{*CDCtx.RepositoryUrl})});  
+      Obj.insert({"Location", extractValue(L)});  
   }
   return Obj;
 }
@@ -225,7 +263,6 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
   Obj.insert({"EnumName", EnumType});
   Obj.insert({"HasComment", HasComment});
   Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
-  
   Value Arr = Array();
   for (const EnumValueInfo& M: I.Members) {
     Value EnumValue = Object();
@@ -252,75 +289,175 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
     Obj.insert({"EnumComments", ArrDesc});
   }
   
-  if (I.DefLoc) {
-    if (!CDCtx.RepositoryUrl)
-      Obj.insert({"Location", extractValue(*I.DefLoc)});
+  if (I.DefLoc.has_value()) {
+    Location L = *I.DefLoc;
+    if (CDCtx.RepositoryUrl.has_value())
+      Obj.insert({"Location", extractValue(L,
+                                           StringRef{*CDCtx.RepositoryUrl})});
     else
-      Obj.insert({"Location", extractValue(*I.DefLoc, 
-                                           StringRef{*CDCtx.RepositoryUrl})});  
+      Obj.insert({"Location", extractValue(L)});  
   }
   
   return Obj;
 }
 
-Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
-  Object NamespaceValue = Object();
-  std::string InfoTitle;
-  if (I.Name.str() == "")
-    InfoTitle = "Global Namespace";
-  else
-    InfoTitle = ("namespace " + I.Name).str();  
-  
-  StringRef BasePath = I.getRelativeFilePath("");
-  NamespaceValue.insert({"NamespaceTitle", InfoTitle});
-  NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
-  
-  if (!I.Description.empty()) {
-    Value ArrDesc = Array();
-    for (const CommentInfo& Child : I.Description) 
-      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
-    NamespaceValue.insert({"NamespaceComments", ArrDesc });
-  }
-
+void extractScopeChildren(const ScopeChildren &S, Object &Obj, 
+                          StringRef ParentInfoDir,
+                          const ClangDocContext &CDCtx) {
   Value ArrNamespace = Array();
-  for (const Reference& Child : I.Children.Namespaces)
-    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  for (const Reference& Child : S.Namespaces)
+    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
   
   if (!ArrNamespace.getAsArray()->empty())
-    NamespaceValue.insert({"Namespace", Object{{"Links", ArrNamespace}}});
+    Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
   
   Value ArrRecord = Array();
-  for (const Reference& Child : I.Children.Records)
-    ArrRecord.getAsArray()->emplace_back(extractValue(Child, BasePath));
+  for (const Reference& Child : S.Records)
+    ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
   
   if (!ArrRecord.getAsArray()->empty())
-    NamespaceValue.insert({"Record", Object{{"Links", ArrRecord}}});
+    Obj.insert({"Record", Object{{"Links", ArrRecord}}});
   
   Value ArrFunction = Array();
-  for (const FunctionInfo& Child : I.Children.Functions)
-    ArrFunction.getAsArray()->emplace_back(extractValue(Child, BasePath,
-                                                        CDCtx));
+  Value PublicFunction = Array();
+  Value ProtectedFunction = Array();
+  Value PrivateFunction = Array();
+  
+  for (const FunctionInfo& Child : S.Functions) {
+    Value F = extractValue(Child, ParentInfoDir, CDCtx);
+    AccessSpecifier Access = Child.Access;
+    if (Access == AccessSpecifier::AS_public)
+      PublicFunction.getAsArray()->emplace_back(F);
+    else if (Access == AccessSpecifier::AS_protected)
+      ProtectedFunction.getAsArray()->emplace_back(F);
+    else
+      ArrFunction.getAsArray()->emplace_back(F);
+  }  
   if (!ArrFunction.getAsArray()->empty())
-    NamespaceValue.insert({"Function", Object{{"Obj", ArrFunction}}});
+    Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
+  
+  if (!PublicFunction.getAsArray()->empty())
+    Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
+  
+  if (!ProtectedFunction.getAsArray()->empty())
+    Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
+  
   
   Value ArrEnum = Array();
-  for (const EnumInfo& Child : I.Children.Enums)
+  for (const EnumInfo& Child : S.Enums)
     ArrEnum.getAsArray()->emplace_back(extractValue(Child, CDCtx));
   
   if (!ArrEnum.getAsArray()->empty())
-    NamespaceValue.insert({"Enums", Object{{"Obj", ArrEnum }}});
+    Obj.insert({"Enums", Object{{"Obj", ArrEnum }}});
   
   Value ArrTypedefs = Array();
-  for (const TypedefInfo& Child : I.Children.Typedefs) 
+  for (const TypedefInfo& Child : S.Typedefs) 
     ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
   
   if (!ArrTypedefs.getAsArray()->empty())
-    NamespaceValue.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+    Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+}
+
+Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
+  Object NamespaceValue = Object();
+  std::string InfoTitle;
+  if (I.Name.str() == "")
+    InfoTitle = "Global Namespace";
+  else
+    InfoTitle = ("namespace " + I.Name).str();  
+  
+  StringRef BasePath = I.getRelativeFilePath("");
+  NamespaceValue.insert({"NamespaceTitle", InfoTitle});
+  NamespaceValue.insert({"NamespacePath", I.getRelativeFilePath("")});
   
+  if (!I.Description.empty()) {
+    Value ArrDesc = Array();
+    for (const CommentInfo& Child : I.Description) 
+      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+    NamespaceValue.insert({"NamespaceComments", ArrDesc });
+  }
+  extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
   return NamespaceValue;
 }
 
+Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
+  Object RecordValue = Object();
+  
+  if (!I.Description.empty()) {
+    Value ArrDesc = Array();
+    for (const CommentInfo& Child : I.Description) 
+      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+    RecordValue.insert({"RecordComments", ArrDesc });
+  }
+  RecordValue.insert({"Name", I.Name});
+  RecordValue.insert({"FullName", I.FullName});
+  RecordValue.insert({"RecordType", getTagType(I.TagType)});
+  
+  if (I.DefLoc.has_value()) {
+    Location L = *I.DefLoc;
+    if (CDCtx.RepositoryUrl.has_value())
+      RecordValue.insert({"Location", extractValue(L,
+                                                   StringRef{*CDCtx.RepositoryUrl})});
+    else
+      RecordValue.insert({"Location", extractValue(L)});  
+  }
+  
+  StringRef BasePath = I.getRelativeFilePath("");
+  extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
+  Value PublicMembers = Array();
+  Value ProtectedMembers = Array();
+  Value PrivateMembers = Array();
+  for (const MemberTypeInfo &Member : I.Members ) {
+    Value MemberValue = Object();
+    MemberValue.getAsObject()->insert({"Name", Member.Name});
+    MemberValue.getAsObject()->insert({"Type", Member.Type.Name});
+    if (!Member.Description.empty()) {
+      Value ArrDesc = Array();
+      for (const CommentInfo& Child : Member.Description) 
+        ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+      MemberValue.getAsObject()->insert({"MemberComments", ArrDesc });
+    }
+    
+    if (Member.Access == AccessSpecifier::AS_public)
+      PublicMembers.getAsArray()->emplace_back(MemberValue);
+    else if (Member.Access == AccessSpecifier::AS_protected)
+      ProtectedMembers.getAsArray()->emplace_back(MemberValue);
+    else if (Member.Access == AccessSpecifier::AS_private)
+      PrivateMembers.getAsArray()->emplace_back(MemberValue);
+  }
+  if (!PublicMembers.getAsArray()->empty())
+    RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
+  if (!ProtectedMembers.getAsArray()->empty())
+    RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
+  if (!PrivateMembers.getAsArray()->empty())
+    RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
+  
+  return RecordValue;
+}
 
+void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
+  V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
+  Value StylesheetArr = Array();
+  auto InfoPath = I->getRelativeFilePath("");
+  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+  for (const auto &FilePath  : CDCtx.UserStylesheets) {
+    SmallString<128> StylesheetPath = RelativePath;
+    llvm::sys::path::append(StylesheetPath,
+                            llvm::sys::path::filename(FilePath));
+    llvm::sys::path::native(StylesheetPath, llvm::sys::path::Style::posix);
+    StylesheetArr.getAsArray()->emplace_back(StylesheetPath);
+  }
+  V.getAsObject()->insert({"Stylesheets", StylesheetArr});
+  
+  Value ScriptArr = Array();
+  for (auto Script : CDCtx.JsScripts) {
+    SmallString<128> JsPath = RelativePath;
+    llvm::sys::path::append(JsPath, llvm::sys::path::filename(Script));
+    ScriptArr.getAsArray()->emplace_back(JsPath);
+  }
+  V.getAsObject()->insert({"Scripts", ScriptArr});
+}
+ 
 
 llvm::Error
 MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -328,17 +465,27 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   switch (I->IT) {
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
-    llvm::outs() << V << "\n";
+    setupTemplateValue(CDCtx, V, I);
     OS << NamespaceTemplate->render(V);
     break;
   }
-  case InfoType::IT_record:
+  case InfoType::IT_record: {
+    Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
+    setupTemplateValue(CDCtx, V, I);
+    // Serialize the JSON value to the output stream in a readable format.
+    llvm::outs() << "Visit: " << I->Name << "\n";
+    //llvm::outs() << llvm::formatv("{0:2}", V) << "\n";
+    llvm::outs() << RecordTemplate->render(V);
     break;
+  }  
   case InfoType::IT_enum:
+    llvm::outs() << "IT_enum\n";
     break;
   case InfoType::IT_function:
+    llvm::outs() << "IT_Function\n";
     break;
   case InfoType::IT_typedef:
+    llvm::outs() << "IT_typedef\n";
     break;
   case InfoType::IT_default:
     return createStringError(llvm::inconvertibleErrorCode(),
@@ -348,6 +495,17 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
 }
 
 llvm::Error MustacheHTMLGenerator::createResources(ClangDocContext &CDCtx) {
+  llvm::Error Err = llvm::Error::success();
+  for (const auto &FilePath : CDCtx.UserStylesheets) {
+    Err = copyFile(FilePath, CDCtx.OutDirectory);
+    if (Err)
+      return Err;
+  }
+  for (const auto &FilePath : CDCtx.JsScripts) {
+    Err = copyFile(FilePath, CDCtx.OutDirectory);
+    if (Err)
+      return Err;
+  }
   return llvm::Error::success();
 }
 
@@ -355,7 +513,7 @@ const char *MustacheHTMLGenerator::Format = "mhtml";
 
 
 static GeneratorRegistry::Add<MustacheHTMLGenerator> MHTML(MustacheHTMLGenerator::Format,
-                                                  "Generator for mustache HTML output.");
+                                                           "Generator for mustache HTML output.");
 
 // This anchor is used to force the linker to link in the generated object
 // file and thus register the generator.
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 2f07baa7ca0a1a..a885a36cb4a3dc 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -1,3 +1,4 @@
+/* css for clang-doc mustache backend */
 @import "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap";
 
 *,*::before *::after {
@@ -18,6 +19,7 @@ video {
     display:block;
     max-width:100%
 }
+
 * {
     --brand-light:#ce6300;
     --text1-light:#000000;
@@ -30,6 +32,7 @@ video {
     --surface1-dark:#161212;
     --surface2-dark:#272424
 }
+
 :root {
     color-scheme:light;
     --brand:var(--brand-light);
@@ -40,6 +43,7 @@ video {
     --surface1:var(--surface1-light);
     --surface2:var(--surface2-light)
 }
+
 @media(prefers-color-scheme:dark) {
     :root {
         color-scheme:dark;
@@ -52,6 +56,7 @@ video {
         --surface2:var(--surface2-dark)
     }
 }
+
 [color-scheme=light] {
     color-scheme:light;
     --brand:var(--brand-light);
@@ -62,6 +67,7 @@ video {
     --surface1:var(--surface1-light);
     --surface2:var(--surface2-light)
 }
+
 [color-scheme=dark] {
     color-scheme:dark;
     --brand:var(--brand-dark);
@@ -72,6 +78,7 @@ video {
     --surface1:var(--surface1-dark);
     --surface2:var(--surface2-dark)
 }
+
 html {
     background-color:var(--surface1)
 }
@@ -200,6 +207,7 @@ body, html {
 .navbar__close:hover {
     color:var(--text1)
 }
+
 @media(min-width:769px) {
     .navbar__close {
         display:none
@@ -212,22 +220,27 @@ body, html {
     margin:0;
     padding:0
 }
+
 @media(max-width:768px) {
     .navbar__links {
         flex-direction:column
     }
 }
+
 .navbar__item {
     list-style-type:none
 }
+
 .navbar__link {
     color:var(--text2);
     text-decoration:none;
     padding:.5rem
 }
+
 .navbar__link:hover {
     color:var(--text1)
 }
+
 .navbar__theme-toggle-button {
     background:0 0;
     color:var(--text2);
@@ -237,6 +250,7 @@ body, html {
     width:2.5rem;
     height:2.5rem
 }
+
 .navbar__theme-toggle-button:hover {
     color:var(--text1)
 }
@@ -248,42 +262,51 @@ body, html {
     align-items:center;
     gap:2rem
 }
+
 .hero__title {
     font-size:2.5rem;
     margin-bottom:.5rem
 }
+
 .hero__title-large {
     font-size:3rem
 }
+
 @media(max-width:768px) {
     .hero__title-large {
         font-size:2.5rem
     }
 }
+
 @media(max-width:480px) {
     .hero__title-large {
         font-size:2rem
     }
 }
+
 @media(max-width:768px) {
     .hero__title {
         font-size:2rem
     }
 }
+
 @media(max-width:480px) {
     .hero__title {
         font-size:1.75rem
     }
 }
+
 .hero__subtitle {
     font-size:1.25rem;
     font-weight:500
 }
+
 @media(max-width:768px) {
     .hero__subtitle {
         font-size:1rem
     }
 }
+
 @media(max-width:480px) {
     .hero__subtitle {
         font-size:.875rem
@@ -299,69 +322,42 @@ body, html {
     padding:1rem 2rem
 }
 
-
 @media(max-width:768px) {
     .section-container {
         padding:1rem
     }
 }
+
 .section-container h2 {
     font-size:1.5rem;
     margin-bottom:1rem;
     color:var(--brand);
     border-bottom: 1px solid var(--text2);
 }
+
 @media(max-width:768px) {
     .section-container h2 {
         font-size:1.25rem
     }
 }
+
 .section-container p {
     font-size:1rem;
     line-height:1.5
 }
+
 @media(max-width:768px) {
     .section-container p {
         font-size:.875rem
     }
 }
+
 .home__row {
     display:grid;
     grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
     gap:2rem
 }
 
-
-.links-wrapper {
-    display:grid;
-    gap:1rem;
-    grid-template-columns:1fr 1fr 1fr 1fr
-}
- at media(max-width:768px) {
-    .links-wrapper {
-        grid-template-columns:1fr 1fr
-    }
-}
- at media(max-width:480px) {
-    .links-wrapper {
-        grid-template-columns:1fr
-    }
-}
-.links-wrapper .link {
-    display:flex;
-    flex-direction:column;
-    align-items:center;
-    padding:1rem;
-    border:1px solid var(--text1);
-    border-radius:.25rem
-}
-.links-wrapper .link__icon {
-    font-size:2rem
-}
-.links-wrapper .link__title {
-    font-size:1rem
-}
-
 .table-wrapper {
     display:flex;
     flex-direction:column;
@@ -375,7 +371,6 @@ body, html {
     text-align: left;
 }
 
-
 .block-command-command {
     font-weight: bold;
 }
@@ -442,7 +437,6 @@ body, html {
     color: var(--brand)
 }
 
-
 /* Content */
 .content {
     background-color: var(--text1-inverse);
@@ -452,6 +446,7 @@ body, html {
     width: calc(100% - 250px);
     height: 100vh;
 }
+
 .sidebar-item {
     color: var(--text1);
 }
diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
index 4061fd026886e4..21cbaa7ec5cf3f 100644
--- a/clang-tools-extra/clang-doc/assets/namespace-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
@@ -35,47 +35,7 @@
                         Duis aute irure dolor in reprehenderit in voluptate velit esse 
                         cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat 
                         cupidatat non proident, sunt in culpa qui officia deserunt mollit 
-                        anim id est laborum.
-                        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
-                        sed do eiusmod tempor incididunt ut labore et dolore magna
-                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
-                        laboris nisi ut aliquip ex ea commodo consequat.
-                        Duis aute irure dolor in reprehenderit in voluptate velit esse
-                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
-                        cupidatat non proident, sunt in culpa qui officia deserunt mollit
-                        anim id est laborum.
-                        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
-                        sed do eiusmod tempor incididunt ut labore et dolore magna
-                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
-                        laboris nisi ut aliquip ex ea commodo consequat.
-                        Duis aute irure dolor in reprehenderit in voluptate velit esse
-                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
-                        cupidatat non proident, sunt in culpa qui officia deserunt mollit
-                        anim id est laborum.
-                        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
-                        sed do eiusmod tempor incididunt ut labore et dolore magna
-                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
-                        laboris nisi ut aliquip ex ea commodo consequat.
-                        Duis aute irure dolor in reprehenderit in voluptate velit esse
-                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
-                        cupidatat non proident, sunt in culpa qui officia deserunt mollit
-                        anim id est laborum.
-                        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
-                        sed do eiusmod tempor incididunt ut labore et dolore magna
-                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
-                        laboris nisi ut aliquip ex ea commodo consequat.
-                        Duis aute irure dolor in reprehenderit in voluptate velit esse
-                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
-                        cupidatat non proident, sunt in culpa qui officia deserunt mollit
-                        anim id est laborum.
-                        Lorem ipsum dolor sit amet, consectetur adipiscing elit,
-                        sed do eiusmod tempor incididunt ut labore et dolore magna
-                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
-                        laboris nisi ut aliquip ex ea commodo consequat.
-                        Duis aute irure dolor in reprehenderit in voluptate velit esse
-                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
-                        cupidatat non proident, sunt in culpa qui officia deserunt mollit
-                        anim id est laborum.
+                        anim id est laborum
                 </div>
                 <div class="resizer" id="resizer"></div>
                 <div class="content">
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index 8eb067dbe6de87..eccbc99a7ecc4b 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -21,7 +21,13 @@ target_link_libraries(clang-doc
 
 set(assets
   index.js
-  template.mustache
+  mustache-index.js
+  class-template.mustache
+  comments-template.mustache
+  enum-template.mustache      
+  function-template.mustache
+  namespace-template.mustache      
+  clang-doc-mustache.css
   clang-doc-default-stylesheet.css
 )
 
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index eee8dbf093cc53..256e29c3c9f894 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -163,6 +163,15 @@ llvm::Error getAssetFiles(clang::doc::ClangDocContext &CDCtx) {
   return llvm::Error::success();
 }
 
+llvm::SmallString<128> appendPathNative(llvm::SmallString<128> Path, 
+                                    llvm::StringRef Asset) {
+  llvm::SmallString<128> Default;
+  llvm::sys::path::native(Path, Default);
+  llvm::sys::path::append(Default, Asset);
+  return Default;
+}
+
+
 llvm::Error getDefaultAssetFiles(const char *Argv0,
                                  clang::doc::ClangDocContext &CDCtx) {
   void *MainAddr = (void *)(intptr_t)getExecutablePath;
@@ -173,13 +182,10 @@ llvm::Error getDefaultAssetFiles(const char *Argv0,
   llvm::SmallString<128> AssetsPath;
   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
-  llvm::SmallString<128> DefaultStylesheet;
-  llvm::sys::path::native(AssetsPath, DefaultStylesheet);
-  llvm::sys::path::append(DefaultStylesheet,
-                          "clang-doc-default-stylesheet.css");
-  llvm::SmallString<128> IndexJS;
-  llvm::sys::path::native(AssetsPath, IndexJS);
-  llvm::sys::path::append(IndexJS, "index.js");
+  llvm::SmallString<128> DefaultStylesheet =
+      appendPathNative(AssetsPath, "clang-doc-default-stylesheet.css");
+  llvm::SmallString<128> IndexJS =
+      appendPathNative(AssetsPath, "index.js");
 
   if (!llvm::sys::fs::is_regular_file(IndexJS))
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
@@ -210,6 +216,7 @@ llvm::Error getHtmlAssetFiles(const char *Argv0,
   return getDefaultAssetFiles(Argv0, CDCtx);
 }
 
+
 llvm::Error getMustacheHtmlFiles(const char *Argv0,
                                  clang::doc::ClangDocContext &CDCtx) {
   if (!UserAssetPath.empty() &&
@@ -228,10 +235,34 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
   AssetsPath = llvm::sys::path::parent_path(NativeClangDocPath);
   llvm::sys::path::append(AssetsPath, "..", "share", "clang-doc");
   
-  llvm::SmallString<128> MustacheTemplate;
-  llvm::sys::path::native(AssetsPath, MustacheTemplate);
-  llvm::sys::path::append(MustacheTemplate, "template.mustache");
-  CDCtx.MustacheTemplates.insert({"template", MustacheTemplate.c_str()});
+  llvm::SmallString<128> DefaultStylesheet
+      = appendPathNative(AssetsPath, "clang-doc-mustache.css");
+  llvm::SmallString<128> NamespaceTemplate 
+      = appendPathNative(AssetsPath, "namespace-template.mustache");
+  llvm::SmallString<128> ClassTemplate
+      = appendPathNative(AssetsPath, "class-template.mustache");
+  llvm::SmallString<128> EnumTemplate
+      = appendPathNative(AssetsPath, "enum-template.mustache");
+  llvm::SmallString<128> FunctionTemplate
+      = appendPathNative(AssetsPath, "function-template.mustache");
+  llvm::SmallString<128> CommentTemplate
+      = appendPathNative(AssetsPath, "comments-template.mustache");
+  llvm::SmallString<128> IndexJS
+      = appendPathNative(AssetsPath, "mustache-index.js");
+  
+  CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
+  CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
+                               std::string(DefaultStylesheet));
+  CDCtx.MustacheTemplates.insert({"namespace-template", 
+                                  NamespaceTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"class-template",
+                                  ClassTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"enum-template",
+                                  EnumTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"function-template",
+                                  FunctionTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"comments-template", 
+                                  CommentTemplate.c_str()});
   
   return llvm::Error::success();
 }

>From 42d29951ddd29259be61492aaa9d5afa82d08af3 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Thu, 31 Oct 2024 05:07:02 -0400
Subject: [PATCH 60/62] clang-doc improve mustache output

---
 clang-tools-extra/clang-doc/BitcodeReader.cpp |  30 ++-
 clang-tools-extra/clang-doc/BitcodeWriter.cpp |  27 ++-
 clang-tools-extra/clang-doc/BitcodeWriter.h   |   4 +
 .../clang-doc/HTMLMustacheGenerator.cpp       | 185 +++++++++++++--
 .../clang-doc/Representation.cpp              |   2 +
 clang-tools-extra/clang-doc/Representation.h  |  16 +-
 clang-tools-extra/clang-doc/Serialize.cpp     | 216 ++++++++++++++++--
 .../clang-doc/assets/clang-doc-mustache.css   |  91 +++++---
 .../clang-doc/assets/class-template.mustache  |  67 ++----
 .../clang-doc/assets/enum-template.mustache   |   4 +-
 .../assets/function-template.mustache         |   2 +-
 .../clang-doc/assets/mustache-index.js        | 195 +++++++++++++++-
 .../assets/namespace-template.mustache        |  75 ++++--
 .../clang-doc/tool/ClangDocMain.cpp           |  10 +
 14 files changed, 770 insertions(+), 154 deletions(-)

diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 1f2fb0a8b2b855..4d639937a59152 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -169,6 +169,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
     return decodeRecord(R, I->USR, Blob);
   case RECORD_NAME:
     return decodeRecord(R, I->Name, Blob);
+  case RECORD_FULLNAME: 
+    return decodeRecord(R, I->FullName, Blob);
   case RECORD_PATH:
     return decodeRecord(R, I->Path, Blob);
   case RECORD_DEFLOCATION:
@@ -268,6 +270,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
     return decodeRecord(R, I->Name, Blob);
   case FUNCTION_DEFLOCATION:
     return decodeRecord(R, I->DefLoc, Blob);
+  case FUNCTION_PROTOTYPE:
+    return decodeRecord(R, I->ProtoType, Blob);
   case FUNCTION_LOCATION:
     return decodeRecord(R, I->Loc, Blob);
   case FUNCTION_ACCESS:
@@ -282,7 +286,15 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
 
 llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
                         TypeInfo *I) {
-  return llvm::Error::success();
+  switch (ID) {
+  case TYPE_IS_BUILTIN:
+    return decodeRecord(R, I->IsBuiltIn, Blob);
+  case TYPE_IS_TEMPLATE:
+    return decodeRecord(R, I->IsTemplate, Blob);
+  default:
+    return llvm::createStringError(llvm::inconvertibleErrorCode(),
+                                   "invalid field for TypeInfo");
+  }
 }
 
 llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
@@ -468,6 +480,22 @@ template <> llvm::Error addTypeInfo(TypedefInfo *I, TypeInfo &&T) {
   return llvm::Error::success();
 }
 
+template <> llvm::Error addTypeInfo(FieldTypeInfo *I, TypeInfo &&T) {
+  I->Type = std::move(T.Type);
+  I->IsTemplate = T.IsTemplate;
+  I->IsBuiltIn = T.IsBuiltIn;
+  return llvm::Error::success();
+}
+
+template <> llvm::Error addTypeInfo(MemberTypeInfo *I, FieldTypeInfo &&T) {
+  I->Type = std::move(T.Type);
+  I->IsTemplate = T.IsTemplate;
+  I->IsBuiltIn = T.IsBuiltIn;
+  I->Name = std::move(T.Name);
+  I->DefaultValue = std::move(T.DefaultValue);
+  return llvm::Error::success();
+}
+
 template <typename T> llvm::Error addReference(T I, Reference &&R, FieldId F) {
   return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                  "invalid type cannot contain Reference");
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 06f30f76e33d8c..4b8f67302ce9d9 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -154,6 +154,8 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
           {COMMENT_ARG, {"Arg", &StringAbbrev}},
           {FIELD_TYPE_NAME, {"Name", &StringAbbrev}},
           {FIELD_DEFAULT_VALUE, {"DefaultValue", &StringAbbrev}},
+          {TYPE_IS_BUILTIN, {"IsBuiltIn", &BoolAbbrev}},
+          {TYPE_IS_TEMPLATE, {"IsTemplate", &BoolAbbrev}},
           {MEMBER_TYPE_NAME, {"Name", &StringAbbrev}},
           {MEMBER_TYPE_ACCESS, {"Access", &IntAbbrev}},
           {NAMESPACE_USR, {"USR", &SymbolIDAbbrev}},
@@ -169,6 +171,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
           {ENUM_VALUE_EXPR, {"Expr", &StringAbbrev}},
           {RECORD_USR, {"USR", &SymbolIDAbbrev}},
           {RECORD_NAME, {"Name", &StringAbbrev}},
+          {RECORD_FULLNAME, {"FullName", &StringAbbrev}},
           {RECORD_PATH, {"Path", &StringAbbrev}},
           {RECORD_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
           {RECORD_LOCATION, {"Location", &LocationAbbrev}},
@@ -187,6 +190,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
           {FUNCTION_LOCATION, {"Location", &LocationAbbrev}},
           {FUNCTION_ACCESS, {"Access", &IntAbbrev}},
           {FUNCTION_IS_METHOD, {"IsMethod", &BoolAbbrev}},
+          {FUNCTION_PROTOTYPE, {"ProtoType", &StringAbbrev}},
           {REFERENCE_USR, {"USR", &SymbolIDAbbrev}},
           {REFERENCE_NAME, {"Name", &StringAbbrev}},
           {REFERENCE_QUAL_NAME, {"QualName", &StringAbbrev}},
@@ -218,7 +222,7 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
           COMMENT_PARAMNAME, COMMENT_CLOSENAME, COMMENT_SELFCLOSING,
           COMMENT_EXPLICIT, COMMENT_ATTRKEY, COMMENT_ATTRVAL, COMMENT_ARG}},
         // Type Block
-        {BI_TYPE_BLOCK_ID, {}},
+        {BI_TYPE_BLOCK_ID, {TYPE_IS_BUILTIN, TYPE_IS_TEMPLATE}},
         // FieldType Block
         {BI_FIELD_TYPE_BLOCK_ID, {FIELD_TYPE_NAME, FIELD_DEFAULT_VALUE}},
         // MemberType Block
@@ -237,7 +241,7 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
          {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}},
         // Record Block
         {BI_RECORD_BLOCK_ID,
-         {RECORD_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION,
+         {RECORD_USR, RECORD_NAME, RECORD_FULLNAME, RECORD_PATH, RECORD_DEFLOCATION,
           RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF}},
         // BaseRecord Block
         {BI_BASE_RECORD_BLOCK_ID,
@@ -246,11 +250,11 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
           BASE_RECORD_IS_PARENT}},
         // Function Block
         {BI_FUNCTION_BLOCK_ID,
-         {FUNCTION_USR, FUNCTION_NAME, FUNCTION_DEFLOCATION, FUNCTION_LOCATION,
-          FUNCTION_ACCESS, FUNCTION_IS_METHOD}},
+         {FUNCTION_USR, FUNCTION_PROTOTYPE, FUNCTION_NAME, FUNCTION_DEFLOCATION, 
+          FUNCTION_LOCATION, FUNCTION_ACCESS, FUNCTION_IS_METHOD}},
         // Reference Block
         {BI_REFERENCE_BLOCK_ID,
-         {REFERENCE_USR, REFERENCE_NAME, REFERENCE_QUAL_NAME, REFERENCE_TYPE,
+         {REFERENCE_USR, REFERENCE_NAME, REFERENCE_QUAL_NAME, REFERENCE_TYPE, 
           REFERENCE_PATH, REFERENCE_FIELD}},
         // Template Blocks.
         {BI_TEMPLATE_BLOCK_ID, {}},
@@ -425,6 +429,11 @@ void ClangDocBitcodeWriter::emitBlockInfo(BlockId BID,
 void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) {
   if (R.USR == EmptySID && R.Name.empty())
     return;
+  
+  if (R.Name == "const Shape &") {
+    llvm::outs() << "const Shape & USR: " << 
+                    llvm::toHex(llvm::toStringRef(R.USR)) << "\n";
+  }
   StreamSubBlockGuard Block(Stream, BI_REFERENCE_BLOCK_ID);
   emitRecord(R.USR, REFERENCE_USR);
   emitRecord(R.Name, REFERENCE_NAME);
@@ -437,6 +446,8 @@ void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) {
 void ClangDocBitcodeWriter::emitBlock(const TypeInfo &T) {
   StreamSubBlockGuard Block(Stream, BI_TYPE_BLOCK_ID);
   emitBlock(T.Type, FieldId::F_type);
+  emitRecord(T.IsBuiltIn, TYPE_IS_BUILTIN);
+  emitRecord(T.IsTemplate, TYPE_IS_TEMPLATE);
 }
 
 void ClangDocBitcodeWriter::emitBlock(const TypedefInfo &T) {
@@ -455,14 +466,14 @@ void ClangDocBitcodeWriter::emitBlock(const TypedefInfo &T) {
 
 void ClangDocBitcodeWriter::emitBlock(const FieldTypeInfo &T) {
   StreamSubBlockGuard Block(Stream, BI_FIELD_TYPE_BLOCK_ID);
-  emitBlock(T.Type, FieldId::F_type);
+  emitBlock(static_cast<TypeInfo>(T));
   emitRecord(T.Name, FIELD_TYPE_NAME);
   emitRecord(T.DefaultValue, FIELD_DEFAULT_VALUE);
 }
 
 void ClangDocBitcodeWriter::emitBlock(const MemberTypeInfo &T) {
   StreamSubBlockGuard Block(Stream, BI_MEMBER_TYPE_BLOCK_ID);
-  emitBlock(T.Type, FieldId::F_type);
+  emitBlock(static_cast<FieldTypeInfo>(T));
   emitRecord(T.Name, MEMBER_TYPE_NAME);
   emitRecord(T.Access, MEMBER_TYPE_ACCESS);
   for (const auto &CI : T.Description)
@@ -544,6 +555,7 @@ void ClangDocBitcodeWriter::emitBlock(const RecordInfo &I) {
   StreamSubBlockGuard Block(Stream, BI_RECORD_BLOCK_ID);
   emitRecord(I.USR, RECORD_USR);
   emitRecord(I.Name, RECORD_NAME);
+  emitRecord(I.FullName, RECORD_FULLNAME);
   emitRecord(I.Path, RECORD_PATH);
   for (const auto &N : I.Namespace)
     emitBlock(N, FieldId::F_namespace);
@@ -593,6 +605,7 @@ void ClangDocBitcodeWriter::emitBlock(const BaseRecordInfo &I) {
 void ClangDocBitcodeWriter::emitBlock(const FunctionInfo &I) {
   StreamSubBlockGuard Block(Stream, BI_FUNCTION_BLOCK_ID);
   emitRecord(I.USR, FUNCTION_USR);
+  emitRecord(I.ProtoType, FUNCTION_PROTOTYPE);
   emitRecord(I.Name, FUNCTION_NAME);
   for (const auto &N : I.Namespace)
     emitBlock(N, FieldId::F_namespace);
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index 9a572e40e352f1..117527cf89c969 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -77,6 +77,7 @@ enum RecordId {
   VERSION = 1,
   FUNCTION_USR,
   FUNCTION_NAME,
+  FUNCTION_PROTOTYPE,
   FUNCTION_DEFLOCATION,
   FUNCTION_LOCATION,
   FUNCTION_ACCESS,
@@ -94,6 +95,8 @@ enum RecordId {
   COMMENT_ARG,
   FIELD_TYPE_NAME,
   FIELD_DEFAULT_VALUE,
+  TYPE_IS_BUILTIN,
+  TYPE_IS_TEMPLATE,
   MEMBER_TYPE_NAME,
   MEMBER_TYPE_ACCESS,
   NAMESPACE_USR,
@@ -109,6 +112,7 @@ enum RecordId {
   ENUM_VALUE_EXPR,
   RECORD_USR,
   RECORD_NAME,
+  RECORD_FULLNAME,
   RECORD_PATH,
   RECORD_DEFLOCATION,
   RECORD_LOCATION,
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 1f96202f1f6de6..d7079497c25096 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -10,6 +10,7 @@
 #include "FileHelpersClangDoc.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Mustache.h"
+#include "llvm/ADT/SmallSet.h"
 
 using namespace llvm;
 using namespace llvm::json;
@@ -18,6 +19,44 @@ using namespace llvm::mustache;
 namespace clang {
 namespace doc {
 
+static StringMap<llvm::SmallString<16>> Index;
+
+void generateIndex(Info *I) {
+  switch (I->IT) {
+  case InfoType::IT_namespace: {
+    const NamespaceInfo& N = *static_cast<clang::doc::NamespaceInfo *>(I);
+    SmallString<16> Name = N.getFileBaseName();
+    std::string HexId = llvm::toHex(llvm::toStringRef(N.USR));
+    Index[HexId] = HexId;
+    for (const EnumInfo& E : N.Children.Enums)
+      Index[llvm::toHex(llvm::toStringRef(E.USR))] = HexId;
+    for (const TypedefInfo& T : N.Children.Typedefs)
+      Index[llvm::toHex(llvm::toStringRef(T.USR))] = HexId;
+    break;
+  }
+  case InfoType::IT_record: {
+    const RecordInfo& R = *static_cast<clang::doc::RecordInfo *>(I);
+    SmallString<16> Name = R.getFileBaseName();
+    std::string HexId = llvm::toHex(llvm::toStringRef(R.USR));
+    Index[HexId] = HexId;
+    for (const EnumInfo& E : R.Children.Enums) {
+      Index[llvm::toHex(llvm::toStringRef(E.USR))] = HexId;
+    }
+    for (const TypedefInfo& T : R.Children.Typedefs) {
+      Index[llvm::toHex(llvm::toStringRef(T.USR))] = HexId;
+    }
+    break;
+  }  
+  case InfoType::IT_enum:
+    break;
+  case InfoType::IT_function:
+    break;
+  case InfoType::IT_typedef:
+    break;
+  case InfoType::IT_default:
+    return;
+  }
+}
 
 class MustacheHTMLGenerator : public Generator {
 public:
@@ -119,19 +158,11 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
   llvm::StringMap<std::vector<doc::Info *>> FileToInfos;
   for (const auto &Group : Infos) {
     doc::Info *Info = Group.getValue().get();
-    
     llvm::SmallString<128> Path;
     llvm::sys::path::native(RootDir, Path);
-    llvm::sys::path::append(Path, Info->getRelativeFilePath(""));
-    if (!CreatedDirs.contains(Path)) {
-      if (std::error_code Err = llvm::sys::fs::create_directories(Path);
-          Err != std::error_code())
-        return llvm::createStringError(Err, "Failed to create directory '%s'.",
-                                       Path.c_str());
-      CreatedDirs.insert(Path);
-    }
-
-    llvm::sys::path::append(Path, Info->getFileBaseName() + ".html");
+    std::string InfoFile = llvm::toHex(llvm::toStringRef(Info->USR)) + ".html";
+    llvm::sys::path::append(Path, InfoFile);
+    generateIndex(Info);
     FileToInfos[Path].push_back(Info);
   }
   
@@ -157,9 +188,9 @@ Value extractValue(const Location &L,
   Obj.insert({"LineNumber", L.LineNumber});
   Obj.insert({"Filename", L.Filename});
   
-  if (!L.IsFileInRootDir || !RepositoryUrl) {
+  if (!L.IsFileInRootDir || !RepositoryUrl)
     return Obj;
-  }
+  
   SmallString<128> FileURL(*RepositoryUrl);
   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
   FileURL += "#" + std::to_string(L.LineNumber);
@@ -218,14 +249,124 @@ Value extractValue(const CommentInfo &I) {
   return Obj;
 }
 
+
+// Function to replace a substring within a SmallString with another SmallString
+void replaceSubstring(llvm::SmallString<256> &Input, 
+                      const llvm::SmallString<16> &From, 
+                      const llvm::SmallString<16> &To) {
+  llvm::StringRef InputStr = Input;
+  llvm::SmallString<16> FromStr;
+  raw_svector_ostream Stream(FromStr);
+  printHTMLEscaped(From, Stream);
+  llvm::StringRef ToStr = To;
+  
+  // Find the first occurrence of 'from' in 'input'
+  size_t Pos = InputStr.find(FromStr);
+  while (Pos != llvm::StringRef::npos) {
+    // Create a new SmallString to hold the modified string
+    llvm::SmallString<256> NewString;
+
+    // Append the part before the found substring
+    NewString.append(Input.begin(), Input.begin() + Pos);
+        
+    // Append the replacement substring
+    NewString.append(ToStr.begin(), ToStr.end());
+        
+    // Append the part after the found substring
+    NewString.append(Input.begin() + Pos + FromStr.size(), Input.end());
+
+    // Update 'input' with the modified string
+    Input = NewString;
+
+    // Update the input string and find the next occurrence
+    InputStr = Input;
+    Pos = InputStr.find(FromStr, Pos + ToStr.size());
+  }
+}
+
+SmallString<64> extractLink(const Reference& R) {
+  std::string HexId = llvm::toHex(llvm::toStringRef(R.USR));
+  SmallString<16> Name = Index[HexId];
+  SmallString<64> Link({Name, ".html"});
+  return Link;
+}
+
+Value extractRecordPrototype(const SmallString<16> Prototype,
+                             llvm::SmallVector<Reference, 4> Parents,
+                             llvm::SmallVector<Reference, 4> VirtualParents)
+{
+  SmallString<256> Result;
+  raw_svector_ostream Stream(Result);
+  printHTMLEscaped(Prototype, Stream);
+  
+  for (Reference& R : Parents) 
+  {
+    SmallString<16> ParentLink({
+        "<a style=\"color: #08637D;\" class=\"code-highlight\"",
+        " href=\"",extractLink(R),"\">", 
+        R.Name,
+        "</a>"
+    });
+    replaceSubstring(Result, R.Name, ParentLink);
+  }
+  
+  for (Reference& V : VirtualParents) 
+  {
+    SmallString<16> ParentLink({
+        "<a style=\"color: #08637D;\" class=\"code-highlight\"",
+        " href=\"",extractLink(V),"\">", 
+        V.Name,
+        "</a>"
+    });
+    replaceSubstring(Result, V.Name, ParentLink);
+  }
+  
+  return Result;
+}
+
+// extract the prototype and adds anchor tags to it
+Value extractFunctionPrototype(const SmallString<256>& Prototype,
+                       const llvm::SmallVector<FieldTypeInfo, 4>& Params,
+                       const TypeInfo& ReturnType) {
+  SmallString<256> Result;
+  raw_svector_ostream Stream(Result);
+  printHTMLEscaped(Prototype, Stream);
+  
+  if (!ReturnType.IsBuiltIn && !ReturnType.IsTemplate) {
+    replaceSubstring(Result, ReturnType.Type.Name, {
+       "<a style=\"color: #08637D;\" class=\"code-highlight\"",
+       " href=\"",extractLink(ReturnType.Type),"\">", 
+       ReturnType.Type.Name, 
+      "</a>"
+    });
+  }
+  SmallSet<StringRef, 16> ParamNames;
+  for (const FieldTypeInfo& F : Params) 
+  {
+    if (ParamNames.count(F.Type.Name) > 0 || F.IsBuiltIn || F.IsTemplate)
+      continue;
+    
+    ParamNames.insert(F.Type.Name);
+    SmallString<16> ParamLink({
+        "<a style=\"color: #08637D;\" class=\"code-highlight\"",
+        " href=\"",extractLink(F.Type),"\">", 
+        F.Type.Name,
+        "</a>"
+    });
+    replaceSubstring(Result, F.Type.Name, ParamLink);
+  }
+  return Result;
+}
+
 Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
                    const ClangDocContext &CDCtx) {
   Object Obj = Object();
   Obj.insert({"Name", I.Name});
+  Obj.insert({"FunctionPrototype",
+              extractFunctionPrototype(I.ProtoType, I.Params, I.ReturnType)});
   Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
   Obj.insert({"Access", getAccessSpelling(I.Access).str()});
   Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
-  
   Value ParamArr = Array();
   for (const auto Val : llvm::enumerate(I.Params)) {
     Value V = Object();
@@ -390,14 +531,17 @@ Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
     RecordValue.insert({"RecordComments", ArrDesc });
   }
   RecordValue.insert({"Name", I.Name});
-  RecordValue.insert({"FullName", I.FullName});
+  RecordValue.insert({"FullName", extractRecordPrototype(I.FullName, 
+                                                         I.Parents, 
+                                                         I.VirtualParents)});
+  
   RecordValue.insert({"RecordType", getTagType(I.TagType)});
   
   if (I.DefLoc.has_value()) {
     Location L = *I.DefLoc;
     if (CDCtx.RepositoryUrl.has_value())
       RecordValue.insert({"Location", extractValue(L,
-                                                   StringRef{*CDCtx.RepositoryUrl})});
+                          StringRef{*CDCtx.RepositoryUrl})});
     else
       RecordValue.insert({"Location", extractValue(L)});  
   }
@@ -440,7 +584,7 @@ void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
   Value StylesheetArr = Array();
   auto InfoPath = I->getRelativeFilePath("");
   SmallString<128> RelativePath = computeRelativePath("", InfoPath);
-  for (const auto &FilePath  : CDCtx.UserStylesheets) {
+  for (const auto &FilePath : CDCtx.UserStylesheets) {
     SmallString<128> StylesheetPath = RelativePath;
     llvm::sys::path::append(StylesheetPath,
                             llvm::sys::path::filename(FilePath));
@@ -457,7 +601,6 @@ void setupTemplateValue(const ClangDocContext &CDCtx, Value &V, Info *I) {
   }
   V.getAsObject()->insert({"Scripts", ScriptArr});
 }
- 
 
 llvm::Error
 MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
@@ -466,16 +609,14 @@ MustacheHTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS,
   case InfoType::IT_namespace: {
     Value V = extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
     setupTemplateValue(CDCtx, V, I);
-    OS << NamespaceTemplate->render(V);
+    NamespaceTemplate->render(V, OS);
     break;
   }
   case InfoType::IT_record: {
     Value V = extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
     setupTemplateValue(CDCtx, V, I);
     // Serialize the JSON value to the output stream in a readable format.
-    llvm::outs() << "Visit: " << I->Name << "\n";
-    //llvm::outs() << llvm::formatv("{0:2}", V) << "\n";
-    llvm::outs() << RecordTemplate->render(V);
+    RecordTemplate->render(V, OS);
     break;
   }  
   case InfoType::IT_enum:
diff --git a/clang-tools-extra/clang-doc/Representation.cpp b/clang-tools-extra/clang-doc/Representation.cpp
index 4da93b24c131fd..cb42709f467a36 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -239,6 +239,8 @@ RecordInfo::RecordInfo(SymbolID USR, StringRef Name, StringRef Path)
 
 void RecordInfo::merge(RecordInfo &&Other) {
   assert(mergeable(Other));
+  if (FullName.empty())
+    FullName = std::move(Other.FullName);
   if (!llvm::to_underlying(TagType))
     TagType = Other.TagType;
   IsTypeDef = IsTypeDef || Other.IsTypeDef;
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 3175b8209e3be3..edd9143f444fed 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -113,7 +113,8 @@ struct Reference {
   llvm::SmallString<16> getFileBaseName() const;
 
   SymbolID USR = SymbolID(); // Unique identifier for referenced decl
-
+  
+  
   // Name of type (possibly unresolved). Not including namespaces or template
   // parameters (so for a std::vector<int> this would be "vector"). See also
   // QualName.
@@ -152,7 +153,9 @@ struct ScopeChildren {
 
 // A base struct for TypeInfos
 struct TypeInfo {
+  
   TypeInfo() = default;
+  
   TypeInfo(const Reference &R) : Type(R) {}
 
   // Convenience constructor for when there is no symbol ID or info type
@@ -161,8 +164,11 @@ struct TypeInfo {
       : Type(SymbolID(), Name, InfoType::IT_default, Name, Path) {}
 
   bool operator==(const TypeInfo &Other) const { return Type == Other.Type; }
-
+  
   Reference Type; // Referenced type in this info.
+  
+  bool IsTemplate = false;
+  bool IsBuiltIn = false;
 };
 
 // Represents one template parameter.
@@ -209,6 +215,7 @@ struct FieldTypeInfo : public TypeInfo {
     return std::tie(Type, Name, DefaultValue) ==
            std::tie(Other.Type, Other.Name, Other.DefaultValue);
   }
+  
 
   SmallString<16> Name; // Name associated with this info.
 
@@ -359,6 +366,9 @@ struct FunctionInfo : public SymbolInfo {
   // Full qualified name of this function, including namespaces and template
   // specializations.
   SmallString<16> FullName;
+  
+  // Function Prototype
+  SmallString<256> ProtoType;
 
   // When present, this function is a template or specialization.
   std::optional<TemplateInfo> Template;
@@ -379,7 +389,7 @@ struct RecordInfo : public SymbolInfo {
   // Full qualified name of this record, including namespaces and template
   // specializations.
   SmallString<16> FullName;
-
+  
   // When present, this record is a template or specialization.
   std::optional<TemplateInfo> Template;
 
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index b9db78cf7d688f..560647e4e03c1d 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -32,6 +32,144 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
 
 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);
 
+void printTemplateParameters(const TemplateParameterList *TemplateParams, llvm::raw_ostream &stream) {
+  stream << "template <";
+  
+  for (unsigned i = 0; i < TemplateParams->size(); ++i) {
+    if (i > 0) {
+      stream << ", ";
+    }
+
+    const NamedDecl *Param = TemplateParams->getParam(i);
+    if (const auto *TTP = llvm::dyn_cast<TemplateTypeParmDecl>(Param)) {
+      if (TTP->wasDeclaredWithTypename()) {
+        stream << "typename";
+      } else {
+        stream << "class";
+      }
+
+      if (TTP->isParameterPack()) {
+        stream << "...";
+      }
+      stream << " " << TTP->getNameAsString();
+    } else if (const auto *NTTP = llvm::dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+      NTTP->getType().print(stream, NTTP->getASTContext().getPrintingPolicy());
+      if (NTTP->isParameterPack()) {
+        stream << "...";
+      }
+      stream << " " << NTTP->getNameAsString();
+    } else if (const auto *TTPD = llvm::dyn_cast<TemplateTemplateParmDecl>(Param)) {
+      stream << "template <";
+      printTemplateParameters(TTPD->getTemplateParameters(), stream);
+      stream << "> class " << TTPD->getNameAsString();
+    }
+  }
+
+  stream << "> ";
+}
+
+// Extract the full function prototype from a FunctionDecl including 
+// Full Decl
+llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl) {
+  llvm::SmallString<256> Result; 
+  llvm::raw_svector_ostream Stream(Result);
+  const ASTContext& Ctx = FuncDecl->getASTContext();
+  // If it's a templated function, handle the template parameters
+  if (const auto *TmplDecl = FuncDecl->getDescribedTemplate()) {
+    printTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
+  }
+  
+  // Print return type
+  FuncDecl->getReturnType().print(Stream, Ctx.getPrintingPolicy());
+
+  // Print function name
+  Stream << " " << FuncDecl->getNameAsString() << "(";
+
+  // Print parameter list with types, names, and default values
+  for (unsigned I = 0; I < FuncDecl->getNumParams(); ++I) {
+    if (I > 0) {
+      Stream << ", ";
+    }
+    const ParmVarDecl *ParamDecl = FuncDecl->getParamDecl(I);
+    QualType ParamType = ParamDecl->getType();
+    ParamType.print(Stream, Ctx.getPrintingPolicy());
+
+    // Print parameter name if it has one
+    if (!ParamDecl->getName().empty()) {
+      Stream << " " << ParamDecl->getNameAsString();
+    }
+
+    // Print default argument if it exists
+    if (ParamDecl->hasDefaultArg()) {
+      const Expr *DefaultArg = ParamDecl->getDefaultArg();
+      if (DefaultArg) {
+        Stream << " = ";
+        DefaultArg->printPretty(Stream, nullptr, Ctx.getPrintingPolicy());
+      }
+    }
+  }
+
+  // If it is a variadic function, add '...'
+  if (FuncDecl->isVariadic()) {
+    if (FuncDecl->getNumParams() > 0) {
+      Stream << ", ";
+    }
+    Stream << "...";
+  }
+
+  Stream << ")";
+
+  // If it's a const method, add 'const' qualifier
+  if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl)) {
+    if (Method->isConst()) {
+      Stream << " const";
+    }
+  }
+  return Result; // Convert SmallString to std::string for return
+}
+
+// extract full cl for record declaration
+llvm::SmallString<16> getRecordPrototype(const CXXRecordDecl *CXXRD) {
+  llvm::SmallString<16> ReturnVal;
+  LangOptions LangOpts;
+  PrintingPolicy Policy(LangOpts);
+  Policy.SuppressTagKeyword = false;
+  Policy.FullyQualifiedName = true;
+  Policy.IncludeNewlines = false;
+  llvm::raw_svector_ostream OS(ReturnVal);
+  if (const auto *TD = CXXRD->getDescribedClassTemplate()) {
+    OS << "template <";
+    bool FirstParam = true;
+    for (const auto *Param : *TD->getTemplateParameters()) {
+      if (!FirstParam) OS << ", ";
+      Param->print(OS, Policy);
+      FirstParam = false;
+    }
+    OS << ">\n";
+  }
+  if (CXXRD->isStruct()) {
+    OS << "struct ";
+  } else if (CXXRD->isClass()) {
+    OS << "class ";
+  } else if (CXXRD->isUnion()) {
+    OS << "union ";
+  }
+  OS << CXXRD->getNameAsString();
+  if (CXXRD->getNumBases() > 0) {
+    OS << " : ";
+    bool FirstBase = true;
+    for (const auto &Base : CXXRD->bases()) {
+      if (!FirstBase) OS << ", ";
+      if (Base.isVirtual()) OS << "virtual ";
+      OS << getAccessSpelling(Base.getAccessSpecifier()) << " ";
+      OS << Base.getType().getAsString(Policy);
+      FirstBase = false;
+    }
+  }
+  return ReturnVal;
+}
+
+
 // A function to extract the appropriate relative path for a given info's
 // documentation. The path returned is a composite of the parent namespaces.
 //
@@ -224,10 +362,38 @@ static SymbolID getUSRForDecl(const Decl *D) {
   return hashUSR(USR);
 }
 
-static TagDecl *getTagDeclForType(const QualType &T) {
-  if (const TagDecl *D = T->getAsTagDecl())
-    return D->getDefinition();
-  return nullptr;
+static QualType getBaseQualType(const QualType &T) {
+  QualType QT = T;
+  // Get the base type of the QualType
+  // eg. int* -> int, int& -> int, const int -> int
+  bool Modified = true;  // Track whether we've modified `qt` in this loop iteration
+  while (Modified) {
+    Modified = false;
+    // If it's a reference type, strip the reference
+    if (QT->isReferenceType()) {
+      QT = QT->getPointeeType();
+      Modified = true;
+    }
+    // If it's a pointer type, strip the pointer
+    else if (QT->isPointerType()) {
+      QT = QT->getPointeeType();
+      Modified = true;
+    }
+    else if (const auto *ElaboratedType = QT->getAs<clang::ElaboratedType>()) {
+      QT = ElaboratedType->desugar();
+      Modified = true;
+    }
+    // Remove const/volatile qualifiers if present
+    else if (QT.hasQualifiers()) {
+      QT = QT.getUnqualifiedType();
+      Modified = true;
+    }
+    else if (const auto *TypedefType = QT->getAs<clang::TypedefType>()) {
+      return QT;
+    }
+  }
+  
+  return QT;
 }
 
 static RecordDecl *getRecordDeclForType(const QualType &T) {
@@ -237,27 +403,35 @@ static RecordDecl *getRecordDeclForType(const QualType &T) {
 }
 
 TypeInfo getTypeInfoForType(const QualType &T) {
-  const TagDecl *TD = getTagDeclForType(T);
-  if (!TD)
-    return TypeInfo(Reference(SymbolID(), T.getAsString()));
-
+  const QualType QT = getBaseQualType(T);
+  const TagDecl *TD = QT->getAsTagDecl();
+  if (!TD) {
+    TypeInfo TI = TypeInfo(Reference(SymbolID(), T.getAsString()));
+    TI.IsBuiltIn = QT->isBuiltinType();
+    TI.IsTemplate = QT->isTemplateTypeParmType();
+    return TI;
+  }
   InfoType IT;
-  if (dyn_cast<EnumDecl>(TD)) {
+  if (isa<EnumDecl>(TD))
     IT = InfoType::IT_enum;
-  } else if (dyn_cast<RecordDecl>(TD)) {
+  else if (isa<RecordDecl>(TD))
     IT = InfoType::IT_record;
-  } else {
+  else
     IT = InfoType::IT_default;
-  }
-  return TypeInfo(Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
-                            T.getAsString(), getInfoRelativePath(TD)));
+  
+  Reference R = Reference(getUSRForDecl(TD), TD->getNameAsString(), IT,
+                          T.getAsString(), getInfoRelativePath(TD));
+  TypeInfo TI = TypeInfo(R);
+  TI.IsBuiltIn = QT->isBuiltinType();
+  TI.IsTemplate = QT->isTemplateTypeParmType();
+  return TI;
 }
 
 static bool isPublic(const clang::AccessSpecifier AS,
                      const clang::Linkage Link) {
   if (AS == clang::AccessSpecifier::AS_private)
     return false;
-  else if ((Link == clang::Linkage::Module) ||
+  if ((Link == clang::Linkage::Module) ||
            (Link == clang::Linkage::External))
     return true;
   return false; // otherwise, linkage is some form of internal linkage
@@ -425,6 +599,7 @@ static void parseBases(RecordInfo &I, const CXXRecordDecl *D) {
   // Don't parse bases if this isn't a definition.
   if (!D->isThisDeclarationADefinition())
     return;
+  
   for (const CXXBaseSpecifier &B : D->bases()) {
     if (B.isVirtual())
       continue;
@@ -487,7 +662,7 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
                             InfoType::IT_namespace);
 }
 
-void PopulateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
+void populateTemplateParameters(std::optional<TemplateInfo> &TemplateInfo,
                                 const clang::Decl *D) {
   if (const TemplateParameterList *ParamList =
           D->getDescribedTemplateParams()) {
@@ -542,9 +717,10 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
   populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
                      IsInAnonymousNamespace);
   I.ReturnType = getTypeInfoForType(D->getReturnType());
+  I.ProtoType = getFunctionPrototype(D);
   parseParameters(I, D);
 
-  PopulateTemplateParameters(I.Template, D);
+  populateTemplateParameters(I.Template, D);
 
   // Handle function template specializations.
   if (const FunctionTemplateSpecializationInfo *FTSI =
@@ -668,10 +844,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
                      IsInAnonymousNamespace);
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
-
+  
   I->TagType = D->getTagKind();
   parseFields(*I, D, PublicOnly);
   if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
+    I->FullName = getRecordPrototype(C);
     if (const TypedefNameDecl *TD = C->getTypedefNameForAnonDecl()) {
       I->Name = TD->getNameAsString();
       I->IsTypeDef = true;
@@ -682,7 +859,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
   }
   I->Path = getInfoRelativePath(I->Namespace);
 
-  PopulateTemplateParameters(I->Template, D);
+  populateTemplateParameters(I->Template, D);
 
   // Full and partial specializations.
   if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
@@ -721,7 +898,6 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
       }
     }
   }
-
   // Records are inserted into the parent by reference, so we need to return
   // both the parent and the record itself.
   auto Parent = MakeAndInsertIntoParent<const RecordInfo &>(*I);
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 a885a36cb4a3dc..d17fe54bb4009d 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -1,4 +1,3 @@
-/* css for clang-doc mustache backend */
 @import "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap";
 
 *,*::before *::after {
@@ -19,7 +18,6 @@ video {
     display:block;
     max-width:100%
 }
-
 * {
     --brand-light:#ce6300;
     --text1-light:#000000;
@@ -32,7 +30,6 @@ video {
     --surface1-dark:#161212;
     --surface2-dark:#272424
 }
-
 :root {
     color-scheme:light;
     --brand:var(--brand-light);
@@ -43,7 +40,6 @@ video {
     --surface1:var(--surface1-light);
     --surface2:var(--surface2-light)
 }
-
 @media(prefers-color-scheme:dark) {
     :root {
         color-scheme:dark;
@@ -56,7 +52,6 @@ video {
         --surface2:var(--surface2-dark)
     }
 }
-
 [color-scheme=light] {
     color-scheme:light;
     --brand:var(--brand-light);
@@ -67,7 +62,6 @@ video {
     --surface1:var(--surface1-light);
     --surface2:var(--surface2-light)
 }
-
 [color-scheme=dark] {
     color-scheme:dark;
     --brand:var(--brand-dark);
@@ -78,7 +72,6 @@ video {
     --surface1:var(--surface1-dark);
     --surface2:var(--surface2-dark)
 }
-
 html {
     background-color:var(--surface1)
 }
@@ -92,7 +85,9 @@ html, body {
 
 .container {
     display: flex;
-    margin-top: 60px;
+    position: relative;
+    flex-direction: row;
+    top: 60px;
     height: calc(100% - 60px);
     box-sizing: border-box;
 }
@@ -121,7 +116,6 @@ body, html {
     z-index: 1000;
 }
 
-
 .navbar__container {
     display:flex;
     justify-content:space-between;
@@ -131,21 +125,25 @@ body, html {
     max-width:2048px;
     margin:auto
 }
+
 .navbar__logo {
     display:flex;
     align-items:center;
     height:40px
 }
+
 .navbar__logo a {
     display:flex;
     align-items:center;
     text-decoration:none;
     height:100%
 }
+
 .navbar__logo img {
     height:100%;
     width:auto
 }
+
 .navbar__toggle {
     background:0 0;
     color:var(--text2);
@@ -156,14 +154,17 @@ body, html {
     height:2.5rem;
     margin-left:auto
 }
+
 .navbar__toggle:hover {
     color:var(--text1)
 }
+
 @media(min-width:769px) {
     .navbar__toggle {
         display:none
     }
 }
+
 .navbar__menu {
     display:flex;
     justify-content:space-between;
@@ -191,11 +192,13 @@ body, html {
         transition:transform .5s ease-in-out
     }
 }
+
 @media(max-width:768px) {
     .navbar__menu.active {
         transform:translateX(0)
     }
 }
+
 .navbar__close {
     background:0 0;
     border:none;
@@ -204,6 +207,7 @@ body, html {
     color:var(--text2);
     margin-left:auto
 }
+
 .navbar__close:hover {
     color:var(--text1)
 }
@@ -213,6 +217,7 @@ body, html {
         display:none
     }
 }
+
 .navbar__links {
     display:flex;
     gap:1rem;
@@ -263,13 +268,8 @@ body, html {
     gap:2rem
 }
 
-.hero__title {
-    font-size:2.5rem;
-    margin-bottom:.5rem
-}
-
 .hero__title-large {
-    font-size:3rem
+    font-size:2rem
 }
 
 @media(max-width:768px) {
@@ -277,25 +277,21 @@ body, html {
         font-size:2.5rem
     }
 }
-
 @media(max-width:480px) {
     .hero__title-large {
         font-size:2rem
     }
 }
-
 @media(max-width:768px) {
     .hero__title {
         font-size:2rem
     }
 }
-
 @media(max-width:480px) {
     .hero__title {
         font-size:1.75rem
     }
 }
-
 .hero__subtitle {
     font-size:1.25rem;
     font-weight:500
@@ -327,7 +323,6 @@ body, html {
         padding:1rem
     }
 }
-
 .section-container h2 {
     font-size:1.5rem;
     margin-bottom:1rem;
@@ -352,10 +347,38 @@ body, html {
     }
 }
 
-.home__row {
+.links-wrapper {
     display:grid;
-    grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
-    gap:2rem
+    gap:1rem;
+    grid-template-columns:1fr 1fr 1fr 1fr
+}
+
+ at media(max-width:768px) {
+    .links-wrapper {
+        grid-template-columns:1fr 1fr
+    }
+}
+ at media(max-width:480px) {
+    .links-wrapper {
+        grid-template-columns:1fr
+    }
+}
+
+.links-wrapper .link {
+    display:flex;
+    flex-direction:column;
+    align-items:center;
+    padding:1rem;
+    border:1px solid var(--text1);
+    border-radius:.25rem
+}
+
+.links-wrapper .link__icon {
+    font-size:2rem
+}
+
+.links-wrapper .link__title {
+    font-size:1rem
 }
 
 .table-wrapper {
@@ -371,23 +394,31 @@ body, html {
     text-align: left;
 }
 
+
 .block-command-command {
     font-weight: bold;
 }
 
 .code-clang-doc {
-    font-size: 1.1rem;
+    font-size: 1.2rem;
+}
+
+.code-highlight:hover {
+    text-decoration: underline;
 }
 
 .delimiter-container {
     padding: 0.5rem 1rem;
-    margin-bottom:1rem; 
+    margin-bottom:1rem;
 }
 
 .resizer {
     width: 5px;
     cursor: col-resize;
-    background-color: var(--text2);
+    border-left: 3px solid black;
+    top: 60px;
+    left: 250px;
+    position: relative;
 }
 
 .resizer:hover {
@@ -400,7 +431,7 @@ body, html {
     left: 0;
     height: 100%;
     position: fixed;
-    background-color: var(--surface1);
+    background-color: var(--surface2);
     display: flex;
     border-left: 1px solid var(--text2);
     flex-direction: column;
@@ -433,6 +464,7 @@ body, html {
     margin-bottom: 1rem;
     padding: 3rem;
 }
+
 .sidebar-section a {
     color: var(--brand)
 }
@@ -444,7 +476,8 @@ body, html {
     left: 250px;
     position: relative;
     width: calc(100% - 250px);
-    height: 100vh;
+    max-width: 960px;
+    margin-right: auto;
 }
 
 .sidebar-item {
@@ -468,4 +501,4 @@ body, html {
 a, a:visited, a:hover, a:active {
     text-decoration: none;
     color: inherit;
-}
+}
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/class-template.mustache b/clang-tools-extra/clang-doc/assets/class-template.mustache
index 7ce51c6e162110..e611000474f14e 100644
--- a/clang-tools-extra/clang-doc/assets/class-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/class-template.mustache
@@ -9,13 +9,10 @@
 <html lang="en-US">
 <head>
     <meta charset="utf-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>{{Name}}</title>
-    {{#Stylesheets}}
-        <link rel="stylesheet" type="text/css" href="{{.}}"/>
-    {{/Stylesheets}}
-    {{#Scripts}}
-        <script src="{{.}}"></script>
-    {{/Scripts}}
+    <link rel="stylesheet" type="text/css" href="clang-doc-mustache.css"/>
+    <script src="mustache-index.js"></script>
     {{! Highlight.js dependency for syntax highlighting }}
     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
     <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
@@ -25,30 +22,21 @@
 <nav class="navbar">
     <div class="navbar__container">
         {{#ProjectName}}
-            <div class="navbar__logo">
-                {{ProjectName}}
-            </div>
-        {{/ProjectName}}
-        <div class="navbar__menu">
-            <ul class="navbar__links">
-                <li class="navbar__item">
-                    <a href="/" class="navbar__link">Namespace</a>
-                </li>
-                <li class="navbar__item">
-                    <a href="/" class="navbar__link">Class</a>
-                </li>
-            </ul>
+        <div class="navbar__logo">
+            {{ProjectName}}
         </div>
+        {{/ProjectName}}
     </div>
 </nav>
 <main>
+    <!-- Sidebar -->
     <div class="container">
         <div class="sidebar">
             <h2>{{RecordType}} {{Name}}</h2>
             <ul>
                 {{#PublicMembers}}
-                <li class="sidebar-section">
-                    <a class="sidebar-item" href="#PublicMethods">Public Members</a>
+                <li class="sidebar-section sidebar-item-container">
+                    <a href="#PublicMethods">Public Members</a>
                 </li>
                 <ul>
                     {{#Obj}}
@@ -71,7 +59,7 @@
                     </ul>
                 {{/ProtectedMembers}}
                 {{#PublicFunction}}
-                <li class="sidebar-section">
+                <li class="sidebar-section sidebar-item-container">
                     <a class="sidebar-item" href="#PublicMethods">Public Method</a>
                 </li>
                 <ul>
@@ -84,7 +72,7 @@
                 {{/PublicFunction}}
                 {{#ProtectedFunction}}
                 <li class="sidebar-section">
-                    <a class="sidebar-item" href="#ProtectedFunction">Protected Method</a>
+                    <a href="#ProtectedFunction">Protected Method</a>
                 </li>
                 <ul>
                     {{#Obj}}
@@ -96,7 +84,7 @@
                 {{/ProtectedFunction}}
                 {{#Enums}}
                 <li class="sidebar-section">
-                    <a class="sidebar-item" href="#Enums">Enums</a>
+                    <a href="#Enums">Enums</a>
                 </li>
                 <ul>
                     {{#Obj}}
@@ -107,33 +95,26 @@
                 </ul>
                 {{/Enums}}
                 {{#Typedef}}
-                <li class="sidebar-section">Typedef</li>
-                {{/Typedef}}
-                {{#Record}}
                 <li class="sidebar-section">
-                    <a class="sidebar-item" href="#Classes">Inner Classes</a>
+                    <a href="#ProtectedFunction">Typedef</a>
                 </li>
-                <ul>
-                    {{#Links}}
-                    <li class="sidebar-item-container">
-                        <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
-                    </li>
-                    {{/Links}}
-                </ul>
-                {{/Record}}
+                {{/Typedef}}
             </ul>
         </div>
         <div class="resizer" id="resizer"></div>
         <div class="content">
             <section class="hero section-container">
-                <div class="hero__title">
-                    <h1 class="hero__title-large">{{RecordType}} {{Name}}</h1>
-                    {{#RecordComments}}
-                    <div class="hero__subtitle">
-                        {{>Comments}}
-                    </div>
-                    {{/RecordComments}}
+                <pre>
+                    <code class="language-cpp hero__title-large">
+{{&FullName}}
+                    </code>
+                </pre>
+                {{#RecordComments}}
+                <h2>Description</h2>    
+                <div class="hero__subtitle">
+                    {{>Comments}}
                 </div>
+                {{/RecordComments}}
             </section>
             {{#PublicMembers}}
             <section id="PublicMembers" class="section-container">
diff --git a/clang-tools-extra/clang-doc/assets/enum-template.mustache b/clang-tools-extra/clang-doc/assets/enum-template.mustache
index d63bf258f8f0fb..54608842eaf837 100644
--- a/clang-tools-extra/clang-doc/assets/enum-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/enum-template.mustache
@@ -20,7 +20,7 @@
             <th>Name</th>
             <th>Value</th>
             {{#HasComment}}
-                <th>Comment</th>
+            <th>Comment</th>
             {{/HasComment}}
         </tr>
         {{#EnumValues}}
@@ -28,7 +28,7 @@
                 <td>{{Name}}</td>
                 <td>{{Value}}</td>
                 {{#EnumValueComments}}
-                    <td>{{>Comments}}</td>
+                <td>{{>Comments}}</td>
                 {{/EnumValueComments}}
             </tr>
         {{/EnumValues}}
diff --git a/clang-tools-extra/clang-doc/assets/function-template.mustache b/clang-tools-extra/clang-doc/assets/function-template.mustache
index 0564647467aa60..d023fca17ec626 100644
--- a/clang-tools-extra/clang-doc/assets/function-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/function-template.mustache
@@ -10,7 +10,7 @@
         {{! Function Prototype }}
         <pre>
             <code class="language-cpp code-clang-doc">
-{{ReturnType.Name}} {{Name}} ({{#Params}}{{^End}}{{Type}} {{Name}}, {{/End}}{{#End}}{{Type}} {{Name}}{{/End}}{{/Params}})
+{{&FunctionPrototype}}
             </code>
         </pre>
         {{! Function Comments }}
diff --git a/clang-tools-extra/clang-doc/assets/mustache-index.js b/clang-tools-extra/clang-doc/assets/mustache-index.js
index db320cd8ae403c..438c4e44b195d4 100644
--- a/clang-tools-extra/clang-doc/assets/mustache-index.js
+++ b/clang-tools-extra/clang-doc/assets/mustache-index.js
@@ -1,7 +1,195 @@
+
+// highlight.js plugin to merge the original HTML stream and the highlighted stream
+// see this issue: https://github.com/highlightjs/highlight.js/issues/2889
+var mergeHTMLPlugin = (function () {
+    'use strict';
+
+    var originalStream;
+
+    /**
+     * @param {string} value
+     * @returns {string}
+     */
+    function escapeHTML(value) {
+        return value
+            .replace(/&/g, '&')
+            .replace(/</g, '<')
+            .replace(/>/g, '>')
+            .replace(/"/g, '"')
+            .replace(/'/g, '&#x27;');
+    }
+
+    /* plugin itself */
+
+    /** @type {HLJSPlugin} */
+    const mergeHTMLPlugin = {
+        // preserve the original HTML token stream
+        "before:highlightElement": ({ el }) => {
+            originalStream = nodeStream(el);
+        },
+        // merge it afterwards with the highlighted token stream
+        "after:highlightElement": ({ el, result, text }) => {
+            if (!originalStream.length) return;
+
+            const resultNode = document.createElement('div');
+            resultNode.innerHTML = result.value;
+            result.value = mergeStreams(originalStream, nodeStream(resultNode), text);
+            el.innerHTML = result.value;
+        }
+    };
+
+    /* Stream merging support functions */
+
+    /**
+     * @typedef Event
+     * @property {'start'|'stop'} event
+     * @property {number} offset
+     * @property {Node} node
+     */
+
+    /**
+     * @param {Node} node
+     */
+    function tag(node) {
+        return node.nodeName.toLowerCase();
+    }
+
+    /**
+     * @param {Node} node
+     */
+    function nodeStream(node) {
+        /** @type Event[] */
+        const result = [];
+        (function _nodeStream(node, offset) {
+            for (let child = node.firstChild; child; child = child.nextSibling) {
+                if (child.nodeType === 3) {
+                    offset += child.nodeValue.length;
+                } else if (child.nodeType === 1) {
+                    result.push({
+                        event: 'start',
+                        offset: offset,
+                        node: child
+                    });
+                    offset = _nodeStream(child, offset);
+                    // Prevent void elements from having an end tag that would actually
+                    // double them in the output. There are more void elements in HTML
+                    // but we list only those realistically expected in code display.
+                    if (!tag(child).match(/br|hr|img|input/)) {
+                        result.push({
+                            event: 'stop',
+                            offset: offset,
+                            node: child
+                        });
+                    }
+                }
+            }
+            return offset;
+        })(node, 0);
+        return result;
+    }
+
+    /**
+     * @param {any} original - the original stream
+     * @param {any} highlighted - stream of the highlighted source
+     * @param {string} value - the original source itself
+     */
+    function mergeStreams(original, highlighted, value) {
+        let processed = 0;
+        let result = '';
+        const nodeStack = [];
+
+        function selectStream() {
+            if (!original.length || !highlighted.length) {
+                return original.length ? original : highlighted;
+            }
+            if (original[0].offset !== highlighted[0].offset) {
+                return (original[0].offset < highlighted[0].offset) ? original : highlighted;
+            }
+
+            /*
+            To avoid starting the stream just before it should stop the order is
+            ensured that original always starts first and closes last:
+      
+            if (event1 == 'start' && event2 == 'start')
+              return original;
+            if (event1 == 'start' && event2 == 'stop')
+              return highlighted;
+            if (event1 == 'stop' && event2 == 'start')
+              return original;
+            if (event1 == 'stop' && event2 == 'stop')
+              return highlighted;
+      
+            ... which is collapsed to:
+            */
+            return highlighted[0].event === 'start' ? original : highlighted;
+        }
+
+        /**
+         * @param {Node} node
+         */
+        function open(node) {
+            /** @param {Attr} attr */
+            function attributeString(attr) {
+                return ' ' + attr.nodeName + '="' + escapeHTML(attr.value) + '"';
+            }
+            // @ts-ignore
+            result += '<' + tag(node) + [].map.call(node.attributes, attributeString).join('') + '>';
+        }
+
+        /**
+         * @param {Node} node
+         */
+        function close(node) {
+            result += '</' + tag(node) + '>';
+        }
+
+        /**
+         * @param {Event} event
+         */
+        function render(event) {
+            (event.event === 'start' ? open : close)(event.node);
+        }
+
+        while (original.length || highlighted.length) {
+            let stream = selectStream();
+            result += escapeHTML(value.substring(processed, stream[0].offset));
+            processed = stream[0].offset;
+            if (stream === original) {
+                /*
+                On any opening or closing tag of the original markup we first close
+                the entire highlighted node stack, then render the original tag along
+                with all the following original tags at the same offset and then
+                reopen all the tags on the highlighted stack.
+                */
+                nodeStack.reverse().forEach(close);
+                do {
+                    render(stream.splice(0, 1)[0]);
+                    stream = selectStream();
+                } while (stream === original && stream.length && stream[0].offset === processed);
+                nodeStack.reverse().forEach(open);
+            } else {
+                if (stream[0].event === 'start') {
+                    nodeStack.push(stream[0].node);
+                } else {
+                    nodeStack.pop();
+                }
+                render(stream.splice(0, 1)[0]);
+            }
+        }
+        return result + escapeHTML(value.substr(processed));
+    }
+
+    return mergeHTMLPlugin;
+
+}());
+
 document.addEventListener("DOMContentLoaded", function() {
     const resizer = document.getElementById('resizer');
     const sidebar = document.querySelector('.sidebar');
-    
+    const content = document.querySelector('.content');
+    const minWidth = 100;
+    const maxWidth = 500;
+    hljs.addPlugin(mergeHTMLPlugin);
     let isResizing = false;
     resizer.addEventListener('mousedown', (e) => {
         isResizing = true;
@@ -10,8 +198,11 @@ document.addEventListener("DOMContentLoaded", function() {
     document.addEventListener('mousemove', (e) => {
         if (!isResizing) return;
         const newWidth = e.clientX;
-        if (newWidth > 100 && newWidth < window.innerWidth - 100) {
+        if (newWidth > minWidth && newWidth < maxWidth) {
             sidebar.style.width = `${newWidth}px`;
+            resizer.style.left = `${newWidth}px`;
+            content.style.left = `${newWidth}px`
+            content.style.width =  `calc(100% - ${newWidth}px)`;
         }
     });
 
diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
index 21cbaa7ec5cf3f..19b0ce28addd9d 100644
--- a/clang-tools-extra/clang-doc/assets/namespace-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
@@ -9,39 +9,66 @@
 <html lang="en-US">
     <head>
         <meta charset="utf-8"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <title>{{NamespaceTitle}}</title>
-        {{#Stylesheets}}
-        <link rel="stylesheet" type="text/css" href="{{.}}"/>
-        {{/Stylesheets}}
-        {{#Scripts}}
-        <script src="{{.}}"></script>
-        {{/Scripts}}
+        <link rel="stylesheet" type="text/css" href="clang-doc-mustache.css"/>
+        <script src="mustache-index.js"></script>
         {{! Highlight.js dependency for syntax highlighting }}
         <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
         <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
         <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js"></script>
     </head>
     <body>
-        <nav class="navbar">
-            Navbar
+        <nav class="sidebar">
+            {{#ProjectName}}
+            <h2 class="sidebar-project-name">
+                {{ProjectName}}
+            </h2>
+            {{/ProjectName}}
+            <h1>{{NamespaceTitle}}</h1>
+            {{#Namespace}}
+            <h3 id="Namespace">Namespace</h3>
+            <ul>
+                {{#Links}}
+                <li>
+                    <a href="{{Link}}">{{Name}}</a>
+                </li>
+                {{/Links}}
+            </ul>
+            {{/Namespace}}
+            {{#Record}}
+            <h3 id="Class">Class</h3>
+            <ul>
+                {{#Links}}
+                <li>
+                    <a href="{{Link}}">{{Name}}</a>
+                </li>
+                {{/Links}}
+            </ul>
+            {{/Record}}
+            {{#Function}}
+            <h3 id="Function">Function</h3>
+            <div>
+                {{#Obj}}
+                {{/Obj}}
+            </div>
+            {{/Function}}
+            {{#Enums}}
+            <h3 id="Enums">Enums</h3>
+            <div>
+                {{#Obj}}
+                {{/Obj}}
+            </div>
+            {{/Enums}}
         </nav>
+        <div class="sidebar-resizer"></div>
         <main>
-            <div class="container">
-                <div class="sidebar">
-                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, 
-                        sed do eiusmod tempor incididunt ut labore et dolore magna 
-                        aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco 
-                        laboris nisi ut aliquip ex ea commodo consequat. 
-                        Duis aute irure dolor in reprehenderit in voluptate velit esse 
-                        cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat 
-                        cupidatat non proident, sunt in culpa qui officia deserunt mollit 
-                        anim id est laborum
-                </div>
-                <div class="resizer" id="resizer"></div>
-                <div class="content">
-                    Content
-                </div>
-            </div>
+            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec nunc
+            ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam nec purus
+            nec nunc ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam
+            nec purus nec nunc ultricies ultricies. Nullam nec purus nec nunc
+            ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam nec purus
+            nec nunc ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam
         </main>
     </body>
 </html>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 256e29c3c9f894..054856c158dd86 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -427,6 +427,16 @@ Example usage for a project using a compile commands database:
     return 1;
 
   sortUsrToInfo(USRToInfo);
+  
+  for (const auto &G : USRToInfo) {
+    const clang::doc::Info& I = *G.getValue();
+    
+    if (I.IT == clang::doc::InfoType::IT_record) {
+      const clang::doc::RecordInfo& R = *static_cast<const clang::doc::RecordInfo *>(&I);
+      llvm::outs() << "Full Name from record " << R.Name << " Full Name: " << R.FullName << "\n";
+    }
+    
+  }
 
   // Ensure the root output directory exists.
   if (std::error_code Err = llvm::sys::fs::create_directories(OutDirectory);

>From 377884941796b77ed17d6438cc923d6600d7322c Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Fri, 1 Nov 2024 17:47:28 -0400
Subject: [PATCH 61/62] updated working copy

---
 clang-tools-extra/clang-doc/BitcodeReader.cpp |   2 +
 clang-tools-extra/clang-doc/BitcodeWriter.cpp |   9 +-
 clang-tools-extra/clang-doc/BitcodeWriter.h   |   1 +
 .../clang-doc/HTMLMustacheGenerator.cpp       | 138 ++++-----
 clang-tools-extra/clang-doc/Mapper.cpp        |  14 +-
 clang-tools-extra/clang-doc/Representation.h  |  12 +-
 clang-tools-extra/clang-doc/Serialize.cpp     | 112 +++++--
 .../clang-doc/assets/clang-doc-mustache.css   | 277 +++++-------------
 .../clang-doc/assets/class-template.mustache  | 178 ++++++-----
 .../clang-doc/assets/enum-template.mustache   |   4 +-
 .../assets/function-template.mustache         |   5 +
 .../assets/typedef-template.mustache          |  29 ++
 .../clang-doc/tool/CMakeLists.txt             |   3 +-
 .../clang-doc/tool/ClangDocMain.cpp           |  21 +-
 14 files changed, 401 insertions(+), 404 deletions(-)
 create mode 100644 clang-tools-extra/clang-doc/assets/typedef-template.mustache

diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 4d639937a59152..468fd7e023deaf 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -234,6 +234,8 @@ llvm::Error parseRecord(const Record &R, unsigned ID, llvm::StringRef Blob,
   switch (ID) {
   case TYPEDEF_USR:
     return decodeRecord(R, I->USR, Blob);
+  case TYPEDEF_DECLARATION:
+    return decodeRecord(R, I->TypeDeclaration, Blob);
   case TYPEDEF_NAME:
     return decodeRecord(R, I->Name, Blob);
   case TYPEDEF_DEFLOCATION:
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 4b8f67302ce9d9..96f00d67470ec3 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -200,6 +200,7 @@ static const llvm::IndexedMap<RecordIdDsc, RecordIdToIndexFunctor>
           {TEMPLATE_PARAM_CONTENTS, {"Contents", &StringAbbrev}},
           {TEMPLATE_SPECIALIZATION_OF, {"SpecializationOf", &SymbolIDAbbrev}},
           {TYPEDEF_USR, {"USR", &SymbolIDAbbrev}},
+          {TYPEDEF_DECLARATION, {"Declaration", &StringAbbrev}},
           {TYPEDEF_NAME, {"Name", &StringAbbrev}},
           {TYPEDEF_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
           {TYPEDEF_IS_USING, {"IsUsing", &BoolAbbrev}}};
@@ -235,7 +236,8 @@ static const std::vector<std::pair<BlockId, std::vector<RecordId>>>
          {ENUM_VALUE_NAME, ENUM_VALUE_VALUE, ENUM_VALUE_EXPR}},
         // Typedef Block
         {BI_TYPEDEF_BLOCK_ID,
-         {TYPEDEF_USR, TYPEDEF_NAME, TYPEDEF_DEFLOCATION, TYPEDEF_IS_USING}},
+         {TYPEDEF_USR, TYPEDEF_NAME, TYPEDEF_DECLARATION, TYPEDEF_DEFLOCATION, 
+          TYPEDEF_IS_USING}},
         // Namespace Block
         {BI_NAMESPACE_BLOCK_ID,
          {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}},
@@ -430,10 +432,6 @@ void ClangDocBitcodeWriter::emitBlock(const Reference &R, FieldId Field) {
   if (R.USR == EmptySID && R.Name.empty())
     return;
   
-  if (R.Name == "const Shape &") {
-    llvm::outs() << "const Shape & USR: " << 
-                    llvm::toHex(llvm::toStringRef(R.USR)) << "\n";
-  }
   StreamSubBlockGuard Block(Stream, BI_REFERENCE_BLOCK_ID);
   emitRecord(R.USR, REFERENCE_USR);
   emitRecord(R.Name, REFERENCE_NAME);
@@ -454,6 +452,7 @@ void ClangDocBitcodeWriter::emitBlock(const TypedefInfo &T) {
   StreamSubBlockGuard Block(Stream, BI_TYPEDEF_BLOCK_ID);
   emitRecord(T.USR, TYPEDEF_USR);
   emitRecord(T.Name, TYPEDEF_NAME);
+  emitRecord(T.TypeDeclaration, TYPEDEF_DECLARATION);
   for (const auto &N : T.Namespace)
     emitBlock(N, FieldId::F_namespace);
   for (const auto &CI : T.Description)
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h b/clang-tools-extra/clang-doc/BitcodeWriter.h
index 117527cf89c969..fa3e9e32b5c3bb 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -135,6 +135,7 @@ enum RecordId {
   TEMPLATE_SPECIALIZATION_OF,
   TYPEDEF_USR,
   TYPEDEF_NAME,
+  TYPEDEF_DECLARATION,
   TYPEDEF_DEFLOCATION,
   TYPEDEF_IS_USING,
   RI_LAST,
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index d7079497c25096..313d1c5346961c 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -25,7 +25,6 @@ void generateIndex(Info *I) {
   switch (I->IT) {
   case InfoType::IT_namespace: {
     const NamespaceInfo& N = *static_cast<clang::doc::NamespaceInfo *>(I);
-    SmallString<16> Name = N.getFileBaseName();
     std::string HexId = llvm::toHex(llvm::toStringRef(N.USR));
     Index[HexId] = HexId;
     for (const EnumInfo& E : N.Children.Enums)
@@ -36,15 +35,12 @@ void generateIndex(Info *I) {
   }
   case InfoType::IT_record: {
     const RecordInfo& R = *static_cast<clang::doc::RecordInfo *>(I);
-    SmallString<16> Name = R.getFileBaseName();
     std::string HexId = llvm::toHex(llvm::toStringRef(R.USR));
     Index[HexId] = HexId;
-    for (const EnumInfo& E : R.Children.Enums) {
+    for (const EnumInfo& E : R.Children.Enums)
       Index[llvm::toHex(llvm::toStringRef(E.USR))] = HexId;
-    }
-    for (const TypedefInfo& T : R.Children.Typedefs) {
+    for (const TypedefInfo& T : R.Children.Typedefs)
       Index[llvm::toHex(llvm::toStringRef(T.USR))] = HexId;
-    }
     break;
   }  
   case InfoType::IT_enum:
@@ -127,10 +123,12 @@ setupTemplateFiles(const clang::doc::ClangDocContext &CDCtx) {
   auto CommentFilePath = CDCtx.MustacheTemplates.lookup("comments-template");
   auto FunctionFilePath = CDCtx.MustacheTemplates.lookup("function-template");
   auto EnumFilePath = CDCtx.MustacheTemplates.lookup("enum-template");
+  auto TypeDefFilePath = CDCtx.MustacheTemplates.lookup("typedef-template");
   std::vector<std::pair<StringRef, StringRef>> Partials = {
       {"Comments", CommentFilePath},
       {"FunctionPartial", FunctionFilePath},
-      {"EnumPartial", EnumFilePath}
+      {"EnumPartial", EnumFilePath},
+      {"TypedefPartial", TypeDefFilePath}
   };
   
   auto Err = setupTemplate(NamespaceTemplate, NamespaceFilePath, Partials);
@@ -152,8 +150,6 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
                                     const clang::doc::ClangDocContext &CDCtx) {
   if (auto Err = setupTemplateFiles(CDCtx))
     return Err;
-  // Track which directories we already tried to create.
-  llvm::StringSet<> CreatedDirs;
   // Collect all output by file name and create the necessary directories.
   llvm::StringMap<std::vector<doc::Info *>> FileToInfos;
   for (const auto &Group : Infos) {
@@ -182,41 +178,6 @@ MustacheHTMLGenerator::generateDocs(llvm::StringRef RootDir,
   return llvm::Error::success();
 }
 
-Value extractValue(const Location &L, 
-                   std::optional<StringRef> RepositoryUrl = std::nullopt) {
-  Object Obj = Object();
-  Obj.insert({"LineNumber", L.LineNumber});
-  Obj.insert({"Filename", L.Filename});
-  
-  if (!L.IsFileInRootDir || !RepositoryUrl)
-    return Obj;
-  
-  SmallString<128> FileURL(*RepositoryUrl);
-  llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
-  FileURL += "#" + std::to_string(L.LineNumber);
-  Obj.insert({"FileURL", FileURL});
-  
-  return Obj;
-}
-
-Value extractValue(const Reference &I, StringRef CurrentDirectory) {
-  llvm::SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
-  llvm::sys::path::append(Path, I.getFileBaseName() + ".html");
-  llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
-  Object Obj = Object();
-  Obj.insert({"Link", Path});
-  Obj.insert({"Name", I.Name});
-  Obj.insert({"QualName", I.QualName});
-  Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
-  return Obj;
-}
-
-
-Value extractValue(const TypedefInfo &I) {
-  // Not Supported
-  return nullptr;
-}
-
 Value extractValue(const CommentInfo &I) {
   Object Obj = Object();
   Value Child = Object();
@@ -238,6 +199,7 @@ Value extractValue(const CommentInfo &I) {
   if (I.Kind == "BlockCommandComment") {
     Child.getAsObject()->insert({"Command", I.Name});
     Value ChildArr = Array();
+    
     for (const auto& C: I.Children)
       ChildArr.getAsArray()->emplace_back(extractValue(*C));
     Child.getAsObject()->insert({"Children", ChildArr});
@@ -250,6 +212,56 @@ Value extractValue(const CommentInfo &I) {
 }
 
 
+Value extractValue(const std::optional<Location> &Loc, 
+                   std::optional<StringRef> RepositoryUrl = std::nullopt) {
+  
+  Object Obj = Object();
+  if (Loc.has_value()) {
+    Location L = *Loc;
+    SmallString<128> Filename(llvm::sys::path::filename(L.Filename));
+    Obj.insert({"LineNumber", L.LineNumber});
+    Obj.insert({"Filename", Filename});
+    if (!RepositoryUrl)
+      return Obj;
+    // This link only works specifically for github
+    SmallString<128> FileURL(*RepositoryUrl);
+    llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, "blob/main");
+    llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
+    FileURL += "#L" + std::to_string(L.LineNumber);
+    Obj.insert({"FileURL", FileURL});
+  }
+  return Obj;
+}
+
+Value extractValue(const Reference &I, StringRef CurrentDirectory) {
+  llvm::SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
+  llvm::sys::path::append(Path, I.getFileBaseName() + ".html");
+  llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
+  Object Obj = Object();
+  Obj.insert({"Link", Path});
+  Obj.insert({"Name", I.Name});
+  Obj.insert({"QualName", I.QualName});
+  Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+  return Obj;
+}
+
+
+Value extractValue(const TypedefInfo &I, const ClangDocContext& CDCtx) {
+  Object Obj = Object();
+  Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
+  Obj.insert({"TypeDeclaration", I.TypeDeclaration});
+  Obj.insert({"Name", I.Name});
+  Obj.insert({"IsAlias", I.IsUsing});
+  Obj.insert({"Location", extractValue(I.DefLoc, CDCtx.RepositoryUrl)});
+  if (!I.Description.empty()) {
+    Value ArrDesc = Array();
+    for (const CommentInfo& Child : I.Description) 
+      ArrDesc.getAsArray()->emplace_back(extractValue(Child));
+    Obj.insert({"TypeDefComments", ArrDesc});
+  }
+  return Obj;
+}
+
 // Function to replace a substring within a SmallString with another SmallString
 void replaceSubstring(llvm::SmallString<256> &Input, 
                       const llvm::SmallString<16> &From, 
@@ -383,14 +395,7 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
       ArrDesc.getAsArray()->emplace_back(extractValue(Child));
     Obj.insert({"FunctionComments", ArrDesc});
   }
-  if (I.DefLoc.has_value()) {
-    Location L = *I.DefLoc;
-    if (CDCtx.RepositoryUrl.has_value())
-      Obj.insert({"Location", extractValue(L,
-                                           StringRef{*CDCtx.RepositoryUrl})});
-    else
-      Obj.insert({"Location", extractValue(L)});  
-  }
+  Obj.insert({"Location", extractValue(I.DefLoc, CDCtx.RepositoryUrl)});
   return Obj;
 }
 
@@ -429,16 +434,7 @@ Value extractValue(const EnumInfo &I, const ClangDocContext &CDCtx) {
       ArrDesc.getAsArray()->emplace_back(extractValue(Child));
     Obj.insert({"EnumComments", ArrDesc});
   }
-  
-  if (I.DefLoc.has_value()) {
-    Location L = *I.DefLoc;
-    if (CDCtx.RepositoryUrl.has_value())
-      Obj.insert({"Location", extractValue(L,
-                                           StringRef{*CDCtx.RepositoryUrl})});
-    else
-      Obj.insert({"Location", extractValue(L)});  
-  }
-  
+  Obj.insert({"Location", extractValue(I.DefLoc, CDCtx.RepositoryUrl)});
   return Obj;
 }
 
@@ -493,10 +489,15 @@ void extractScopeChildren(const ScopeChildren &S, Object &Obj,
   
   Value ArrTypedefs = Array();
   for (const TypedefInfo& Child : S.Typedefs) 
-    ArrTypedefs.getAsArray()->emplace_back(extractValue(Child));
+    ArrTypedefs.getAsArray()->emplace_back(extractValue(Child, CDCtx));
   
   if (!ArrTypedefs.getAsArray()->empty())
     Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
+  
+  
+  llvm::raw_fd_ostream os(1, false);
+  llvm::json::OStream jStream(os, /*Indent=*/2);
+  jStream.value(ArrTypedefs);
 }
 
 Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
@@ -536,16 +537,7 @@ Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
                                                          I.VirtualParents)});
   
   RecordValue.insert({"RecordType", getTagType(I.TagType)});
-  
-  if (I.DefLoc.has_value()) {
-    Location L = *I.DefLoc;
-    if (CDCtx.RepositoryUrl.has_value())
-      RecordValue.insert({"Location", extractValue(L,
-                          StringRef{*CDCtx.RepositoryUrl})});
-    else
-      RecordValue.insert({"Location", extractValue(L)});  
-  }
-  
+  RecordValue.insert({"Location", extractValue(I.DefLoc, CDCtx.RepositoryUrl)});
   StringRef BasePath = I.getRelativeFilePath("");
   extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
   Value PublicMembers = Array();
diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp
index 6c90db03424c65..53f9f129c187e8 100644
--- a/clang-tools-extra/clang-doc/Mapper.cpp
+++ b/clang-tools-extra/clang-doc/Mapper.cpp
@@ -22,9 +22,8 @@ static llvm::StringSet<> USRVisited;
 static llvm::sys::Mutex USRVisitedGuard;
 
 template <typename T> bool isTypedefAnonRecord(const T *D) {
-  if (const auto *C = dyn_cast<CXXRecordDecl>(D)) {
+  if (const auto *C = dyn_cast<CXXRecordDecl>(D))
     return C->getTypedefNameForAnonDecl();
-  }
   return false;
 }
 
@@ -121,6 +120,7 @@ int MapASTVisitor::getLine(const NamedDecl *D,
   return Context.getSourceManager().getPresumedLoc(D->getBeginLoc()).getLine();
 }
 
+
 llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
                                               const ASTContext &Context,
                                               llvm::StringRef RootDir,
@@ -128,9 +128,12 @@ llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
   llvm::SmallString<128> File(Context.getSourceManager()
                                   .getPresumedLoc(D->getBeginLoc())
                                   .getFilename());
+  llvm::SmallString<128> Result;
   IsFileInRootDir = false;
+  Result = llvm::sys::path::remove_leading_dotslash(File);
+  Result = llvm::SmallString<128>(llvm::sys::path::convert_to_slash(Result));
   if (RootDir.empty() || !File.starts_with(RootDir))
-    return File;
+    return Result;
   IsFileInRootDir = true;
   llvm::SmallString<128> Prefix(RootDir);
   // replace_path_prefix removes the exact prefix provided. The result of
@@ -139,8 +142,9 @@ llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
   // ends with a / and the result has the desired format.
   if (!llvm::sys::path::is_separator(Prefix.back()))
     Prefix += llvm::sys::path::get_separator();
-  llvm::sys::path::replace_path_prefix(File, Prefix, "");
-  return File;
+  
+  llvm::sys::path::replace_path_prefix(Result, Prefix, "");
+  return Result;
 }
 
 } // namespace doc
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index edd9143f444fed..0bd2ec9d201ae0 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -422,12 +422,15 @@ struct TypedefInfo : public SymbolInfo {
   void merge(TypedefInfo &&I);
 
   TypeInfo Underlying;
-
-  // Inidicates if this is a new C++ "using"-style typedef:
+  // Underlying type declaration
+  SmallString<16> TypeDeclaration;
+  // Indicates if this is a new C++ "using"-style typedef:
   //   using MyVector = std::vector<int>
   // False means it's a C-style typedef:
   //   typedef std::vector<int> MyVector;
   bool IsUsing = false;
+  
+  std::vector<CommentInfo> Description; 
 };
 
 struct BaseRecordInfo : public RecordInfo {
@@ -465,8 +468,9 @@ struct EnumValueInfo {
   // Stores the user-supplied initialization expression for this enumeration
   // constant. This will be empty for implicit enumeration values.
   SmallString<16> ValueExpr;
-
-  std::vector<CommentInfo> Description; /// Comment description of this field.
+  
+  /// Comment description of this field.
+  std::vector<CommentInfo> Description; 
 };
 
 // TODO: Expand to allow for documenting templating.
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index 560647e4e03c1d..f65974a91ee946 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -32,40 +32,40 @@ populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
 
 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D);
 
-void printTemplateParameters(const TemplateParameterList *TemplateParams, llvm::raw_ostream &stream) {
-  stream << "template <";
+void getTemplateParameters(const TemplateParameterList *TemplateParams, 
+                           llvm::raw_ostream &Stream) {
+  Stream << "template <";
   
   for (unsigned i = 0; i < TemplateParams->size(); ++i) {
     if (i > 0) {
-      stream << ", ";
+      Stream << ", ";
     }
 
     const NamedDecl *Param = TemplateParams->getParam(i);
     if (const auto *TTP = llvm::dyn_cast<TemplateTypeParmDecl>(Param)) {
       if (TTP->wasDeclaredWithTypename()) {
-        stream << "typename";
+        Stream << "typename";
       } else {
-        stream << "class";
+        Stream << "class";
       }
-
       if (TTP->isParameterPack()) {
-        stream << "...";
+        Stream << "...";
       }
-      stream << " " << TTP->getNameAsString();
+      Stream << " " << TTP->getNameAsString();
     } else if (const auto *NTTP = llvm::dyn_cast<NonTypeTemplateParmDecl>(Param)) {
-      NTTP->getType().print(stream, NTTP->getASTContext().getPrintingPolicy());
+      NTTP->getType().print(Stream, NTTP->getASTContext().getPrintingPolicy());
       if (NTTP->isParameterPack()) {
-        stream << "...";
+        Stream << "...";
       }
-      stream << " " << NTTP->getNameAsString();
+      Stream << " " << NTTP->getNameAsString();
     } else if (const auto *TTPD = llvm::dyn_cast<TemplateTemplateParmDecl>(Param)) {
-      stream << "template <";
-      printTemplateParameters(TTPD->getTemplateParameters(), stream);
-      stream << "> class " << TTPD->getNameAsString();
+      Stream << "template <";
+      getTemplateParameters(TTPD->getTemplateParameters(), Stream);
+      Stream << "> class " << TTPD->getNameAsString();
     }
   }
 
-  stream << "> ";
+  Stream << "> ";
 }
 
 // Extract the full function prototype from a FunctionDecl including 
@@ -76,9 +76,14 @@ llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl) {
   const ASTContext& Ctx = FuncDecl->getASTContext();
   // If it's a templated function, handle the template parameters
   if (const auto *TmplDecl = FuncDecl->getDescribedTemplate()) {
-    printTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
+    getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
+  }
+  // If it's a const method, add 'const' qualifier
+  if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl)) {
+    if (Method->isVirtual()) {
+      Stream << "virtual ";
+    }
   }
-  
   // Print return type
   FuncDecl->getReturnType().print(Stream, Ctx.getPrintingPolicy());
 
@@ -121,22 +126,50 @@ llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl) {
 
   // If it's a const method, add 'const' qualifier
   if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl)) {
-    if (Method->isConst()) {
+    if (Method->isConst())
       Stream << " const";
-    }
+    if (Method->isPureVirtual()) 
+      Stream << " = 0";
   }
   return Result; // Convert SmallString to std::string for return
 }
 
-// extract full cl for record declaration
+llvm::SmallString<16> getTypeDefDecl(const TypedefDecl *TypeDef) {
+  llvm::SmallString<16> Result;
+  llvm::raw_svector_ostream Stream(Result);
+  const ASTContext& Ctx = TypeDef->getASTContext();
+  Stream << "typedef ";
+  QualType Q = TypeDef->getUnderlyingType();
+  Q.print(Stream, Ctx.getPrintingPolicy());
+  Stream << " " << TypeDef->getNameAsString();
+  return Result;
+}
+
+llvm::SmallString<16> getTypeAlias(const TypeAliasDecl *Alias) {
+  llvm::SmallString<16> Result;
+  llvm::raw_svector_ostream Stream(Result);
+  const ASTContext& Ctx = Alias->getASTContext();
+  if (const auto *TmplDecl = Alias->getDescribedTemplate()) {
+    getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
+  }
+  Stream << "using " 
+         << Alias->getNameAsString() 
+         << " = ";
+  QualType Q = Alias->getUnderlyingType();
+  Q.print(Stream, Ctx.getPrintingPolicy());
+  
+  return Result;
+}
+
+// extract full syntax for record declaration
 llvm::SmallString<16> getRecordPrototype(const CXXRecordDecl *CXXRD) {
-  llvm::SmallString<16> ReturnVal;
+  llvm::SmallString<16> Result;
   LangOptions LangOpts;
   PrintingPolicy Policy(LangOpts);
   Policy.SuppressTagKeyword = false;
   Policy.FullyQualifiedName = true;
   Policy.IncludeNewlines = false;
-  llvm::raw_svector_ostream OS(ReturnVal);
+  llvm::raw_svector_ostream OS(Result);
   if (const auto *TD = CXXRD->getDescribedClassTemplate()) {
     OS << "template <";
     bool FirstParam = true;
@@ -166,7 +199,7 @@ llvm::SmallString<16> getRecordPrototype(const CXXRecordDecl *CXXRD) {
       FirstBase = false;
     }
   }
-  return ReturnVal;
+  return Result;
 }
 
 
@@ -574,7 +607,6 @@ static void parseEnumerators(EnumInfo &I, const EnumDecl *D) {
     ASTContext &Context = E->getASTContext();
     if (RawComment *Comment =
             E->getASTContext().getRawCommentForDeclNoCache(E)) {
-      CommentInfo CInfo;
       Comment->setAttached();
       if (comments::FullComment *Fc = Comment->parse(Context, nullptr, E)) {
         EnumValueInfo &Member = I.Members.back();
@@ -719,7 +751,6 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
   I.ReturnType = getTypeInfoForType(D->getReturnType());
   I.ProtoType = getFunctionPrototype(D);
   parseParameters(I, D);
-
   populateTemplateParameters(I.Template, D);
 
   // Handle function template specializations.
@@ -743,7 +774,8 @@ static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
 
 static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
   assert(D && "Expect non-null FieldDecl in populateMemberTypeInfo");
-
+  if (!D)
+    return;
   ASTContext& Context = D->getASTContext();
   // TODO investigate whether we can use ASTContext::getCommentForDecl instead
   // of this logic. See also similar code in Mapper.cpp.
@@ -752,9 +784,9 @@ static void populateMemberTypeInfo(MemberTypeInfo &I, const FieldDecl *D) {
     return;
 
   Comment->setAttached();
-  if (comments::FullComment *fc = Comment->parse(Context, nullptr, D)) {
+  if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
     I.Description.emplace_back();
-    parseFullComment(fc, I.Description.back());
+    parseFullComment(Fc, I.Description.back());
   }
 }
 
@@ -952,7 +984,7 @@ std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
 emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
          StringRef File, bool IsFileInRootDir, bool PublicOnly) {
   TypedefInfo Info;
-
+  ASTContext& Context = D->getASTContext();
   bool IsInAnonymousNamespace = false;
   populateInfo(Info, D, FC, IsInAnonymousNamespace);
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
@@ -960,6 +992,8 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
 
   Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
   Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
+  Info.TypeDeclaration = getTypeDefDecl(D);
+  
   if (Info.Underlying.Type.Name.empty()) {
     // Typedef for an unnamed type. This is like "typedef struct { } Foo;"
     // The record serializer explicitly checks for this syntax and constructs
@@ -967,7 +1001,13 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
     return {};
   }
   Info.IsUsing = false;
-
+  if (RawComment *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) {
+    Comment->setAttached();
+    if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
+      Info.Description.emplace_back();
+      parseFullComment(Fc, Info.Description.back());
+    }
+  }
   // Info is wrapped in its parent scope so is returned in the second position.
   return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
 }
@@ -978,7 +1018,7 @@ std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
 emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
          StringRef File, bool IsFileInRootDir, bool PublicOnly) {
   TypedefInfo Info;
-
+  ASTContext& Context = D->getASTContext();
   bool IsInAnonymousNamespace = false;
   populateInfo(Info, D, FC, IsInAnonymousNamespace);
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
@@ -986,8 +1026,16 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
 
   Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
   Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
+  Info.TypeDeclaration = getTypeAlias(D);
   Info.IsUsing = true;
-
+  
+  if (RawComment *Comment = D->getASTContext().getRawCommentForDeclNoCache(D)) {
+    Comment->setAttached();
+    if (comments::FullComment *Fc = Comment->parse(Context, nullptr, D)) {
+      Info.Description.emplace_back();
+      parseFullComment(Fc, Info.Description.back());
+    }
+  }
   // Info is wrapped in its parent scope so is returned in the second position.
   return {nullptr, MakeAndInsertIntoParent<TypedefInfo &&>(std::move(Info))};
 }
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 d17fe54bb4009d..9fdf43bfbea1e9 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -28,7 +28,8 @@ video {
     --text1-dark:#ffffff;
     --text2-dark:#cccccc;
     --surface1-dark:#161212;
-    --surface2-dark:#272424
+    --surface2-dark:#272424;
+    --sidebar-width: 300px;
 }
 :root {
     color-scheme:light;
@@ -77,188 +78,22 @@ html {
 }
 
 html, body {
+    font-family:Inter,sans-serif;
     min-height: 100vh;
     margin: 0;
     padding: 0;
     width: 100%;
+    height: 100%;
 }
 
 .container {
     display: flex;
     position: relative;
     flex-direction: row;
-    top: 60px;
-    height: calc(100% - 60px);
-    box-sizing: border-box;
-}
-
-body, html {
-    font-family:Inter,sans-serif;
-    margin: 0;
-    padding: 0;
-    height: 100%;
-}
-
-/* Navbar Styles */
-.navbar {
-    background-color: var(--surface2);
-    border-bottom: 1px solid var(--text2);
-    position: fixed;
-    width: 100%;
-    top: 0;
-    left: 0;
-    height: 60px; /* Adjust as needed */
-    color: white;
-    display: flex;
-    align-items: center;
-    padding: 0 20px;
+    height: calc(100% - var(--sidebar-width));
     box-sizing: border-box;
-    z-index: 1000;
-}
-
-.navbar__container {
-    display:flex;
-    justify-content:space-between;
-    align-items:center;
-    padding:1rem;
-    color:var(--text1);
-    max-width:2048px;
-    margin:auto
-}
-
-.navbar__logo {
-    display:flex;
-    align-items:center;
-    height:40px
-}
-
-.navbar__logo a {
-    display:flex;
-    align-items:center;
-    text-decoration:none;
-    height:100%
 }
 
-.navbar__logo img {
-    height:100%;
-    width:auto
-}
-
-.navbar__toggle {
-    background:0 0;
-    color:var(--text2);
-    border:none;
-    cursor:pointer;
-    font-size:1.5rem;
-    width:2.5rem;
-    height:2.5rem;
-    margin-left:auto
-}
-
-.navbar__toggle:hover {
-    color:var(--text1)
-}
-
- at media(min-width:769px) {
-    .navbar__toggle {
-        display:none
-    }
-}
-
-.navbar__menu {
-    display:flex;
-    justify-content:space-between;
-    align-items:center;
-    list-style:none;
-    margin:0;
-    padding:0;
-    gap:.25rem;
-    margin-left:auto
-}
-
- at media(max-width:768px) {
-    .navbar__menu {
-        flex-direction:column;
-        justify-content:flex-start;
-        width:100%;
-        background-color:var(--surface2);
-        position:fixed;
-        top:0;
-        left:0;
-        right:0;
-        bottom:0;
-        padding:1.5rem;
-        transform:translateX(100%);
-        transition:transform .5s ease-in-out
-    }
-}
-
- at media(max-width:768px) {
-    .navbar__menu.active {
-        transform:translateX(0)
-    }
-}
-
-.navbar__close {
-    background:0 0;
-    border:none;
-    cursor:pointer;
-    font-size:1.5rem;
-    color:var(--text2);
-    margin-left:auto
-}
-
-.navbar__close:hover {
-    color:var(--text1)
-}
-
- at media(min-width:769px) {
-    .navbar__close {
-        display:none
-    }
-}
-
-.navbar__links {
-    display:flex;
-    gap:1rem;
-    align-items:center;
-    margin:0;
-    padding:0
-}
-
- at media(max-width:768px) {
-    .navbar__links {
-        flex-direction:column
-    }
-}
-
-.navbar__item {
-    list-style-type:none
-}
-
-.navbar__link {
-    color:var(--text2);
-    text-decoration:none;
-    padding:.5rem
-}
-
-.navbar__link:hover {
-    color:var(--text1)
-}
-
-.navbar__theme-toggle-button {
-    background:0 0;
-    color:var(--text2);
-    border:none;
-    cursor:pointer;
-    font-size:1.5rem;
-    width:2.5rem;
-    height:2.5rem
-}
-
-.navbar__theme-toggle-button:hover {
-    color:var(--text1)
-}
 
 .hero__container {
     margin-top:1rem;
@@ -394,7 +229,6 @@ body, html {
     text-align: left;
 }
 
-
 .block-command-command {
     font-weight: bold;
 }
@@ -403,9 +237,7 @@ body, html {
     font-size: 1.2rem;
 }
 
-.code-highlight:hover {
-    text-decoration: underline;
-}
+
 
 .delimiter-container {
     padding: 0.5rem 1rem;
@@ -416,9 +248,11 @@ body, html {
     width: 5px;
     cursor: col-resize;
     border-left: 3px solid black;
-    top: 60px;
-    left: 250px;
-    position: relative;
+    top: 0;
+    left: var(--sidebar-width);
+    height: 100%;
+    position: fixed;
+    z-index: 1000;
 }
 
 .resizer:hover {
@@ -426,14 +260,14 @@ body, html {
 }
 
 .sidebar {
-    width: 250px;
+    width: var(--sidebar-width);
     top: 0;
     left: 0;
     height: 100%;
+    padding-left: 1rem;
     position: fixed;
     background-color: var(--surface2);
     display: flex;
-    border-left: 1px solid var(--text2);
     flex-direction: column;
     overflow-y: auto;
     scrollbar-width: thin;
@@ -451,31 +285,27 @@ body, html {
     list-style-type: none;
 }
 
-.sidebar ul li {
-    padding-right: 1rem;
-    padding-left: 2rem;
-    padding-top: 0.25rem;
-    padding-bottom: 0.25rem;
-}
-
 .sidebar-section {
+    padding-right: 1rem;
     font-size:1.5rem;
     font-weight: bold;
-    margin-bottom: 1rem;
-    padding: 3rem;
 }
 
-.sidebar-section a {
-    color: var(--brand)
+.sidebar-section h2 {
+    font-size:1.25rem;
+}
+
+.sidebar-section h1 {
+    margin: 1rem;
+    font-size:2rem;
 }
 
-/* Content */
 .content {
     background-color: var(--text1-inverse);
     padding: 20px;
-    left: 250px;
+    left: var(--sidebar-width);
     position: relative;
-    width: calc(100% - 250px);
+    width: calc(100% - var(--sidebar-width));
     max-width: 960px;
     margin-right: auto;
 }
@@ -484,14 +314,51 @@ body, html {
     color: var(--text1);
 }
 
+.sidebar-item-container {
+    font-size: 16px;
+    padding-left: 5px;
+    color: #0e79c0;
+}
+
+
 .sidebar-item-container:hover {
-    width: 100%;
-    background-color: grey;
+    border-left: 3px solid var(--brand);
 }
 
-.sidebar-item-container:hover a {
-    width: 100%;
-    color: var(--text1-inverse);
+.sidebar-section input[type="checkbox"] {
+    display: none;
+}
+
+.sidebar-section label {
+    cursor: pointer;
+    display: block;
+    font-size: 1.1rem;
+    font-weight: bold;
+    padding: 1rem 2rem;
+    position: relative;
+}
+
+.sidebar-section label::before {
+    content: '\25B6';
+    position: absolute;
+    left: 1rem;
+    font-size: 0.8rem; 
+    color: grey;
+    transition: transform 0.3s ease;
+}
+
+
+.sidebar-section input[type="checkbox"]:checked + label::before {
+    transform: rotate(90deg);
+}
+
+.sidebar-section ul {
+    display: none;
+    padding-left: 1rem;
+}
+
+.sidebar-section input[type="checkbox"]:checked + label + ul {
+    display: block;
 }
 
 .class-container {
@@ -501,4 +368,16 @@ body, html {
 a, a:visited, a:hover, a:active {
     text-decoration: none;
     color: inherit;
-}
\ No newline at end of file
+}
+
+.definition {
+    margin-top: 10px;
+}
+
+.definition a {
+    color: #0e84b5;
+}
+
+.definition a:hover {
+    text-decoration: underline;
+}
diff --git a/clang-tools-extra/clang-doc/assets/class-template.mustache b/clang-tools-extra/clang-doc/assets/class-template.mustache
index e611000474f14e..313d578b30c8e4 100644
--- a/clang-tools-extra/clang-doc/assets/class-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/class-template.mustache
@@ -19,91 +19,121 @@
     <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js"></script>
 </head>
 <body>
-<nav class="navbar">
-    <div class="navbar__container">
-        {{#ProjectName}}
-        <div class="navbar__logo">
-            {{ProjectName}}
-        </div>
-        {{/ProjectName}}
-    </div>
-</nav>
 <main>
     <!-- Sidebar -->
     <div class="container">
         <div class="sidebar">
-            <h2>{{RecordType}} {{Name}}</h2>
             <ul>
-                {{#PublicMembers}}
-                <li class="sidebar-section sidebar-item-container">
-                    <a href="#PublicMethods">Public Members</a>
+
+                <li class="sidebar-section">
+                    <h1>
+                        {{ProjectName}}
+                    </h1>
                 </li>
-                <ul>
-                    {{#Obj}}
-                    <li class="sidebar-item-container">
-                        <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
-                    </li>
-                    {{/Obj}}
-                </ul>
-                {{/PublicMembers}}
-                {{#ProtectedMembers}}
-                    <li class="sidebar-section">
-                        <a class="sidebar-item" href="#PublicMethods">Protected Members</a>
-                    </li>
+                <li class="sidebar-section">
+                    <h2>
+                        {{RecordType}} {{Name}}
+                    </h2>
+                </li>
+                <!-- Members -->
+                <li class="sidebar-section">
+                    <input type="checkbox" id="members-toggle">
+                    <label for="members-toggle">Members</label>
                     <ul>
-                        {{#Obj}}
-                            <li class="sidebar-item-container">
-                                <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
-                            </li>
-                        {{/Obj}}
+                        {{#PublicMembers}}
+                        <li class="sidebar-subsection">
+                            <input type="checkbox" id="public-members-toggle">
+                            <label for="public-members-toggle">Public</label>
+                            <ul>
+                                {{#Obj}}
+                                    <li class="sidebar-item-container">
+                                        <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+                                    </li>
+                                {{/Obj}}
+                            </ul>
+                        </li>
+                        {{/PublicMembers}}
+                        {{#ProtectedMembers}}
+                        <li class="sidebar-subsection">
+                            <input type="checkbox" id="protected-members-toggle">
+                            <label for="protected-members-toggle">Protected</label>
+                            <ul>
+                                {{#Obj}}
+                                    <li class="sidebar-item-container">
+                                        <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+                                    </li>
+                                {{/Obj}}
+                            </ul>
+                        </li>
+                        {{/ProtectedMembers}}
                     </ul>
-                {{/ProtectedMembers}}
-                {{#PublicFunction}}
-                <li class="sidebar-section sidebar-item-container">
-                    <a class="sidebar-item" href="#PublicMethods">Public Method</a>
                 </li>
-                <ul>
-                    {{#Obj}}
-                    <li class="sidebar-item-container">
-                        <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
-                    </li>
-                    {{/Obj}}
-                </ul>
-                {{/PublicFunction}}
-                {{#ProtectedFunction}}
+                <!-- Methods -->
                 <li class="sidebar-section">
-                    <a href="#ProtectedFunction">Protected Method</a>
+                    <input type="checkbox" id="methods-toggle">
+                    <label for="methods-toggle">Methods</label>
+                    <ul>
+                        {{#PublicFunction}}
+                            <li class="sidebar-subsection">
+                                <input type="checkbox" id="public-methods-toggle">
+                                <label for="public-methods-toggle">Public</label>
+                                <ul>
+                                    {{#Obj}}
+                                        <li class="sidebar-item-container">
+                                            <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                                        </li>
+                                    {{/Obj}}
+                                </ul>
+                            </li>
+                        {{/PublicFunction}}
+                        {{#ProtectedFunction}}
+                            <li class="sidebar-subsection">
+                                <input type="checkbox" id="protected-methods-toggle">
+                                <label for="protected-methods-toggle">Protected</label>
+                                <ul>
+                                    {{#Obj}}
+                                        <li class="sidebar-item-container">
+                                            <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                                        </li>
+                                    {{/Obj}}
+                                </ul>
+                            </li>
+                        {{/ProtectedFunction}}
+                    </ul>
                 </li>
-                <ul>
-                    {{#Obj}}
-                    <li class="sidebar-item-container">
-                        <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
-                    </li>
-                    {{/Obj}}
-                </ul>
-                {{/ProtectedFunction}}
+                <!-- Enums -->
                 {{#Enums}}
                 <li class="sidebar-section">
-                    <a href="#Enums">Enums</a>
+                    <input type="checkbox" id="enums-toggle">
+                    <label for="enums-toggle">Enums</label>
+                    <ul>
+                        {{#Obj}}
+                            <li class="sidebar-item-container">
+                                <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
+                            </li>
+                        {{/Obj}}
+                    </ul>
                 </li>
-                <ul>
-                    {{#Obj}}
-                    <li class="sidebar-item-container">
-                        <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
-                    </li>
-                    {{/Obj}}
-                </ul>
                 {{/Enums}}
-                {{#Typedef}}
+                <!-- Typedefs -->
+                {{#Typedefs}}
                 <li class="sidebar-section">
-                    <a href="#ProtectedFunction">Typedef</a>
-                </li>
-                {{/Typedef}}
+                    <input type="checkbox" id="enums-toggle">
+                    <label for="enums-toggle">Typedefs</label>
+                    <ul>
+                        {{#Obj}}
+                        <li class="sidebar-item-container">
+                            <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                        </li>
+                        {{/Obj}}
+                    </ul>
+                </li>    
+                {{/Typedefs}}
             </ul>
         </div>
         <div class="resizer" id="resizer"></div>
         <div class="content">
-            <section class="hero section-container">
+            <section id="class" class="hero section-container">
                 <pre>
                     <code class="language-cpp hero__title-large">
 {{&FullName}}
@@ -115,6 +145,11 @@
                     {{>Comments}}
                 </div>
                 {{/RecordComments}}
+                {{#Location}}
+                <div class="definition">
+                    Defined on line {{LineNumber}} at <a href="{{FileURL}}">{{Filename}}</a>
+                </div>    
+                {{/Location}}
             </section>
             {{#PublicMembers}}
             <section id="PublicMembers" class="section-container">
@@ -196,11 +231,16 @@
                 </ul>
             </section>
             {{/Record}}
-            {{#Typedef}}
-            <section class="section-container">
-                <h2 id="Enums">Enums</h2>
+            {{#Typedefs}}
+            <section id="Typedefs" class="section-container">
+                <h2>Typedefs</h2>
+                <div>
+                    {{#Obj}}
+{{>TypedefPartial}}
+                    {{/Obj}}
+                </div>
             </section>
-            {{/Typedef}}
+            {{/Typedefs}}
         </div>
     </div>
 </main>
diff --git a/clang-tools-extra/clang-doc/assets/enum-template.mustache b/clang-tools-extra/clang-doc/assets/enum-template.mustache
index 54608842eaf837..a09322ce44c174 100644
--- a/clang-tools-extra/clang-doc/assets/enum-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/enum-template.mustache
@@ -40,8 +40,8 @@
     </div>
     {{/EnumComments}}
     {{#Location}}
-    <div>
-        Defined at line {{LineNumber}} of file {{Filename}}
+    <div class="definition">
+        Defined on line {{LineNumber}} at <a href="{{FileURL}}">{{Filename}}</a>
     </div>
     {{/Location}}
 </div>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/function-template.mustache b/clang-tools-extra/clang-doc/assets/function-template.mustache
index d023fca17ec626..8d0130f5d3591f 100644
--- a/clang-tools-extra/clang-doc/assets/function-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/function-template.mustache
@@ -19,5 +19,10 @@
             {{>Comments}}
         </div>
         {{/FunctionComments}}
+        {{#Location}}
+        <div class="definition">
+            Defined on line {{LineNumber}} at <a href="{{FileURL}}">{{Filename}}</a>
+        </div>
+        {{/Location}}
     </div>
 </div>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/assets/typedef-template.mustache b/clang-tools-extra/clang-doc/assets/typedef-template.mustache
new file mode 100644
index 00000000000000..6d72b31563ee82
--- /dev/null
+++ b/clang-tools-extra/clang-doc/assets/typedef-template.mustache
@@ -0,0 +1,29 @@
+{{! 
+    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
+    
+    This file defines the template for type definition
+}}
+<div class="delimiter-container">
+    <div id="{{ID}}">
+        {{! Function Prototype }}
+        <pre>
+            <code class="language-cpp code-clang-doc">
+{{TypeDeclaration}};
+            </code>
+        </pre>
+        {{#TypeDefComments}}
+        <div>
+            {{>Comments}}
+        </div>
+        {{/TypeDefComments}}
+        {{#Location}}
+        <div class="definition">
+            <div>
+                Defined on line {{LineNumber}} at <a href="{{FileURL}}">{{Filename}}</a>
+            </div>
+        </div>
+        {{/Location}}
+    </div>
+</div>
\ No newline at end of file
diff --git a/clang-tools-extra/clang-doc/tool/CMakeLists.txt b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
index eccbc99a7ecc4b..b2d0c3a1feab28 100644
--- a/clang-tools-extra/clang-doc/tool/CMakeLists.txt
+++ b/clang-tools-extra/clang-doc/tool/CMakeLists.txt
@@ -26,7 +26,8 @@ set(assets
   comments-template.mustache
   enum-template.mustache      
   function-template.mustache
-  namespace-template.mustache      
+  namespace-template.mustache
+  typedef-template.mustache      
   clang-doc-mustache.css
   clang-doc-default-stylesheet.css
 )
diff --git a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
index 054856c158dd86..2a8a346e57bd79 100644
--- a/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ b/clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -230,7 +230,6 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
   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");
@@ -247,12 +246,11 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
       = appendPathNative(AssetsPath, "function-template.mustache");
   llvm::SmallString<128> CommentTemplate
       = appendPathNative(AssetsPath, "comments-template.mustache");
+  llvm::SmallString<128> TypedefTemplate
+      = appendPathNative(AssetsPath, "typedef-template.mustache");
   llvm::SmallString<128> IndexJS
       = appendPathNative(AssetsPath, "mustache-index.js");
   
-  CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
-  CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
-                               std::string(DefaultStylesheet));
   CDCtx.MustacheTemplates.insert({"namespace-template", 
                                   NamespaceTemplate.c_str()});
   CDCtx.MustacheTemplates.insert({"class-template",
@@ -263,6 +261,11 @@ llvm::Error getMustacheHtmlFiles(const char *Argv0,
                                   FunctionTemplate.c_str()});
   CDCtx.MustacheTemplates.insert({"comments-template", 
                                   CommentTemplate.c_str()});
+  CDCtx.MustacheTemplates.insert({"typedef-template",
+                                  TypedefTemplate.c_str()});
+  CDCtx.JsScripts.insert(CDCtx.JsScripts.begin(), IndexJS.c_str());
+  CDCtx.UserStylesheets.insert(CDCtx.UserStylesheets.begin(),
+                               std::string(DefaultStylesheet));
   
   return llvm::Error::success();
 }
@@ -428,16 +431,6 @@ Example usage for a project using a compile commands database:
 
   sortUsrToInfo(USRToInfo);
   
-  for (const auto &G : USRToInfo) {
-    const clang::doc::Info& I = *G.getValue();
-    
-    if (I.IT == clang::doc::InfoType::IT_record) {
-      const clang::doc::RecordInfo& R = *static_cast<const clang::doc::RecordInfo *>(&I);
-      llvm::outs() << "Full Name from record " << R.Name << " Full Name: " << R.FullName << "\n";
-    }
-    
-  }
-
   // Ensure the root output directory exists.
   if (std::error_code Err = llvm::sys::fs::create_directories(OutDirectory);
       Err != std::error_code()) {

>From 2b490ed6636256590adf700d9721e6f37580e166 Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Mon, 11 Nov 2024 22:57:44 -0500
Subject: [PATCH 62/62] add source ranges

---
 clang-tools-extra/clang-doc/BitcodeReader.cpp |   4 +-
 clang-tools-extra/clang-doc/BitcodeWriter.cpp |  14 +-
 clang-tools-extra/clang-doc/HTMLGenerator.cpp |   6 +-
 .../clang-doc/HTMLMustacheGenerator.cpp       |  54 +++--
 clang-tools-extra/clang-doc/MDGenerator.cpp   |   6 +-
 clang-tools-extra/clang-doc/Mapper.cpp        |  24 +-
 clang-tools-extra/clang-doc/Mapper.h          |   3 +-
 clang-tools-extra/clang-doc/Representation.h  |  23 +-
 clang-tools-extra/clang-doc/Serialize.cpp     |  83 +++----
 clang-tools-extra/clang-doc/Serialize.h       |  28 +--
 clang-tools-extra/clang-doc/YAMLGenerator.cpp |   2 +-
 .../clang-doc/assets/clang-doc-mustache.css   |   4 +-
 .../clang-doc/assets/class-template.mustache  |  84 +++----
 .../clang-doc/assets/mustache-index.js        |   1 -
 .../assets/namespace-template.mustache        | 211 +++++++++++++-----
 .../unittests/clang-doc/ClangDocTest.cpp      |   2 +-
 16 files changed, 343 insertions(+), 206 deletions(-)

diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index 468fd7e023deaf..0aa78a807b0268 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -85,7 +85,7 @@ llvm::Error decodeRecord(const Record &R, std::optional<Location> &Field,
   if (R[0] > INT_MAX)
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "integer too large to parse");
-  Field.emplace((int)R[0], Blob, (bool)R[1]);
+  Field.emplace((int)R[0], (int)R[1], Blob, (bool)R[2]);
   return llvm::Error::success();
 }
 
@@ -135,7 +135,7 @@ llvm::Error decodeRecord(const Record &R,
   if (R[0] > INT_MAX)
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "integer too large to parse");
-  Field.emplace_back((int)R[0], Blob, (bool)R[1]);
+  Field.emplace_back((int)R[0], (int)R[1], Blob, (bool)R[2]);
   return llvm::Error::success();
 }
 
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index 96f00d67470ec3..2b08e29ded75be 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -74,16 +74,19 @@ static void StringAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
 static void LocationAbbrev(std::shared_ptr<llvm::BitCodeAbbrev> &Abbrev) {
   AbbrevGen(
       Abbrev,
-      {// 0. Fixed-size integer (line number)
+      {// 0. Start fixed-size integer (line number)
        llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
                              BitCodeConstants::LineNumberSize),
-       // 1. Boolean (IsFileInRootDir)
+       // 1. End fixed-size integer (line number)
+       llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
+                             BitCodeConstants::LineNumberSize),
+       // 2. Boolean (IsFileInRootDir)
        llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
                              BitCodeConstants::BoolSize),
-       // 2. Fixed-size integer (length of the following string (filename))
+       // 3. Fixed-size integer (length of the following string (filename))
        llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Fixed,
                              BitCodeConstants::StringLengthSize),
-       // 3. The string blob
+       // 4. The string blob
        llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Blob)});
 }
 
@@ -358,7 +361,8 @@ void ClangDocBitcodeWriter::emitRecord(const Location &Loc, RecordId ID) {
   if (!prepRecordData(ID, true))
     return;
   // FIXME: Assert that the line number is of the appropriate size.
-  Record.push_back(Loc.LineNumber);
+  Record.push_back(Loc.StartLineNumber);
+  Record.push_back(Loc.EndLineNumber);
   assert(Loc.Filename.size() < (1U << BitCodeConstants::StringLengthSize));
   Record.push_back(Loc.IsFileInRootDir);
   Record.push_back(Loc.Filename.size());
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index ed65ac965114b5..07c4bb5765e15e 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -496,18 +496,18 @@ writeFileDefinition(const Location &L,
                     std::optional<StringRef> RepositoryUrl = std::nullopt) {
   if (!L.IsFileInRootDir || !RepositoryUrl)
     return std::make_unique<TagNode>(
-        HTMLTag::TAG_P, "Defined at line " + std::to_string(L.LineNumber) +
+        HTMLTag::TAG_P, "Defined at line " + std::to_string(L.StartLineNumber) +
                             " of file " + L.Filename);
   SmallString<128> FileURL(*RepositoryUrl);
   llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
   auto Node = std::make_unique<TagNode>(HTMLTag::TAG_P);
   Node->Children.emplace_back(std::make_unique<TextNode>("Defined at line "));
   auto LocNumberNode =
-      std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.LineNumber));
+      std::make_unique<TagNode>(HTMLTag::TAG_A, std::to_string(L.StartLineNumber));
   // The links to a specific line in the source code use the github /
   // googlesource notation so it won't work for all hosting pages.
   LocNumberNode->Attributes.emplace_back(
-      "href", (FileURL + "#" + std::to_string(L.LineNumber)).str());
+      "href", (FileURL + "#" + std::to_string(L.StartLineNumber)).str());
   Node->Children.emplace_back(std::move(LocNumberNode));
   Node->Children.emplace_back(std::make_unique<TextNode>(" of file "));
   auto LocFileNode = std::make_unique<TagNode>(
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 313d1c5346961c..03255da637a988 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -219,7 +219,7 @@ Value extractValue(const std::optional<Location> &Loc,
   if (Loc.has_value()) {
     Location L = *Loc;
     SmallString<128> Filename(llvm::sys::path::filename(L.Filename));
-    Obj.insert({"LineNumber", L.LineNumber});
+    Obj.insert({"LineNumber", L.StartLineNumber});
     Obj.insert({"Filename", Filename});
     if (!RepositoryUrl)
       return Obj;
@@ -227,18 +227,17 @@ Value extractValue(const std::optional<Location> &Loc,
     SmallString<128> FileURL(*RepositoryUrl);
     llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, "blob/main");
     llvm::sys::path::append(FileURL, llvm::sys::path::Style::posix, L.Filename);
-    FileURL += "#L" + std::to_string(L.LineNumber);
+    FileURL += "#L" + std::to_string(L.StartLineNumber) + 
+               "-L" + std::to_string(L.EndLineNumber);
     Obj.insert({"FileURL", FileURL});
   }
   return Obj;
 }
 
-Value extractValue(const Reference &I, StringRef CurrentDirectory) {
-  llvm::SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
-  llvm::sys::path::append(Path, I.getFileBaseName() + ".html");
-  llvm::sys::path::native(Path, llvm::sys::path::Style::posix);
+Value extractValue(const Reference &I) {
+  std::string Link = llvm::toHex(llvm::toStringRef(I.USR)) + ".html";
   Object Obj = Object();
-  Obj.insert({"Link", Path});
+  Obj.insert({"Link", Link});
   Obj.insert({"Name", I.Name});
   Obj.insert({"QualName", I.QualName});
   Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
@@ -299,7 +298,7 @@ void replaceSubstring(llvm::SmallString<256> &Input,
 SmallString<64> extractLink(const Reference& R) {
   std::string HexId = llvm::toHex(llvm::toStringRef(R.USR));
   SmallString<16> Name = Index[HexId];
-  SmallString<64> Link({Name, ".html"});
+  SmallString<64> Link({Name, ".html#", HexId});
   return Link;
 }
 
@@ -378,7 +377,7 @@ Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
               extractFunctionPrototype(I.ProtoType, I.Params, I.ReturnType)});
   Obj.insert({"ID", llvm::toHex(llvm::toStringRef(I.USR))});
   Obj.insert({"Access", getAccessSpelling(I.Access).str()});
-  Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
+  Obj.insert({"ReturnType", extractValue(I.ReturnType.Type)});
   Value ParamArr = Array();
   for (const auto Val : llvm::enumerate(I.Params)) {
     Value V = Object();
@@ -443,14 +442,14 @@ void extractScopeChildren(const ScopeChildren &S, Object &Obj,
                           const ClangDocContext &CDCtx) {
   Value ArrNamespace = Array();
   for (const Reference& Child : S.Namespaces)
-    ArrNamespace.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
+    ArrNamespace.getAsArray()->emplace_back(extractValue(Child));
   
   if (!ArrNamespace.getAsArray()->empty())
     Obj.insert({"Namespace", Object{{"Links", ArrNamespace}}});
   
   Value ArrRecord = Array();
   for (const Reference& Child : S.Records)
-    ArrRecord.getAsArray()->emplace_back(extractValue(Child, ParentInfoDir));
+    ArrRecord.getAsArray()->emplace_back(extractValue(Child));
   
   if (!ArrRecord.getAsArray()->empty())
     Obj.insert({"Record", Object{{"Links", ArrRecord}}});
@@ -470,15 +469,23 @@ void extractScopeChildren(const ScopeChildren &S, Object &Obj,
     else
       ArrFunction.getAsArray()->emplace_back(F);
   }  
-  if (!ArrFunction.getAsArray()->empty())
+  
+  bool HasFunction = !ArrFunction.getAsArray()->empty();
+  bool HasPublicFunction = !PublicFunction.getAsArray()->empty();
+  bool HasProtectedFunction = !ProtectedFunction.getAsArray()->empty();
+  
+  if (HasFunction) 
     Obj.insert({"Function", Object{{"Obj", ArrFunction}}});
   
-  if (!PublicFunction.getAsArray()->empty())
+  if (HasPublicFunction)
     Obj.insert({"PublicFunction", Object{{"Obj", PublicFunction}}});
   
-  if (!ProtectedFunction.getAsArray()->empty())
+  if (HasProtectedFunction)
     Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunction}}});
   
+  if (HasFunction || HasPublicFunction || HasProtectedFunction)
+    Obj.insert({"FunctionFlag", true});
+  
   
   Value ArrEnum = Array();
   for (const EnumInfo& Child : S.Enums)
@@ -493,11 +500,6 @@ void extractScopeChildren(const ScopeChildren &S, Object &Obj,
   
   if (!ArrTypedefs.getAsArray()->empty())
     Obj.insert({"Typedefs", Object{{"Obj", ArrTypedefs }}});
-  
-  
-  llvm::raw_fd_ostream os(1, false);
-  llvm::json::OStream jStream(os, /*Indent=*/2);
-  jStream.value(ArrTypedefs);
 }
 
 Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
@@ -519,6 +521,7 @@ Value extractValue(const NamespaceInfo &I, const ClangDocContext &CDCtx) {
     NamespaceValue.insert({"NamespaceComments", ArrDesc });
   }
   extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
+  
   return NamespaceValue;
 }
 
@@ -561,13 +564,20 @@ Value extractValue(const RecordInfo &I, const ClangDocContext &CDCtx) {
     else if (Member.Access == AccessSpecifier::AS_private)
       PrivateMembers.getAsArray()->emplace_back(MemberValue);
   }
-  if (!PublicMembers.getAsArray()->empty())
+  bool HasPublicMembers = !PublicMembers.getAsArray()->empty();
+  bool HasProtectedMembers = !ProtectedMembers.getAsArray()->empty();
+  bool HasPrivateMembers = !PrivateMembers.getAsArray()->empty();
+  
+  if (HasPublicMembers)
     RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
-  if (!ProtectedMembers.getAsArray()->empty())
+  if (HasProtectedMembers)
     RecordValue.insert({"ProtectedMembers", Object{{"Obj", ProtectedMembers}}});
-  if (!PrivateMembers.getAsArray()->empty())
+  if (HasPrivateMembers)
     RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
   
+  if (HasPublicMembers || HasProtectedMembers || HasPrivateMembers)
+    RecordValue.insert({"MemberFlag", true});
+  
   return RecordValue;
 }
 
diff --git a/clang-tools-extra/clang-doc/MDGenerator.cpp b/clang-tools-extra/clang-doc/MDGenerator.cpp
index 795eb4b904e3ef..37b1ee2d39e101 100644
--- a/clang-tools-extra/clang-doc/MDGenerator.cpp
+++ b/clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -54,13 +54,13 @@ static void writeFileDefinition(const ClangDocContext &CDCtx, const Location &L,
                                 raw_ostream &OS) {
 
   if (!CDCtx.RepositoryUrl) {
-    OS << "*Defined at " << L.Filename << "#" << std::to_string(L.LineNumber)
+    OS << "*Defined at " << L.Filename << "#" << std::to_string(L.StartLineNumber)
        << "*";
   } else {
-    OS << "*Defined at [" << L.Filename << "#" << std::to_string(L.LineNumber)
+    OS << "*Defined at [" << L.Filename << "#" << std::to_string(L.StartLineNumber)
        << "](" << StringRef{*CDCtx.RepositoryUrl}
        << llvm::sys::path::relative_path(L.Filename) << "#"
-       << std::to_string(L.LineNumber) << ")"
+       << std::to_string(L.StartLineNumber) << ")"
        << "*";
   }
   OS << "\n\n";
diff --git a/clang-tools-extra/clang-doc/Mapper.cpp b/clang-tools-extra/clang-doc/Mapper.cpp
index 53f9f129c187e8..f2c6e6bcb15bdc 100644
--- a/clang-tools-extra/clang-doc/Mapper.cpp
+++ b/clang-tools-extra/clang-doc/Mapper.cpp
@@ -31,6 +31,21 @@ void MapASTVisitor::HandleTranslationUnit(ASTContext &Context) {
   TraverseDecl(Context.getTranslationUnitDecl());
 }
 
+Location MapASTVisitor::getDeclLocation(const NamedDecl *D) const {
+  bool IsFileInRootDir;
+  llvm::SmallString<128> File =
+      getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
+  ASTContext &Context = D->getASTContext();
+  int Start = Context.getSourceManager()
+                     .getPresumedLoc(D->getBeginLoc())
+                     .getLine();
+  int End = Context.getSourceManager()
+                   .getPresumedLoc(D->getEndLoc())
+                   .getLine();
+  
+  return Location(Start, End, File, IsFileInRootDir);
+}
+
 template <typename T>
 bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
   // If we're looking a decl not in user files, skip this decl.
@@ -59,8 +74,7 @@ bool MapASTVisitor::mapDecl(const T *D, bool IsDefinition) {
   llvm::SmallString<128> File =
       getFile(D, D->getASTContext(), CDCtx.SourceRoot, IsFileInRootDir);
   auto I = serialize::emitInfo(D, getComment(D, D->getASTContext()),
-                               getLine(D, D->getASTContext()), File,
-                               IsFileInRootDir, CDCtx.PublicOnly);
+                               getDeclLocation(D), CDCtx.PublicOnly);
 
   // A null in place of I indicates that the serializer is skipping this decl
   // for some reason (e.g. we're only reporting public decls).
@@ -115,12 +129,6 @@ MapASTVisitor::getComment(const NamedDecl *D, const ASTContext &Context) const {
   return nullptr;
 }
 
-int MapASTVisitor::getLine(const NamedDecl *D,
-                           const ASTContext &Context) const {
-  return Context.getSourceManager().getPresumedLoc(D->getBeginLoc()).getLine();
-}
-
-
 llvm::SmallString<128> MapASTVisitor::getFile(const NamedDecl *D,
                                               const ASTContext &Context,
                                               llvm::StringRef RootDir,
diff --git a/clang-tools-extra/clang-doc/Mapper.h b/clang-tools-extra/clang-doc/Mapper.h
index 75c8e947c8f903..e1dd34ea6f0cc6 100644
--- a/clang-tools-extra/clang-doc/Mapper.h
+++ b/clang-tools-extra/clang-doc/Mapper.h
@@ -45,7 +45,8 @@ class MapASTVisitor : public clang::RecursiveASTVisitor<MapASTVisitor>,
 private:
   template <typename T> bool mapDecl(const T *D, bool IsDefinition);
 
-  int getLine(const NamedDecl *D, const ASTContext &Context) const;
+  Location getDeclLocation(const NamedDecl *D) const;
+  
   llvm::SmallString<128> getFile(const NamedDecl *D, const ASTContext &Context,
                                  StringRef RootDir,
                                  bool &IsFileInRootDir) const;
diff --git a/clang-tools-extra/clang-doc/Representation.h b/clang-tools-extra/clang-doc/Representation.h
index 0bd2ec9d201ae0..3c7ada38b2ab89 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -245,19 +245,23 @@ struct MemberTypeInfo : public FieldTypeInfo {
 };
 
 struct Location {
-  Location(int LineNumber = 0, StringRef Filename = StringRef(),
+  Location(int StartLineNumber = 0, 
+           int EndLineNumber = 0,
+           StringRef Filename = StringRef(),
            bool IsFileInRootDir = false)
-      : LineNumber(LineNumber), Filename(Filename),
+      : StartLineNumber(StartLineNumber),
+        EndLineNumber(EndLineNumber),
+        Filename(Filename),
         IsFileInRootDir(IsFileInRootDir) {}
 
   bool operator==(const Location &Other) const {
-    return std::tie(LineNumber, Filename) ==
-           std::tie(Other.LineNumber, Other.Filename);
+    return std::tie(StartLineNumber, EndLineNumber, Filename) ==
+           std::tie(Other.StartLineNumber, Other.EndLineNumber, Other.Filename);
   }
 
   bool operator!=(const Location &Other) const {
-    return std::tie(LineNumber, Filename) !=
-           std::tie(Other.LineNumber, Other.Filename);
+    return std::tie(StartLineNumber, Filename) !=
+           std::tie(Other.StartLineNumber, Other.Filename);
   }
 
   // This operator is used to sort a vector of Locations.
@@ -265,11 +269,12 @@ struct Location {
   // sort is enough, the order is only needed to call std::unique after sorting
   // the vector.
   bool operator<(const Location &Other) const {
-    return std::tie(LineNumber, Filename) <
-           std::tie(Other.LineNumber, Other.Filename);
+    return std::tie(StartLineNumber, Filename) <
+           std::tie(Other.StartLineNumber, Other.Filename);
   }
 
-  int LineNumber = 0;           // Line number of this Location.
+  int StartLineNumber = 0;      // Line number of this Location.
+  int EndLineNumber = 0;        // End line number of this Location.
   SmallString<32> Filename;     // File for this Location.
   bool IsFileInRootDir = false; // Indicates if file is inside root directory
 };
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp b/clang-tools-extra/clang-doc/Serialize.cpp
index f65974a91ee946..fe31c368dd8c98 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -11,6 +11,7 @@
 #include "clang/AST/Comment.h"
 #include "clang/Index/USRGeneration.h"
 #include "clang/Lex/Lexer.h"
+#include "clang/AST/Attr.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/SHA1.h"
@@ -74,13 +75,15 @@ llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl) {
   llvm::SmallString<256> Result; 
   llvm::raw_svector_ostream Stream(Result);
   const ASTContext& Ctx = FuncDecl->getASTContext();
+  const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl);
   // If it's a templated function, handle the template parameters
   if (const auto *TmplDecl = FuncDecl->getDescribedTemplate()) {
     getTemplateParameters(TmplDecl->getTemplateParameters(), Stream);
   }
-  // If it's a const method, add 'const' qualifier
-  if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl)) {
-    if (Method->isVirtual()) {
+  // If it's a virtual method
+  if (Method) {
+    if (Method->isVirtual())
+    {
       Stream << "virtual ";
     }
   }
@@ -125,7 +128,11 @@ llvm::SmallString<256> getFunctionPrototype(const FunctionDecl *FuncDecl) {
   Stream << ")";
 
   // If it's a const method, add 'const' qualifier
-  if (const auto *Method = llvm::dyn_cast<CXXMethodDecl>(FuncDecl)) {
+  if (Method) {
+    if (Method->size_overridden_methods())
+      Stream << " override";
+    if (Method->hasAttr<clang::FinalAttr>())
+      Stream << " final";
     if (Method->isConst())
       Stream << " const";
     if (Method->isPureVirtual()) 
@@ -732,22 +739,20 @@ static void populateInfo(Info &I, const T *D, const FullComment *C,
 
 template <typename T>
 static void populateSymbolInfo(SymbolInfo &I, const T *D, const FullComment *C,
-                               int LineNumber, StringRef Filename,
-                               bool IsFileInRootDir,
+                               Location Loc,
                                bool &IsInAnonymousNamespace) {
   populateInfo(I, D, C, IsInAnonymousNamespace);
   if (D->isThisDeclarationADefinition())
-    I.DefLoc.emplace(LineNumber, Filename, IsFileInRootDir);
+    I.DefLoc = Loc;
   else
-    I.Loc.emplace_back(LineNumber, Filename, IsFileInRootDir);
+    I.Loc.emplace_back(Loc);
 }
 
 static void populateFunctionInfo(FunctionInfo &I, const FunctionDecl *D,
-                                 const FullComment *FC, int LineNumber,
-                                 StringRef Filename, bool IsFileInRootDir,
+                                 const FullComment *FC, 
+                                 Location Loc,
                                  bool &IsInAnonymousNamespace) {
-  populateSymbolInfo(I, D, FC, LineNumber, Filename, IsFileInRootDir,
-                     IsInAnonymousNamespace);
+  populateSymbolInfo(I, D, FC, Loc, IsInAnonymousNamespace);
   I.ReturnType = getTypeInfoForType(D->getReturnType());
   I.ProtoType = getFunctionPrototype(D);
   parseParameters(I, D);
@@ -828,8 +833,7 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
             // reference, its value is not relevant in here so it's not used
             // anywhere besides the function call.
             bool IsInAnonymousNamespace;
-            populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*LineNumber=*/{},
-                                 /*FileName=*/{}, IsFileInRootDir,
+            populateFunctionInfo(FI, MD, /*FullComment=*/{}, /*Location=*/{},
                                  IsInAnonymousNamespace);
             FI.Access =
                 getFinalAccessSpecifier(BI.Access, MD->getAccessUnsafe());
@@ -847,8 +851,8 @@ parseBases(RecordInfo &I, const CXXRecordDecl *D, bool IsFileInRootDir,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
-         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc, 
+         bool PublicOnly) {
   auto I = std::make_unique<NamespaceInfo>();
   bool IsInAnonymousNamespace = false;
   populateInfo(*I, D, FC, IsInAnonymousNamespace);
@@ -868,12 +872,11 @@ emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
-         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const RecordDecl *D, const FullComment *FC, 
+         Location Loc, bool PublicOnly) {
   auto I = std::make_unique<RecordInfo>();
   bool IsInAnonymousNamespace = false;
-  populateSymbolInfo(*I, D, FC, LineNumber, File, IsFileInRootDir,
-                     IsInAnonymousNamespace);
+  populateSymbolInfo(*I, D, FC, Loc, IsInAnonymousNamespace);
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
   
@@ -887,7 +890,7 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
     }
     // TODO: remove first call to parseBases, that function should be deleted
     parseBases(*I, C);
-    parseBases(*I, C, IsFileInRootDir, PublicOnly, true);
+    parseBases(*I, C, true, PublicOnly, true);
   }
   I->Path = getInfoRelativePath(I->Namespace);
 
@@ -937,12 +940,11 @@ emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
-         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const FunctionDecl *D, const FullComment *FC,
+         Location Loc, bool PublicOnly) {
   FunctionInfo Func;
   bool IsInAnonymousNamespace = false;
-  populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
-                       IsInAnonymousNamespace);
+  populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
   Func.Access = clang::AccessSpecifier::AS_none;
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
@@ -952,12 +954,11 @@ emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
-         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const CXXMethodDecl *D, const FullComment *FC,
+         Location Loc, bool PublicOnly) {
   FunctionInfo Func;
   bool IsInAnonymousNamespace = false;
-  populateFunctionInfo(Func, D, FC, LineNumber, File, IsFileInRootDir,
-                       IsInAnonymousNamespace);
+  populateFunctionInfo(Func, D, FC, Loc, IsInAnonymousNamespace);
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
 
@@ -981,16 +982,18 @@ emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc, 
+         bool PublicOnly) {
+  
   TypedefInfo Info;
   ASTContext& Context = D->getASTContext();
   bool IsInAnonymousNamespace = false;
   populateInfo(Info, D, FC, IsInAnonymousNamespace);
+  
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
-
-  Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
+  
+  Info.DefLoc = Loc;
   Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
   Info.TypeDeclaration = getTypeDefDecl(D);
   
@@ -1015,8 +1018,8 @@ emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
 // A type alias is a C++ "using" declaration for a type. It gets mapped to a
 // TypedefInfo with the IsUsing flag set.
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const TypeAliasDecl *D, const FullComment *FC, 
+         Location Loc, bool PublicOnly) {
   TypedefInfo Info;
   ASTContext& Context = D->getASTContext();
   bool IsInAnonymousNamespace = false;
@@ -1024,7 +1027,7 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
 
-  Info.DefLoc.emplace(LineNumber, File, IsFileInRootDir);
+  Info.DefLoc = Loc;
   Info.Underlying = getTypeInfoForType(D->getUnderlyingType());
   Info.TypeDeclaration = getTypeAlias(D);
   Info.IsUsing = true;
@@ -1041,12 +1044,12 @@ emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
 }
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
-         llvm::StringRef File, bool IsFileInRootDir, bool PublicOnly) {
+emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc, 
+         bool PublicOnly) {
   EnumInfo Enum;
   bool IsInAnonymousNamespace = false;
-  populateSymbolInfo(Enum, D, FC, LineNumber, File, IsFileInRootDir,
-                     IsInAnonymousNamespace);
+  populateSymbolInfo(Enum, D, FC, Loc, IsInAnonymousNamespace);
+  
   if (!shouldSerializeInfo(PublicOnly, IsInAnonymousNamespace, D))
     return {};
 
diff --git a/clang-tools-extra/clang-doc/Serialize.h b/clang-tools-extra/clang-doc/Serialize.h
index 4e203ca7891aca..8874299e9af9e5 100644
--- a/clang-tools-extra/clang-doc/Serialize.h
+++ b/clang-tools-extra/clang-doc/Serialize.h
@@ -37,32 +37,32 @@ namespace serialize {
 // its parent scope. For NamespaceDecl and RecordDecl both elements are not
 // nullptr.
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const NamespaceDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const NamespaceDecl *D, const FullComment *FC, Location Loc, 
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const RecordDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const RecordDecl *D, const FullComment *FC, Location Loc, 
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const EnumDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const EnumDecl *D, const FullComment *FC, Location Loc, 
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const FunctionDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const FunctionDecl *D, const FullComment *FC, Location Loc, 
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const CXXMethodDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const CXXMethodDecl *D, const FullComment *FC, Location Loc, 
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypedefDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const TypedefDecl *D, const FullComment *FC, Location Loc, 
+         bool PublicOnly);
 
 std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
-emitInfo(const TypeAliasDecl *D, const FullComment *FC, int LineNumber,
-         StringRef File, bool IsFileInRootDir, bool PublicOnly);
+emitInfo(const TypeAliasDecl *D, const FullComment *FC, Location Loc, 
+         bool PublicOnly);
 
 // Function to hash a given USR value for storage.
 // As USRs (Unified Symbol Resolution) could be large, especially for functions
diff --git a/clang-tools-extra/clang-doc/YAMLGenerator.cpp b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
index b612380a3cfa7f..ae4102c33382d0 100644
--- a/clang-tools-extra/clang-doc/YAMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/YAMLGenerator.cpp
@@ -169,7 +169,7 @@ static void CommentInfoMapping(IO &IO, CommentInfo &I) {
 
 template <> struct MappingTraits<Location> {
   static void mapping(IO &IO, Location &Loc) {
-    IO.mapOptional("LineNumber", Loc.LineNumber, 0);
+    IO.mapOptional("LineNumber", Loc.StartLineNumber, 0);
     IO.mapOptional("Filename", Loc.Filename, SmallString<32>());
   }
 };
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 9fdf43bfbea1e9..fd1f2d11d521a3 100644
--- a/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
+++ b/clang-tools-extra/clang-doc/assets/clang-doc-mustache.css
@@ -237,8 +237,6 @@ html, body {
     font-size: 1.2rem;
 }
 
-
-
 .delimiter-container {
     padding: 0.5rem 1rem;
     margin-bottom:1rem;
@@ -317,12 +315,12 @@ html, body {
 .sidebar-item-container {
     font-size: 16px;
     padding-left: 5px;
-    color: #0e79c0;
 }
 
 
 .sidebar-item-container:hover {
     border-left: 3px solid var(--brand);
+    color: #0e79c0;
 }
 
 .sidebar-section input[type="checkbox"] {
diff --git a/clang-tools-extra/clang-doc/assets/class-template.mustache b/clang-tools-extra/clang-doc/assets/class-template.mustache
index 313d578b30c8e4..3d7d2babe51422 100644
--- a/clang-tools-extra/clang-doc/assets/class-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/class-template.mustache
@@ -12,6 +12,7 @@
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>{{Name}}</title>
     <link rel="stylesheet" type="text/css" href="clang-doc-mustache.css"/>
+    {{! Highlight.js dependency for syntax highlighting }}
     <script src="mustache-index.js"></script>
     {{! Highlight.js dependency for syntax highlighting }}
     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
@@ -20,11 +21,10 @@
 </head>
 <body>
 <main>
-    <!-- Sidebar -->
     <div class="container">
+        <!-- Sidebar -->
         <div class="sidebar">
             <ul>
-
                 <li class="sidebar-section">
                     <h1>
                         {{ProjectName}}
@@ -36,81 +36,85 @@
                     </h2>
                 </li>
                 <!-- Members -->
+                {{#MemberFlag}}
                 <li class="sidebar-section">
-                    <input type="checkbox" id="members-toggle">
+                    <input type="checkbox" id="members-toggle" checked>
                     <label for="members-toggle">Members</label>
                     <ul>
                         {{#PublicMembers}}
                         <li class="sidebar-subsection">
-                            <input type="checkbox" id="public-members-toggle">
+                            <input type="checkbox" id="public-members-toggle" checked>
                             <label for="public-members-toggle">Public</label>
                             <ul>
                                 {{#Obj}}
-                                    <li class="sidebar-item-container">
-                                        <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
-                                    </li>
+                                <li class="sidebar-item-container">
+                                    <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+                                </li>
                                 {{/Obj}}
                             </ul>
                         </li>
                         {{/PublicMembers}}
                         {{#ProtectedMembers}}
                         <li class="sidebar-subsection">
-                            <input type="checkbox" id="protected-members-toggle">
+                            <input type="checkbox" id="protected-members-toggle" checked>
                             <label for="protected-members-toggle">Protected</label>
                             <ul>
                                 {{#Obj}}
-                                    <li class="sidebar-item-container">
-                                        <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
-                                    </li>
+                                <li class="sidebar-item-container">
+                                    <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
+                                </li>
                                 {{/Obj}}
                             </ul>
                         </li>
                         {{/ProtectedMembers}}
                     </ul>
                 </li>
+                {{/MemberFlag}}    
                 <!-- Methods -->
+                {{#FunctionFlag}}
                 <li class="sidebar-section">
-                    <input type="checkbox" id="methods-toggle">
+                    <input type="checkbox" id="methods-toggle" checked>
                     <label for="methods-toggle">Methods</label>
                     <ul>
                         {{#PublicFunction}}
-                            <li class="sidebar-subsection">
-                                <input type="checkbox" id="public-methods-toggle">
-                                <label for="public-methods-toggle">Public</label>
-                                <ul>
-                                    {{#Obj}}
-                                        <li class="sidebar-item-container">
-                                            <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
-                                        </li>
-                                    {{/Obj}}
-                                </ul>
-                            </li>
+                        <li class="sidebar-subsection">
+                            <input type="checkbox" id="public-methods-toggle" checked>
+                            <label for="public-methods-toggle">Public</label>
+                            <ul>
+                                {{#Obj}}
+                                <li class="sidebar-item-container">
+                                    <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                                </li>
+                                {{/Obj}}
+                            </ul>
+                        </li>
                         {{/PublicFunction}}
                         {{#ProtectedFunction}}
-                            <li class="sidebar-subsection">
-                                <input type="checkbox" id="protected-methods-toggle">
-                                <label for="protected-methods-toggle">Protected</label>
-                                <ul>
-                                    {{#Obj}}
-                                        <li class="sidebar-item-container">
-                                            <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
-                                        </li>
-                                    {{/Obj}}
-                                </ul>
-                            </li>
+                        <li class="sidebar-subsection">
+                            <input type="checkbox" id="protected-methods-toggle" checked>
+                            <label for="protected-methods-toggle">Protected</label>
+                            <ul>
+                                {{#Obj}}
+                                <li class="sidebar-item-container">
+                                    <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                                </li>
+                                {{/Obj}}
+                            </ul>
+                        </li>
                         {{/ProtectedFunction}}
                     </ul>
-                </li>
+                </li>    
+                {{/FunctionFlag}}
                 <!-- Enums -->
                 {{#Enums}}
                 <li class="sidebar-section">
-                    <input type="checkbox" id="enums-toggle">
+                    <input type="checkbox" id="enums-toggle" checked>
                     <label for="enums-toggle">Enums</label>
                     <ul>
                         {{#Obj}}
-                            <li class="sidebar-item-container">
-                                <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
-                            </li>
+                        <li class="sidebar-item-container">
+                            <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
+                        </li>
                         {{/Obj}}
                     </ul>
                 </li>
@@ -118,7 +122,7 @@
                 <!-- Typedefs -->
                 {{#Typedefs}}
                 <li class="sidebar-section">
-                    <input type="checkbox" id="enums-toggle">
+                    <input type="checkbox" id="enums-toggle" checked>
                     <label for="enums-toggle">Typedefs</label>
                     <ul>
                         {{#Obj}}
diff --git a/clang-tools-extra/clang-doc/assets/mustache-index.js b/clang-tools-extra/clang-doc/assets/mustache-index.js
index 438c4e44b195d4..4f70a65399041f 100644
--- a/clang-tools-extra/clang-doc/assets/mustache-index.js
+++ b/clang-tools-extra/clang-doc/assets/mustache-index.js
@@ -1,4 +1,3 @@
-
 // highlight.js plugin to merge the original HTML stream and the highlighted stream
 // see this issue: https://github.com/highlightjs/highlight.js/issues/2889
 var mergeHTMLPlugin = (function () {
diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
index 19b0ce28addd9d..efd88e76cf96ff 100644
--- a/clang-tools-extra/clang-doc/assets/namespace-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
@@ -7,68 +7,173 @@
 }}
 <!DOCTYPE html>
 <html lang="en-US">
-    <head>
-        <meta charset="utf-8"/>
-        <meta name="viewport" content="width=device-width, initial-scale=1.0">
-        <title>{{NamespaceTitle}}</title>
-        <link rel="stylesheet" type="text/css" href="clang-doc-mustache.css"/>
-        <script src="mustache-index.js"></script>
-        {{! Highlight.js dependency for syntax highlighting }}
-        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
-        <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
-        <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js"></script>
-    </head>
-    <body>
-        <nav class="sidebar">
-            {{#ProjectName}}
-            <h2 class="sidebar-project-name">
-                {{ProjectName}}
-            </h2>
-            {{/ProjectName}}
-            <h1>{{NamespaceTitle}}</h1>
-            {{#Namespace}}
-            <h3 id="Namespace">Namespace</h3>
+<head>
+    <meta charset="utf-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>{{Name}}</title>
+    <link rel="stylesheet" type="text/css" href="clang-doc-mustache.css"/>
+    <script src="mustache-index.js"></script>
+    {{! Highlight.js dependency for syntax highlighting }}
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js"></script>
+</head>
+<body>
+<main>
+    <div class="container">
+        <!-- Sidebar -->
+        <div class="sidebar">
             <ul>
-                {{#Links}}
-                <li>
-                    <a href="{{Link}}">{{Name}}</a>
+                <li class="sidebar-section">
+                    <h1>
+                        {{ProjectName}}
+                    </h1>
+                </li>
+                <li class="sidebar-section">
+                    <h2>
+                        {{NamespaceTitle}}
+                    </h2>
+                </li>
+                <!-- Namespace -->
+                {{#Namespace}}
+                <li class="sidebar-section">
+                    <input type="checkbox" id="namespace-toggle" checked>
+                    <label for="namespace-toggle">Namespace</label>
+                    <ul>
+                        {{#Links}}
+                        <li class="sidebar-item-container">
+                            <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                        </li>
+                        {{/Links}}
+                    </ul>
+                </li>
+                {{/Namespace}}
+                {{#Record}}
+                <li class="sidebar-section">
+                    <input type="checkbox" id="namespace-toggle" checked>
+                    <label for="namespace-toggle">Classes</label>
+                    <ul>
+                        {{#Links}}
+                        <li class="sidebar-item-container">
+                            <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                        </li>
+                        {{/Links}}
+                    </ul>
+                </li>
+                {{/Record}}
+                <!-- Methods -->
+                {{#Function}}    
+                <li class="sidebar-section">
+                    <input type="checkbox" id="function-toggle" checked>
+                    <label for="function-toggle">Functions</label>
+                    <ul>
+                        {{#Obj}}
+                        <li class="sidebar-item-container">
+                            <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                        </li>
+                        {{/Obj}}
+                    </ul>
+                </li>
+                {{/Function}}
+                <!-- Enums -->
+                {{#Enums}}
+                <li class="sidebar-section">
+                    <input type="checkbox" id="enums-toggle" checked>
+                    <label for="enums-toggle">Enums</label>
+                    <ul>
+                        {{#Obj}}
+                        <li class="sidebar-item-container">
+                            <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
+                        </li>
+                        {{/Obj}}
+                    </ul>
+                </li>
+                {{/Enums}}
+                <!-- Typedefs -->
+                {{#Typedefs}}
+                <li class="sidebar-section">
+                    <input type="checkbox" id="typedef-toggle" checked>
+                    <label for="typedef-toggle">Typedefs</label>
+                    <ul>
+                        {{#Obj}}
+                            <li class="sidebar-item-container">
+                                <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                            </li>
+                        {{/Obj}}
+                    </ul>
                 </li>
-                {{/Links}}
+                {{/Typedefs}}
             </ul>
+        </div>
+        <div class="resizer" id="resizer"></div>
+        <div class="content">
+            <section id="class" class="hero section-container">
+                <pre>
+                    <code class="language-cpp hero__title-large">
+{{&NamespaceTitle}}
+                    </code>
+                </pre>
+                {{#NamespaceComments}}
+                <h2>Description</h2>
+                <div class="hero__subtitle">
+                    {{>Comments}}
+                </div>
+                {{/NamespaceComments}}
+            </section>
+            {{#Namespace}}
+            <section id="Classes" class="section-container">
+                <h2>Namespace</h2>
+                <ul class="class-container">
+                    {{#Links}}
+                    <li id="{{ID}}" style="max-height: 40px;">
+<a href="{{Link}}"><pre><code class="language-cpp code-clang-doc" >namespace {{Name}}</code></pre></a>
+                    </li>
+                    {{/Links}}
+                </ul>
+            </section>
             {{/Namespace}}
             {{#Record}}
-            <h3 id="Class">Class</h3>
-            <ul>
-                {{#Links}}
-                <li>
-                    <a href="{{Link}}">{{Name}}</a>
-                </li>
-                {{/Links}}
-            </ul>
+            <section id="Classes" class="section-container">
+                <h2>Classes</h2>
+                <ul class="class-container">
+                    {{#Links}}
+                        <li id="{{ID}}" style="max-height: 40px;">
+<a href="{{Link}}"><pre><code class="language-cpp code-clang-doc" >class {{Name}}</code></pre></a>
+                        </li>
+                    {{/Links}}
+                </ul>
+            </section>
             {{/Record}}
             {{#Function}}
-            <h3 id="Function">Function</h3>
-            <div>
-                {{#Obj}}
-                {{/Obj}}
-            </div>
+            <section id="Classes" class="section-container">
+                <h2>Functions</h2>
+                <div>
+                    {{#Obj}}
+{{>FunctionPartial}}
+                    {{/Obj}}
+                </div>
+            </section>    
             {{/Function}}
             {{#Enums}}
-            <h3 id="Enums">Enums</h3>
-            <div>
+            <section id="Enums" class="section-container">
+                <h2>Enumerations</h2>
+                <div>
                 {{#Obj}}
+{{>EnumPartial}}
                 {{/Obj}}
-            </div>
+                </div>
+            </section>
             {{/Enums}}
-        </nav>
-        <div class="sidebar-resizer"></div>
-        <main>
-            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam nec purus nec nunc
-            ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam nec purus
-            nec nunc ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam
-            nec purus nec nunc ultricies ultricies. Nullam nec purus nec nunc
-            ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam nec purus
-            nec nunc ultricies ultricies. Nullam nec purus nec nunc ultricies ultricies. Nullam
-        </main>
-    </body>
-</html>
\ No newline at end of file
+            {{#Typedefs}}
+            <section id="Typedefs" class="section-container">
+                <h2>Typedefs</h2>
+                <div>
+                    {{#Obj}}
+ {{>TypedefPartial}}
+                    {{/Obj}}
+                </div>
+            </section>
+            {{/Typedefs}}
+        </div>
+    </div>
+</main>
\ No newline at end of file
diff --git a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
index 6e28aff09f9d95..d640f4065bd6f3 100644
--- a/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/ClangDocTest.cpp
@@ -118,7 +118,7 @@ void CheckSymbolInfo(SymbolInfo *Expected, SymbolInfo *Actual) {
   CheckBaseInfo(Expected, Actual);
   EXPECT_EQ(Expected->DefLoc.has_value(), Actual->DefLoc.has_value());
   if (Expected->DefLoc && Actual->DefLoc.has_value()) {
-    EXPECT_EQ(Expected->DefLoc->LineNumber, Actual->DefLoc->LineNumber);
+    EXPECT_EQ(Expected->DefLoc->StartLineNumber, Actual->DefLoc->StartLineNumber);
     EXPECT_EQ(Expected->DefLoc->Filename, Actual->DefLoc->Filename);
   }
   ASSERT_EQ(Expected->Loc.size(), Actual->Loc.size());



More information about the cfe-commits mailing list