Index: clang-tools-extra/clang-doc/Generators.h =================================================================== --- clang-tools-extra/clang-doc/Generators.h +++ clang-tools-extra/clang-doc/Generators.h @@ -26,7 +26,9 @@ virtual ~Generator() = default; // Write out the decl info in the specified format. - virtual llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) = 0; + virtual llvm::Error generateDocForInfo( + Info *I, llvm::raw_ostream &OS, + const std::map> &Infos) = 0; }; typedef llvm::Registry GeneratorRegistry; @@ -38,8 +40,6 @@ 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 @@ -57,19 +57,6 @@ 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; Index: clang-tools-extra/clang-doc/HTMLGenerator.cpp =================================================================== --- clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -26,10 +26,47 @@ return "<" + Tag.str() + ">" + Text.str() + ""; } +std::string genTag(const Twine &Text, const Twine &Tag, + const llvm::StringMap &Options) { + std::string OptionsText = ""; + for (auto &Option : Options) { + OptionsText = OptionsText + " " + Option.getKey().str() + "=\"" + + Option.getValue() + "\""; + } + return "<" + Tag.str() + OptionsText + ">" + Text.str() + ""; +} + std::string genItalic(const Twine &Text) { return genTag(Text, "em"); } std::string genEmphasis(const Twine &Text) { return genTag(Text, "strong"); } +std::string genLink(const Twine &Text, const Twine &Link) { + llvm::StringMap Options; + Options.try_emplace("href", Link.str()); + return genTag(Text.str(), "a", Options); +} + +std::string genReferenceList( + const llvm::SmallVectorImpl &Refs, + const std::map> &Infos) { + std::string Buffer; + llvm::raw_string_ostream Stream(Buffer); + bool First = true; + for (const auto &R : Refs) { + if (!First) + Stream << ", "; + const auto &It = Infos.find(toHex(llvm::toStringRef(R.USR))); + if (It != Infos.end()) { + Stream << genLink(R.Name, It->second->Path); + } else { + Stream << R.Name; + } + First = false; + } + return Stream.str(); +} + void writeLine(const Twine &Text, raw_ostream &OS) { OS << genTag(Text, "p") << "\n"; } @@ -182,7 +219,8 @@ } } -void genHTML(const RecordInfo &I, llvm::raw_ostream &OS) { +void genHTML(const RecordInfo &I, llvm::raw_ostream &OS, + const std::map> &Infos) { writeHeader(getTagType(I.TagType) + " " + I.Name, "1", OS); if (I.DefLoc) writeFileDefinition(I.DefLoc.getValue(), OS); @@ -193,8 +231,8 @@ writeNewLine(OS); } - std::string Parents = genReferenceList(I.Parents); - std::string VParents = genReferenceList(I.VirtualParents); + std::string Parents = genReferenceList(I.Parents, Infos); + std::string VParents = genReferenceList(I.VirtualParents, Infos); if (!Parents.empty() || !VParents.empty()) { if (Parents.empty()) writeLine("Inherits from " + VParents, OS); @@ -242,18 +280,22 @@ public: static const char *Format; - llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; + llvm::Error generateDocForInfo( + Info *I, llvm::raw_ostream &OS, + const std::map> &Infos) override; }; const char *HTMLGenerator::Format = "html"; -llvm::Error HTMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { +llvm::Error HTMLGenerator::generateDocForInfo( + Info *I, llvm::raw_ostream &OS, + const std::map> &Infos) { switch (I->IT) { case InfoType::IT_namespace: genHTML(*static_cast(I), OS); break; case InfoType::IT_record: - genHTML(*static_cast(I), OS); + genHTML(*static_cast(I), OS, Infos); break; case InfoType::IT_enum: genHTML(*static_cast(I), OS); Index: clang-tools-extra/clang-doc/MDGenerator.cpp =================================================================== --- clang-tools-extra/clang-doc/MDGenerator.cpp +++ clang-tools-extra/clang-doc/MDGenerator.cpp @@ -30,6 +30,19 @@ 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"; } @@ -240,12 +253,16 @@ public: static const char *Format; - llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; -}; - -const char *MDGenerator::Format = "md"; - -llvm::Error MDGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { + llvm::Error generateDocForInfo( + Info *I, llvm::raw_ostream &OS, + const std::map> &Infos) override; + }; + + const char *MDGenerator::Format = "md"; + +llvm::Error MDGenerator::generateDocForInfo( + Info *I, llvm::raw_ostream &OS, + const std::map> &Infos) { switch (I->IT) { case InfoType::IT_namespace: genMarkdown(*static_cast(I), OS); Index: clang-tools-extra/clang-doc/Representation.h =================================================================== --- clang-tools-extra/clang-doc/Representation.h +++ clang-tools-extra/clang-doc/Representation.h @@ -171,6 +171,7 @@ llvm::SmallVector Namespace; // List of parent namespaces for this decl. std::vector Description; // Comment description of this decl. + llvm::SmallString<128> Path; // Path of generated file by clang-doc void mergeBase(Info &&I); bool mergeable(const Info &Other); Index: clang-tools-extra/clang-doc/YAMLGenerator.cpp =================================================================== --- clang-tools-extra/clang-doc/YAMLGenerator.cpp +++ clang-tools-extra/clang-doc/YAMLGenerator.cpp @@ -241,12 +241,16 @@ public: static const char *Format; - llvm::Error generateDocForInfo(Info *I, llvm::raw_ostream &OS) override; -}; - -const char *YAMLGenerator::Format = "yaml"; - -llvm::Error YAMLGenerator::generateDocForInfo(Info *I, llvm::raw_ostream &OS) { + llvm::Error generateDocForInfo( + Info *I, llvm::raw_ostream &OS, + const std::map> &Infos) override; + }; + + const char *YAMLGenerator::Format = "yaml"; + +llvm::Error YAMLGenerator::generateDocForInfo( + Info *I, llvm::raw_ostream &OS, + const std::map> &Infos) { llvm::yaml::Output InfoYAML(OS); switch (I->IT) { case InfoType::IT_namespace: 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 @@ -217,6 +217,7 @@ return 1; // First reducing phase (reduce all decls into one info per decl). + std::map> ReducedInfos; llvm::outs() << "Reducing " << USRToInfos.size() << " infos...\n"; for (auto &Group : USRToInfos) { auto Reduced = doc::mergeInfos(Group.getValue()); @@ -233,14 +234,23 @@ llvm::errs() << toString(InfoPath.takeError()) << "\n"; continue; } + + I->Path = InfoPath.get(); + ReducedInfos.emplace(Group.getKey(), std::move(Reduced.get())); + } + + llvm::outs() << "Generating docs...\n"; + for (auto &Reduced : ReducedInfos) { + doc::Info *I = Reduced.second.get(); + std::error_code FileErr; - llvm::raw_fd_ostream InfoOS(InfoPath.get(), FileErr, llvm::sys::fs::F_None); + llvm::raw_fd_ostream InfoOS(I->Path, FileErr, llvm::sys::fs::F_None); if (FileErr != OK) { llvm::errs() << "Error opening info file: " << FileErr.message() << "\n"; continue; } - if (auto Err = G->get()->generateDocForInfo(I, InfoOS)) + if (auto Err = G->get()->generateDocForInfo(I, InfoOS, ReducedInfos)) llvm::errs() << toString(std::move(Err)) << "\n"; } Index: clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp =================================================================== --- clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp +++ clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp @@ -9,6 +9,7 @@ #include "ClangDocTest.h" #include "Generators.h" #include "Representation.h" +#include "Serialize.h" #include "gtest/gtest.h" namespace clang { @@ -38,7 +39,9 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(

