Index: clang-tools-extra/clang-doc/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-doc/CMakeLists.txt
+++ clang-tools-extra/clang-doc/CMakeLists.txt
@@ -9,6 +9,7 @@
BitcodeWriter.cpp
ClangDoc.cpp
Generators.cpp
+ HTMLGenerator.cpp
Mapper.cpp
MDGenerator.cpp
Representation.cpp
Index: clang-tools-extra/clang-doc/Generators.h
===================================================================
--- clang-tools-extra/clang-doc/Generators.h
+++ clang-tools-extra/clang-doc/Generators.h
@@ -34,6 +34,12 @@
llvm::Expected>
findGeneratorByName(llvm::StringRef Format);
+std::string getAccess(AccessSpecifier AS);
+
+std::string getTagType(TagTypeKind AS);
+
+std::string genReferenceList(const llvm::SmallVectorImpl &Refs);
+
} // namespace doc
} // namespace clang
Index: clang-tools-extra/clang-doc/Generators.cpp
===================================================================
--- clang-tools-extra/clang-doc/Generators.cpp
+++ clang-tools-extra/clang-doc/Generators.cpp
@@ -25,14 +25,62 @@
llvm::inconvertibleErrorCode());
}
+// Enum conversion
+
+std::string getAccess(AccessSpecifier AS) {
+ switch (AS) {
+ case AccessSpecifier::AS_public:
+ return "public";
+ case AccessSpecifier::AS_protected:
+ return "protected";
+ case AccessSpecifier::AS_private:
+ return "private";
+ case AccessSpecifier::AS_none:
+ return {};
+ }
+ llvm_unreachable("Unknown AccessSpecifier");
+}
+
+std::string getTagType(TagTypeKind AS) {
+ switch (AS) {
+ case TagTypeKind::TTK_Class:
+ return "class";
+ case TagTypeKind::TTK_Union:
+ return "union";
+ case TagTypeKind::TTK_Interface:
+ return "interface";
+ case TagTypeKind::TTK_Struct:
+ return "struct";
+ case TagTypeKind::TTK_Enum:
+ return "enum";
+ }
+ llvm_unreachable("Unknown TagTypeKind");
+}
+
+std::string genReferenceList(const llvm::SmallVectorImpl &Refs) {
+ std::string Buffer;
+ llvm::raw_string_ostream Stream(Buffer);
+ bool First = true;
+ for (const auto &R : Refs) {
+ if (!First)
+ Stream << ", ";
+ Stream << R.Name;
+ First = false;
+ }
+ return Stream.str();
+}
+
// This anchor is used to force the linker to link in the generated object file
// and thus register the generators.
extern volatile int YAMLGeneratorAnchorSource;
extern volatile int MDGeneratorAnchorSource;
+extern volatile int HTMLGeneratorAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED YAMLGeneratorAnchorDest =
YAMLGeneratorAnchorSource;
static int LLVM_ATTRIBUTE_UNUSED MDGeneratorAnchorDest =
MDGeneratorAnchorSource;
+static int LLVM_ATTRIBUTE_UNUSED HTMLGeneratorAnchorDest =
+ HTMLGeneratorAnchorSource;
} // namespace doc
} // namespace clang
Index: clang-tools-extra/clang-doc/HTMLGenerator.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -0,0 +1,279 @@
+//===-- HTMLGenerator.cpp - HTML Generator ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Generators.h"
+#include "Representation.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include
+
+using namespace llvm;
+
+namespace clang {
+namespace doc {
+
+namespace {
+
+// HTML generation
+
+std::string genTag(const Twine &Text, const Twine &Tag) {
+ return "<" + Tag.str() + ">" + Text.str() + "" + Tag.str() + ">";
+}
+
+std::string genItalic(const Twine &Text) { return genTag(Text, "em"); }
+
+std::string genEmphasis(const Twine &Text) { return genTag(Text, "strong"); }
+
+void writeLine(const Twine &Text, raw_ostream &OS) {
+ OS << genTag(Text, "p") << "\n";
+}
+
+void writeNewLine(raw_ostream &OS) { OS << "
\n"; }
+
+void writeHeader(const Twine &Text, const Twine &Num, raw_ostream &OS) {
+ OS << genTag(Text, "h" + Num) << "\n";
+}
+
+void writeFileDefinition(const Location &L, raw_ostream &OS) {
+ writeLine(genItalic("Defined at line " + std::to_string(L.LineNumber) +
+ " of " + L.Filename),
+ OS);
+}
+
+void writeDescription(const CommentInfo &I, raw_ostream &OS) {
+ if (I.Kind == "FullComment") {
+ for (const auto &Child : I.Children)
+ writeDescription(*Child, OS);
+ } else if (I.Kind == "ParagraphComment") {
+ for (const auto &Child : I.Children)
+ writeDescription(*Child, OS);
+ writeNewLine(OS);
+ } else if (I.Kind == "BlockCommandComment") {
+ OS << genEmphasis(I.Name);
+ for (const auto &Child : I.Children)
+ writeDescription(*Child, OS);
+ } else if (I.Kind == "InlineCommandComment") {
+ OS << genEmphasis(I.Name) << " " << I.Text;
+ } else if (I.Kind == "ParamCommandComment") {
+ std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
+ OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n\n";
+ } else if (I.Kind == "TParamCommandComment") {
+ std::string Direction = I.Explicit ? (" " + I.Direction).str() : "";
+ OS << genEmphasis(I.ParamName) << I.Text << Direction << "\n\n";
+ } else if (I.Kind == "VerbatimBlockComment") {
+ for (const auto &Child : I.Children)
+ writeDescription(*Child, OS);
+ } else if (I.Kind == "VerbatimBlockLineComment") {
+ OS << I.Text;
+ writeNewLine(OS);
+ } else if (I.Kind == "VerbatimLineComment") {
+ OS << I.Text;
+ writeNewLine(OS);
+ } else if (I.Kind == "HTMLStartTagComment") {
+ if (I.AttrKeys.size() != I.AttrValues.size())
+ return;
+ std::string Buffer;
+ llvm::raw_string_ostream Attrs(Buffer);
+ for (unsigned Idx = 0; Idx < I.AttrKeys.size(); ++Idx)
+ Attrs << " \"" << I.AttrKeys[Idx] << "=" << I.AttrValues[Idx] << "\"";
+
+ std::string CloseTag = I.SelfClosing ? "/>" : ">";
+ writeLine("<" + I.Name + Attrs.str() + CloseTag, OS);
+ } else if (I.Kind == "HTMLEndTagComment") {
+ writeLine("" + I.Name + ">", OS);
+ } else if (I.Kind == "TextComment") {
+ OS << I.Text;
+ } else {
+ OS << "Unknown comment kind: " << I.Kind << ".\n\n";
+ }
+}
+
+} // namespace
+
+void genHTML(const EnumInfo &I, llvm::raw_ostream &OS) {
+ if (I.Scoped)
+ writeLine("| enum class " + I.Name + " |", OS);
+ else
+ writeLine("| enum " + I.Name + " |", OS);
+ writeLine("--", OS);
+
+ std::string Buffer;
+ llvm::raw_string_ostream Members(Buffer);
+ if (!I.Members.empty())
+ for (const auto &N : I.Members)
+ Members << "| " << N << " |\n";
+ writeLine(Members.str(), OS);
+ if (I.DefLoc)
+ writeFileDefinition(I.DefLoc.getValue(), OS);
+
+ for (const auto &C : I.Description)
+ writeDescription(C, OS);
+}
+
+void genHTML(const FunctionInfo &I, llvm::raw_ostream &OS) {
+ std::string Buffer;
+ llvm::raw_string_ostream Stream(Buffer);
+ bool First = true;
+ for (const auto &N : I.Params) {
+ if (!First)
+ Stream << ", ";
+ Stream << N.Type.Name + " " + N.Name;
+ First = false;
+ }
+ writeHeader(I.Name, "3", OS);
+ std::string Access = getAccess(I.Access);
+ if (Access != "")
+ writeLine(genItalic(Access + " " + I.ReturnType.Type.Name + " " + I.Name +
+ "(" + Stream.str() + ")"),
+ OS);
+ else
+ writeLine(genItalic(I.ReturnType.Type.Name + " " + I.Name + "(" +
+ Stream.str() + ")"),
+ OS);
+ if (I.DefLoc)
+ writeFileDefinition(I.DefLoc.getValue(), OS);
+
+ for (const auto &C : I.Description)
+ writeDescription(C, OS);
+}
+
+void genHTML(const NamespaceInfo &I, llvm::raw_ostream &OS) {
+ if (I.Name == "")
+ writeHeader("Global Namespace", "1", OS);
+ else
+ writeHeader("namespace " + I.Name, "1", OS);
+ writeNewLine(OS);
+
+ if (!I.Description.empty()) {
+ for (const auto &C : I.Description)
+ writeDescription(C, OS);
+ writeNewLine(OS);
+ }
+
+ if (!I.ChildNamespaces.empty()) {
+ writeHeader("Namespaces", "2", OS);
+ for (const auto &R : I.ChildNamespaces)
+ writeLine(R.Name, OS);
+ writeNewLine(OS);
+ }
+ if (!I.ChildRecords.empty()) {
+ writeHeader("Records", "2", OS);
+ for (const auto &R : I.ChildRecords)
+ writeLine(R.Name, OS);
+ writeNewLine(OS);
+ }
+ if (!I.ChildFunctions.empty()) {
+ writeHeader("Functions", "2", OS);
+ for (const auto &F : I.ChildFunctions)
+ genHTML(F, OS);
+ writeNewLine(OS);
+ }
+ if (!I.ChildEnums.empty()) {
+ writeHeader("Enums", "2", OS);
+ for (const auto &E : I.ChildEnums)
+ genHTML(E, OS);
+ writeNewLine(OS);
+ }
+}
+
+void genHTML(const RecordInfo &I, llvm::raw_ostream &OS) {
+ writeHeader(getTagType(I.TagType) + " " + I.Name, "1", OS);
+ if (I.DefLoc)
+ writeFileDefinition(I.DefLoc.getValue(), OS);
+
+ if (!I.Description.empty()) {
+ for (const auto &C : I.Description)
+ writeDescription(C, OS);
+ writeNewLine(OS);
+ }
+
+ std::string Parents = genReferenceList(I.Parents);
+ std::string VParents = genReferenceList(I.VirtualParents);
+ if (!Parents.empty() || !VParents.empty()) {
+ if (Parents.empty())
+ writeLine("Inherits from " + VParents, OS);
+ else if (VParents.empty())
+ writeLine("Inherits from " + Parents, OS);
+ else
+ writeLine("Inherits from " + Parents + ", " + VParents, OS);
+ writeNewLine(OS);
+ }
+
+ if (!I.Members.empty()) {
+ writeHeader("Members", "2", OS);
+ for (const auto Member : I.Members) {
+ std::string Access = getAccess(Member.Access);
+ if (Access != "")
+ writeLine(Access + " " + Member.Type.Name + " " + Member.Name, OS);
+ else
+ writeLine(Member.Type.Name + " " + Member.Name, OS);
+ }
+ writeNewLine(OS);
+ }
+
+ if (!I.ChildRecords.empty()) {
+ writeHeader("Records", "2", OS);
+ for (const auto &R : I.ChildRecords)
+ writeLine(R.Name, OS);
+ writeNewLine(OS);
+ }
+ if (!I.ChildFunctions.empty()) {
+ writeHeader("Functions", "2", OS);
+ for (const auto &F : I.ChildFunctions)
+ genHTML(F, OS);
+ writeNewLine(OS);
+ }
+ if (!I.ChildEnums.empty()) {
+ writeHeader("Enums", "2", OS);
+ for (const auto &E : I.ChildEnums)
+ genHTML(E, OS);
+ writeNewLine(OS);
+ }
+}
+
+/// Generator for HTML documentation.
+class HTMLGenerator : public Generator {
+public:
+ static const char *Format;
+
+ llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override;
+};
+
+const char *HTMLGenerator::Format = "html";
+
+llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) {
+ switch (I->IT) {
+ case InfoType::IT_namespace:
+ genHTML(*static_cast(I), OS);
+ break;
+ case InfoType::IT_record:
+ genHTML(*static_cast(I), OS);
+ break;
+ case InfoType::IT_enum:
+ genHTML(*static_cast(I), OS);
+ break;
+ case InfoType::IT_function:
+ genHTML(*static_cast(I), OS);
+ break;
+ case InfoType::IT_default:
+ return llvm::make_error("Unexpected info type.\n",
+ llvm::inconvertibleErrorCode());
+ }
+ return llvm::Error::success();
+}
+
+static GeneratorRegistry::Add HTML(HTMLGenerator::Format,
+ "Generator for HTML output.");
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the generator.
+volatile int HTMLGeneratorAnchorSource = 0;
+
+} // namespace doc
+} // namespace clang
Index: clang-tools-extra/clang-doc/MDGenerator.cpp
===================================================================
--- clang-tools-extra/clang-doc/MDGenerator.cpp
+++ clang-tools-extra/clang-doc/MDGenerator.cpp
@@ -18,37 +18,7 @@
namespace clang {
namespace doc {
-// Enum conversion
-
-std::string getAccess(AccessSpecifier AS) {
- switch (AS) {
- case AccessSpecifier::AS_public:
- return "public";
- case AccessSpecifier::AS_protected:
- return "protected";
- case AccessSpecifier::AS_private:
- return "private";
- case AccessSpecifier::AS_none:
- return {};
- }
- llvm_unreachable("Unknown AccessSpecifier");
-}
-
-std::string getTagType(TagTypeKind AS) {
- switch (AS) {
- case TagTypeKind::TTK_Class:
- return "class";
- case TagTypeKind::TTK_Union:
- return "union";
- case TagTypeKind::TTK_Interface:
- return "interface";
- case TagTypeKind::TTK_Struct:
- return "struct";
- case TagTypeKind::TTK_Enum:
- return "enum";
- }
- llvm_unreachable("Unknown TagTypeKind");
-}
+namespace {
// Markdown generation
@@ -60,19 +30,6 @@
return "[" + Text.str() + "](" + Link.str() + ")";
}
-std::string genReferenceList(const llvm::SmallVectorImpl &Refs) {
- std::string Buffer;
- llvm::raw_string_ostream Stream(Buffer);
- bool First = true;
- for (const auto &R : Refs) {
- if (!First)
- Stream << ", ";
- Stream << R.Name;
- First = false;
- }
- return Stream.str();
-}
-
void writeLine(const Twine &Text, raw_ostream &OS) { OS << Text << "\n\n"; }
void writeNewLine(raw_ostream &OS) { OS << "\n\n"; }
@@ -135,6 +92,8 @@
}
}
+} // namespace
+
void genMarkdown(const EnumInfo &I, llvm::raw_ostream &OS) {
if (I.Scoped)
writeLine("| enum class " + I.Name + " |", OS);
Index: clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
===================================================================
--- clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
+++ clang-tools-extra/clang-doc/tool/ClangDocMain.cpp
@@ -65,6 +65,7 @@
enum OutputFormatTy {
md,
yaml,
+ html,
};
static llvm::cl::opt
@@ -72,7 +73,9 @@
llvm::cl::values(clEnumValN(OutputFormatTy::yaml, "yaml",
"Documentation in YAML format."),
clEnumValN(OutputFormatTy::md, "md",
- "Documentation in MD format.")),
+ "Documentation in MD format."),
+ clEnumValN(OutputFormatTy::html, "html",
+ "Documentation in HTML format.")),
llvm::cl::init(OutputFormatTy::yaml),
llvm::cl::cat(ClangDocCategory));
@@ -82,6 +85,8 @@
return "yaml";
case OutputFormatTy::md:
return "md";
+ case OutputFormatTy::html:
+ return "html";
}
llvm_unreachable("Unknown OutputFormatTy");
}
Index: clang-tools-extra/unittests/clang-doc/CMakeLists.txt
===================================================================
--- clang-tools-extra/unittests/clang-doc/CMakeLists.txt
+++ clang-tools-extra/unittests/clang-doc/CMakeLists.txt
@@ -13,6 +13,7 @@
add_extra_unittest(ClangDocTests
BitcodeTest.cpp
ClangDocTest.cpp
+ HTMLGeneratorTest.cpp
MDGeneratorTest.cpp
MergeTest.cpp
SerializeTest.cpp
Index: clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
@@ -0,0 +1,306 @@
+//===-- clang-doc/HTMLGeneratorTest.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangDocTest.h"
+#include "Generators.h"
+#include "Representation.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace doc {
+
+std::unique_ptr getHTMLGenerator() {
+ auto G = doc::findGeneratorByName("html");
+ if (!G)
+ return nullptr;
+ return std::move(G.get());
+}
+
+TEST(HTMLGeneratorTest, emitNamespaceHTML) {
+ NamespaceInfo I;
+ I.Name = "Namespace";
+ I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+ I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
+ InfoType::IT_namespace);
+ I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+ I.ChildFunctions.emplace_back();
+ I.ChildFunctions.back().Name = "OneFunction";
+ I.ChildEnums.emplace_back();
+ I.ChildEnums.back().Name = "OneEnum";
+
+ auto G = getHTMLGenerator();
+ assert(G);
+ std::string Buffer;
+ llvm::raw_string_ostream Actual(Buffer);
+ auto Err = G->generateDocForInfo(&I, Actual);
+ assert(!Err);
+ std::string Expected = R"raw(namespace Namespace
+
+Namespaces
+ChildNamespace
+
+Records
+ChildStruct
+
+Functions
+OneFunction
+ OneFunction()
+
+Enums
+| enum OneEnum |
+--
+
+
+)raw";
+
+ EXPECT_EQ(Expected, Actual.str());
+}
+
+TEST(HTMLGeneratorTest, emitRecordHTML) {
+ RecordInfo I;
+ I.Name = "r";
+ I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+ I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+ I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+
+ I.Members.emplace_back("int", "X", AccessSpecifier::AS_private);
+ I.TagType = TagTypeKind::TTK_Class;
+ I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
+ I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
+
+ I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record);
+ I.ChildFunctions.emplace_back();
+ I.ChildFunctions.back().Name = "OneFunction";
+ I.ChildEnums.emplace_back();
+ I.ChildEnums.back().Name = "OneEnum";
+
+ auto G = getHTMLGenerator();
+ assert(G);
+ std::string Buffer;
+ llvm::raw_string_ostream Actual(Buffer);
+ auto Err = G->generateDocForInfo(&I, Actual);
+ assert(!Err);
+ std::string Expected = R"raw(class r
+Defined at line 10 of test.cpp
+Inherits from F, G
+
+Members
+private int X
+
+Records
+ChildStruct
+
+Functions
+OneFunction
+ OneFunction()
+
+Enums
+| enum OneEnum |
+--
+
+
+)raw";
+
+ EXPECT_EQ(Expected, Actual.str());
+}
+
+TEST(HTMLGeneratorTest, emitFunctionHTML) {
+ FunctionInfo I;
+ I.Name = "f";
+ I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+ I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+ I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+
+ I.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default);
+ I.Params.emplace_back("int", "P");
+ I.IsMethod = true;
+ I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
+
+ auto G = getHTMLGenerator();
+ assert(G);
+ std::string Buffer;
+ llvm::raw_string_ostream Actual(Buffer);
+ auto Err = G->generateDocForInfo(&I, Actual);
+ assert(!Err);
+ std::string Expected = R"raw(f
+void f(int P)
+Defined at line 10 of test.cpp
+)raw";
+
+ EXPECT_EQ(Expected, Actual.str());
+}
+
+TEST(HTMLGeneratorTest, emitEnumHTML) {
+ EnumInfo I;
+ I.Name = "e";
+ I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
+
+ I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+ I.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"});
+
+ I.Members.emplace_back("X");
+ I.Scoped = true;
+
+ auto G = getHTMLGenerator();
+ assert(G);
+ std::string Buffer;
+ llvm::raw_string_ostream Actual(Buffer);
+ auto Err = G->generateDocForInfo(&I, Actual);
+ assert(!Err);
+ std::string Expected = R"raw(| enum class e |
+--
+| X |
+
+Defined at line 10 of test.cpp
+)raw";
+
+ EXPECT_EQ(Expected, Actual.str());
+}
+
+TEST(HTMLGeneratorTest, emitCommentHTML) {
+ FunctionInfo I;
+ I.Name = "f";
+ I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"});
+ I.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default);
+ I.Params.emplace_back("int", "I");
+ I.Params.emplace_back("int", "J");
+
+ CommentInfo Top;
+ Top.Kind = "FullComment";
+
+ Top.Children.emplace_back(llvm::make_unique());
+ CommentInfo *BlankLine = Top.Children.back().get();
+ BlankLine->Kind = "ParagraphComment";
+ BlankLine->Children.emplace_back(llvm::make_unique());
+ BlankLine->Children.back()->Kind = "TextComment";
+
+ Top.Children.emplace_back(llvm::make_unique());
+ CommentInfo *Brief = Top.Children.back().get();
+ Brief->Kind = "ParagraphComment";
+ Brief->Children.emplace_back(llvm::make_unique());
+ Brief->Children.back()->Kind = "TextComment";
+ Brief->Children.back()->Name = "ParagraphComment";
+ Brief->Children.back()->Text = " Brief description.";
+
+ Top.Children.emplace_back(llvm::make_unique());
+ CommentInfo *Extended = Top.Children.back().get();
+ Extended->Kind = "ParagraphComment";
+ Extended->Children.emplace_back(llvm::make_unique());
+ Extended->Children.back()->Kind = "TextComment";
+ Extended->Children.back()->Text = " Extended description that";
+ Extended->Children.emplace_back(llvm::make_unique());
+ Extended->Children.back()->Kind = "TextComment";
+ Extended->Children.back()->Text = " continues onto the next line.";
+
+ Top.Children.emplace_back(llvm::make_unique());
+ CommentInfo *HTML = Top.Children.back().get();
+ HTML->Kind = "ParagraphComment";
+ HTML->Children.emplace_back(llvm::make_unique());
+ HTML->Children.back()->Kind = "TextComment";
+ HTML->Children.emplace_back(llvm::make_unique());
+ HTML->Children.back()->Kind = "HTMLStartTagComment";
+ HTML->Children.back()->Name = "ul";
+ HTML->Children.back()->AttrKeys.emplace_back("class");
+ HTML->Children.back()->AttrValues.emplace_back("test");
+ HTML->Children.emplace_back(llvm::make_unique());
+ HTML->Children.back()->Kind = "HTMLStartTagComment";
+ HTML->Children.back()->Name = "li";
+ HTML->Children.emplace_back(llvm::make_unique());
+ HTML->Children.back()->Kind = "TextComment";
+ HTML->Children.back()->Text = " Testing.";
+ HTML->Children.emplace_back(llvm::make_unique());
+ HTML->Children.back()->Kind = "HTMLEndTagComment";
+ HTML->Children.back()->Name = "ul";
+ HTML->Children.back()->SelfClosing = true;
+
+ Top.Children.emplace_back(llvm::make_unique());
+ CommentInfo *Verbatim = Top.Children.back().get();
+ Verbatim->Kind = "VerbatimBlockComment";
+ Verbatim->Name = "verbatim";
+ Verbatim->CloseName = "endverbatim";
+ Verbatim->Children.emplace_back(llvm::make_unique());
+ Verbatim->Children.back()->Kind = "VerbatimBlockLineComment";
+ Verbatim->Children.back()->Text = " The description continues.";
+
+ Top.Children.emplace_back(llvm::make_unique());
+ CommentInfo *ParamOut = Top.Children.back().get();
+ ParamOut->Kind = "ParamCommandComment";
+ ParamOut->Direction = "[out]";
+ ParamOut->ParamName = "I";
+ ParamOut->Explicit = true;
+ ParamOut->Children.emplace_back(llvm::make_unique());
+ ParamOut->Children.back()->Kind = "ParagraphComment";
+ ParamOut->Children.back()->Children.emplace_back(
+ llvm::make_unique());
+ ParamOut->Children.back()->Children.back()->Kind = "TextComment";
+ ParamOut->Children.back()->Children.emplace_back(
+ llvm::make_unique());
+ ParamOut->Children.back()->Children.back()->Kind = "TextComment";
+ ParamOut->Children.back()->Children.back()->Text = " is a parameter.";
+
+ Top.Children.emplace_back(llvm::make_unique());
+ CommentInfo *ParamIn = Top.Children.back().get();
+ ParamIn->Kind = "ParamCommandComment";
+ ParamIn->Direction = "[in]";
+ ParamIn->ParamName = "J";
+ ParamIn->Children.emplace_back(llvm::make_unique());
+ ParamIn->Children.back()->Kind = "ParagraphComment";
+ ParamIn->Children.back()->Children.emplace_back(
+ llvm::make_unique());
+ ParamIn->Children.back()->Children.back()->Kind = "TextComment";
+ ParamIn->Children.back()->Children.back()->Text = " is a parameter.";
+ ParamIn->Children.back()->Children.emplace_back(
+ llvm::make_unique());
+ ParamIn->Children.back()->Children.back()->Kind = "TextComment";
+
+ Top.Children.emplace_back(llvm::make_unique());
+ CommentInfo *Return = Top.Children.back().get();
+ Return->Kind = "BlockCommandComment";
+ Return->Name = "return";
+ Return->Explicit = true;
+ Return->Children.emplace_back(llvm::make_unique());
+ Return->Children.back()->Kind = "ParagraphComment";
+ Return->Children.back()->Children.emplace_back(
+ llvm::make_unique());
+ Return->Children.back()->Children.back()->Kind = "TextComment";
+ Return->Children.back()->Children.back()->Text = "void";
+
+ I.Description.emplace_back(std::move(Top));
+
+ auto G = getHTMLGenerator();
+ assert(G);
+ std::string Buffer;
+ llvm::raw_string_ostream Actual(Buffer);
+ auto Err = G->generateDocForInfo(&I, Actual);
+ assert(!Err);
+ std::string Expected = R"raw(f
+void f(int I, int J)
+Defined at line 10 of test.cpp
+
+ Brief description.
+ Extended description that continues onto the next line.
+
+
+ Testing.
+
+ The description continues.
+I [out]
+
+J
+
+returnvoid
+)raw";
+
+ EXPECT_EQ(Expected, Actual.str());
+}
+
+} // namespace doc
+} // namespace clang