[clang-tools-extra] [clang-doc] integrate JSON generator with Mustache templates (PR #149006)
Erick Velez via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 16 09:11:01 PDT 2025
https://github.com/evelez7 updated https://github.com/llvm/llvm-project/pull/149006
>From 8d6dd85b824de1158a943d09c50aefaa2eac484c Mon Sep 17 00:00:00 2001
From: Erick Velez <erickvelez7 at 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 | 488 +++---------------
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, 420 insertions(+), 714 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..32fe37ac12ad3 100644
--- a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp
@@ -27,6 +27,9 @@ 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 +135,74 @@ 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);
- }
+ {
+ llvm::TimeTraceScope TS("Generate JSON for Mustache");
+ if (auto JSONGenerator = findGeneratorByName("json")) {
+ if (Error Err = JSONGenerator.get()->generateDocs(
+ RootDir, std::move(Infos), CDCtx))
+ return Err;
+ } else
+ return JSONGenerator.takeError();
+ }
- sys::path::append(Path, Info->getFileBaseName() + ".html");
- FileToInfos[Path].push_back(Info);
+ StringMap<json::Value> JSONFileMap;
+ {
+ llvm::TimeTraceScope TS("Iterate JSON files");
+ std::error_code EC;
+ sys::fs::directory_iterator JSONIter(RootDir, EC);
+ std::vector<json::Value> JSONFiles;
+ JSONFiles.reserve(Infos.size());
+ 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;
+ }
+
+ 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> HTMLPath(Path.begin(), Path.end());
+ sys::path::replace_extension(HTMLPath, "html");
+ raw_fd_ostream InfoOS(HTMLPath, 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(HTMLPath),
+ HTMLPath, 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 +228,43 @@ 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",
More information about the cfe-commits
mailing list