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