https://github.com/evelez7 created https://github.com/llvm/llvm-project/pull/149589
None >From b36a2d4bc00eee19dae7bd0eaaf08a3b4d8a70a2 Mon Sep 17 00:00:00 2001 From: Erick Velez <erickvel...@gmail.com> Date: Fri, 18 Jul 2025 13:59:44 -0700 Subject: [PATCH] [clang-doc] integrate JSON as the source for Mustache templates --- .../clang-doc/HTMLMustacheGenerator.cpp | 487 +++--------------- .../clang-doc/assets/class-template.mustache | 62 +-- .../clang-doc/assets/enum-template.mustache | 12 +- .../assets/function-template.mustache | 2 +- .../assets/namespace-template.mustache | 45 +- .../clang-doc/basic-project.mustache.test | 70 +-- .../test/clang-doc/mustache-index.cpp | 14 +- .../clang-doc/mustache-separate-namespace.cpp | 8 +- .../clang-doc/HTMLMustacheGeneratorTest.cpp | 129 ----- 9 files changed, 173 insertions(+), 656 deletions(-) diff --git a/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp b/clang-tools-extra/clang-doc/HTMLMustacheGenerator.cpp index 7aeaa1b7cf67d..98e2935a8aada 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,65 @@ 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); - } - - sys::path::append(Path, Info->getFileBaseName() + ".html"); - FileToInfos[Path].push_back(Info); + { + 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(); } + StringMap<json::Value> JSONFileMap; { - llvm::TimeTraceScope TS("Generate Docs"); - for (const auto &Group : FileToInfos) { - llvm::TimeTraceScope TS("Info to Doc"); + 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(); + std::error_code FileErr; - raw_fd_ostream InfoOS(Group.getKey(), FileErr, sys::fs::OF_None); + 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(Path, FileErr); - for (const auto &Info : Group.getValue()) - if (Error Err = generateDocForInfo(Info, InfoOS, CDCtx)) - return Err; + if (Error Err = generateDocForJSON(*Parsed, sys::path::stem(HTMLPath), + HTMLPath, InfoOS, CDCtx)) + return Err; + JSONIter.increment(EC); } } - 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; + return Error::success(); } -static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V, - Info *I) { +static Error setupTemplateValue(const ClangDocContext &CDCtx, json::Value &V) { 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 +219,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)) 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)) 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/assets/class-template.mustache b/clang-tools-extra/clang-doc/assets/class-template.mustache index f9e78f5cd6bc9..a4077323f29e2 100644 --- a/clang-tools-extra/clang-doc/assets/class-template.mustache +++ b/clang-tools-extra/clang-doc/assets/class-template.mustache @@ -44,20 +44,20 @@ <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}} + {{/HasPublicMembers}} {{#ProtectedMembers}} <li class="sidebar-section"> <a class="sidebar-item" href="#PublicMethods">Protected Members</a> @@ -70,18 +70,18 @@ {{/Obj}} </ul> {{/ProtectedMembers}} - {{#PublicFunction}} + {{#HasPublicFunctions}} <li class="sidebar-section"> <a class="sidebar-item" href="#PublicMethods">Public Method</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}} + {{/HasPublicFunctions}} {{#ProtectedFunction}} <li class="sidebar-section"> <a class="sidebar-item" href="#ProtectedFunction">Protected Method</a> @@ -101,7 +101,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> @@ -127,7 +127,7 @@ <div class="content"> <section class="hero section-container"> <div class="hero__title"> - <h1 class="hero__title-large">{{RecordType}} {{Name}}</h1> + <h1 class="hero__title-large">{{TagType}} {{Name}}</h1> {{#RecordComments}} <div class="hero__subtitle"> {{>Comments}} @@ -135,14 +135,14 @@ {{/RecordComments}} </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,10 +150,10 @@ </div> {{/MemberComments}} </div> - {{/Obj}} + {{/PublicMembers}} </div> </section> - {{/PublicMembers}} + {{/HasPublicMembers}} {{#ProtectedMembers}} <section id="ProtectedMembers" class="section-container"> <h2>Protected Members</h2> @@ -173,26 +173,28 @@ </div> </section> {{/ProtectedMembers}} - {{#PublicFunction}} + {{#HasPublicFunctions}} <section id="PublicMethods" class="section-container"> <h2>Public Methods</h2> <div> - {{#Obj}} + {{#PublicFunctions}} {{>FunctionPartial}} - {{/Obj}} + {{/PublicFunctions}} </div> </section> - {{/PublicFunction}} - {{#ProtectedFunction}} - <section id="ProtectedFunction" class="section-container"> - <h2>Protected Methods</h2> - <div> + {{/PublicFunctions}} + {{#ProtectedFunction}} + <li class="sidebar-section"> + <a class="sidebar-item" href="#ProtectedFunction">Protected Method</a> + </li> + <ul> {{#Obj}} -{{>FunctionPartial}} + <li class="sidebar-item-container"> + <a class="sidebar-item" href="#{{ID}}">{{Name}}</a> + </li> {{/Obj}} - </div> - </section> - {{/ProtectedFunction}} + </ul> + {{/ProtectedFunction}} {{#Enums}} <section id="Enums" class="section-container"> <h2>Enumerations</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..a285dfb0d207b 100644 --- a/clang-tools-extra/clang-doc/assets/enum-template.mustache +++ b/clang-tools-extra/clang-doc/assets/enum-template.mustache @@ -9,7 +9,7 @@ <div> <pre> <code class="language-cpp code-clang-doc"> -{{EnumName}} +{{Name}} </code> </pre> </div> @@ -23,15 +23,21 @@ <th>Comment</th> {{/HasComment}} </tr> - {{#EnumValues}} + {{#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}} 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..4dd6f4165f65e 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> @@ -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"> @@ -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,17 +279,6 @@ 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: </li> @@ -313,21 +302,6 @@ 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> -HTML-RECTANGLE: </section> HTML-RECTANGLE: <section id="PublicMethods" class="section-container"> HTML-RECTANGLE: <h2>Public Methods</h2> HTML-RECTANGLE: <div> @@ -372,8 +346,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,14 +374,6 @@ 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: </li> @@ -431,16 +397,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/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 -} _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits