[llvm] Revert "[llvm] add support for mustache templating language (#105893)" (PR #130873)

via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 11 18:26:17 PDT 2025


https://github.com/PeterChou1 created https://github.com/llvm/llvm-project/pull/130873

This patch caused certain GCC buildbots to failed
errors: https://lab.llvm.org/buildbot/#/builders/66/builds/11049

>From 9542b943dcacfd87a0005bb5c07dd189e137b19b Mon Sep 17 00:00:00 2001
From: PeterChou1 <peter.chou at mail.utoronto.ca>
Date: Tue, 11 Mar 2025 21:20:17 -0400
Subject: [PATCH] revert llvm-mustache

---
 llvm/include/llvm/Support/Mustache.h    |  127 ---
 llvm/lib/Support/CMakeLists.txt         |    1 -
 llvm/lib/Support/Mustache.cpp           |  793 ---------------
 llvm/unittests/Support/CMakeLists.txt   |    1 -
 llvm/unittests/Support/MustacheTest.cpp | 1226 -----------------------
 5 files changed, 2148 deletions(-)
 delete mode 100644 llvm/include/llvm/Support/Mustache.h
 delete mode 100644 llvm/lib/Support/Mustache.cpp
 delete mode 100644 llvm/unittests/Support/MustacheTest.cpp

diff --git a/llvm/include/llvm/Support/Mustache.h b/llvm/include/llvm/Support/Mustache.h
deleted file mode 100644
index 41173b96d1a9a..0000000000000
--- a/llvm/include/llvm/Support/Mustache.h
+++ /dev/null
@@ -1,127 +0,0 @@
-//===--- 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
-// 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
-//
-// The Template class is a container class that outputs the Mustache template
-// string and is the main class for users. It stores all the lambdas and the
-// ASTNode Tree. When the Template is instantiated it tokenizes 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
-//   // Creating a simple template and rendering it
-//   auto Template = Template("Hello, {{name}}!");
-//   Value Data = {{"name", "World"}};
-//   std::string Out;
-//   raw_string_ostream OS(Out);
-//   T.render(Data, OS);
-//   // Out == "Hello, World!"
-//
-//   // Creating a template with a partial and rendering it
-//   auto Template = Template("{{>partial}}");
-//   Template.registerPartial("partial", "Hello, {{name}}!");
-//   Value Data = {{"name", "World"}};
-//   std::string Out;
-//   raw_string_ostream OS(Out);
-//   T.render(Data, OS);
-//   // Out == "Hello, World!"
-//
-//   // Creating a template with a lambda and rendering it
-//   Value D = Object{};
-//   auto T = Template("Hello, {{lambda}}!");
-//   Lambda L = []() -> llvm::json::Value { return "World"; };
-//   T.registerLambda("lambda", L);
-//   std::string Out;
-//   raw_string_ostream OS(Out);
-//   T.render(D, OS);
-//   // Out == "Hello, World!"
-// \endcode
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_SUPPORT_MUSTACHE
-#define LLVM_SUPPORT_MUSTACHE
-
-#include "Error.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/Support/Allocator.h"
-#include "llvm/Support/JSON.h"
-#include "llvm/Support/StringSaver.h"
-#include <functional>
-#include <vector>
-
-namespace llvm::mustache {
-
-using Lambda = std::function<llvm::json::Value()>;
-using SectionLambda = std::function<llvm::json::Value(std::string)>;
-
-class ASTNode;
-
-// A Template represents the container for the AST and the partials
-// and Lambdas that are registered with it.
-class Template {
-public:
-  Template(StringRef TemplateStr);
-
-  Template(const Template &) = delete;
-
-  Template &operator=(const Template &) = delete;
-
-  Template(Template &&Other) noexcept;
-
-  Template &operator=(Template &&Other) noexcept;
-
-  void render(const llvm::json::Value &Data, llvm::raw_ostream &OS);
-
-  void registerPartial(std::string Name, std::string Partial);
-
-  void registerLambda(std::string Name, Lambda Lambda);
-
-  void registerLambda(std::string 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 overrideEscapeCharacters(DenseMap<char, std::string> Escapes);
-
-private:
-  StringMap<ASTNode *> Partials;
-  StringMap<Lambda> Lambdas;
-  StringMap<SectionLambda> SectionLambdas;
-  DenseMap<char, std::string> Escapes;
-  // The allocator for the ASTNode Tree
-  llvm::BumpPtrAllocator AstAllocator;
-  // Allocator for each render call resets after each render
-  llvm::BumpPtrAllocator RenderAllocator;
-  ASTNode *Tree;
-};
-} // namespace llvm::mustache
-
-#endif // LLVM_SUPPORT_MUSTACHE
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index 2754c97fce6c1..49a26a618de83 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -220,7 +220,6 @@ 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
deleted file mode 100644
index 2735bf2ca3b9b..0000000000000
--- a/llvm/lib/Support/Mustache.cpp
+++ /dev/null
@@ -1,793 +0,0 @@
-//===-- 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/ADT/SmallVector.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/raw_ostream.h"
-#include <sstream>
-
-using namespace llvm;
-using namespace llvm::mustache;
-
-namespace {
-
-using Accessor = SmallVector<std::string>;
-
-static bool isFalsey(const json::Value &V) {
-  return V.getAsNull() || (V.getAsBoolean() && !V.getAsBoolean().value()) ||
-         (V.getAsArray() && V.getAsArray()->empty());
-}
-
-static Accessor splitMustacheString(StringRef Str) {
-  // We split the mustache string into an accessor.
-  // For example:
-  //    "a.b.c" would be split into {"a", "b", "c"}
-  // We make an exception for a single dot which
-  // refers to the current context.
-  Accessor Tokens;
-  if (Str == ".") {
-    Tokens.emplace_back(Str);
-    return Tokens;
-  }
-  while (!Str.empty()) {
-    StringRef Part;
-    std::tie(Part, Str) = Str.split(".");
-    Tokens.emplace_back(Part.trim());
-  }
-  return Tokens;
-}
-} // namespace
-
-namespace llvm::mustache {
-
-class Token {
-public:
-  enum class Type {
-    Text,
-    Variable,
-    Partial,
-    SectionOpen,
-    SectionClose,
-    InvertSectionOpen,
-    UnescapeVariable,
-    Comment,
-  };
-
-  Token(std::string Str)
-      : TokenType(Type::Text), RawBody(std::move(Str)), TokenBody(RawBody),
-        AccessorValue({}), Indentation(0) {};
-
-  Token(std::string RawBody, std::string TokenBody, char Identifier)
-      : RawBody(std::move(RawBody)), TokenBody(std::move(TokenBody)),
-        Indentation(0) {
-    TokenType = getTokenType(Identifier);
-    if (TokenType == Type::Comment)
-      return;
-    StringRef AccessorStr(this->TokenBody);
-    if (TokenType != Type::Variable)
-      AccessorStr = AccessorStr.substr(1);
-    AccessorValue = splitMustacheString(StringRef(AccessorStr).trim());
-  }
-
-  Accessor getAccessor() const { return AccessorValue; }
-
-  Type getType() const { return TokenType; }
-
-  void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; }
-
-  size_t getIndentation() const { return Indentation; }
-
-  static Type getTokenType(char Identifier) {
-    switch (Identifier) {
-    case '#':
-      return Type::SectionOpen;
-    case '/':
-      return Type::SectionClose;
-    case '^':
-      return Type::InvertSectionOpen;
-    case '!':
-      return Type::Comment;
-    case '>':
-      return Type::Partial;
-    case '&':
-      return Type::UnescapeVariable;
-    default:
-      return Type::Variable;
-    }
-  }
-
-  Type TokenType;
-  // RawBody is the original string that was tokenized.
-  std::string RawBody;
-  // TokenBody is the original string with the identifier removed.
-  std::string TokenBody;
-  Accessor AccessorValue;
-  size_t Indentation;
-};
-
-class ASTNode {
-public:
-  enum Type {
-    Root,
-    Text,
-    Partial,
-    Variable,
-    UnescapeVariable,
-    Section,
-    InvertSection,
-  };
-
-  ASTNode(llvm::BumpPtrAllocator &Alloc, llvm::StringMap<ASTNode *> &Partials,
-          llvm::StringMap<Lambda> &Lambdas,
-          llvm::StringMap<SectionLambda> &SectionLambdas,
-          llvm::DenseMap<char, std::string> &Escapes)
-      : Allocator(Alloc), Partials(Partials), Lambdas(Lambdas),
-        SectionLambdas(SectionLambdas), Escapes(Escapes), Ty(Type::Root),
-        Parent(nullptr), ParentContext(nullptr) {}
-
-  ASTNode(std::string Body, ASTNode *Parent, llvm::BumpPtrAllocator &Alloc,
-          llvm::StringMap<ASTNode *> &Partials,
-          llvm::StringMap<Lambda> &Lambdas,
-          llvm::StringMap<SectionLambda> &SectionLambdas,
-          llvm::DenseMap<char, std::string> &Escapes)
-      : Allocator(Alloc), Partials(Partials), Lambdas(Lambdas),
-        SectionLambdas(SectionLambdas), Escapes(Escapes), Ty(Type::Text),
-        Body(std::move(Body)), Parent(Parent), ParentContext(nullptr) {}
-
-  // Constructor for Section/InvertSection/Variable/UnescapeVariable Nodes
-  ASTNode(Type Ty, Accessor Accessor, ASTNode *Parent,
-          llvm::BumpPtrAllocator &Alloc, llvm::StringMap<ASTNode *> &Partials,
-          llvm::StringMap<Lambda> &Lambdas,
-          llvm::StringMap<SectionLambda> &SectionLambdas,
-          llvm::DenseMap<char, std::string> &Escapes)
-      : Allocator(Alloc), Partials(Partials), Lambdas(Lambdas),
-        SectionLambdas(SectionLambdas), Escapes(Escapes), Ty(Ty),
-        Parent(Parent), AccessorValue(std::move(Accessor)),
-        ParentContext(nullptr) {}
-
-  void addChild(ASTNode *Child) { Children.emplace_back(Child); };
-
-  void setRawBody(std::string NewBody) { RawBody = std::move(NewBody); };
-
-  void setIndentation(size_t NewIndentation) { Indentation = NewIndentation; };
-
-  void render(const llvm::json::Value &Data, llvm::raw_ostream &OS);
-
-private:
-  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,
-                     ASTNode *Partial);
-
-  void renderChild(const llvm::json::Value &Context, llvm::raw_ostream &OS);
-
-  const llvm::json::Value *findContext();
-
-  llvm::BumpPtrAllocator &Allocator;
-  StringMap<ASTNode *> &Partials;
-  StringMap<Lambda> &Lambdas;
-  StringMap<SectionLambda> &SectionLambdas;
-  DenseMap<char, std::string> &Escapes;
-  Type Ty;
-  size_t Indentation = 0;
-  std::string RawBody;
-  std::string Body;
-  ASTNode *Parent;
-  // TODO: switch implementation to SmallVector<T>
-  std::vector<ASTNode *> Children;
-  const Accessor AccessorValue;
-  const llvm::json::Value *ParentContext;
-};
-
-// A wrapper for arena allocator for ASTNodes
-ASTNode *createRootNode(void *Node, llvm::BumpPtrAllocator &Alloc,
-                        llvm::StringMap<ASTNode *> &Partials,
-                        llvm::StringMap<Lambda> &Lambdas,
-                        llvm::StringMap<SectionLambda> &SectionLambdas,
-                        llvm::DenseMap<char, std::string> &Escapes) {
-  return new (Node) ASTNode(Alloc, Partials, Lambdas, SectionLambdas, Escapes);
-}
-
-ASTNode *createNode(void *Node, ASTNode::Type T, Accessor A, ASTNode *Parent,
-                    llvm::BumpPtrAllocator &Alloc,
-                    llvm::StringMap<ASTNode *> &Partials,
-                    llvm::StringMap<Lambda> &Lambdas,
-                    llvm::StringMap<SectionLambda> &SectionLambdas,
-                    llvm::DenseMap<char, std::string> &Escapes) {
-  return new (Node) ASTNode(T, std::move(A), Parent, Alloc, Partials, Lambdas,
-                            SectionLambdas, Escapes);
-}
-
-ASTNode *createTextNode(void *Node, std::string Body, ASTNode *Parent,
-                        llvm::BumpPtrAllocator &Alloc,
-                        llvm::StringMap<ASTNode *> &Partials,
-                        llvm::StringMap<Lambda> &Lambdas,
-                        llvm::StringMap<SectionLambda> &SectionLambdas,
-                        llvm::DenseMap<char, std::string> &Escapes) {
-  return new (Node) ASTNode(std::move(Body), Parent, Alloc, Partials, Lambdas,
-                            SectionLambdas, Escapes);
-}
-
-// Function to check if there is meaningful text behind.
-// We determine if a token has meaningful text behind
-// if the right of previous token contains anything that is
-// not a newline.
-// For example:
-//  "Stuff {{#Section}}" (returns true)
-//   vs
-//  "{{#Section}} \n" (returns false)
-// We make an exception for when previous token is empty
-// and the current token is the second token.
-// For example:
-//  "{{#Section}}"
-bool hasTextBehind(size_t Idx, const ArrayRef<Token> &Tokens) {
-  if (Idx == 0)
-    return true;
-
-  size_t PrevIdx = Idx - 1;
-  if (Tokens[PrevIdx].getType() != Token::Type::Text)
-    return true;
-
-  const Token &PrevToken = Tokens[PrevIdx];
-  StringRef TokenBody = StringRef(PrevToken.RawBody).rtrim(" \r\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 text ahead if the left of previous
-// token does not start with a newline.
-bool hasTextAhead(size_t Idx, const ArrayRef<Token> &Tokens) {
-  if (Idx >= Tokens.size() - 1)
-    return true;
-
-  size_t NextIdx = Idx + 1;
-  if (Tokens[NextIdx].getType() != Token::Type::Text)
-    return true;
-
-  const Token &NextToken = Tokens[NextIdx];
-  StringRef TokenBody = StringRef(NextToken.RawBody).ltrim(" ");
-  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;
-}
-
-// Adjust next token body if there is no text ahead.
-// For example:
-// The template string
-//  "{{! Comment }} \nLine 2"
-// would be considered as no text ahead and should be rendered as
-//  " Line 2"
-void stripTokenAhead(SmallVectorImpl<Token> &Tokens, size_t Idx) {
-  Token &NextToken = Tokens[Idx + 1];
-  StringRef NextTokenBody = NextToken.TokenBody;
-  // Cut off the leading newline which could be \n or \r\n.
-  if (NextTokenBody.starts_with("\r\n"))
-    NextToken.TokenBody = NextTokenBody.substr(2).str();
-  else if (NextTokenBody.starts_with("\n"))
-    NextToken.TokenBody = NextTokenBody.substr(1).str();
-}
-
-// Adjust previous token body if there no text behind.
-// For example:
-//  The template string
-//  " \t{{#section}}A{{/section}}"
-// 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.
-void stripTokenBefore(SmallVectorImpl<Token> &Tokens, size_t Idx,
-                      Token &CurrentToken, Token::Type CurrentType) {
-  Token &PrevToken = Tokens[Idx - 1];
-  StringRef PrevTokenBody = PrevToken.TokenBody;
-  StringRef Unindented = PrevTokenBody.rtrim(" \r\t\v");
-  size_t Indentation = PrevTokenBody.size() - Unindented.size();
-  if (CurrentType != Token::Type::Partial)
-    PrevToken.TokenBody = Unindented.str();
-  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> tokenize(StringRef Template) {
-  SmallVector<Token> Tokens;
-  StringLiteral Open("{{");
-  StringLiteral Close("}}");
-  size_t Start = 0;
-  size_t DelimiterStart = Template.find(Open);
-  if (DelimiterStart == StringRef::npos) {
-    Tokens.emplace_back(Template.str());
-    return Tokens;
-  }
-  while (DelimiterStart != StringRef::npos) {
-    if (DelimiterStart != Start)
-      Tokens.emplace_back(Template.substr(Start, DelimiterStart - Start).str());
-    size_t DelimiterEnd = Template.find(Close, DelimiterStart);
-    if (DelimiterEnd == StringRef::npos)
-      break;
-
-    // Extract the Interpolated variable without delimiters.
-    size_t InterpolatedStart = DelimiterStart + Open.size();
-    size_t InterpolatedEnd = DelimiterEnd - DelimiterStart - Close.size();
-    std::string Interpolated =
-        Template.substr(InterpolatedStart, InterpolatedEnd).str();
-    std::string RawBody = Open.str() + Interpolated + Close.str();
-    Tokens.emplace_back(RawBody, Interpolated, Interpolated[0]);
-    Start = DelimiterEnd + Close.size();
-    DelimiterStart = Template.find(Open, Start);
-  }
-
-  if (Start < Template.size())
-    Tokens.emplace_back(Template.substr(Start).str());
-
-  // Fix up white spaces for:
-  //   - open sections
-  //   - inverted sections
-  //   - close sections
-  //   - comments
-  //
-  // This loop attempts to find standalone tokens and tries to trim out
-  // the surrounding whitespace.
-  // For example:
-  // if you have the template string
-  //  {{#section}} \n Example \n{{/section}}
-  // The output should would be
-  // For example:
-  //  \n Example \n
-  size_t LastIdx = Tokens.size() - 1;
-  for (size_t Idx = 0, End = Tokens.size(); Idx < End; ++Idx) {
-    Token &CurrentToken = Tokens[Idx];
-    Token::Type CurrentType = CurrentToken.getType();
-    // Check if token type requires cleanup.
-    bool RequiresCleanUp = requiresCleanUp(CurrentType);
-
-    if (!RequiresCleanUp)
-      continue;
-
-    // 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 HasTextBehind = hasTextBehind(Idx, Tokens);
-    bool HasTextAhead = hasTextAhead(Idx, Tokens);
-
-    if ((!HasTextAhead && !HasTextBehind) || (!HasTextAhead && Idx == 0))
-      stripTokenAhead(Tokens, Idx);
-
-    if ((!HasTextBehind && !HasTextAhead) || (!HasTextBehind && Idx == LastIdx))
-      stripTokenBefore(Tokens, Idx, CurrentToken, CurrentType);
-  }
-  return Tokens;
-}
-
-// Custom stream to escape strings.
-class EscapeStringStream : public raw_ostream {
-public:
-  explicit EscapeStringStream(llvm::raw_ostream &WrappedStream,
-                              DenseMap<char, std::string> &Escape)
-      : Escape(Escape), WrappedStream(WrappedStream) {
-    SetUnbuffered();
-  }
-
-protected:
-  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;
-    }
-  }
-
-  uint64_t current_pos() const override { return WrappedStream.tell(); }
-
-private:
-  DenseMap<char, std::string> &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) {
-    SetUnbuffered();
-  }
-
-protected:
-  void write_impl(const char *Ptr, size_t Size) override {
-    llvm::StringRef Data(Ptr, Size);
-    SmallString<0> Indent;
-    Indent.resize(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;
-};
-
-class Parser {
-public:
-  Parser(StringRef TemplateStr, BumpPtrAllocator &Allocator)
-      : ASTAllocator(Allocator), TemplateStr(TemplateStr) {}
-
-  ASTNode *parse(llvm::BumpPtrAllocator &RenderAlloc,
-                 llvm::StringMap<ASTNode *> &Partials,
-                 llvm::StringMap<Lambda> &Lambdas,
-                 llvm::StringMap<SectionLambda> &SectionLambdas,
-                 llvm::DenseMap<char, std::string> &Escapes);
-
-private:
-  void parseMustache(ASTNode *Parent, llvm::BumpPtrAllocator &Alloc,
-                     llvm::StringMap<ASTNode *> &Partials,
-                     llvm::StringMap<Lambda> &Lambdas,
-                     llvm::StringMap<SectionLambda> &SectionLambdas,
-                     llvm::DenseMap<char, std::string> &Escapes);
-
-  BumpPtrAllocator &ASTAllocator;
-  SmallVector<Token> Tokens;
-  size_t CurrentPtr;
-  StringRef TemplateStr;
-};
-
-ASTNode *Parser::parse(llvm::BumpPtrAllocator &RenderAlloc,
-                       llvm::StringMap<ASTNode *> &Partials,
-                       llvm::StringMap<Lambda> &Lambdas,
-                       llvm::StringMap<SectionLambda> &SectionLambdas,
-                       llvm::DenseMap<char, std::string> &Escapes) {
-  Tokens = tokenize(TemplateStr);
-  CurrentPtr = 0;
-  void *Root = ASTAllocator.Allocate(sizeof(ASTNode), alignof(ASTNode));
-  ASTNode *RootNode = createRootNode(Root, RenderAlloc, Partials, Lambdas,
-                                     SectionLambdas, Escapes);
-  parseMustache(RootNode, RenderAlloc, Partials, Lambdas, SectionLambdas,
-                Escapes);
-  return RootNode;
-}
-
-void Parser::parseMustache(ASTNode *Parent, llvm::BumpPtrAllocator &Alloc,
-                           llvm::StringMap<ASTNode *> &Partials,
-                           llvm::StringMap<Lambda> &Lambdas,
-                           llvm::StringMap<SectionLambda> &SectionLambdas,
-                           llvm::DenseMap<char, std::string> &Escapes) {
-
-  while (CurrentPtr < Tokens.size()) {
-    Token CurrentToken = Tokens[CurrentPtr];
-    CurrentPtr++;
-    Accessor A = CurrentToken.getAccessor();
-    ASTNode *CurrentNode;
-    void *Node = ASTAllocator.Allocate(sizeof(ASTNode), alignof(ASTNode));
-
-    switch (CurrentToken.getType()) {
-    case Token::Type::Text: {
-      CurrentNode =
-          createTextNode(Node, std::move(CurrentToken.TokenBody), Parent, Alloc,
-                         Partials, Lambdas, SectionLambdas, Escapes);
-      Parent->addChild(CurrentNode);
-      break;
-    }
-    case Token::Type::Variable: {
-      CurrentNode =
-          createNode(Node, ASTNode::Variable, std::move(A), Parent, Alloc,
-                     Partials, Lambdas, SectionLambdas, Escapes);
-      Parent->addChild(CurrentNode);
-      break;
-    }
-    case Token::Type::UnescapeVariable: {
-      CurrentNode =
-          createNode(Node, ASTNode::UnescapeVariable, std::move(A), Parent,
-                     Alloc, Partials, Lambdas, SectionLambdas, Escapes);
-      Parent->addChild(CurrentNode);
-      break;
-    }
-    case Token::Type::Partial: {
-      CurrentNode =
-          createNode(Node, ASTNode::Partial, std::move(A), Parent, Alloc,
-                     Partials, Lambdas, SectionLambdas, Escapes);
-      CurrentNode->setIndentation(CurrentToken.getIndentation());
-      Parent->addChild(CurrentNode);
-      break;
-    }
-    case Token::Type::SectionOpen: {
-      CurrentNode = createNode(Node, ASTNode::Section, A, Parent, Alloc,
-                               Partials, Lambdas, SectionLambdas, Escapes);
-      size_t Start = CurrentPtr;
-      parseMustache(CurrentNode, Alloc, Partials, Lambdas, SectionLambdas,
-                    Escapes);
-      const size_t End = CurrentPtr - 1;
-      std::string RawBody;
-      for (std::size_t I = Start; I < End; I++)
-        RawBody += Tokens[I].RawBody;
-      CurrentNode->setRawBody(std::move(RawBody));
-      Parent->addChild(CurrentNode);
-      break;
-    }
-    case Token::Type::InvertSectionOpen: {
-      CurrentNode = createNode(Node, ASTNode::InvertSection, A, Parent, Alloc,
-                               Partials, Lambdas, SectionLambdas, Escapes);
-      size_t Start = CurrentPtr;
-      parseMustache(CurrentNode, Alloc, Partials, Lambdas, SectionLambdas,
-                    Escapes);
-      const size_t End = CurrentPtr - 1;
-      std::string RawBody;
-      for (size_t Idx = Start; Idx < End; Idx++)
-        RawBody += Tokens[Idx].RawBody;
-      CurrentNode->setRawBody(std::move(RawBody));
-      Parent->addChild(CurrentNode);
-      break;
-    }
-    case Token::Type::Comment:
-      break;
-    case Token::Type::SectionClose:
-      return;
-    }
-  }
-}
-void toMustacheString(const json::Value &Data, raw_ostream &OS) {
-  switch (Data.kind()) {
-  case json::Value::Null:
-    return;
-  case json::Value::Number: {
-    auto Num = *Data.getAsNumber();
-    std::ostringstream SS;
-    SS << Num;
-    OS << SS.str();
-    return;
-  }
-  case json::Value::String: {
-    auto Str = *Data.getAsString();
-    OS << Str.str();
-    return;
-  }
-
-  case json::Value::Array: {
-    auto Arr = *Data.getAsArray();
-    if (Arr.empty())
-      return;
-    [[fallthrough]];
-  }
-  case json::Value::Object:
-  case json::Value::Boolean: {
-    llvm::json::OStream JOS(OS, 2);
-    JOS.value(Data);
-    break;
-  }
-  }
-}
-
-void ASTNode::render(const json::Value &Data, raw_ostream &OS) {
-  ParentContext = &Data;
-  const json::Value *ContextPtr = Ty == Root ? ParentContext : findContext();
-  const json::Value &Context = ContextPtr ? *ContextPtr : nullptr;
-
-  switch (Ty) {
-  case Root:
-    renderChild(Data, OS);
-    return;
-  case Text:
-    OS << Body;
-    return;
-  case Partial: {
-    auto Partial = Partials.find(AccessorValue[0]);
-    if (Partial != Partials.end())
-      renderPartial(Data, OS, Partial->getValue());
-    return;
-  }
-  case Variable: {
-    auto Lambda = Lambdas.find(AccessorValue[0]);
-    if (Lambda != Lambdas.end())
-      renderLambdas(Data, OS, Lambda->getValue());
-    else {
-      EscapeStringStream ES(OS, Escapes);
-      toMustacheString(Context, ES);
-    }
-    return;
-  }
-  case UnescapeVariable: {
-    auto Lambda = Lambdas.find(AccessorValue[0]);
-    if (Lambda != Lambdas.end())
-      renderLambdas(Data, OS, Lambda->getValue());
-    else
-      toMustacheString(Context, OS);
-    return;
-  }
-  case Section: {
-    // Sections are not rendered if the context is falsey.
-    auto SectionLambda = SectionLambdas.find(AccessorValue[0]);
-    bool IsLambda = SectionLambda != SectionLambdas.end();
-    if (isFalsey(Context) && !IsLambda)
-      return;
-
-    if (IsLambda) {
-      renderSectionLambdas(Data, OS, SectionLambda->getValue());
-      return;
-    }
-
-    if (Context.getAsArray()) {
-      const json::Array *Arr = Context.getAsArray();
-      for (const json::Value &V : *Arr)
-        renderChild(V, OS);
-      return;
-    }
-    renderChild(Context, OS);
-    return;
-  }
-  case InvertSection: {
-    bool IsLambda =
-        SectionLambdas.find(AccessorValue[0]) != SectionLambdas.end();
-    if (!isFalsey(Context) || IsLambda)
-      return;
-    renderChild(Context, OS);
-    return;
-  }
-  }
-  llvm_unreachable("Invalid ASTNode type");
-}
-
-const json::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, then we traverse the parent nodes to find the context until we
-  // reach the root node or the context is found.
-  if (AccessorValue.empty())
-    return nullptr;
-  if (AccessorValue[0] == ".")
-    return ParentContext;
-
-  const json::Object *CurrentContext = ParentContext->getAsObject();
-  StringRef CurrentAccessor = AccessorValue[0];
-  ASTNode *CurrentParent = Parent;
-
-  while (!CurrentContext || !CurrentContext->get(CurrentAccessor)) {
-    if (CurrentParent->Ty != Root) {
-      CurrentContext = CurrentParent->ParentContext->getAsObject();
-      CurrentParent = CurrentParent->Parent;
-      continue;
-    }
-    return nullptr;
-  }
-  const json::Value *Context = nullptr;
-  for (auto [Idx, Acc] : enumerate(AccessorValue)) {
-    const json::Value *CurrentValue = CurrentContext->get(Acc);
-    if (!CurrentValue)
-      return nullptr;
-    if (Idx < AccessorValue.size() - 1) {
-      CurrentContext = CurrentValue->getAsObject();
-      if (!CurrentContext)
-        return nullptr;
-    } else
-      Context = CurrentValue;
-  }
-  return Context;
-}
-
-void ASTNode::renderChild(const json::Value &Contexts, llvm::raw_ostream &OS) {
-  for (ASTNode *Child : Children)
-    Child->render(Contexts, OS);
-}
-
-void ASTNode::renderPartial(const json::Value &Contexts, llvm::raw_ostream &OS,
-                            ASTNode *Partial) {
-  AddIndentationStringStream IS(OS, Indentation);
-  Partial->render(Contexts, IS);
-}
-
-void ASTNode::renderLambdas(const json::Value &Contexts, llvm::raw_ostream &OS,
-                            Lambda &L) {
-  json::Value LambdaResult = L();
-  std::string LambdaStr;
-  raw_string_ostream Output(LambdaStr);
-  toMustacheString(LambdaResult, Output);
-  Parser P = Parser(LambdaStr, Allocator);
-  ASTNode *LambdaNode =
-      P.parse(Allocator, Partials, Lambdas, SectionLambdas, Escapes);
-
-  EscapeStringStream ES(OS, Escapes);
-  if (Ty == Variable) {
-    LambdaNode->render(Contexts, ES);
-    return;
-  }
-  LambdaNode->render(Contexts, OS);
-}
-
-void ASTNode::renderSectionLambdas(const json::Value &Contexts,
-                                   llvm::raw_ostream &OS, SectionLambda &L) {
-  json::Value Return = L(RawBody);
-  if (isFalsey(Return))
-    return;
-  std::string LambdaStr;
-  raw_string_ostream Output(LambdaStr);
-  toMustacheString(Return, Output);
-  Parser P = Parser(LambdaStr, Allocator);
-  ASTNode *LambdaNode =
-      P.parse(Allocator, Partials, Lambdas, SectionLambdas, Escapes);
-  LambdaNode->render(Contexts, OS);
-  return;
-}
-
-void Template::render(const json::Value &Data, llvm::raw_ostream &OS) {
-  Tree->render(Data, OS);
-  RenderAllocator.Reset();
-}
-
-void Template::registerPartial(std::string Name, std::string Partial) {
-  Parser P = Parser(Partial, AstAllocator);
-  ASTNode *PartialTree =
-      P.parse(RenderAllocator, Partials, Lambdas, SectionLambdas, Escapes);
-  Partials.insert(std::make_pair(Name, PartialTree));
-}
-
-void Template::registerLambda(std::string Name, Lambda L) { Lambdas[Name] = L; }
-
-void Template::registerLambda(std::string Name, SectionLambda L) {
-  SectionLambdas[Name] = L;
-}
-
-void Template::overrideEscapeCharacters(DenseMap<char, std::string> E) {
-  Escapes = std::move(E);
-}
-
-Template::Template(StringRef TemplateStr) {
-  Parser P = Parser(TemplateStr, AstAllocator);
-  Tree = P.parse(RenderAllocator, Partials, Lambdas, SectionLambdas, Escapes);
-  // The default behavior is to escape html entities.
-  DenseMap<char, std::string> HtmlEntities = {{'&', "&"},
-                                              {'<', "<"},
-                                              {'>', ">"},
-                                              {'"', """},
-                                              {'\'', "'"}};
-  overrideEscapeCharacters(HtmlEntities);
-}
-
-Template::Template(Template &&Other) noexcept
-    : Partials(std::move(Other.Partials)), Lambdas(std::move(Other.Lambdas)),
-      SectionLambdas(std::move(Other.SectionLambdas)),
-      Escapes(std::move(Other.Escapes)),
-      AstAllocator(std::move(Other.AstAllocator)),
-      RenderAllocator(std::move(Other.RenderAllocator)), Tree(Other.Tree) {
-  Other.Tree = nullptr;
-}
-
-Template &Template::operator=(Template &&Other) noexcept {
-  if (this != &Other) {
-    Partials = std::move(Other.Partials);
-    Lambdas = std::move(Other.Lambdas);
-    SectionLambdas = std::move(Other.SectionLambdas);
-    Escapes = std::move(Other.Escapes);
-    AstAllocator = std::move(Other.AstAllocator);
-    RenderAllocator = std::move(Other.RenderAllocator);
-    Tree = Other.Tree;
-    Other.Tree = nullptr;
-  }
-  return *this;
-}
-} // namespace llvm::mustache
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index 6c4e7cb689b20..6de8165826442 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -61,7 +61,6 @@ add_llvm_unittest(SupportTests
   MemoryBufferRefTest.cpp
   MemoryBufferTest.cpp
   MemoryTest.cpp
-  MustacheTest.cpp      
   ModRefTest.cpp
   NativeFormatTests.cpp
   OptimizedStructLayoutTest.cpp
diff --git a/llvm/unittests/Support/MustacheTest.cpp b/llvm/unittests/Support/MustacheTest.cpp
deleted file mode 100644
index 6ab3d4b01bc1b..0000000000000
--- a/llvm/unittests/Support/MustacheTest.cpp
+++ /dev/null
@@ -1,1226 +0,0 @@
-//===- 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 "llvm/Support/raw_ostream.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("Hello from {Mustache}!\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("Hello, {{subject}}!");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{template}}: {{planet}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("{{planet}}: Earth", Out);
-}
-
-TEST(MustacheInterpolation, HTMLEscaping) {
-  // Interpolated tag output should not be re-interpolated.
-  Value D = Object{
-      {"forbidden", "& \" < >"},
-  };
-  auto T = Template("These characters should be HTML escaped: {{forbidden}}\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("These characters should not be HTML escaped: {{&forbidden}}\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{mph}} miles an hour!");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("85 miles an hour!", Out);
-}
-
-TEST(MustacheInterpolation, AmpersandIntegerInterpolation) {
-  // Integers should interpolate seamlessly.
-  Value D = Object{{"mph", 85}};
-  auto T = Template("{{&mph}} miles an hour!");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{power}} jiggawatts!");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("1.21 jiggawatts!", Out);
-}
-
-TEST(MustacheInterpolation, BasicNullInterpolation) {
-  // Nulls should interpolate as the empty string.
-  Value D = Object{{"cannot", nullptr}};
-  auto T = Template("I ({{cannot}}) be seen!");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("I () be seen!", Out);
-}
-
-TEST(MustacheInterpolation, AmpersandNullInterpolation) {
-  // Nulls should interpolate as the empty string.
-  Value D = Object{{"cannot", nullptr}};
-  auto T = Template("I ({{&cannot}}) be seen!");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("I () be seen!", Out);
-}
-
-TEST(MustacheInterpolation, BasicContextMissInterpolation) {
-  // Failed context lookups should default to empty strings.
-  Value D = Object{};
-  auto T = Template("I ({{cannot}}) be seen!");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{person.name}} == {{#person}}{{name}}{{/person}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{&person.name}} == {{#person}}{{&name}}{{/person}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Joe == Joe", Out);
-}
-
-TEST(MustacheInterpolation, DottedNamesArbitraryDepth) {
-  // Dotted names should be functional to any level of nesting.
-  Value D = Object{
-      {"a",
-       Object{{"b",
-               Object{{"c",
-                       Object{{"d",
-                               Object{{"e", Object{{"name", "Phil"}}}}}}}}}}}};
-  auto T = Template("{{a.b.c.d.e.name}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("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("{{a.b.c}} == ");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{a.b.c.name}} == ");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{#a}}{{b.c.d.e.name}}{{/a}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("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("{{#a}}{{b.c}}{{/a}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{a.b}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{a.b}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("d", Out);
-}
-
-TEST(MustacheInterpolation, ImplicitIteratorsBasicInterpolation) {
-  // Unadorned tags should interpolate content into the template.
-  Value D = "world";
-  auto T = Template("Hello, {{.}}!\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Hello, world!\n", Out);
-}
-
-TEST(MustacheInterpolation, ImplicitIteratorsAmersand) {
-  // Basic interpolation should be HTML escaped.
-  Value D = "& \" < >";
-  auto T = Template("These characters should not be HTML escaped: {{&.}}\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("These characters should not be HTML escaped: & \" < >\n", Out);
-}
-
-TEST(MustacheInterpolation, ImplicitIteratorsInteger) {
-  // Integers should interpolate seamlessly.
-  Value D = 85;
-  auto T = Template("{{.}} miles an hour!\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("85 miles an hour!\n", Out);
-}
-
-TEST(MustacheInterpolation, InterpolationSurroundingWhitespace) {
-  // Interpolation should not alter surrounding whitespace.
-  Value D = Object{{"string", "---"}};
-  auto T = Template("| {{string}} |");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("| --- |", Out);
-}
-
-TEST(MustacheInterpolation, AmersandSurroundingWhitespace) {
-  // Interpolation should not alter surrounding whitespace.
-  Value D = Object{{"string", "---"}};
-  auto T = Template("| {{&string}} |");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("| --- |", Out);
-}
-
-TEST(MustacheInterpolation, StandaloneInterpolationWithWhitespace) {
-  // Standalone interpolation should not alter surrounding whitespace.
-  Value D = Object{{"string", "---"}};
-  auto T = Template("  {{string}}\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("  ---\n", Out);
-}
-
-TEST(MustacheInterpolation, StandaloneAmpersandWithWhitespace) {
-  // Standalone interpolation should not alter surrounding whitespace.
-  Value D = Object{{"string", "---"}};
-  auto T = Template("  {{&string}}\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("  ---\n", Out);
-}
-
-TEST(MustacheInterpolation, InterpolationWithPadding) {
-  // Superfluous in-tag whitespace should be ignored.
-  Value D = Object{{"string", "---"}};
-  auto T = Template("|{{ string }}|");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("|---|", Out);
-}
-
-TEST(MustacheInterpolation, AmpersandWithPadding) {
-  // Superfluous in-tag whitespace should be ignored.
-  Value D = Object{{"string", "---"}};
-  auto T = Template("|{{& string }}|");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("|---|", Out);
-}
-
-TEST(MustacheInterpolation, InterpolationWithPaddingAndNewlines) {
-  // Superfluous in-tag whitespace should be ignored.
-  Value D = Object{{"string", "---"}};
-  auto T = Template("|{{ string \n\n\n }}|");
-  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}}");
-  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}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("", Out);
-}
-
-TEST(MustacheInterpolation, IsFalseyNull) {
-  // Mustache-free templates should render as-is.
-  Value D = Object{{"boolean", nullptr}};
-  auto T = Template("Hello, {{#boolean}}World{{/boolean}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Hello, ", Out);
-}
-
-TEST(MustacheInterpolation, IsFalseyArray) {
-  // Mustache-free templates should render as-is.
-  Value D = Object{{"boolean", Array()}};
-  auto T = Template("Hello, {{#boolean}}World{{/boolean}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Hello, ", Out);
-}
-
-TEST(MustacheInterpolation, IsFalseyObject) {
-  // Mustache-free templates should render as-is.
-  Value D = Object{{"boolean", Object{}}};
-  auto T = Template("Hello, {{#boolean}}World{{/boolean}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Hello, World", Out);
-}
-
-TEST(MustacheInterpolation, DoubleRendering) {
-  // Mustache-free templates should render as-is.
-  Value D1 = Object{{"subject", "World"}};
-  auto T = Template("Hello, {{subject}}!");
-  std::string Out1;
-  raw_string_ostream OS1(Out1);
-  T.render(D1, OS1);
-  EXPECT_EQ("Hello, World!", Out1);
-  std::string Out2;
-  raw_string_ostream OS2(Out2);
-  Value D2 = Object{{"subject", "New World"}};
-  T.render(D2, OS2);
-  EXPECT_EQ("Hello, New World!", Out2);
-}
-
-TEST(MustacheSections, NullIsFalsey) {
-  Value D = Object{{"null", nullptr}};
-  auto T = Template("{{#null}}This should not be rendered.{{/null}}");
-  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}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{#sec}}{{a}}, {{b}}, {{c.d}}{{/sec}}");
-  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}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("{{#tops}}"
-                    "{{#middles}}"
-                    "{{tname.lower}}{{mname}}."
-                    "{{#bottoms}}"
-                    "{{tname.upper}}{{mname}}{{bname}}."
-                    "{{/bottoms}}"
-                    "{{/middles}}"
-                    "{{/tops}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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(
-      "{{#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");
-  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);
-}
-
-TEST(MustacheSections, List) {
-  Value D = Object{{"list", Array{Object{{"item", 1}}, Object{{"item", 2}},
-                                  Object{{"item", 3}}}}};
-  auto T = Template("{{#list}}{{item}}{{/list}}");
-  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}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("", Out);
-}
-
-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");
-  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 |");
-  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 |");
-  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}}]");
-  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}}");
-  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}}");
-  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}}");
-  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}}");
-  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}}");
-  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}}){{/.}}");
-  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");
-  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}} == ");
-  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}} == ");
-  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");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ(" | \t|\t | \n", Out);
-}
-
-TEST(MustacheSections, InternalWhitespace) {
-  Value D = Object{{"boolean", true}};
-  auto T = Template(
-      " | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ(" |  \n  | \n", Out);
-}
-
-TEST(MustacheSections, IndentedInlineSections) {
-  Value D = Object{{"boolean", true}};
-  auto T =
-      Template(" {{#boolean}}YES{{/boolean}}\n {{#boolean}}GOOD{{/boolean}}\n");
-  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");
-  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");
-  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|");
-  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/");
-  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}}");
-  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 }}|");
-  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}}");
-  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}}");
-  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}}");
-  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}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("", Out);
-}
-
-TEST(MustacheInvertedSections, List) {
-  Value D = Object{
-      {"list", Array{Object{{"n", 1}}, Object{{"n", 2}}, Object{{"n", 3}}}}};
-  auto T = Template("{{^list}}{{n}}{{/list}}");
-  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}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Yay lists!", Out);
-}
-
-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");
-  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 |");
-  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 |");
-  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}}]");
-  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}} == ");
-  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");
-  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");
-  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");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ(" | \t|\t | \n", Out);
-}
-
-TEST(MustacheInvertedSections, InternalWhitespace) {
-  Value D = Object{{"boolean", false}};
-  auto T = Template(
-      " | {{^boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ(" |  \n  | \n", Out);
-}
-
-TEST(MustacheInvertedSections, IndentedInlineSections) {
-  Value D = Object{{"boolean", false}};
-  auto T =
-      Template(" {{^boolean}}NO{{/boolean}}\n {{^boolean}}WAY{{/boolean}}\n");
-  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");
-  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");
-  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|");
-  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/");
-  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}}");
-  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 }}|");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("|=|", Out);
-}
-
-TEST(MustachePartials, BasicBehavior) {
-  Value D = Object{};
-  auto T = Template("{{>text}}");
-  T.registerPartial("text", "from partial");
-  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}}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("", Out);
-}
-
-TEST(MustachePartials, Context) {
-  Value D = Object{{"text", "content"}};
-  auto T = Template("{{>partial}}");
-  T.registerPartial("partial", "*{{text}}*");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("*content*", Out);
-}
-
-TEST(MustachePartials, Recursion) {
-  Value D =
-      Object{{"content", "X"},
-             {"nodes", Array{Object{{"content", "Y"}, {"nodes", Array{}}}}}};
-  auto T = Template("{{>node}}");
-  T.registerPartial("node", "{{content}}({{#nodes}}{{>node}}{{/nodes}})");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("X(Y())", Out);
-}
-
-TEST(MustachePartials, Nested) {
-  Value D = Object{{"a", "hello"}, {"b", "world"}};
-  auto T = Template("{{>outer}}");
-  T.registerPartial("outer", "*{{a}} {{>inner}}*");
-  T.registerPartial("inner", "{{b}}!");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("*hello world!*", Out);
-}
-
-TEST(MustachePartials, SurroundingWhitespace) {
-  Value D = Object{};
-  auto T = Template("| {{>partial}} |");
-  T.registerPartial("partial", "\t|\t");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("| \t|\t |", Out);
-}
-
-TEST(MustachePartials, InlineIndentation) {
-  Value D = Object{{"data", "|"}};
-  auto T = Template("  {{data}}  {{> partial}}\n");
-  T.registerPartial("partial", "<\n<");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("  |  <\n<\n", Out);
-}
-
-TEST(MustachePartials, PaddingWhitespace) {
-  Value D = Object{{"boolean", true}};
-  auto T = Template("|{{> partial }}|");
-  T.registerPartial("partial", "[]");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("|[]|", Out);
-}
-
-TEST(MustacheLambdas, BasicInterpolation) {
-  Value D = Object{};
-  auto T = Template("Hello, {{lambda}}!");
-  Lambda L = []() -> llvm::json::Value { return "World"; };
-  T.registerLambda("lambda", L);
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Hello, World!", Out);
-}
-
-TEST(MustacheLambdas, InterpolationExpansion) {
-  Value D = Object{{"planet", "World"}};
-  auto T = Template("Hello, {{lambda}}!");
-  Lambda L = []() -> llvm::json::Value { return "{{planet}}"; };
-  T.registerLambda("lambda", L);
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Hello, World!", Out);
-}
-
-TEST(MustacheLambdas, BasicMultipleCalls) {
-  Value D = Object{};
-  auto T = Template("{{lambda}} == {{lambda}} == {{lambda}}");
-  int I = 0;
-  Lambda L = [&I]() -> llvm::json::Value {
-    I += 1;
-    return I;
-  };
-  T.registerLambda("lambda", L);
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("1 == 2 == 3", Out);
-}
-
-TEST(MustacheLambdas, Escaping) {
-  Value D = Object{};
-  auto T = Template("<{{lambda}}{{&lambda}}");
-  Lambda L = []() -> llvm::json::Value { return ">"; };
-  T.registerLambda("lambda", L);
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("<>>", Out);
-}
-
-TEST(MustacheLambdas, Sections) {
-  Value D = Object{};
-  auto T = Template("<{{#lambda}}{{x}}{{/lambda}}>");
-  SectionLambda L = [](StringRef Text) -> llvm::json::Value {
-    if (Text == "{{x}}") {
-      return "yes";
-    }
-    return "no";
-  };
-  T.registerLambda("lambda", L);
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("<yes>", Out);
-}
-
-TEST(MustacheLambdas, SectionExpansion) {
-  Value D = Object{
-      {"planet", "Earth"},
-  };
-  auto T = Template("<{{#lambda}}-{{/lambda}}>");
-  SectionLambda L = [](StringRef Text) -> llvm::json::Value {
-    SmallString<128> Result;
-    Result += Text;
-    Result += "{{planet}}";
-    Result += Text;
-    return Result;
-  };
-  T.registerLambda("lambda", L);
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("<-Earth->", Out);
-}
-
-TEST(MustacheLambdas, SectionsMultipleCalls) {
-  Value D = Object{};
-  auto T = Template("{{#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);
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("__FILE__ != __LINE__", Out);
-}
-
-TEST(MustacheLambdas, InvertedSections) {
-  Value D = Object{{"static", "static"}};
-  auto T = Template("<{{^lambda}}{{static}}{{/lambda}}>");
-  SectionLambda L = [](StringRef Text) -> llvm::json::Value { return false; };
-  T.registerLambda("lambda", L);
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("<>", Out);
-}
-
-TEST(MustacheComments, Inline) {
-  // Comment blocks should be removed from the template.
-  Value D = {};
-  auto T = Template("12345{{! Comment Block! }}67890");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("1234567890", Out);
-}
-
-TEST(MustacheComments, Multiline) {
-  // Multiline comments should be permitted.
-  Value D = {};
-  auto T =
-      Template("12345{{!\n  This is a\n  multi-line comment...\n}}67890\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("1234567890\n", Out);
-}
-
-TEST(MustacheComments, Standalone) {
-  // All standalone comment lines should be removed.
-  Value D = {};
-  auto T = Template("Begin.\n{{! Comment Block! }}\nEnd.\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Begin.\nEnd.\n", Out);
-}
-
-TEST(MustacheComments, IndentedStandalone) {
-  // All standalone comment lines should be removed.
-  Value D = {};
-  auto T = Template("Begin.\n  {{! Indented Comment Block! }}\nEnd.\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Begin.\nEnd.\n", Out);
-}
-
-TEST(MustacheComments, StandaloneLineEndings) {
-  // "\r\n" should be considered a newline for standalone tags.
-  Value D = {};
-  auto T = Template("|\r\n{{! Standalone Comment }}\r\n|");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("|\r\n|", Out);
-}
-
-TEST(MustacheComments, StandaloneWithoutPreviousLine) {
-  // Standalone tags should not require a newline to precede them.
-  Value D = {};
-  auto T = Template("  {{! I'm Still Standalone }}\n!");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("!", Out);
-}
-
-TEST(MustacheComments, StandaloneWithoutNewline) {
-  // Standalone tags should not require a newline to follow them.
-  Value D = {};
-  auto T = Template("!\n  {{! I'm Still Standalone }}");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("!\n", Out);
-}
-
-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");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Begin.\nEnd.\n", Out);
-}
-
-TEST(MustacheComments, IndentedMultilineStandalone) {
-  // All standalone comment lines should be removed.
-  Value D = {};
-  auto T =
-      Template("Begin.\n  {{!\n    Something's going on here...\n  }}\nEnd.\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("Begin.\nEnd.\n", Out);
-}
-
-TEST(MustacheComments, IndentedInline) {
-  // Inline comments should not strip whitespace.
-  Value D = {};
-  auto T = Template("  12 {{! 34 }}\n");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("  12 \n", Out);
-}
-
-TEST(MustacheComments, SurroundingWhitespace) {
-  // Comment removal should preserve surrounding whitespace.
-  Value D = {};
-  auto T = Template("12345 {{! Comment Block! }} 67890");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  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("comments never show: >{{! comment }}<");
-  std::string Out;
-  raw_string_ostream OS(Out);
-  T.render(D, OS);
-  EXPECT_EQ("comments never show: ><", Out);
-}



More information about the llvm-commits mailing list