namespace Namespace


@@ -72,7 +75,7 @@ I.Members.emplace_back("int", "X", AccessSpecifier::AS_private); I.TagType = TagTypeKind::TTK_Class; - I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record); + I.Parents.emplace_back(serialize::hashUSR("1"), "F", InfoType::IT_record); I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); @@ -81,15 +84,20 @@ I.ChildEnums.emplace_back(); I.ChildEnums.back().Name = "OneEnum"; + std::map> Infos; + auto A = llvm::make_unique(); + A->Path = "path/to/A.html"; + Infos.emplace(toHex(llvm::toStringRef(I.Parents[0].USR)), std::move(A)); + auto G = getHTMLGenerator(); assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(

class r

Defined at line 10 of test.cpp

-

Inherits from F, G

+

Inherits from F, G


Members

private int X

@@ -128,7 +136,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(

f

void f(int P)

@@ -153,7 +162,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(

| enum class e |

--

@@ -279,7 +289,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(

f

void f(int I, int J)

Index: clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp =================================================================== --- clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp +++ clang-tools-extra/unittests/clang-doc/MDGeneratorTest.cpp @@ -38,7 +38,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(# namespace Namespace @@ -101,7 +102,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(# class r @@ -162,7 +164,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(### f @@ -190,7 +193,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(| enum class e | @@ -320,7 +324,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(### f Index: clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp =================================================================== --- clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp +++ clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp @@ -39,7 +39,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(--- @@ -89,7 +90,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(--- @@ -148,7 +150,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(--- @@ -194,7 +197,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(--- @@ -331,7 +335,8 @@ assert(G); std::string Buffer; llvm::raw_string_ostream Actual(Buffer); - auto Err = G->generateDocForInfo(&I, Actual); + std::map> Infos; + auto Err = G->generateDocForInfo(&I, Actual, Infos); assert(!Err); std::string Expected = R"raw(---