https://github.com/evelez7 created 
https://github.com/llvm/llvm-project/pull/149006

This patch changes the HTML Mustache generator to use the JSON generator
and consume its output to generate its templates.

>From 9f050635fe7181dc08a81cce6f6eb0033051badd Mon Sep 17 00:00:00 2001
From: Erick Velez <erickvel...@gmail.com>
Date: Thu, 10 Jul 2025 11:40:30 -0700
Subject: [PATCH] [clang-doc] integrate the JSON generator with Mustache
 templates

This patch changes the HTML Mustache generator to use the JSON generator
and consume its output to generate its templates.
---
 clang-tools-extra/clang-doc/BitcodeReader.cpp |   2 +
 clang-tools-extra/clang-doc/BitcodeWriter.cpp |   4 +-
 clang-tools-extra/clang-doc/BitcodeWriter.h   |   1 +
 .../clang-doc/HTMLMustacheGenerator.cpp       | 474 +++---------------
 clang-tools-extra/clang-doc/JSONGenerator.cpp |  69 ++-
 .../clang-doc/Representation.cpp              |   9 +-
 clang-tools-extra/clang-doc/Representation.h  |  15 +
 clang-tools-extra/clang-doc/Serialize.cpp     |   8 +-
 .../clang-doc/assets/class-template.mustache  | 128 ++---
 .../clang-doc/assets/enum-template.mustache   |  30 +-
 .../assets/function-template.mustache         |   2 +-
 .../assets/namespace-template.mustache        |  45 +-
 .../clang-doc/basic-project.mustache.test     |  96 ++--
 .../test/clang-doc/json/class-requires.cpp    |   1 +
 .../test/clang-doc/json/class-template.cpp    |   1 +
 .../test/clang-doc/json/class.cpp             |  21 +
 .../clang-doc/json/compound-constraints.cpp   |   4 +
 .../test/clang-doc/json/concept.cpp           |   2 +
 .../test/clang-doc/json/function-requires.cpp |   7 +
 .../test/clang-doc/json/method-template.cpp   |   2 +
 .../test/clang-doc/json/namespace.cpp         |  18 +
 .../test/clang-doc/json/nested-namespace.cpp  |   4 +
 .../test/clang-doc/mustache-index.cpp         |  14 +-
 .../clang-doc/mustache-separate-namespace.cpp |   8 +-
 .../clang-doc/HTMLMustacheGeneratorTest.cpp   | 129 -----
 .../unittests/clang-doc/JSONGeneratorTest.cpp |  26 +
 26 files changed, 407 insertions(+), 713 deletions(-)

diff --git a/clang-tools-extra/clang-doc/BitcodeReader.cpp 
b/clang-tools-extra/clang-doc/BitcodeReader.cpp
index dce34a8434ff8..4efbbd34730cf 100644
--- a/clang-tools-extra/clang-doc/BitcodeReader.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeReader.cpp
@@ -384,6 +384,8 @@ static llvm::Error parseRecord(const Record &R, unsigned ID,
     return decodeRecord(R, I->Path, Blob);
   case REFERENCE_FIELD:
     return decodeRecord(R, F, Blob);
+  case REFERENCE_FILE:
+    return decodeRecord(R, I->DocumentationFileName, Blob);
   default:
     return llvm::createStringError(llvm::inconvertibleErrorCode(),
                                    "invalid field for Reference");
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.cpp 
b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
index eed23726e17bf..e23511bf63690 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.cpp
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.cpp
@@ -210,6 +210,7 @@ static const llvm::IndexedMap<RecordIdDsc, 
RecordIdToIndexFunctor>
           {REFERENCE_TYPE, {"RefType", &genIntAbbrev}},
           {REFERENCE_PATH, {"Path", &genStringAbbrev}},
           {REFERENCE_FIELD, {"Field", &genIntAbbrev}},
+          {REFERENCE_FILE, {"File", &genStringAbbrev}},
           {TEMPLATE_PARAM_CONTENTS, {"Contents", &genStringAbbrev}},
           {TEMPLATE_SPECIALIZATION_OF,
            {"SpecializationOf", &genSymbolIdAbbrev}},
@@ -286,7 +287,7 @@ static const std::vector<std::pair<BlockId, 
std::vector<RecordId>>>
         // Reference Block
         {BI_REFERENCE_BLOCK_ID,
          {REFERENCE_USR, REFERENCE_NAME, REFERENCE_QUAL_NAME, REFERENCE_TYPE,
-          REFERENCE_PATH, REFERENCE_FIELD}},
+          REFERENCE_PATH, REFERENCE_FIELD, REFERENCE_FILE}},
         // Template Blocks.
         {BI_TEMPLATE_BLOCK_ID, {}},
         {BI_TEMPLATE_PARAM_BLOCK_ID, {TEMPLATE_PARAM_CONTENTS}},
@@ -479,6 +480,7 @@ void ClangDocBitcodeWriter::emitBlock(const Reference &R, 
FieldId Field) {
   emitRecord((unsigned)R.RefType, REFERENCE_TYPE);
   emitRecord(R.Path, REFERENCE_PATH);
   emitRecord((unsigned)Field, REFERENCE_FIELD);
+  emitRecord(R.DocumentationFileName, REFERENCE_FILE);
 }
 
 void ClangDocBitcodeWriter::emitBlock(const FriendInfo &R) {
diff --git a/clang-tools-extra/clang-doc/BitcodeWriter.h 
b/clang-tools-extra/clang-doc/BitcodeWriter.h
index 501af12582a8e..688f886b45308 100644
--- a/clang-tools-extra/clang-doc/BitcodeWriter.h
+++ b/clang-tools-extra/clang-doc/BitcodeWriter.h
@@ -140,6 +140,7 @@ enum RecordId {
   REFERENCE_TYPE,
   REFERENCE_PATH,
   REFERENCE_FIELD,
+  REFERENCE_FILE,
   TEMPLATE_PARAM_CONTENTS,
   TEMPLATE_SPECIALIZATION_OF,
   TYPEDEF_USR,
diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp 
b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
index 7aeaa1b7cf67d..a6839edcfe23f 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -27,6 +27,7 @@ using namespace llvm::mustache;
 
 namespace clang {
 namespace doc {
+static Error generateDocForJSON(json::Value &JSON, StringRef Filename, 
StringRef Path, raw_fd_ostream &OS, const ClangDocContext &CDCtx);
 
 static Error createFileOpenError(StringRef FileName, std::error_code EC) {
   return createFileError("cannot open file " + FileName, EC);
@@ -132,404 +133,69 @@ Error MustacheHTMLGenerator::generateDocs(
       return Err;
   }
 
-  // Track which directories we already tried to create.
-  StringSet<> CreatedDirs;
-  // Collect all output by file name and create the necessary directories.
-  StringMap<std::vector<doc::Info *>> FileToInfos;
-  for (const auto &Group : Infos) {
-    llvm::TimeTraceScope TS("setup directories");
-    doc::Info *Info = Group.getValue().get();
-
-    SmallString<128> Path;
-    sys::path::native(RootDir, Path);
-    sys::path::append(Path, Info->getRelativeFilePath(""));
-    if (!CreatedDirs.contains(Path)) {
-      if (std::error_code EC = sys::fs::create_directories(Path))
-        return createStringError(EC, "failed to create directory '%s'.",
-                                 Path.c_str());
-      CreatedDirs.insert(Path);
+  if (auto JSONGenerator = findGeneratorByName("json")) {
+    if (Error Err =
+            JSONGenerator.get()->generateDocs(RootDir, std::move(Infos), 
CDCtx))
+      return Err;
+  } else
+    return JSONGenerator.takeError();
+
+  std::error_code EC;
+  sys::fs::directory_iterator JSONIter(RootDir, EC);
+  std::vector<json::Value> JSONFiles;
+  JSONFiles.reserve(Infos.size());
+  StringMap<json::Value> JSONFileMap;
+  if (EC)
+    return createStringError("Failed to create directory iterator.");
+
+  while (JSONIter != sys::fs::directory_iterator()) {
+    if (EC)
+      return createStringError(EC, "Failed to iterate directory");
+
+    auto Path = StringRef(JSONIter->path());
+    if (!Path.ends_with(".json")) {
+      JSONIter.increment(EC);
+      continue;
     }
 
-    sys::path::append(Path, Info->getFileBaseName() + ".html");
-    FileToInfos[Path].push_back(Info);
+    auto File = MemoryBuffer::getFile(Path);
+    if ((EC = File.getError()))
+      continue;
+
+    auto Parsed = json::parse((*File)->getBuffer());
+    if (!Parsed)
+      return Parsed.takeError();;
+    JSONFileMap.try_emplace(Path, *Parsed);
+    JSONIter.increment(EC);
   }
 
   {
     llvm::TimeTraceScope TS("Generate Docs");
-    for (const auto &Group : FileToInfos) {
+    for (const auto &File : JSONFileMap) {
       llvm::TimeTraceScope TS("Info to Doc");
       std::error_code FileErr;
-      raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_None);
+      auto JSON = File.getValue();
+      auto Path = File.getKey().str();
+      SmallString<16> PathModified(Path.begin(), Path.end());
+      sys::path::replace_extension(PathModified, "html");
+      auto PathModStr = PathModified.str();
+      raw_fd_ostream InfoOS(PathModStr, FileErr, sys::fs::OF_None);
       if (FileErr)
-        return createFileOpenError(Group.getKey(), FileErr);
+        return createFileOpenError(File.getKey(), FileErr);
 
-      for (const auto &Info : Group.getValue())
-        if (Error Err = generateDocForInfo(Info, InfoOS, CDCtx))
-          return Err;
+      if (Error Err =
+              generateDocForJSON(JSON, sys::path::stem(PathModStr), 
PathModStr, InfoOS, CDCtx))
+        return Err;
     }
   }
   return Error::success();
 }
 
-static json::Value
-extractValue(const Location &L,
-             std::optional<StringRef> RepositoryUrl = std::nullopt) {
-  Object Obj = Object();
-  // TODO: Consider using both Start/End line numbers to improve location 
report
-  Obj.insert({"LineNumber", L.StartLineNumber});
-  Obj.insert({"Filename", L.Filename});
-
-  if (!L.IsFileInRootDir || !RepositoryUrl)
-    return Obj;
-  SmallString<128> FileURL(*RepositoryUrl);
-  sys::path::append(FileURL, sys::path::Style::posix, L.Filename);
-  FileURL += "#" + std::to_string(L.StartLineNumber);
-  Obj.insert({"FileURL", FileURL});
-
-  return Obj;
-}
-
-static json::Value extractValue(const Reference &I,
-                                StringRef CurrentDirectory) {
-  SmallString<64> Path = I.getRelativeFilePath(CurrentDirectory);
-  sys::path::append(Path, I.getFileBaseName() + ".html");
-  sys::path::native(Path, sys::path::Style::posix);
-  Object Obj = Object();
-  Obj.insert({"Link", Path});
-  Obj.insert({"Name", I.Name});
-  Obj.insert({"QualName", I.QualName});
-  Obj.insert({"ID", toHex(toStringRef(I.USR))});
-  return Obj;
-}
-
-static json::Value extractValue(const TypedefInfo &I) {
-  // Not Supported
-  return nullptr;
-}
-
-static json::Value extractValue(const CommentInfo &I) {
-  Object Obj = Object();
-
-  json::Value ChildVal = Object();
-  Object &Child = *ChildVal.getAsObject();
-
-  json::Value ChildArr = Array();
-  auto &CARef = *ChildArr.getAsArray();
-  CARef.reserve(I.Children.size());
-  for (const auto &C : I.Children)
-    CARef.emplace_back(extractValue(*C));
-
-  switch (I.Kind) {
-  case CommentKind::CK_TextComment: {
-    Obj.insert({commentKindToString(I.Kind), I.Text});
-    return Obj;
-  }
-
-  case CommentKind::CK_BlockCommandComment: {
-    Child.insert({"Command", I.Name});
-    Child.insert({"Children", ChildArr});
-    Obj.insert({commentKindToString(I.Kind), ChildVal});
-    return Obj;
-  }
-
-  case CommentKind::CK_InlineCommandComment: {
-    json::Value ArgsArr = Array();
-    auto &ARef = *ArgsArr.getAsArray();
-    ARef.reserve(I.Args.size());
-    for (const auto &Arg : I.Args)
-      ARef.emplace_back(Arg);
-    Child.insert({"Command", I.Name});
-    Child.insert({"Args", ArgsArr});
-    Child.insert({"Children", ChildArr});
-    Obj.insert({commentKindToString(I.Kind), ChildVal});
-    return Obj;
-  }
-
-  case CommentKind::CK_ParamCommandComment:
-  case CommentKind::CK_TParamCommandComment: {
-    Child.insert({"ParamName", I.ParamName});
-    Child.insert({"Direction", I.Direction});
-    Child.insert({"Explicit", I.Explicit});
-    Child.insert({"Children", ChildArr});
-    Obj.insert({commentKindToString(I.Kind), ChildVal});
-    return Obj;
-  }
-
-  case CommentKind::CK_VerbatimBlockComment: {
-    Child.insert({"Text", I.Text});
-    if (!I.CloseName.empty())
-      Child.insert({"CloseName", I.CloseName});
-    Child.insert({"Children", ChildArr});
-    Obj.insert({commentKindToString(I.Kind), ChildVal});
-    return Obj;
-  }
-
-  case CommentKind::CK_VerbatimBlockLineComment:
-  case CommentKind::CK_VerbatimLineComment: {
-    Child.insert({"Text", I.Text});
-    Child.insert({"Children", ChildArr});
-    Obj.insert({commentKindToString(I.Kind), ChildVal});
-    return Obj;
-  }
-
-  case CommentKind::CK_HTMLStartTagComment: {
-    json::Value AttrKeysArray = json::Array();
-    json::Value AttrValuesArray = json::Array();
-    auto &KeyArr = *AttrKeysArray.getAsArray();
-    auto &ValArr = *AttrValuesArray.getAsArray();
-    KeyArr.reserve(I.AttrKeys.size());
-    ValArr.reserve(I.AttrValues.size());
-    for (const auto &K : I.AttrKeys)
-      KeyArr.emplace_back(K);
-    for (const auto &V : I.AttrValues)
-      ValArr.emplace_back(V);
-    Child.insert({"Name", I.Name});
-    Child.insert({"SelfClosing", I.SelfClosing});
-    Child.insert({"AttrKeys", AttrKeysArray});
-    Child.insert({"AttrValues", AttrValuesArray});
-    Child.insert({"Children", ChildArr});
-    Obj.insert({commentKindToString(I.Kind), ChildVal});
-    return Obj;
-  }
-
-  case CommentKind::CK_HTMLEndTagComment: {
-    Child.insert({"Name", I.Name});
-    Child.insert({"Children", ChildArr});
-    Obj.insert({commentKindToString(I.Kind), ChildVal});
-    return Obj;
-  }
-
-  case CommentKind::CK_FullComment:
-  case CommentKind::CK_ParagraphComment: {
-    Child.insert({"Children", ChildArr});
-    Obj.insert({commentKindToString(I.Kind), ChildVal});
-    return Obj;
-  }
-
-  case CommentKind::CK_Unknown: {
-    Obj.insert({commentKindToString(I.Kind), I.Text});
-    return Obj;
-  }
-  }
-  llvm_unreachable("Unknown comment kind encountered.");
-}
-
-static void maybeInsertLocation(std::optional<Location> Loc,
-                                const ClangDocContext &CDCtx, Object &Obj) {
-  if (!Loc)
-    return;
-  Location L = *Loc;
-  Obj.insert({"Location", extractValue(L, CDCtx.RepositoryUrl)});
-}
-
-static void extractDescriptionFromInfo(ArrayRef<CommentInfo> Descriptions,
-                                       json::Object &EnumValObj) {
-  if (Descriptions.empty())
-    return;
-  json::Value DescArr = Array();
-  json::Array &DescARef = *DescArr.getAsArray();
-  DescARef.reserve(Descriptions.size());
-  for (const CommentInfo &Child : Descriptions)
-    DescARef.emplace_back(extractValue(Child));
-  EnumValObj.insert({"EnumValueComments", DescArr});
-}
-
-static json::Value extractValue(const FunctionInfo &I, StringRef ParentInfoDir,
-                                const ClangDocContext &CDCtx) {
-  Object Obj = Object();
-  Obj.insert({"Name", I.Name});
-  Obj.insert({"ID", toHex(toStringRef(I.USR))});
-  Obj.insert({"Access", getAccessSpelling(I.Access).str()});
-  Obj.insert({"ReturnType", extractValue(I.ReturnType.Type, ParentInfoDir)});
-
-  json::Value ParamArr = Array();
-  json::Array &ParamARef = *ParamArr.getAsArray();
-  ParamARef.reserve(I.Params.size());
-  for (const auto Val : enumerate(I.Params)) {
-    json::Value V = Object();
-    auto &VRef = *V.getAsObject();
-    VRef.insert({"Name", Val.value().Name});
-    VRef.insert({"Type", Val.value().Type.Name});
-    VRef.insert({"End", Val.index() + 1 == I.Params.size()});
-    ParamARef.emplace_back(V);
-  }
-  Obj.insert({"Params", ParamArr});
-
-  maybeInsertLocation(I.DefLoc, CDCtx, Obj);
-  return Obj;
-}
-
-static json::Value extractValue(const EnumInfo &I,
-                                const ClangDocContext &CDCtx) {
-  Object Obj = Object();
-  std::string EnumType = I.Scoped ? "enum class " : "enum ";
-  EnumType += I.Name;
-  bool HasComment = llvm::any_of(
-      I.Members, [](const EnumValueInfo &M) { return !M.Description.empty(); 
});
-  Obj.insert({"EnumName", EnumType});
-  Obj.insert({"HasComment", HasComment});
-  Obj.insert({"ID", toHex(toStringRef(I.USR))});
-  json::Value EnumArr = Array();
-  json::Array &EnumARef = *EnumArr.getAsArray();
-  EnumARef.reserve(I.Members.size());
-  for (const EnumValueInfo &M : I.Members) {
-    json::Value EnumValue = Object();
-    auto &EnumValObj = *EnumValue.getAsObject();
-    EnumValObj.insert({"Name", M.Name});
-    if (!M.ValueExpr.empty())
-      EnumValObj.insert({"ValueExpr", M.ValueExpr});
-    else
-      EnumValObj.insert({"Value", M.Value});
-
-    extractDescriptionFromInfo(M.Description, EnumValObj);
-    EnumARef.emplace_back(EnumValue);
-  }
-  Obj.insert({"EnumValues", EnumArr});
-
-  extractDescriptionFromInfo(I.Description, Obj);
-  maybeInsertLocation(I.DefLoc, CDCtx, Obj);
-
-  return Obj;
-}
-
-static void extractScopeChildren(const ScopeChildren &S, Object &Obj,
-                                 StringRef ParentInfoDir,
-                                 const ClangDocContext &CDCtx) {
-  json::Value NamespaceArr = Array();
-  json::Array &NamespaceARef = *NamespaceArr.getAsArray();
-  NamespaceARef.reserve(S.Namespaces.size());
-  for (const Reference &Child : S.Namespaces)
-    NamespaceARef.emplace_back(extractValue(Child, ParentInfoDir));
-
-  if (!NamespaceARef.empty())
-    Obj.insert({"Namespace", Object{{"Links", NamespaceArr}}});
-
-  json::Value RecordArr = Array();
-  json::Array &RecordARef = *RecordArr.getAsArray();
-  RecordARef.reserve(S.Records.size());
-  for (const Reference &Child : S.Records)
-    RecordARef.emplace_back(extractValue(Child, ParentInfoDir));
-
-  if (!RecordARef.empty())
-    Obj.insert({"Record", Object{{"Links", RecordArr}}});
-
-  json::Value FunctionArr = Array();
-  json::Array &FunctionARef = *FunctionArr.getAsArray();
-  FunctionARef.reserve(S.Functions.size());
-
-  json::Value PublicFunctionArr = Array();
-  json::Array &PublicFunctionARef = *PublicFunctionArr.getAsArray();
-  PublicFunctionARef.reserve(S.Functions.size());
-
-  json::Value ProtectedFunctionArr = Array();
-  json::Array &ProtectedFunctionARef = *ProtectedFunctionArr.getAsArray();
-  ProtectedFunctionARef.reserve(S.Functions.size());
-
-  for (const FunctionInfo &Child : S.Functions) {
-    json::Value F = extractValue(Child, ParentInfoDir, CDCtx);
-    AccessSpecifier Access = Child.Access;
-    if (Access == AccessSpecifier::AS_public)
-      PublicFunctionARef.emplace_back(F);
-    else if (Access == AccessSpecifier::AS_protected)
-      ProtectedFunctionARef.emplace_back(F);
-    else
-      FunctionARef.emplace_back(F);
-  }
-
-  if (!FunctionARef.empty())
-    Obj.insert({"Function", Object{{"Obj", FunctionArr}}});
-
-  if (!PublicFunctionARef.empty())
-    Obj.insert({"PublicFunction", Object{{"Obj", PublicFunctionArr}}});
-
-  if (!ProtectedFunctionARef.empty())
-    Obj.insert({"ProtectedFunction", Object{{"Obj", ProtectedFunctionArr}}});
-
-  json::Value EnumArr = Array();
-  auto &EnumARef = *EnumArr.getAsArray();
-  EnumARef.reserve(S.Enums.size());
-  for (const EnumInfo &Child : S.Enums)
-    EnumARef.emplace_back(extractValue(Child, CDCtx));
-
-  if (!EnumARef.empty())
-    Obj.insert({"Enums", Object{{"Obj", EnumArr}}});
-
-  json::Value TypedefArr = Array();
-  auto &TypedefARef = *TypedefArr.getAsArray();
-  TypedefARef.reserve(S.Typedefs.size());
-  for (const TypedefInfo &Child : S.Typedefs)
-    TypedefARef.emplace_back(extractValue(Child));
-
-  if (!TypedefARef.empty())
-    Obj.insert({"Typedefs", Object{{"Obj", TypedefArr}}});
-}
-
-static json::Value extractValue(const NamespaceInfo &I,
-                                const ClangDocContext &CDCtx) {
-  Object NamespaceValue = Object();
-  std::string InfoTitle = I.Name.empty() ? "Global Namespace"
-                                         : (Twine("namespace ") + 
I.Name).str();
-
-  SmallString<64> BasePath = I.getRelativeFilePath("");
-  NamespaceValue.insert({"NamespaceTitle", InfoTitle});
-  NamespaceValue.insert({"NamespacePath", BasePath});
-
-  extractDescriptionFromInfo(I.Description, NamespaceValue);
-  extractScopeChildren(I.Children, NamespaceValue, BasePath, CDCtx);
-  return NamespaceValue;
-}
-
-static json::Value extractValue(const RecordInfo &I,
-                                const ClangDocContext &CDCtx) {
-  Object RecordValue = Object();
-  extractDescriptionFromInfo(I.Description, RecordValue);
-  RecordValue.insert({"Name", I.Name});
-  RecordValue.insert({"FullName", I.FullName});
-  RecordValue.insert({"RecordType", getTagType(I.TagType)});
-
-  maybeInsertLocation(I.DefLoc, CDCtx, RecordValue);
-
-  SmallString<64> BasePath = I.getRelativeFilePath("");
-  extractScopeChildren(I.Children, RecordValue, BasePath, CDCtx);
-  json::Value PublicMembers = Array();
-  json::Array &PubMemberRef = *PublicMembers.getAsArray();
-  PubMemberRef.reserve(I.Members.size());
-  json::Value ProtectedMembers = Array();
-  json::Array &ProtMemberRef = *ProtectedMembers.getAsArray();
-  ProtMemberRef.reserve(I.Members.size());
-  json::Value PrivateMembers = Array();
-  json::Array &PrivMemberRef = *PrivateMembers.getAsArray();
-  PrivMemberRef.reserve(I.Members.size());
-  for (const MemberTypeInfo &Member : I.Members) {
-    json::Value MemberValue = Object();
-    auto &MVRef = *MemberValue.getAsObject();
-    MVRef.insert({"Name", Member.Name});
-    MVRef.insert({"Type", Member.Type.Name});
-    extractDescriptionFromInfo(Member.Description, MVRef);
-
-    if (Member.Access == AccessSpecifier::AS_public)
-      PubMemberRef.emplace_back(MemberValue);
-    else if (Member.Access == AccessSpecifier::AS_protected)
-      ProtMemberRef.emplace_back(MemberValue);
-    else if (Member.Access == AccessSpecifier::AS_private)
-      ProtMemberRef.emplace_back(MemberValue);
-  }
-  if (!PubMemberRef.empty())
-    RecordValue.insert({"PublicMembers", Object{{"Obj", PublicMembers}}});
-  if (!ProtMemberRef.empty())
-    RecordValue.insert({"ProtectedMembers", Object{{"Obj", 
ProtectedMembers}}});
-  if (!PrivMemberRef.empty())
-    RecordValue.insert({"PrivateMembers", Object{{"Obj", PrivateMembers}}});
-
-  return RecordValue;
-}
-
 static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V,
-                                Info *I) {
+                                StringRef JSONPath) {
   V.getAsObject()->insert({"ProjectName", CDCtx.ProjectName});
   json::Value StylesheetArr = Array();
-  auto InfoPath = I->getRelativeFilePath("");
-  SmallString<128> RelativePath = computeRelativePath("", InfoPath);
+  SmallString<128> RelativePath("./");
   sys::path::native(RelativePath, sys::path::Style::posix);
 
   auto *SSA = StylesheetArr.getAsArray();
@@ -555,38 +221,38 @@ static Error setupTemplateValue(const ClangDocContext 
&CDCtx, json::Value &V,
   return Error::success();
 }
 
-Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
-                                                const ClangDocContext &CDCtx) {
-  switch (I->IT) {
-  case InfoType::IT_namespace: {
-    json::Value V =
-        extractValue(*static_cast<clang::doc::NamespaceInfo *>(I), CDCtx);
-    if (auto Err = setupTemplateValue(CDCtx, V, I))
+static Error generateDocForJSON(json::Value &JSON, StringRef Filename, 
StringRef Path, raw_fd_ostream &OS, const ClangDocContext &CDCtx) {
+   auto StrValue = (*JSON.getAsObject())["InfoType"];
+  if (StrValue.kind() != json::Value::Kind::String)
+    return createStringError("JSON file '%s' does not contain 'InfoType' 
field.", Filename.str().c_str());
+  auto ObjTypeStr = StrValue.getAsString();
+  if (!ObjTypeStr.has_value())
+    return createStringError("JSON file '%s' does not contain 'InfoType' field 
as a string.", Filename.str().c_str());
+
+
+ if (ObjTypeStr.value() == "namespace") {
+    if (auto Err = setupTemplateValue(CDCtx, JSON, Path))
       return Err;
     assert(NamespaceTemplate && "NamespaceTemplate is nullptr.");
-    NamespaceTemplate->render(V, OS);
-    break;
-  }
-  case InfoType::IT_record: {
-    json::Value V =
-        extractValue(*static_cast<clang::doc::RecordInfo *>(I), CDCtx);
-    if (auto Err = setupTemplateValue(CDCtx, V, I))
+    NamespaceTemplate->render(JSON, OS);
+  } else if (ObjTypeStr.value() == "record") {
+    if (auto Err = setupTemplateValue(CDCtx, JSON, Path))
       return Err;
-    // Serialize the JSON value to the output stream in a readable format.
-    RecordTemplate->render(V, OS);
-    break;
+    assert(RecordTemplate && "RecordTemplate is nullptr.");
+    RecordTemplate->render(JSON, OS);
   }
+  return Error::success();
+}
+
+Error MustacheHTMLGenerator::generateDocForInfo(Info *I, raw_ostream &OS,
+                                                const ClangDocContext &CDCtx) {
+  switch (I->IT) {
   case InfoType::IT_enum:
-    OS << "IT_enum\n";
-    break;
   case InfoType::IT_function:
-    OS << "IT_Function\n";
-    break;
   case InfoType::IT_typedef:
-    OS << "IT_typedef\n";
-    break;
+  case InfoType::IT_namespace:
+  case InfoType::IT_record:
   case InfoType::IT_concept:
-    break;
   case InfoType::IT_variable:
   case InfoType::IT_friend:
     break;
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp 
b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index cc4c68346ec53..3ba2d9cb3a58d 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -43,6 +43,30 @@ static auto SerializeReferenceLambda = [](const auto &Ref, 
Object &Object) {
   serializeReference(Ref, Object);
 };
 
+static std::string infoTypeToString(InfoType IT) {
+  switch (IT) {
+  case InfoType::IT_default:
+    return "default";
+  case InfoType::IT_namespace:
+    return "namespace";
+  case InfoType::IT_record:
+    return "record";
+  case InfoType::IT_function:
+    return "function";
+  case InfoType::IT_enum:
+    return "enum";
+  case InfoType::IT_typedef:
+    return "typedef";
+  case InfoType::IT_concept:
+    return "concept";
+  case InfoType::IT_variable:
+    return "variable";
+  case InfoType::IT_friend:
+    return "friend";
+  }
+  llvm_unreachable("Unknown InfoType encountered.");
+}
+
 static json::Object
 serializeLocation(const Location &Loc,
                   const std::optional<StringRef> RepositoryUrl) {
@@ -172,6 +196,9 @@ serializeCommonAttributes(const Info &I, json::Object &Obj,
                           const std::optional<StringRef> RepositoryUrl) {
   Obj["Name"] = I.Name;
   Obj["USR"] = toHex(toStringRef(I.USR));
+  Obj["InfoType"] = infoTypeToString(I.IT);
+  if (!I.DocumentationFileName.empty())
+    Obj["DocumentationFileName"] = I.DocumentationFileName;
 
   if (!I.Path.empty())
     Obj["Path"] = I.Path;
@@ -205,6 +232,8 @@ static void serializeReference(const Reference &Ref, Object 
&ReferenceObj) {
   ReferenceObj["Name"] = Ref.Name;
   ReferenceObj["QualName"] = Ref.QualName;
   ReferenceObj["USR"] = toHex(toStringRef(Ref.USR));
+  if (!Ref.DocumentationFileName.empty())
+    ReferenceObj["DocumentationFileName"] = Ref.DocumentationFileName;
 }
 
 // Although namespaces and records both have ScopeChildren, they serialize them
@@ -217,14 +246,20 @@ serializeCommonChildren(const ScopeChildren &Children, 
json::Object &Obj,
     serializeInfo(Info, Object, RepositoryUrl);
   };
 
-  if (!Children.Enums.empty())
+  if (!Children.Enums.empty()) {
     serializeArray(Children.Enums, Obj, "Enums", SerializeInfo);
+    Obj["HasEnums"] = true;
+  }
 
-  if (!Children.Typedefs.empty())
+  if (!Children.Typedefs.empty()) {
     serializeArray(Children.Typedefs, Obj, "Typedefs", SerializeInfo);
+    Obj["HasTypedefs"] = true;
+  }
 
-  if (!Children.Records.empty())
+  if (!Children.Records.empty()) {
     serializeArray(Children.Records, Obj, "Records", SerializeReferenceLambda);
+    Obj["HasRecords"] = true;
+  }
 }
 
 template <typename Container, typename SerializationFunc>
@@ -234,10 +269,12 @@ static void serializeArray(const Container &Records, 
Object &Obj,
   json::Value RecordsArray = Array();
   auto &RecordsArrayRef = *RecordsArray.getAsArray();
   RecordsArrayRef.reserve(Records.size());
-  for (const auto &Item : Records) {
+  for (size_t Index = 0; Index < Records.size(); ++Index) {
     json::Value ItemVal = Object();
     auto &ItemObj = *ItemVal.getAsObject();
-    SerializeInfo(Item, ItemObj);
+    SerializeInfo(Records[Index], ItemObj);
+    if (Index == Records.size() - 1)
+      ItemObj["End"] = true;
     RecordsArrayRef.push_back(ItemVal);
   }
   Obj[Key] = RecordsArray;
@@ -405,10 +442,14 @@ static void serializeInfo(const RecordInfo &I, 
json::Object &Obj,
         ProtFunctionsArrayRef.push_back(FunctionVal);
     }
 
-    if (!PubFunctionsArrayRef.empty())
+    if (!PubFunctionsArrayRef.empty()) {
       Obj["PublicFunctions"] = PubFunctionsArray;
-    if (!ProtFunctionsArrayRef.empty())
+      Obj["HasPublicFunctions"] = true;
+    }
+    if (!ProtFunctionsArrayRef.empty()) {
       Obj["ProtectedFunctions"] = ProtFunctionsArray;
+      Obj["HasProtectedFunctions"] = true;
+    }
   }
 
   if (!I.Members.empty()) {
@@ -429,10 +470,14 @@ static void serializeInfo(const RecordInfo &I, 
json::Object &Obj,
         ProtMembersArrayRef.push_back(MemberVal);
     }
 
-    if (!PubMembersArrayRef.empty())
+    if (!PubMembersArrayRef.empty()) {
       Obj["PublicMembers"] = PublicMembersArray;
-    if (!ProtMembersArrayRef.empty())
+      Obj["HasPublicMembers"] = true;
+    }
+    if (!ProtMembersArrayRef.empty()) {
       Obj["ProtectedMembers"] = ProtectedMembersArray;
+      Obj["HasProtectedMembers"] = true;
+    }
   }
 
   if (!I.Bases.empty())
@@ -496,10 +541,7 @@ static SmallString<16> determineFileName(Info *I, 
SmallString<128> &Path) {
   SmallString<16> FileName;
   if (I->IT == InfoType::IT_record) {
     auto *RecordSymbolInfo = static_cast<SymbolInfo *>(I);
-    if (RecordSymbolInfo->MangledName.size() < 255)
-      FileName = RecordSymbolInfo->MangledName;
-    else
-      FileName = toStringRef(toHex(RecordSymbolInfo->USR));
+    FileName = RecordSymbolInfo->MangledName;
   } else if (I->IT == InfoType::IT_namespace && I->Name != "")
     // Serialize the global namespace as index.json
     FileName = I->Name;
@@ -528,6 +570,7 @@ Error JSONGenerator::generateDocs(
 
     SmallString<16> FileName = determineFileName(Info, Path);
     FileToInfos[Path].push_back(Info);
+    Info->DocumentationFileName = FileName;
   }
 
   for (const auto &Group : FileToInfos) {
diff --git a/clang-tools-extra/clang-doc/Representation.cpp 
b/clang-tools-extra/clang-doc/Representation.cpp
index beaf314a04ae1..8d0d5eb4e17ce 100644
--- a/clang-tools-extra/clang-doc/Representation.cpp
+++ b/clang-tools-extra/clang-doc/Representation.cpp
@@ -191,9 +191,10 @@ bool CommentInfo::operator<(const CommentInfo &Other) 
const {
   return false;
 }
 
-static llvm::SmallString<64>
-calculateRelativeFilePath(const InfoType &Type, const StringRef &Path,
-                          const StringRef &Name, const StringRef &CurrentPath) 
{
+llvm::SmallString<64> calculateRelativeFilePath(const InfoType &Type,
+                                                const StringRef &Path,
+                                                const StringRef &Name,
+                                                const StringRef &CurrentPath) {
   llvm::SmallString<64> FilePath;
 
   if (CurrentPath != Path) {
@@ -247,6 +248,8 @@ void Reference::merge(Reference &&Other) {
     Name = Other.Name;
   if (Path.empty())
     Path = Other.Path;
+  if (DocumentationFileName.empty())
+    DocumentationFileName = Other.DocumentationFileName;
 }
 
 bool FriendInfo::mergeable(const FriendInfo &Other) {
diff --git a/clang-tools-extra/clang-doc/Representation.h 
b/clang-tools-extra/clang-doc/Representation.h
index 23f0e90daa27f..a825953565af2 100644
--- a/clang-tools-extra/clang-doc/Representation.h
+++ b/clang-tools-extra/clang-doc/Representation.h
@@ -69,6 +69,11 @@ enum class CommentKind {
 CommentKind stringToCommentKind(llvm::StringRef KindStr);
 llvm::StringRef commentKindToString(CommentKind Kind);
 
+llvm::SmallString<64> calculateRelativeFilePath(const InfoType &Type,
+                                                const StringRef &Path,
+                                                const StringRef &Name,
+                                                const StringRef &CurrentPath);
+
 // A representation of a parsed comment.
 struct CommentInfo {
   CommentInfo() = default;
@@ -121,6 +126,10 @@ struct Reference {
   Reference(SymbolID USR, StringRef Name, InfoType IT, StringRef QualName,
             StringRef Path = StringRef())
       : USR(USR), Name(Name), QualName(QualName), RefType(IT), Path(Path) {}
+  Reference(SymbolID USR, StringRef Name, InfoType IT, StringRef QualName,
+            StringRef Path, SmallString<16> DocumentationFileName)
+      : USR(USR), Name(Name), QualName(QualName), RefType(IT), Path(Path),
+        DocumentationFileName(DocumentationFileName) {}
 
   bool operator==(const Reference &Other) const {
     return std::tie(USR, Name, QualName, RefType) ==
@@ -155,6 +164,7 @@ struct Reference {
   // Path of directory where the clang-doc generated file will be saved
   // (possibly unresolved)
   llvm::SmallString<128> Path;
+  SmallString<16> DocumentationFileName;
 };
 
 // Holds the children of a record or namespace.
@@ -331,6 +341,11 @@ struct Info {
   llvm::SmallString<128> Path;          // Path of directory where the 
clang-doc
                                         // generated file will be saved
 
+  // The name used for the file that this info is documented in.
+  // In the JSON generator, infos are documented in files with mangled names.
+  // Thus, we keep track of the physical filename for linking purposes.
+  SmallString<16> DocumentationFileName;
+
   void mergeBase(Info &&I);
   bool mergeable(const Info &Other);
 
diff --git a/clang-tools-extra/clang-doc/Serialize.cpp 
b/clang-tools-extra/clang-doc/Serialize.cpp
index 7a0e00c6d9c2d..3e0e54bf53419 100644
--- a/clang-tools-extra/clang-doc/Serialize.cpp
+++ b/clang-tools-extra/clang-doc/Serialize.cpp
@@ -495,7 +495,8 @@ static void InsertChild(ScopeChildren &Scope, const 
NamespaceInfo &Info) {
 
 static void InsertChild(ScopeChildren &Scope, const RecordInfo &Info) {
   Scope.Records.emplace_back(Info.USR, Info.Name, InfoType::IT_record,
-                             Info.Name, getInfoRelativePath(Info.Namespace));
+                             Info.Name, getInfoRelativePath(Info.Namespace),
+                             Info.MangledName);
 }
 
 static void InsertChild(ScopeChildren &Scope, EnumInfo Info) {
@@ -777,7 +778,10 @@ static void populateSymbolInfo(SymbolInfo &I, const T *D, 
const FullComment *C,
     Mangler->mangleCXXVTable(CXXD, MangledStream);
   else
     MangledStream << D->getNameAsString();
-  I.MangledName = MangledName;
+  if (MangledName.size() > 255)
+    I.MangledName = llvm::toStringRef(llvm::toHex(I.USR));
+  else
+    I.MangledName = MangledName;
   delete Mangler;
 }
 
diff --git a/clang-tools-extra/clang-doc/assets/class-template.mustache 
b/clang-tools-extra/clang-doc/assets/class-template.mustache
index f9e78f5cd6bc9..4f18090359400 100644
--- a/clang-tools-extra/clang-doc/assets/class-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/class-template.mustache
@@ -44,56 +44,60 @@
 <main>
     <div class="container">
         <div class="sidebar">
-            <h2>{{RecordType}} {{Name}}</h2>
+            <h2>{{TagType}} {{Name}}</h2>
             <ul>
-                {{#PublicMembers}}
+                {{#HasPublicMembers}}
                 <li class="sidebar-section">
-                    <a class="sidebar-item" href="#PublicMethods">Public 
Members</a>
+                    <a class="sidebar-item" href="#PublicMembers">Public 
Members</a>
                 </li>
                 <ul>
-                    {{#Obj}}
+                    {{#PublicMembers}}
                     <li class="sidebar-item-container">
                         <a class="sidebar-item" href="#{{Name}}">{{Name}}</a>
                     </li>
-                    {{/Obj}}
+                    {{/PublicMembers}}
                 </ul>
-                {{/PublicMembers}}
-                {{#ProtectedMembers}}
+                {{/HasPublicMembers}}
+
+                {{#HasProtectedMembers}}
                     <li class="sidebar-section">
-                        <a class="sidebar-item" 
href="#PublicMethods">Protected Members</a>
+                        <a class="sidebar-item" 
href="#ProtectedMembers">Protected Members</a>
                     </li>
                     <ul>
-                        {{#Obj}}
+                        {{#ProtectedMembers}}
                             <li class="sidebar-item-container">
                                 <a class="sidebar-item" 
href="#{{Name}}">{{Name}}</a>
                             </li>
-                        {{/Obj}}
+                        {{/ProtectedMembers}}
                     </ul>
-                {{/ProtectedMembers}}
-                {{#PublicFunction}}
+                {{/HasProtectedMembers}}
+                
+                {{#HasPublicFunctions}}
                 <li class="sidebar-section">
-                    <a class="sidebar-item" href="#PublicMethods">Public 
Method</a>
+                    <a class="sidebar-item" href="#PublicMethods">Public 
Methods</a>
                 </li>
                 <ul>
-                    {{#Obj}}
+                    {{#PublicFunctions}}
                     <li class="sidebar-item-container">
-                        <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                        <a class="sidebar-item" href="#{{USR}}">{{Name}}</a>
                     </li>
-                    {{/Obj}}
+                    {{/PublicFunctions}}
                 </ul>
-                {{/PublicFunction}}
-                {{#ProtectedFunction}}
+                {{/HasPublicFunctions}}
+
+                {{#HasProtectedFunctions}}
                 <li class="sidebar-section">
-                    <a class="sidebar-item" 
href="#ProtectedFunction">Protected Method</a>
+                    <a class="sidebar-item" 
href="#ProtectedFunction">Protected Methods</a>
                 </li>
                 <ul>
-                    {{#Obj}}
+                    {{#ProtectedFunctions}}
                     <li class="sidebar-item-container">
-                        <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                        <a class="sidebar-item" href="#{{USR}}">{{Name}}</a>
                     </li>
-                    {{/Obj}}
+                    {{/ProtectedFunctions}}
                 </ul>
-                {{/ProtectedFunction}}
+                {{/HasProtectedFunctions}}
+
                 {{#Enums}}
                 <li class="sidebar-section">
                     <a class="sidebar-item" href="#Enums">Enums</a>
@@ -101,7 +105,7 @@
                 <ul>
                     {{#Obj}}
                     <li class="sidebar-item-container">
-                        <a class="sidebar-item" href="#{{ID}}">{{EnumName}}</a>
+                        <a class="sidebar-item" 
href="#{{USR}}">{{EnumName}}</a>
                     </li>
                     {{/Obj}}
                 </ul>
@@ -116,7 +120,7 @@
                 <ul>
                     {{#Links}}
                     <li class="sidebar-item-container">
-                        <a class="sidebar-item" href="#{{ID}}">{{Name}}</a>
+                        <a class="sidebar-item" href="#{{USR}}">{{Name}}</a>
                     </li>
                     {{/Links}}
                 </ul>
@@ -127,22 +131,22 @@
         <div class="content">
             <section class="hero section-container">
                 <div class="hero__title">
-                    <h1 class="hero__title-large">{{RecordType}} {{Name}}</h1>
-                    {{#RecordComments}}
+                    <h1 class="hero__title-large">{{TagType}} {{Name}}</h1>
+                    {{#Description}}
                     <div class="hero__subtitle">
                         {{>Comments}}
                     </div>
-                    {{/RecordComments}}
+                    {{/Description}}
                 </div>
             </section>
-            {{#PublicMembers}}
+            {{#HasPublicMembers}}
             <section id="PublicMembers" class="section-container">
                 <h2>Public Members</h2>
                 <div>
-                    {{#Obj}}
+                    {{#PublicMembers}}
                     <div id="{{Name}}" class="delimiter-container">
                         <pre>
-<code class="language-cpp code-clang-doc" >{{Type}} {{Name}}</code>
+                            <code class="language-cpp code-clang-doc" 
>{{Type}} {{Name}}</code>
                         </pre>
                         {{#MemberComments}}
                         <div>
@@ -150,71 +154,75 @@
                         </div>
                         {{/MemberComments}}
                     </div>
-                    {{/Obj}}
+                    {{/PublicMembers}}
                 </div>
             </section>    
-            {{/PublicMembers}}
-            {{#ProtectedMembers}}
+            {{/HasPublicMembers}}
+
+            {{#HasProtectedMembers}}
             <section id="ProtectedMembers" class="section-container">
                 <h2>Protected Members</h2>
                 <div>
-                    {{#Obj}}
+                    {{#ProtectedMembers}}
                     <div id="{{Name}}" class="delimiter-container">
-                        <pre>
-<code class="language-cpp code-clang-doc" >{{Type}} {{Name}}</code>
-                        </pre>
+                        <code class="language-cpp code-clang-doc" >{{Type}} 
{{Name}}</code>
                         {{#MemberComments}}
                         <div>
                             {{>Comments}}
                         </div>
                         {{/MemberComments}}
                     </div>
-                    {{/Obj}}
+                    {{/ProtectedMembers}}
                 </div>
             </section>
-            {{/ProtectedMembers}}
-            {{#PublicFunction}}
+            {{/HasProtectedMembers}}
+
+            {{#HasPublicFunctions}}
             <section id="PublicMethods" class="section-container">
                 <h2>Public Methods</h2>
                 <div>
-                    {{#Obj}}
-{{>FunctionPartial}}
-                    {{/Obj}}
+                    {{#PublicFunctions}}
+                    {{>FunctionPartial}}
+                    {{/PublicFunctions}}
                 </div>
             </section>
-            {{/PublicFunction}}
-            {{#ProtectedFunction}}
+            {{/PublicFunctions}}
+
+            {{#HasProtectedFunctions}}
             <section id="ProtectedFunction" class="section-container">
                 <h2>Protected Methods</h2>
                 <div>
-                    {{#Obj}}
-{{>FunctionPartial}}
-                    {{/Obj}}
+                    {{#ProtectedFunctions}}
+                    {{>FunctionPartial}}
+                    {{/ProtectedFunctions}}
                 </div>
             </section>
-            {{/ProtectedFunction}}
-            {{#Enums}}
+            {{/HasProtectedFunctions}}
+
+            {{#HasEnums}}
             <section id="Enums" class="section-container">
                 <h2>Enumerations</h2>
                 <div>
-                    {{#Obj}}
-{{>EnumPartial}}
-                    {{/Obj}}
+                    {{#Enums}}
+                    {{>EnumPartial}}
+                    {{/Enums}}
                 </div>
             </section>
-            {{/Enums}}
-            {{#Record}}
+            {{/HasEnums}}
+
+            {{#HasRecords}}
             <section id="Classes" class="section-container">
                 <h2>Inner Classes</h2>
                 <ul class="class-container">
-                    {{#Links}}
-                    <li id="{{ID}}" style="max-height: 40px;">
+                    {{#Records}}
+                    <li id="{{USR}}" style="max-height: 40px;">
 <a href="{{Link}}"><pre><code class="language-cpp code-clang-doc" >class 
{{Name}}</code></pre></a>
                     </li>
-                    {{/Links}}
+                    {{/Records}}
                 </ul>
             </section>
-            {{/Record}}
+            {{/HasRecords}}
+
             {{#Typedef}}
             <section class="section-container">
                 <h2 id="Enums">Enums</h2>
diff --git a/clang-tools-extra/clang-doc/assets/enum-template.mustache 
b/clang-tools-extra/clang-doc/assets/enum-template.mustache
index c45988416dfcb..831e19f9efab4 100644
--- a/clang-tools-extra/clang-doc/assets/enum-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/enum-template.mustache
@@ -5,40 +5,46 @@
     
     This file defines the template for enums
 }}
-<div id="{{ID}}" class="delimiter-container">
+<div id="{{USR}}" class="delimiter-container">
     <div>
         <pre>
             <code class="language-cpp code-clang-doc">
-{{EnumName}}
+                {{Name}}
             </code>
         </pre>
     </div>
     {{! Enum Values }}
     <table class="table-wrapper">
         <tbody>
-        <tr>
-            <th>Name</th>
-            <th>Value</th>
-            {{#HasComment}}
+            <tr>
+                <th>Name</th>
+                <th>Value</th>
+                {{#HasComment}}
                 <th>Comment</th>
-            {{/HasComment}}
-        </tr>
-        {{#EnumValues}}
+                {{/HasComment}}
+            </tr>
+            {{#Members}}
             <tr>
                 <td>{{Name}}</td>
+                {{! A ValueExpr is an explicitly assigned enum value }}
+                {{#Value}}
                 <td>{{Value}}</td>
+                {{/Value}}
+                {{^Value}}
+                <td>{{ValueExpr}}</td>
+                {{/Value}}
                 {{#EnumValueComments}}
                     <td>{{>Comments}}</td>
                 {{/EnumValueComments}}
             </tr>
-        {{/EnumValues}}
+            {{/Members}}
         </tbody>
     </table>
-    {{#EnumComments}}
+    {{#Description}}
     <div>
         {{>Comments}}
     </div>
-    {{/EnumComments}}
+    {{/Description}}
     {{#Location}}
     <div>
         Defined at line {{LineNumber}} of file {{Filename}}
diff --git a/clang-tools-extra/clang-doc/assets/function-template.mustache 
b/clang-tools-extra/clang-doc/assets/function-template.mustache
index 86e934a47b9ca..6683afa03ea43 100644
--- a/clang-tools-extra/clang-doc/assets/function-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/function-template.mustache
@@ -6,7 +6,7 @@
     This file defines the template for functions/methods
 }}
 <div class="delimiter-container">
-    <div id="{{ID}}">
+    <div id="{{USR}}">
         {{! Function Prototype }}
         <pre>
             <code class="language-cpp code-clang-doc">
diff --git a/clang-tools-extra/clang-doc/assets/namespace-template.mustache 
b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
index 1a44ed3c3cccd..d96bc5ce91f3a 100644
--- a/clang-tools-extra/clang-doc/assets/namespace-template.mustache
+++ b/clang-tools-extra/clang-doc/assets/namespace-template.mustache
@@ -46,56 +46,61 @@
                 <div class="sidebar">
                     <h2>{{RecordType}} {{Name}}</h2>
                     <ul>
-                        {{#Enums}}
+                        {{#HasEnums}}
                         <li class="sidebar-section">
                             <a class="sidebar-item" href="#Enums">Enums</a>
                         </li>
                         <ul>
-                            {{#Obj}}
+                            {{#Enums}}
                             <li class="sidebar-item-container">
-                                <a class="sidebar-item" 
href="#{{ID}}">{{EnumName}}</a>
+                                <a class="sidebar-item" 
href="#{{USR}}">{{Name}}</a>
                             </li>
-                            {{/Obj}}
+                            {{/Enums}}
                         </ul>
-                        {{/Enums}}
-                        {{#Record}}
+                        {{/HasEnums}}
+                        {{#HasRecords}}
                         <li class="sidebar-section">
                             <a class="sidebar-item" href="#Classes">Inner 
Classes</a>
                         </li>
                         <ul>
-                            {{#Links}}
+                            {{#Records}}
                             <li class="sidebar-item-container">
-                                <a class="sidebar-item" 
href="#{{ID}}">{{Name}}</a>
+                                <a class="sidebar-item" 
href="#{{USR}}">{{Name}}</a>
                             </li>
-                            {{/Links}}
+                            {{/Records}}
                         </ul>
-                        {{/Record}}
+                        {{/HasRecrods}}
                     </ul>
                 </div>
                 <div class="resizer" id="resizer"></div>
                 <div class="content">
-                    {{#Enums}}
+                    {{#HasEnums}}
                     <section id="Enums" class="section-container">
                         <h2>Enumerations</h2>
                         <div>
-                            {{#Obj}}
+                            {{#Enums}}
                             {{>EnumPartial}}
-                            {{/Obj}}
+                            {{/Enums}}
                         </div>
                     </section>
-                    {{/Enums}}
-                    {{#Record}}
+                    {{/HasEnums}}
+
+                    {{#HasRecords}}
                     <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>
+                        {{#Records}}
+                            <li id="{{USR}}" style="max-height: 40px;">
+                                <a href="{{DocumentationFileName}}.html">
+                                    <pre>
+                                        <code class="language-cpp 
code-clang-doc">class {{Name}}</code>
+                                    </pre>
+                                </a>
                             </li>
-                        {{/Links}}
+                        {{/Records}}
                         </ul>
+                    {{/HasRecords}}
                     </section>
-                    {{/Record}}
                 </div>
             </div>
         </main>
diff --git a/clang-tools-extra/test/clang-doc/basic-project.mustache.test 
b/clang-tools-extra/test/clang-doc/basic-project.mustache.test
index 7bfdd4bb1dd3f..d55c8f007c83f 100644
--- a/clang-tools-extra/test/clang-doc/basic-project.mustache.test
+++ b/clang-tools-extra/test/clang-doc/basic-project.mustache.test
@@ -2,17 +2,17 @@
 // RUN: sed 's|$test_dir|%/S|g' %S/Inputs/basic-project/database_template.json 
> %t/build/compile_commands.json
 
 // RUN: clang-doc --format=mustache --output=%t/docs --executor=all-TUs 
%t/build/compile_commands.json
-// RUN: FileCheck %s -input-file=%t/docs/GlobalNamespace/Shape.html 
-check-prefix=HTML-SHAPE
-// RUN: FileCheck %s -input-file=%t/docs/GlobalNamespace/Calculator.html 
-check-prefix=HTML-CALC
-// RUN: FileCheck %s -input-file=%t/docs/GlobalNamespace/Rectangle.html 
-check-prefix=HTML-RECTANGLE
-// RUN: FileCheck %s -input-file=%t/docs/GlobalNamespace/Circle.html 
-check-prefix=HTML-CIRCLE
+// RUN: FileCheck %s -input-file=%t/docs/_ZTV5Shape.html 
-check-prefix=HTML-SHAPE
+// RUN: FileCheck %s -input-file=%t/docs/_ZTV10Calculator.html 
-check-prefix=HTML-CALC
+// RUN: FileCheck %s -input-file=%t/docs/_ZTV9Rectangle.html 
-check-prefix=HTML-RECTANGLE
+// RUN: FileCheck %s -input-file=%t/docs/_ZTV6Circle.html 
-check-prefix=HTML-CIRCLE
 
 HTML-SHAPE: <html lang="en-US">
 HTML-SHAPE: <head>
 HTML-SHAPE:     <meta charset="utf-8"/>
 HTML-SHAPE:     <title>Shape</title>
-HTML-SHAPE:         <link rel="stylesheet" type="text/css" 
href="../clang-doc-mustache.css"/>
-HTML-SHAPE:         <script src="../mustache-index.js"></script>
+HTML-SHAPE:         <link rel="stylesheet" type="text/css" 
href="./clang-doc-mustache.css"/>
+HTML-SHAPE:         <script src="./mustache-index.js"></script>
 HTML-SHAPE:     <link rel="stylesheet" 
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css";>
 HTML-SHAPE:     <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js";></script>
 HTML-SHAPE:     <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js";></script>
@@ -40,7 +40,7 @@ HTML-SHAPE:         <div class="sidebar">
 HTML-SHAPE:             <h2>class Shape</h2>
 HTML-SHAPE:             <ul>
 HTML-SHAPE:                 <li class="sidebar-section">
-HTML-SHAPE:                     <a class="sidebar-item" 
href="#PublicMethods">Public Method</a>
+HTML-SHAPE:                     <a class="sidebar-item" 
href="#PublicMethods">Public Methods</a>
 HTML-SHAPE:                 </li>
 HTML-SHAPE:                 <ul>
 HTML-SHAPE:                     <li class="sidebar-item-container">
@@ -106,8 +106,8 @@ HTML-CALC: <html lang="en-US">
 HTML-CALC: <head>
 HTML-CALC:     <meta charset="utf-8"/>
 HTML-CALC:     <title>Calculator</title>
-HTML-CALC:         <link rel="stylesheet" type="text/css" 
href="../clang-doc-mustache.css"/>
-HTML-CALC:         <script src="../mustache-index.js"></script>
+HTML-CALC:         <link rel="stylesheet" type="text/css" 
href="./clang-doc-mustache.css"/>
+HTML-CALC:         <script src="./mustache-index.js"></script>
 HTML-CALC:     <link rel="stylesheet" 
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css";>
 HTML-CALC:     <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js";></script>
 HTML-CALC:     <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js";></script>
@@ -135,7 +135,7 @@ HTML-CALC:         <div class="sidebar">
 HTML-CALC:             <h2>class Calculator</h2>
 HTML-CALC:             <ul>
 HTML-CALC:                 <li class="sidebar-section">
-HTML-CALC:                     <a class="sidebar-item" 
href="#PublicMethods">Public Members</a>
+HTML-CALC:                     <a class="sidebar-item" 
href="#PublicMembers">Public Members</a>
 HTML-CALC:                 </li>
 HTML-CALC:                 <ul>
 HTML-CALC:                     <li class="sidebar-item-container">
@@ -146,7 +146,7 @@ HTML-CALC:                         <a class="sidebar-item" 
href="#static_val">st
 HTML-CALC:                     </li>
 HTML-CALC:                 </ul>
 HTML-CALC:                 <li class="sidebar-section">
-HTML-CALC:                     <a class="sidebar-item" 
href="#PublicMethods">Public Method</a>
+HTML-CALC:                     <a class="sidebar-item" 
href="#PublicMethods">Public Methods</a>
 HTML-CALC:                 </li>
 HTML-CALC:                 <ul>
 HTML-CALC:                     <li class="sidebar-item-container">
@@ -251,8 +251,8 @@ HTML-RECTANGLE: <html lang="en-US">
 HTML-RECTANGLE: <head>
 HTML-RECTANGLE:     <meta charset="utf-8"/>
 HTML-RECTANGLE:     <title>Rectangle</title>
-HTML-RECTANGLE:         <link rel="stylesheet" type="text/css" 
href="../clang-doc-mustache.css"/>
-HTML-RECTANGLE:         <script src="../mustache-index.js"></script>
+HTML-RECTANGLE:         <link rel="stylesheet" type="text/css" 
href="./clang-doc-mustache.css"/>
+HTML-RECTANGLE:         <script src="./mustache-index.js"></script>
 HTML-RECTANGLE:     <link rel="stylesheet" 
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css";>
 HTML-RECTANGLE:     <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js";></script>
 HTML-RECTANGLE:     <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js";></script>
@@ -279,19 +279,8 @@ HTML-RECTANGLE:     <div class="container">
 HTML-RECTANGLE:         <div class="sidebar">
 HTML-RECTANGLE:             <h2>class Rectangle</h2>
 HTML-RECTANGLE:             <ul>
-HTML-RECTANGLE:                     <li class="sidebar-section">
-HTML-RECTANGLE:                         <a class="sidebar-item" 
href="#PublicMethods">Protected Members</a>
-HTML-RECTANGLE:                     </li>
-HTML-RECTANGLE:                     <ul>
-HTML-RECTANGLE:                             <li class="sidebar-item-container">
-HTML-RECTANGLE:                                 <a class="sidebar-item" 
href="#width_">width_</a>
-HTML-RECTANGLE:                             </li>
-HTML-RECTANGLE:                             <li class="sidebar-item-container">
-HTML-RECTANGLE:                                 <a class="sidebar-item" 
href="#height_">height_</a>
-HTML-RECTANGLE:                             </li>
-HTML-RECTANGLE:                     </ul>
 HTML-RECTANGLE:                 <li class="sidebar-section">
-HTML-RECTANGLE:                     <a class="sidebar-item" 
href="#PublicMethods">Public Method</a>
+HTML-RECTANGLE:                     <a class="sidebar-item" 
href="#PublicMethods">Public Methods</a>
 HTML-RECTANGLE:                 </li>
 HTML-RECTANGLE:                 <ul>
 HTML-RECTANGLE:                     <li class="sidebar-item-container">
@@ -311,21 +300,24 @@ HTML-RECTANGLE:         <div class="content">
 HTML-RECTANGLE:             <section class="hero section-container">
 HTML-RECTANGLE:                 <div class="hero__title">
 HTML-RECTANGLE:                     <h1 class="hero__title-large">class 
Rectangle</h1>
-HTML-RECTANGLE:                 </div>
-HTML-RECTANGLE:             </section>
-HTML-RECTANGLE:             <section id="ProtectedMembers" 
class="section-container">
-HTML-RECTANGLE:                 <h2>Protected Members</h2>
-HTML-RECTANGLE:                 <div>
-HTML-RECTANGLE:                     <div id="width_" 
class="delimiter-container">
-HTML-RECTANGLE:                         <pre>
-HTML-RECTANGLE: <code class="language-cpp code-clang-doc" >double width_</code>
-HTML-RECTANGLE:                         </pre>
-HTML-RECTANGLE:                     </div>
-HTML-RECTANGLE:                     <div id="height_" 
class="delimiter-container">
-HTML-RECTANGLE:                         <pre>
-HTML-RECTANGLE: <code class="language-cpp code-clang-doc" >double 
height_</code>
-HTML-RECTANGLE:                         </pre>
-HTML-RECTANGLE:                     </div>
+HTML-RECTANGLE:                     <div class="hero__subtitle">
+HTML-RECTANGLE:                                    <div>
+HTML-RECTANGLE:                                        <p></p>
+HTML-RECTANGLE:                                    </div>
+HTML-RECTANGLE:                                        <div 
class="block-command-comment__command">
+HTML-RECTANGLE:                                    <div 
class="block-command-command">
+HTML-RECTANGLE:                                        brief
+HTML-RECTANGLE:                                    </div>
+HTML-RECTANGLE:                                    <div>
+HTML-RECTANGLE:                                                    <div>
+HTML-RECTANGLE:                                                        <p> 
Rectangle class derived from Shape.</p>
+HTML-RECTANGLE:                                                    </div>
+HTML-RECTANGLE:                                                        </div>
+HTML-RECTANGLE:                                </div>
+HTML-RECTANGLE:                                        <div>
+HTML-RECTANGLE:                                        <p> Represents a 
rectangle with a given width and height.</p>
+HTML-RECTANGLE:                                    </div>
+HTML-RECTANGLE:                                                    </div>
 HTML-RECTANGLE:                 </div>
 HTML-RECTANGLE:             </section>
 HTML-RECTANGLE:             <section id="PublicMethods" 
class="section-container">
@@ -372,8 +364,8 @@ HTML-CIRCLE: <html lang="en-US">
 HTML-CIRCLE: <head>
 HTML-CIRCLE:     <meta charset="utf-8"/>
 HTML-CIRCLE:     <title>Circle</title>
-HTML-CIRCLE:         <link rel="stylesheet" type="text/css" 
href="../clang-doc-mustache.css"/>
-HTML-CIRCLE:         <script src="../mustache-index.js"></script>
+HTML-CIRCLE:         <link rel="stylesheet" type="text/css" 
href="./clang-doc-mustache.css"/>
+HTML-CIRCLE:         <script src="./mustache-index.js"></script>
 HTML-CIRCLE:     <link rel="stylesheet" 
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css";>
 HTML-CIRCLE:     <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js";></script>
 HTML-CIRCLE:     <script 
src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/cpp.min.js";></script>
@@ -400,16 +392,8 @@ HTML-CIRCLE:     <div class="container">
 HTML-CIRCLE:         <div class="sidebar">
 HTML-CIRCLE:             <h2>class Circle</h2>
 HTML-CIRCLE:             <ul>
-HTML-CIRCLE:                     <li class="sidebar-section">
-HTML-CIRCLE:                         <a class="sidebar-item" 
href="#PublicMethods">Protected Members</a>
-HTML-CIRCLE:                     </li>
-HTML-CIRCLE:                     <ul>
-HTML-CIRCLE:                             <li class="sidebar-item-container">
-HTML-CIRCLE:                                 <a class="sidebar-item" 
href="#radius_">radius_</a>
-HTML-CIRCLE:                             </li>
-HTML-CIRCLE:                     </ul>
 HTML-CIRCLE:                 <li class="sidebar-section">
-HTML-CIRCLE:                     <a class="sidebar-item" 
href="#PublicMethods">Public Method</a>
+HTML-CIRCLE:                     <a class="sidebar-item" 
href="#PublicMethods">Public Methods</a>
 HTML-CIRCLE:                 </li>
 HTML-CIRCLE:                 <ul>
 HTML-CIRCLE:                     <li class="sidebar-item-container">
@@ -431,16 +415,6 @@ HTML-CIRCLE:                 <div class="hero__title">
 HTML-CIRCLE:                     <h1 class="hero__title-large">class 
Circle</h1>
 HTML-CIRCLE:                 </div>
 HTML-CIRCLE:             </section>
-HTML-CIRCLE:             <section id="ProtectedMembers" 
class="section-container">
-HTML-CIRCLE:                 <h2>Protected Members</h2>
-HTML-CIRCLE:                 <div>
-HTML-CIRCLE:                     <div id="radius_" class="delimiter-container">
-HTML-CIRCLE:                         <pre>
-HTML-CIRCLE: <code class="language-cpp code-clang-doc" >double radius_</code>
-HTML-CIRCLE:                         </pre>
-HTML-CIRCLE:                     </div>
-HTML-CIRCLE:                 </div>
-HTML-CIRCLE:             </section>
 HTML-CIRCLE:             <section id="PublicMethods" class="section-container">
 HTML-CIRCLE:                 <h2>Public Methods</h2>
 HTML-CIRCLE:                 <div>
diff --git a/clang-tools-extra/test/clang-doc/json/class-requires.cpp 
b/clang-tools-extra/test/clang-doc/json/class-requires.cpp
index 213da93a1adfa..bf6c889849a70 100644
--- a/clang-tools-extra/test/clang-doc/json/class-requires.cpp
+++ b/clang-tools-extra/test/clang-doc/json/class-requires.cpp
@@ -20,6 +20,7 @@ struct MyClass;
 // CHECK-NEXT:  "Template": {
 // CHECK-NEXT:    "Constraints": [
 // CHECK-NEXT:      {
+// CHECK-NEXT:        "End": true,
 // CHECK-NEXT:        "Expression": "Addable<T>",
 // CHECK-NEXT:        "Name": "Addable",
 // CHECK-NEXT:        "Path": "",
diff --git a/clang-tools-extra/test/clang-doc/json/class-template.cpp 
b/clang-tools-extra/test/clang-doc/json/class-template.cpp
index 6cdc3e9175278..149248c772055 100644
--- a/clang-tools-extra/test/clang-doc/json/class-template.cpp
+++ b/clang-tools-extra/test/clang-doc/json/class-template.cpp
@@ -11,6 +11,7 @@ template<typename T> struct MyClass {
 // CHECK:         "Name": "method",
 // CHECK:         "Params": [
 // CHECK-NEXT:      {
+// CHECK-NEXT:        "End": true,
 // CHECK-NEXT:        "Name": "Param",
 // CHECK-NEXT:        "Type": "T"
 // CHECK-NEXT:      } 
diff --git a/clang-tools-extra/test/clang-doc/json/class.cpp 
b/clang-tools-extra/test/clang-doc/json/class.cpp
index d8317eafea91a..d39cefbad8327 100644
--- a/clang-tools-extra/test/clang-doc/json/class.cpp
+++ b/clang-tools-extra/test/clang-doc/json/class.cpp
@@ -60,8 +60,11 @@ struct MyClass {
 // CHECK-NEXT:                         "TextComment": " This is a brief 
description." 
 // CHECK-NEXT:                       }
 // CHECK:                   "Command": "brief"
+// CHECK:         "DocumentationFileName": "_ZTV7MyClass",
 // CHECK:         "Enums": [
 // CHECK-NEXT:      {
+// CHECK-NEXT:        "End": true,
+// CHECK-NEXT:        "InfoType": "enum",
 // CHECK-NEXT:        "Location": {
 // CHECK-NEXT:          "Filename": "{{.*}}class.cpp",
 // CHECK-NEXT:          "LineNumber": 17
@@ -76,6 +79,7 @@ struct MyClass {
 // CHECK-NEXT:            "Value": "1"
 // CHECK-NEXT:          },
 // CHECK-NEXT:          {
+// CHECK-NEXT:            "End": true,
 // CHECK-NEXT:            "Name": "BLUE",
 // CHECK-NEXT:            "ValueExpr": "5"
 // CHECK-NEXT:          }
@@ -94,6 +98,7 @@ struct MyClass {
 // CHECK-NEXT:        "IsClass": false,
 // CHECK-NEXT:        "Params": [
 // CHECK-NEXT:          {
+// CHECK-NEXT:            "End": true,
 // CHECK-NEXT:            "Name": "",
 // CHECK-NEXT:            "Type": "int"
 // CHECK-NEXT:          }
@@ -118,6 +123,7 @@ struct MyClass {
 // CHECK-NEXT:        }
 // CHECK-NEXT:      },
 // CHECK-NEXT:      {
+// CHECK-NEXT:        "End": true,
 // CHECK-NEXT:        "IsClass": true,
 // CHECK-NEXT:        "Reference": {
 // CHECK-NEXT:          "Name": "Foo",
@@ -129,6 +135,14 @@ struct MyClass {
 // CHECK-NEXT:    ],
 // COM:           FIXME: FullName is not emitted correctly.
 // CHECK-NEXT:    "FullName": "",
+// CHECK-NEXT:    "HasEnums": true,
+// CHECK-NEXT:    "HasProtectedFunctions": true,
+// CHECK-NEXT:    "HasProtectedMembers": true,
+// CHECK-NEXT:    "HasPublicFunctions": true,
+// CHECK-NEXT:    "HasPublicMembers": true,
+// CHECK-NEXT:    "HasRecords": true,
+// CHECK-NEXT:    "HasTypedefs": true,
+// CHECK-NEXT:    "InfoType": "record",
 // CHECK-NEXT:    "IsTypedef": false,
 // CHECK-NEXT:    "Location": {
 // CHECK-NEXT:      "Filename": "{{.*}}class.cpp",
@@ -142,6 +156,7 @@ struct MyClass {
 // CHECK-NEXT:   "Path": "GlobalNamespace",
 // CHECK-NEXT:   "ProtectedFunctions": [
 // CHECK-NEXT:     {
+// CHECK-NEXT:       "InfoType": "function",
 // CHECK-NEXT:       "IsStatic": false,
 // CHECK-NEXT:       "Name": "protectedMethod",
 // CHECK-NEXT:       "Namespace": [
@@ -166,6 +181,7 @@ struct MyClass {
 // CHECK-NEXT:    ],
 // CHECK-NEXT:    "PublicFunctions": [
 // CHECK-NEXT:      {
+// CHECK-NEXT:        "InfoType": "function",
 // CHECK-NEXT:        "IsStatic": false,
 // CHECK-NEXT:        "Name": "myMethod",
 // CHECK-NEXT:        "Namespace": [
@@ -174,6 +190,7 @@ struct MyClass {
 // CHECK-NEXT:        ],
 // CHECK-NEXT:        "Params": [
 // CHECK-NEXT:          {
+// CHECK-NEXT:            "End": true,
 // CHECK-NEXT:            "Name": "MyParam",
 // CHECK-NEXT:            "Type": "int"
 // CHECK-NEXT:          }
@@ -204,6 +221,8 @@ struct MyClass {
 // CHECK-NEXT:    ],
 // CHECK-NEXT:    "Records": [
 // CHECK-NEXT:      {
+// CHECK-NEXT:        "DocumentationFileName": "_ZTVN7MyClass11NestedClassE",
+// CHECK-NEXT:        "End": true,
 // CHECK-NEXT:        "Name": "NestedClass",
 // CHECK-NEXT:        "Path": "GlobalNamespace{{[\/]+}}MyClass",
 // CHECK-NEXT:        "QualName": "NestedClass",
@@ -213,6 +232,8 @@ struct MyClass {
 // CHECK-NEXT:    "TagType": "struct",
 // CHECK-NEXT:    "Typedefs": [
 // CHECK-NEXT:      {
+// CHECK-NEXT:        "End": true,
+// CHECK-NEXT:        "InfoType": "typedef",
 // CHECK-NEXT:        "IsUsing": false,
 // CHECK-NEXT:        "Location": {
 // CHECK-NEXT:          "Filename": "{{.*}}class.cpp",
diff --git a/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp 
b/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp
index 34acb6808409d..bb2b4ca770fc0 100644
--- a/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp
+++ b/clang-tools-extra/test/clang-doc/json/compound-constraints.cpp
@@ -37,6 +37,7 @@ template<typename T> requires (Incrementable<T> && 
Decrementable<T>) || PreIncre
 // CHECK-NEXT:          "USR": "{{[0-9A-F]*}}"
 // CHECK-NEXT:        },
 // CHECK-NEXT:        {
+// CHECK-NEXT:          "End": true,
 // CHECK-NEXT:          "Expression": "Decrementable<T>",
 // CHECK-NEXT:          "Name": "Decrementable",
 // CHECK-NEXT:          "Path": "",
@@ -55,6 +56,7 @@ template<typename T> requires (Incrementable<T> && 
Decrementable<T>) || PreIncre
 // CHECK-NEXT:          "USR": "{{[0-9A-F]*}}"
 // CHECK-NEXT:        },
 // CHECK-NEXT:        {
+// CHECK-NEXT:          "End": true,
 // CHECK-NEXT:          "Expression": "Decrementable<T>",
 // CHECK-NEXT:          "Name": "Decrementable",
 // CHECK-NEXT:          "Path": "",
@@ -87,6 +89,7 @@ template<typename T> requires (Incrementable<T> && 
Decrementable<T>) || PreIncre
 // CHECK-NEXT:          "USR": "{{[0-9A-F]*}}"
 // CHECK-NEXT:        },
 // CHECK-NEXT:        {
+// CHECK-NEXT:          "End": true,
 // CHECK-NEXT:          "Expression": "PreDecrementable<T>",
 // CHECK-NEXT:          "Name": "PreDecrementable",
 // CHECK-NEXT:          "Path": "",
@@ -112,6 +115,7 @@ template<typename T> requires (Incrementable<T> && 
Decrementable<T>) || PreIncre
 // CHECK-NEXT:          "USR": "{{[0-9A-F]*}}"
 // CHECK-NEXT:        },
 // CHECK-NEXT:        {
+// CHECK-NEXT:          "End": true,
 // CHECK-NEXT:          "Expression": "PreIncrementable<T>",
 // CHECK-NEXT:          "Name": "PreIncrementable",
 // CHECK-NEXT:          "Path": "",
diff --git a/clang-tools-extra/test/clang-doc/json/concept.cpp 
b/clang-tools-extra/test/clang-doc/json/concept.cpp
index b946393274c85..766415bbbeecd 100644
--- a/clang-tools-extra/test/clang-doc/json/concept.cpp
+++ b/clang-tools-extra/test/clang-doc/json/concept.cpp
@@ -23,6 +23,8 @@ concept Incrementable = requires(T x) {
 // CHECK-NEXT:                      {
 // CHECK-NEXT:                        "TextComment": " Requires that T suports 
post and pre-incrementing."
 // CHECK:             ],
+// CHECK:             "End": true,
+// CHECK-NEXT:        "InfoType": "concept",
 // CHECK-NEXT:        "IsType": true,
 // CHECK-NEXT:        "Name": "Incrementable",
 // CHECK-NEXT:        "Template": {
diff --git a/clang-tools-extra/test/clang-doc/json/function-requires.cpp 
b/clang-tools-extra/test/clang-doc/json/function-requires.cpp
index 08ac4c7ed2ca3..59ed39ee61fda 100644
--- a/clang-tools-extra/test/clang-doc/json/function-requires.cpp
+++ b/clang-tools-extra/test/clang-doc/json/function-requires.cpp
@@ -14,10 +14,12 @@ template<Incrementable T> Incrementable auto incrementTwo(T 
t);
 
 // CHECK:       "Functions": [
 // CHECK-NEXT:    {
+// CHECK-NEXT:      "InfoType": "function",
 // CHECK-NEXT:      "IsStatic": false,
 // CHECK-NEXT:      "Name": "increment",
 // CHECK-NEXT:      "Params": [
 // CHECK-NEXT:        {
+// CHECK-NEXT:          "End": true,
 // CHECK-NEXT:          "Name": "t",
 // CHECK-NEXT:          "Type": "T"
 // CHECK-NEXT:        }
@@ -32,6 +34,7 @@ template<Incrementable T> Incrementable auto incrementTwo(T 
t);
 // CHECK-NEXT:      "Template": {
 // CHECK-NEXT:        "Constraints": [
 // CHECK-NEXT:          {
+// CHECK-NEXT:            "End": true,
 // CHECK-NEXT:            "Expression": "Incrementable<T>",
 // CHECK-NEXT:            "Name": "Incrementable",
 // CHECK-NEXT:            "Path": "",
@@ -46,10 +49,13 @@ template<Incrementable T> Incrementable auto incrementTwo(T 
t);
 // CHECK-NEXT:      "USR": "{{[0-9A-F]*}}" 
 // CHECK-NEXT:    },
 // CHECK-NEXT:    {
+// CHECK-NEXT:      "End": true,
+// CHECK-NEXT:      "InfoType": "function",
 // CHECK-NEXT:      "IsStatic": false,
 // CHECK-NEXT:      "Name": "incrementTwo",
 // CHECK-NEXT:      "Params": [
 // CHECK-NEXT:        {
+// CHECK-NEXT:          "End": true,
 // CHECK-NEXT:          "Name": "t",
 // CHECK-NEXT:          "Type": "T"
 // CHECK-NEXT:        }
@@ -64,6 +70,7 @@ template<Incrementable T> Incrementable auto incrementTwo(T 
t);
 // CHECK-NEXT:      "Template": {
 // CHECK-NEXT:        "Constraints": [
 // CHECK-NEXT:          {
+// CHECK-NEXT:            "End": true,
 // CHECK-NEXT:            "Expression": "Incrementable<T>",
 // CHECK-NEXT:            "Name": "Incrementable",
 // CHECK-NEXT:            "Path": "",
diff --git a/clang-tools-extra/test/clang-doc/json/method-template.cpp 
b/clang-tools-extra/test/clang-doc/json/method-template.cpp
index ac8450a72d3a7..14232d00e277a 100644
--- a/clang-tools-extra/test/clang-doc/json/method-template.cpp
+++ b/clang-tools-extra/test/clang-doc/json/method-template.cpp
@@ -9,6 +9,7 @@ struct MyClass {
 
 // CHECK:           "PublicFunctions": [
 // CHECK-NEXT:        {
+// CHECK-NEXT:          "InfoType": "function",
 // CHECK-NEXT:          "IsStatic": false,
 // CHECK-NEXT:          "Location": {
 // CHECK-NEXT:            "Filename": "{{.*}}method-template.cpp",
@@ -21,6 +22,7 @@ struct MyClass {
 // CHECK-NEXT:          ],
 // CHECK-NEXT:          "Params": [
 // CHECK-NEXT:            {
+// CHECK-NEXT:              "End": true,
 // CHECK-NEXT:              "Name": "param",
 // CHECK-NEXT:              "Type": "T"
 // CHECK-NEXT:            }
diff --git a/clang-tools-extra/test/clang-doc/json/namespace.cpp 
b/clang-tools-extra/test/clang-doc/json/namespace.cpp
index 779d7b49f5581..b31531ecaacce 100644
--- a/clang-tools-extra/test/clang-doc/json/namespace.cpp
+++ b/clang-tools-extra/test/clang-doc/json/namespace.cpp
@@ -20,8 +20,11 @@ enum Color {
 typedef int MyTypedef;
 
 // CHECK:       { 
+// CHECK-NEXT:    "DocumentationFileName": "index",
 // CHECK-NEXT:    "Enums": [
 // CHECK-NEXT:      {
+// CHECK-NEXT:        "End": true,
+// CHECK-NEXT:        "InfoType": "enum",
 // CHECK-NEXT:        "Location": {
 // CHECK-NEXT:          "Filename": "{{.*}}namespace.cpp",
 // CHECK-NEXT:          "LineNumber": 14
@@ -36,6 +39,7 @@ typedef int MyTypedef;
 // CHECK-NEXT:            "Value": "1"
 // CHECK-NEXT:          },
 // CHECK-NEXT:          {
+// CHECK-NEXT:            "End": true,
 // CHECK-NEXT:            "Name": "BLUE",
 // CHECK-NEXT:            "ValueExpr": "5"
 // CHECK-NEXT:          }
@@ -47,10 +51,13 @@ typedef int MyTypedef;
 // CHECK-NEXT:    ],
 // CHECK-NEXT:   "Functions": [
 // CHECK-NEXT:     {
+// CHECK-NEXT:       "End": true,
+// CHECK-NEXT:       "InfoType": "function",
 // CHECK-NEXT:       "IsStatic": false,
 // CHECK-NEXT:       "Name": "myFunction",
 // CHECK-NEXT:       "Params": [
 // CHECK-NEXT:         {
+// CHECK-NEXT:           "End": true,
 // CHECK-NEXT:           "Name": "Param",
 // CHECK-NEXT:           "Type": "int"
 // CHECK-NEXT:         }
@@ -65,9 +72,14 @@ typedef int MyTypedef;
 // CHECK-NEXT:       "USR": "{{[0-9A-F]*}}"
 // CHECK-NEXT:     }
 // CHECK-NEXT:   ],
+// CHECK-NEXT:   "HasEnums": true,
+// CHECK-NEXT:   "HasRecords": true,
+// CHECK-NEXT:   "HasTypedefs": true,
+// CHECK-NEXT:   "InfoType": "namespace",
 // CHECK-NEXT:   "Name": "",
 // CHECK-NEXT:   "Namespaces": [
 // CHECK-NEXT:     {
+// CHECK-NEXT:       "End": true,
 // CHECK-NEXT:       "Name": "NestedNamespace",
 // CHECK-NEXT:       "Path": "",
 // CHECK-NEXT:       "QualName": "NestedNamespace",
@@ -76,6 +88,8 @@ typedef int MyTypedef;
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "Records": [
 // CHECK-NEXT:     {
+// CHECK-NEXT:       "DocumentationFileName": "_ZTV7MyClass",
+// CHECK-NEXT:       "End": true,
 // CHECK-NEXT:       "Name": "MyClass",
 // CHECK-NEXT:       "Path": "GlobalNamespace",
 // CHECK-NEXT:       "QualName": "MyClass",
@@ -84,6 +98,8 @@ typedef int MyTypedef;
 // CHECK-NEXT:   ],
 // CHECK-NEXT:   "Typedefs": [
 // CHECK-NEXT:    {
+// CHECK-NEXT:      "End": true,
+// CHECK-NEXT:      "InfoType": "typedef",
 // CHECK-NEXT:      "IsUsing": false,
 // CHECK-NEXT:      "Location": {
 // CHECK-NEXT:        "Filename": "{{.*}}namespace.cpp",
@@ -104,6 +120,8 @@ typedef int MyTypedef;
 // CHECK-NEXT:    "USR": "0000000000000000000000000000000000000000"
 // CHECK-NEXT:   "Variables": [
 // CHECK-NEXT:     {
+// CHECK-NEXT:       "End": true,
+// CHECK-NEXT:       "InfoType": "variable",
 // CHECK-NEXT:       "IsStatic": true,
 // CHECK-NEXT:       "Location": {
 // CHECK-NEXT:         "Filename": "{{.*}}namespace.cpp",
diff --git a/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp 
b/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp
index 54f95c4d041ca..255e540bd6c7c 100644
--- a/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp
+++ b/clang-tools-extra/test/clang-doc/json/nested-namespace.cpp
@@ -12,6 +12,8 @@ namespace nested {
 
 // NESTED:       "Variables": [
 // NESTED-NEXT:    {
+// NESTED-NEXT:      "End": true,
+// NESTED-NEXT:      "InfoType": "variable",
 // NESTED-NEXT:      "IsStatic": false,
 // NESTED-NEXT:      "Location": {
 // NESTED-NEXT:        "Filename": "{{.*}}nested-namespace.cpp",
@@ -24,6 +26,8 @@ namespace nested {
 
 // INNER:       "Variables": [
 // INNER-NEXT:    {
+// INNER-NEXT:      "End": true,
+// INNER-NEXT:      "InfoType": "variable",
 // INNER-NEXT:      "IsStatic": false,
 // INNER-NEXT:      "Location": {
 // INNER-NEXT:        "Filename": "{{.*}}nested-namespace.cpp",
diff --git a/clang-tools-extra/test/clang-doc/mustache-index.cpp 
b/clang-tools-extra/test/clang-doc/mustache-index.cpp
index cad4cc8b6931a..97bdbbf29e662 100644
--- a/clang-tools-extra/test/clang-doc/mustache-index.cpp
+++ b/clang-tools-extra/test/clang-doc/mustache-index.cpp
@@ -1,6 +1,6 @@
 // RUN: rm -rf %t && mkdir -p %t
 // RUN: clang-doc --format=mustache --output=%t --executor=standalone %s 
-// RUN: FileCheck %s < %t/GlobalNamespace/index.html
+// RUN: FileCheck %s < %t/index.html
 
 enum Color {
   RED,
@@ -15,7 +15,7 @@ class Foo;
 // CHECK-NEXT:  </li>
 // CHECK-NEXT:  <ul>
 // CHECK-NEXT:      <li class="sidebar-item-container">
-// CHECK-NEXT:          <a class="sidebar-item" href="#{{[0-9A-F]*}}">enum 
Color</a>
+// CHECK-NEXT:          <a class="sidebar-item" href="#{{[0-9A-F]*}}">Color</a>
 // CHECK-NEXT:      </li>
 // CHECK-NEXT:  </ul>
 // CHECK:           <li class="sidebar-section">
@@ -34,7 +34,7 @@ class Foo;
 // CHECK-NEXT:              <div>
 // CHECK-NEXT:                  <pre>
 // CHECK-NEXT:                      <code class="language-cpp code-clang-doc">
-// CHECK-NEXT:                          enum Color
+// CHECK-NEXT:                          Color
 // CHECK-NEXT:                      </code>
 // CHECK-NEXT:                  </pre>
 // CHECK-NEXT:              </div>
@@ -59,7 +59,7 @@ class Foo;
 // CHECK-NEXT:                  </tbody>
 // CHECK-NEXT:              </table>
 // CHECK-NEXT:              <div>
-// CHECK-NEXT:                  Defined at line 5 of file 
{{.*}}mustache-index.cpp
+// CHECK:                  Defined at line 5 of file {{.*}}mustache-index.cpp
 // CHECK-NEXT:              </div>
 // CHECK-NEXT:          </div>
 // CHECK-NEXT:      </div>
@@ -69,7 +69,11 @@ class Foo;
 // CHECK-NEXT:      <h2>Inner Classes</h2>
 // CHECK-NEXT:      <ul class="class-container">
 // CHECK-NEXT:          <li id="{{[0-9A-F]*}}" style="max-height: 40px;">
-// CHECK-NEXT:              <a href="Foo.html"><pre><code class="language-cpp 
code-clang-doc" >class Foo</code></pre></a>
+// CHECK-NEXT:              <a href="_ZTV3Foo.html">
+// CHECK-NEXT:                  <pre>
+// CHECK-NEXT:                      <code class="language-cpp 
code-clang-doc">class Foo</code>
+// CHECK-NEXT:                  </pre>
+// CHECK-NEXT:              </a>
 // CHECK-NEXT:          </li>
 // CHECK-NEXT:      </ul>
 // CHECK-NEXT:  </section>
diff --git a/clang-tools-extra/test/clang-doc/mustache-separate-namespace.cpp 
b/clang-tools-extra/test/clang-doc/mustache-separate-namespace.cpp
index ec29b2449169b..7d7d108e63873 100644
--- a/clang-tools-extra/test/clang-doc/mustache-separate-namespace.cpp
+++ b/clang-tools-extra/test/clang-doc/mustache-separate-namespace.cpp
@@ -1,6 +1,6 @@
 // RUN: rm -rf %t && mkdir -p %t
 // RUN: clang-doc --format=mustache --output=%t --executor=standalone %s 
-// RUN: FileCheck %s < %t/MyNamespace/index.html
+// RUN: FileCheck %s < %t/MyNamespace.html
 
 namespace MyNamespace {
   class Foo;
@@ -8,6 +8,10 @@ namespace MyNamespace {
 
 // CHECK:       <ul class="class-container">
 // CHECK-NEXT:    <li id="{{[0-9A-F]*}}" style="max-height: 40px;">
-// CHECK-NEXT:        <a href="Foo.html"><pre><code class="language-cpp 
code-clang-doc" >class Foo</code></pre></a>
+// CHECK-NEXT:        <a href="_ZTVN11MyNamespace3FooE.html">
+// CHECK-NEXT:            <pre>
+// CHECK-NEXT:                <code class="language-cpp code-clang-doc">class 
Foo</code>
+// CHECK-NEXT:            </pre>
+// CHECK-NEXT:        </a>
 // CHECK-NEXT:    </li>
 // CHECK-NEXT: </ul>
diff --git 
a/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp 
b/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp
index 32b0846a02dba..602058f5d9eb8 100644
--- a/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/HTMLMustacheGeneratorTest.cpp
@@ -86,132 +86,3 @@ TEST(HTMLMustacheGeneratorTest, createResources) {
     verifyFileContents(PathBuf, "JavaScript");
   }
 }
-
-TEST(HTMLGeneratorTest, emitFunctionHTML) {
-#if ENABLE_LOCAL_TEST
-  auto G = getHTMLMustacheGenerator();
-  assert(G && "Could not find HTMLMustacheGenerator");
-  ClangDocContext CDCtx = getClangDocContext();
-  std::string Buffer;
-  llvm::raw_string_ostream Actual(Buffer);
-
-  unittest::TempDir RootTestDirectory("emitRecordHTML",
-                                      /*Unique=*/true);
-  CDCtx.OutDirectory = RootTestDirectory.path();
-
-  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
-
-  // FIXME: This is a terrible hack, since we can't initialize the templates
-  // directly. We'll need to update the interfaces so that we can call
-  // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp
-  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
-                    Succeeded())
-      << "Failed to generate docs.";
-
-  CDCtx.RepositoryUrl = "http://www.repository.com";;
-
-  FunctionInfo I;
-  I.Name = "f";
-  I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
-
-  I.DefLoc = Location(10, 10, "dir/test.cpp", true);
-  I.Loc.emplace_back(12, 12, "test.cpp");
-
-  I.Access = AccessSpecifier::AS_none;
-
-  SmallString<16> PathTo;
-  llvm::sys::path::native("path/to", PathTo);
-  I.ReturnType = doc::TypeInfo(
-      Reference(EmptySID, "float", InfoType::IT_default, "float", PathTo));
-  I.Params.emplace_back(doc::TypeInfo("int", PathTo), "P");
-  I.IsMethod = true;
-  I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
-
-  auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
-  assert(!Err);
-  std::string Expected = R"raw(IT_Function
-)raw";
-
-  // FIXME: Functions are not handled yet.
-  EXPECT_EQ(Expected, Actual.str());
-#endif
-}
-
-TEST(HTMLMustacheGeneratorTest, emitCommentHTML) {
-#if ENABLE_LOCAL_TEST
-  auto G = getHTMLMustacheGenerator();
-  assert(G && "Could not find HTMLMustacheGenerator");
-  ClangDocContext CDCtx = getClangDocContext();
-  std::string Buffer;
-  llvm::raw_string_ostream Actual(Buffer);
-
-  unittest::TempDir RootTestDirectory("emitCommentHTML",
-                                      /*Unique=*/true);
-  CDCtx.OutDirectory = RootTestDirectory.path();
-
-  getMustacheHtmlFiles(CLANG_DOC_TEST_ASSET_DIR, CDCtx);
-
-  // FIXME: This is a terrible hack, since we can't initialize the templates
-  // directly. We'll need to update the interfaces so that we can call
-  // SetupTemplateFiles() from outsize of HTMLMustacheGenerator.cpp
-  EXPECT_THAT_ERROR(G->generateDocs(RootTestDirectory.path(), {}, CDCtx),
-                    Succeeded())
-      << "Failed to generate docs.";
-
-  CDCtx.RepositoryUrl = "http://www.repository.com";;
-
-  FunctionInfo I;
-  I.Name = "f";
-  I.DefLoc = Location(10, 10, "test.cpp", true);
-  I.ReturnType = doc::TypeInfo("void");
-  I.Params.emplace_back(doc::TypeInfo("int"), "I");
-  I.Params.emplace_back(doc::TypeInfo("int"), "J");
-  I.Access = AccessSpecifier::AS_none;
-
-  CommentInfo Top;
-  Top.Kind = "FullComment";
-
-  Top.Children.emplace_back(std::make_unique<CommentInfo>());
-  CommentInfo *BlankLine = Top.Children.back().get();
-  BlankLine->Kind = "ParagraphComment";
-  BlankLine->Children.emplace_back(std::make_unique<CommentInfo>());
-  BlankLine->Children.back()->Kind = "TextComment";
-
-  Top.Children.emplace_back(std::make_unique<CommentInfo>());
-  CommentInfo *Brief = Top.Children.back().get();
-  Brief->Kind = "ParagraphComment";
-  Brief->Children.emplace_back(std::make_unique<CommentInfo>());
-  Brief->Children.back()->Kind = "TextComment";
-  Brief->Children.back()->Name = "ParagraphComment";
-  Brief->Children.back()->Text = " Brief description.";
-
-  Top.Children.emplace_back(std::make_unique<CommentInfo>());
-  CommentInfo *Extended = Top.Children.back().get();
-  Extended->Kind = "ParagraphComment";
-  Extended->Children.emplace_back(std::make_unique<CommentInfo>());
-  Extended->Children.back()->Kind = "TextComment";
-  Extended->Children.back()->Text = " Extended description that";
-  Extended->Children.emplace_back(std::make_unique<CommentInfo>());
-  Extended->Children.back()->Kind = "TextComment";
-  Extended->Children.back()->Text = " continues onto the next line.";
-
-  Top.Children.emplace_back(std::make_unique<CommentInfo>());
-  CommentInfo *Entities = Top.Children.back().get();
-  Entities->Kind = "ParagraphComment";
-  Entities->Children.emplace_back(std::make_unique<CommentInfo>());
-  Entities->Children.back()->Kind = "TextComment";
-  Entities->Children.back()->Name = "ParagraphComment";
-  Entities->Children.back()->Text =
-      " Comment with html entities: &, <, >, \", \'.";
-
-  I.Description.emplace_back(std::move(Top));
-
-  auto Err = G->generateDocForInfo(&I, Actual, CDCtx);
-  assert(!Err);
-  std::string Expected = R"raw(IT_Function
-)raw";
-
-  // FIXME: Functions are not handled yet.
-  EXPECT_EQ(Expected, Actual.str());
-#endif
-}
diff --git a/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp 
b/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp
index 5927235b3bd93..f1ab1bd19970b 100644
--- a/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/JSONGeneratorTest.cpp
@@ -63,7 +63,11 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
   "Bases": [
     {
       "Access": "public",
+      "End": true,
       "FullName": "",
+      "HasPublicFunctions": true,
+      "HasPublicMembers": true,
+      "InfoType": "record",
       "IsParent": true,
       "IsTypedef": false,
       "IsVirtual": true,
@@ -72,6 +76,7 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
       "Path": "path/to/F",
       "PublicFunctions": [
         {
+          "InfoType": "function",
           "IsStatic": false,
           "Name": "InheritedFunctionOne",
           "ReturnType": {
@@ -96,8 +101,11 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
   ],
   "Enums": [
     {
+      "End": true,
+      "InfoType": "enum",
       "Members": [
         {
+          "End": true,
           "Name": "RED",
           "Value": "0"
         }
@@ -108,6 +116,11 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
     }
   ],
   "FullName": "",
+  "HasEnums": true,
+  "HasProtectedMembers": true,
+  "HasPublicFunctions": true,
+  "HasRecords": true,
+  "InfoType": "record",
   "IsTypedef": false,
   "Location": {
     "Filename": "main.cpp",
@@ -120,6 +133,7 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
   ],
   "Parents": [
     {
+      "End": true,
       "Name": "F",
       "Path": "",
       "QualName": "",
@@ -135,6 +149,7 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
   ],
   "PublicFunctions": [
     {
+      "InfoType": "function",
       "IsStatic": false,
       "Name": "OneFunction",
       "ReturnType": {
@@ -149,6 +164,7 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
   ],
   "Records": [
     {
+      "End": true,
       "Name": "ChildStruct",
       "Path": "path/to/A/r",
       "QualName": "path::to::A::r::ChildStruct",
@@ -164,6 +180,7 @@ TEST(JSONGeneratorTest, emitRecordJSON) {
   "USR": "0000000000000000000000000000000000000000",
   "VirtualParents": [
     {
+      "End": true,
       "Name": "G",
       "Path": "path/to/G",
       "QualName": "path::to::G::G",
@@ -201,6 +218,8 @@ TEST(JSONGeneratorTest, emitNamespaceJSON) {
   std::string Expected = R"raw({
   "Enums": [
     {
+      "End": true,
+      "InfoType": "enum",
       "Name": "OneEnum",
       "Scoped": false,
       "USR": "0000000000000000000000000000000000000000"
@@ -208,6 +227,8 @@ TEST(JSONGeneratorTest, emitNamespaceJSON) {
   ],
   "Functions": [
     {
+      "End": true,
+      "InfoType": "function",
       "IsStatic": false,
       "Name": "OneFunction",
       "ReturnType": {
@@ -220,12 +241,16 @@ TEST(JSONGeneratorTest, emitNamespaceJSON) {
       "USR": "0000000000000000000000000000000000000000"
     }
   ],
+  "HasEnums": true,
+  "HasRecords": true,
+  "InfoType": "namespace",
   "Name": "Namespace",
   "Namespace": [
     "A"
   ],
   "Namespaces": [
     {
+      "End": true,
       "Name": "ChildNamespace",
       "Path": "path/to/A/Namespace",
       "QualName": "path::to::A::Namespace::ChildNamespace",
@@ -235,6 +260,7 @@ TEST(JSONGeneratorTest, emitNamespaceJSON) {
   "Path": "path/to/A",
   "Records": [
     {
+      "End": true,
       "Name": "ChildStruct",
       "Path": "path/to/A/Namespace",
       "QualName": "path::to::A::Namespace::ChildStruct",

_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to