[clang-tools-extra] r365937 - [clang-doc] Add html links to references

Diego Astiazarán via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 12 15:10:11 PDT 2019


Hi Douglas,

This revision (https://reviews.llvm.org/D64669) should fix that.

On Fri, Jul 12, 2019 at 1:55 PM <douglas.yung at sony.com> wrote:

> Hi Diego and Julie,
>
> The tests you modified, emitFunctionHTML and emitRecordHTML are failing
> when run on Windows due to path separator differences.
>
>
> http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast/builds/26925
>
> FAIL: Extra Tools Unit Tests ::
> clang-doc/./ClangDocTests.exe/HTMLGeneratorTest.emitFunctionHTML (20152 of
> 51140)
> ******************** TEST 'Extra Tools Unit Tests ::
> clang-doc/./ClangDocTests.exe/HTMLGeneratorTest.emitFunctionHTML' FAILED
> ********************
> Note: Google Test filter = HTMLGeneratorTest.emitFunctionHTML
>
> [==========] Running 1 test from 1 test case.
>
> [----------] Global test environment set-up.
>
> [----------] 1 test from HTMLGeneratorTest
>
> [ RUN      ] HTMLGeneratorTest.emitFunctionHTML
>
> C:\ps4-buildslave2\llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast\llvm.src\tools\clang\tools\extra\unittests\clang-doc\HTMLGeneratorTest.cpp(174):
> error:       Expected: Expected
>
>       Which is: "<!DOCTYPE html>\n<meta
> charset=\"utf-8\"/>\n<title></title>\n<div>\n  <h3>f</h3>\n  <p>\n    <a
> href=\"path/to/float.html\">float</a>\n     f(\n    <a
> href=\"path/to/int.html\">int</a>\n     P)\n  </p>\n  <p>\n    Defined at
> line 10 of test.cpp\n  </p>\n</div>\n"
>
> To be equal to: Actual.str()
>
>       Which is: "<!DOCTYPE html>\n<meta
> charset=\"utf-8\"/>\n<title></title>\n<div>\n  <h3>f</h3>\n  <p>\n    <a
> href=\"path/to\\float.html\">float</a>\n     f(\n    <a
> href=\"path/to\\int.html\">int</a>\n     P)\n  </p>\n  <p>\n    Defined at
> line 10 of test.cpp\n  </p>\n</div>\n"
>
> With diff:
>
> @@ -5,7 +5,7 @@
>
>    <h3>f</h3>
>
>    <p>
>
> -    <a href=\"path/to/float.html\">float</a>
>
> +    <a href=\"path/to\\float.html\">float</a>
>
>       f(
>
> -    <a href=\"path/to/int.html\">int</a>
>
> +    <a href=\"path/to\\int.html\">int</a>
>
>       P)
>
>    </p>
>
>
>
> [  FAILED  ] HTMLGeneratorTest.emitFunctionHTML (1 ms)
>
> [----------] 1 test from HTMLGeneratorTest (1 ms total)
>
>
>
> [----------] Global test environment tear-down
>
> [==========] 1 test from 1 test case ran. (1 ms total)
>
> [  PASSED  ] 0 tests.
>
> [  FAILED  ] 1 test, listed below:
>
> [  FAILED  ] HTMLGeneratorTest.emitFunctionHTML
>
>
>
>  1 FAILED TEST
>
>
> ********************
> PASS: Clangd Unit Tests ::
> ./ClangdTests.exe/CompletionTest.CompletionOptions (20153 of 51140)
> FAIL: Extra Tools Unit Tests ::
> clang-doc/./ClangDocTests.exe/HTMLGeneratorTest.emitRecordHTML (20154 of
> 51140)
> ******************** TEST 'Extra Tools Unit Tests ::
> clang-doc/./ClangDocTests.exe/HTMLGeneratorTest.emitRecordHTML' FAILED
> ********************
> Note: Google Test filter = HTMLGeneratorTest.emitRecordHTML
>
> [==========] Running 1 test from 1 test case.
>
> [----------] Global test environment set-up.
>
> [----------] 1 test from HTMLGeneratorTest
>
> [ RUN      ] HTMLGeneratorTest.emitRecordHTML
>
> C:\ps4-buildslave2\llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast\llvm.src\tools\clang\tools\extra\unittests\clang-doc\HTMLGeneratorTest.cpp(135):
> error:       Expected: Expected
>
>       Which is: "<!DOCTYPE html>\n<meta charset=\"utf-8\"/>\n<title>class
> r</title>\n<div>\n  <h1>class r</h1>\n  <p>\n    Defined at line 10 of
> test.cpp\n  </p>\n  <p>\n    Inherits from \n    <a
> href=\"../../../path/to/F.html\">F</a>\n    , G\n  </p>\n
> <h2>Members</h2>\n  <ul>\n    <li>private <a href=\"../int.html\">int</a>
> X</li>\n  </ul>\n  <h2>Records</h2>\n  <ul>\n    <li>ChildStruct</li>\n
> </ul>\n  <h2>Functions</h2>\n  <div>\n    <h3>OneFunction</h3>\n    <p>\n
>     OneFunction()\n    </p>\n  </div>\n  <h2>Enums</h2>\n  <div>\n
> <h3>enum OneEnum</h3>\n  </div>\n</div>\n"
>
> To be equal to: Actual.str()
>
>       Which is: "<!DOCTYPE html>\n<meta charset=\"utf-8\"/>\n<title>class
> r</title>\n<div>\n  <h1>class r</h1>\n  <p>\n    Defined at line 10 of
> test.cpp\n  </p>\n  <p>\n    Inherits from \n    <a
> href=\"..\\..\\..\\path/to\\F.html\">F</a>\n    , G\n  </p>\n
> <h2>Members</h2>\n  <ul>\n    <li>private <a href=\"..\\int.html\">int</a>
> X</li>\n  </ul>\n  <h2>Records</h2>\n  <ul>\n    <li>ChildStruct</li>\n
> </ul>\n  <h2>Functions</h2>\n  <div>\n    <h3>OneFunction</h3>\n    <p>\n
>     OneFunction()\n    </p>\n  </div>\n  <h2>Enums</h2>\n  <div>\n
> <h3>enum OneEnum</h3>\n  </div>\n</div>\n"
>
> With diff:
>
> @@ -9,5 +9,5 @@
>
>    <p>
>
>      Inherits from
>
> -    <a href=\"../../../path/to/F.html\">F</a>
>
> +    <a href=\"..\\..\\..\\path/to\\F.html\">F</a>
>
>      , G
>
>    </p>
>
> @@ -14,5 +14,5 @@
>
>    <h2>Members</h2>
>
>    <ul>
>
> -    <li>private <a href=\"../int.html\">int</a> X</li>
>
> +    <li>private <a href=\"..\\int.html\">int</a> X</li>
>
>    </ul>
>
>    <h2>Records</h2>
>
>
>
> [  FAILED  ] HTMLGeneratorTest.emitRecordHTML (1 ms)
>
> [----------] 1 test from HTMLGeneratorTest (1 ms total)
>
>
>
> [----------] Global test environment tear-down
>
> [==========] 1 test from 1 test case ran. (1 ms total)
>
> [  PASSED  ] 0 tests.
>
> [  FAILED  ] 1 test, listed below:
>
> [  FAILED  ] HTMLGeneratorTest.emitRecordHTML
>
>
>
>  1 FAILED TEST
>
> Can you please take a look?
>
> Douglas Yung
>
> -----Original Message-----
> From: cfe-commits <cfe-commits-bounces at lists.llvm.org> On Behalf Of Julie
> Hockett via cfe-commits
> Sent: Friday, July 12, 2019 11:32
> To: cfe-commits at lists.llvm.org
> Subject: [clang-tools-extra] r365937 - [clang-doc] Add html links to
> references
>
> Author: juliehockett
> Date: Fri Jul 12 11:32:00 2019
> New Revision: 365937
>
> URL: http://llvm.org/viewvc/llvm-project?rev=365937&view=rev
> Log:
> [clang-doc] Add html links to references
>
> <a> tags are added for the parents and members of records and return type
> and
> params of functions. The link redirects to the reference's info file.
>
> The directory path where each info file will be saved is now generated in
> the
> serialization phase and stored as an attribute in each Info.
>
> Bitcode writer and reader were modified to handle the new attributes.
>
> Committed on behalf of Diego Astiazarán (diegoaat97 at gmail.com).
>
> Differential Revision: https://reviews.llvm.org/D63663
>
> Modified:
>     clang-tools-extra/trunk/clang-doc/BitcodeReader.cpp
>     clang-tools-extra/trunk/clang-doc/BitcodeWriter.cpp
>     clang-tools-extra/trunk/clang-doc/BitcodeWriter.h
>     clang-tools-extra/trunk/clang-doc/Generators.cpp
>     clang-tools-extra/trunk/clang-doc/Generators.h
>     clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp
>     clang-tools-extra/trunk/clang-doc/MDGenerator.cpp
>     clang-tools-extra/trunk/clang-doc/Representation.cpp
>     clang-tools-extra/trunk/clang-doc/Representation.h
>     clang-tools-extra/trunk/clang-doc/Serialize.cpp
>     clang-tools-extra/trunk/clang-doc/YAMLGenerator.cpp
>     clang-tools-extra/trunk/clang-doc/tool/ClangDocMain.cpp
>     clang-tools-extra/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp
>     clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp
>
> Modified: clang-tools-extra/trunk/clang-doc/BitcodeReader.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/BitcodeReader.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/BitcodeReader.cpp (original)
> +++ clang-tools-extra/trunk/clang-doc/BitcodeReader.cpp Fri Jul 12
> 11:32:00 2019
> @@ -148,6 +148,8 @@ llvm::Error parseRecord(Record R, unsign
>      return decodeRecord(R, I->USR, Blob);
>    case NAMESPACE_NAME:
>      return decodeRecord(R, I->Name, Blob);
> +  case NAMESPACE_PATH:
> +    return decodeRecord(R, I->Path, Blob);
>    default:
>      return llvm::make_error<llvm::StringError>(
>          "Invalid field for NamespaceInfo.\n",
> llvm::inconvertibleErrorCode());
> @@ -161,6 +163,8 @@ llvm::Error parseRecord(Record R, unsign
>      return decodeRecord(R, I->USR, Blob);
>    case RECORD_NAME:
>      return decodeRecord(R, I->Name, Blob);
> +  case RECORD_PATH:
> +    return decodeRecord(R, I->Path, Blob);
>    case RECORD_DEFLOCATION:
>      return decodeRecord(R, I->DefLoc, Blob);
>    case RECORD_LOCATION:
> @@ -286,6 +290,8 @@ llvm::Error parseRecord(Record R, unsign
>      return decodeRecord(R, I->Name, Blob);
>    case REFERENCE_TYPE:
>      return decodeRecord(R, I->RefType, Blob);
> +  case REFERENCE_PATH:
> +    return decodeRecord(R, I->Path, Blob);
>    case REFERENCE_FIELD:
>      return decodeRecord(R, F, Blob);
>    default:
> @@ -685,7 +691,7 @@ ClangDocBitcodeReader::createInfo(unsign
>    std::unique_ptr<Info> I = llvm::make_unique<T>();
>    if (auto Err = readBlock(ID, static_cast<T *>(I.get())))
>      return std::move(Err);
> -  return std::unique_ptr<Info>{std::move(I)};;
> +  return std::unique_ptr<Info>{std::move(I)};
>  }
>
>  llvm::Expected<std::unique_ptr<Info>>
>
> Modified: clang-tools-extra/trunk/clang-doc/BitcodeWriter.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/BitcodeWriter.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/BitcodeWriter.cpp (original)
> +++ clang-tools-extra/trunk/clang-doc/BitcodeWriter.cpp Fri Jul 12
> 11:32:00 2019
> @@ -148,6 +148,7 @@ static const llvm::IndexedMap<RecordIdDs
>            {MEMBER_TYPE_ACCESS, {"Access", &IntAbbrev}},
>            {NAMESPACE_USR, {"USR", &SymbolIDAbbrev}},
>            {NAMESPACE_NAME, {"Name", &StringAbbrev}},
> +          {NAMESPACE_PATH, {"Path", &StringAbbrev}},
>            {ENUM_USR, {"USR", &SymbolIDAbbrev}},
>            {ENUM_NAME, {"Name", &StringAbbrev}},
>            {ENUM_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
> @@ -156,6 +157,7 @@ static const llvm::IndexedMap<RecordIdDs
>            {ENUM_SCOPED, {"Scoped", &BoolAbbrev}},
>            {RECORD_USR, {"USR", &SymbolIDAbbrev}},
>            {RECORD_NAME, {"Name", &StringAbbrev}},
> +          {RECORD_PATH, {"Path", &StringAbbrev}},
>            {RECORD_DEFLOCATION, {"DefLocation", &LocationAbbrev}},
>            {RECORD_LOCATION, {"Location", &LocationAbbrev}},
>            {RECORD_TAG_TYPE, {"TagType", &IntAbbrev}},
> @@ -169,6 +171,7 @@ static const llvm::IndexedMap<RecordIdDs
>            {REFERENCE_USR, {"USR", &SymbolIDAbbrev}},
>            {REFERENCE_NAME, {"Name", &StringAbbrev}},
>            {REFERENCE_TYPE, {"RefType", &IntAbbrev}},
> +          {REFERENCE_PATH, {"Path", &StringAbbrev}},
>            {REFERENCE_FIELD, {"Field", &IntAbbrev}}};
>        assert(Inits.size() == RecordIdCount);
>        for (const auto &Init : Inits) {
> @@ -199,18 +202,20 @@ static const std::vector<std::pair<Block
>           {ENUM_USR, ENUM_NAME, ENUM_DEFLOCATION, ENUM_LOCATION,
> ENUM_MEMBER,
>            ENUM_SCOPED}},
>          // Namespace Block
> -        {BI_NAMESPACE_BLOCK_ID, {NAMESPACE_USR, NAMESPACE_NAME}},
> +        {BI_NAMESPACE_BLOCK_ID,
> +         {NAMESPACE_USR, NAMESPACE_NAME, NAMESPACE_PATH}},
>          // Record Block
>          {BI_RECORD_BLOCK_ID,
> -         {RECORD_USR, RECORD_NAME, RECORD_DEFLOCATION, RECORD_LOCATION,
> -          RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF}},
> +         {RECORD_USR, RECORD_NAME, RECORD_PATH, RECORD_DEFLOCATION,
> +          RECORD_LOCATION, RECORD_TAG_TYPE, RECORD_IS_TYPE_DEF}},
>          // Function Block
>          {BI_FUNCTION_BLOCK_ID,
>           {FUNCTION_USR, FUNCTION_NAME, FUNCTION_DEFLOCATION,
> FUNCTION_LOCATION,
>            FUNCTION_ACCESS, FUNCTION_IS_METHOD}},
>          // Reference Block
>          {BI_REFERENCE_BLOCK_ID,
> -         {REFERENCE_USR, REFERENCE_NAME, REFERENCE_TYPE,
> REFERENCE_FIELD}}};
> +         {REFERENCE_USR, REFERENCE_NAME, REFERENCE_TYPE, REFERENCE_PATH,
> +          REFERENCE_FIELD}}};
>
>  // AbbreviationMap
>
> @@ -381,6 +386,7 @@ void ClangDocBitcodeWriter::emitBlock(co
>    emitRecord(R.USR, REFERENCE_USR);
>    emitRecord(R.Name, REFERENCE_NAME);
>    emitRecord((unsigned)R.RefType, REFERENCE_TYPE);
> +  emitRecord(R.Path, REFERENCE_PATH);
>    emitRecord((unsigned)Field, REFERENCE_FIELD);
>  }
>
> @@ -428,6 +434,7 @@ void ClangDocBitcodeWriter::emitBlock(co
>    StreamSubBlockGuard Block(Stream, BI_NAMESPACE_BLOCK_ID);
>    emitRecord(I.USR, NAMESPACE_USR);
>    emitRecord(I.Name, NAMESPACE_NAME);
> +  emitRecord(I.Path, NAMESPACE_PATH);
>    for (const auto &N : I.Namespace)
>      emitBlock(N, FieldId::F_namespace);
>    for (const auto &CI : I.Description)
> @@ -463,6 +470,7 @@ void ClangDocBitcodeWriter::emitBlock(co
>    StreamSubBlockGuard Block(Stream, BI_RECORD_BLOCK_ID);
>    emitRecord(I.USR, RECORD_USR);
>    emitRecord(I.Name, RECORD_NAME);
> +  emitRecord(I.Path, RECORD_PATH);
>    for (const auto &N : I.Namespace)
>      emitBlock(N, FieldId::F_namespace);
>    for (const auto &CI : I.Description)
>
> Modified: clang-tools-extra/trunk/clang-doc/BitcodeWriter.h
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/BitcodeWriter.h?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/BitcodeWriter.h (original)
> +++ clang-tools-extra/trunk/clang-doc/BitcodeWriter.h Fri Jul 12 11:32:00
> 2019
> @@ -91,6 +91,7 @@ enum RecordId {
>    MEMBER_TYPE_ACCESS,
>    NAMESPACE_USR,
>    NAMESPACE_NAME,
> +  NAMESPACE_PATH,
>    ENUM_USR,
>    ENUM_NAME,
>    ENUM_DEFLOCATION,
> @@ -99,6 +100,7 @@ enum RecordId {
>    ENUM_SCOPED,
>    RECORD_USR,
>    RECORD_NAME,
> +  RECORD_PATH,
>    RECORD_DEFLOCATION,
>    RECORD_LOCATION,
>    RECORD_TAG_TYPE,
> @@ -106,6 +108,7 @@ enum RecordId {
>    REFERENCE_USR,
>    REFERENCE_NAME,
>    REFERENCE_TYPE,
> +  REFERENCE_PATH,
>    REFERENCE_FIELD,
>    RI_LAST,
>    RI_FIRST = VERSION
>
> Modified: clang-tools-extra/trunk/clang-doc/Generators.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/Generators.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/Generators.cpp (original)
> +++ clang-tools-extra/trunk/clang-doc/Generators.cpp Fri Jul 12 11:32:00
> 2019
> @@ -57,19 +57,6 @@ std::string getTagType(TagTypeKind AS) {
>    llvm_unreachable("Unknown TagTypeKind");
>  }
>
> -// Generates a comma-separated list of Refs
> -// Used to display the parents of a record
> -std::string genReferenceList(const llvm::SmallVectorImpl<Reference>
> &Refs) {
> -  std::string Buffer;
> -  llvm::raw_string_ostream Stream(Buffer);
> -  for (const auto &R : Refs) {
> -    if (&R != Refs.begin())
> -      Stream << ", ";
> -    Stream << R.Name;
> -  }
> -  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;
>
> Modified: clang-tools-extra/trunk/clang-doc/Generators.h
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/Generators.h?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/Generators.h (original)
> +++ clang-tools-extra/trunk/clang-doc/Generators.h Fri Jul 12 11:32:00 2019
> @@ -38,8 +38,6 @@ std::string getAccess(AccessSpecifier AS
>
>  std::string getTagType(TagTypeKind AS);
>
> -std::string genReferenceList(const llvm::SmallVectorImpl<Reference>
> &Refs);
> -
>  } // namespace doc
>  } // namespace clang
>
>
> Modified: clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp (original)
> +++ clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp Fri Jul 12
> 11:32:00 2019
> @@ -18,13 +18,6 @@ using namespace llvm;
>  namespace clang {
>  namespace doc {
>
> -template <typename Derived, typename Base,
> -          typename = std::enable_if<std::is_base_of<Derived,
> Base>::value>>
> -static void AppendVector(std::vector<Derived> &&New,
> -                         std::vector<Base> &Original) {
> -  std::move(New.begin(), New.end(), std::back_inserter(Original));
> -}
> -
>  namespace {
>
>  class HTMLTag {
> @@ -40,6 +33,7 @@ public:
>      TAG_P,
>      TAG_UL,
>      TAG_LI,
> +    TAG_A,
>    };
>
>    HTMLTag() = default;
> @@ -58,15 +52,22 @@ private:
>    TagType Value;
>  };
>
> +enum NodeType {
> +  NODE_TEXT,
> +  NODE_TAG,
> +};
> +
>  struct HTMLNode {
> +  HTMLNode(NodeType Type) : Type(Type) {}
>    virtual ~HTMLNode() = default;
>
>    virtual void Render(llvm::raw_ostream &OS, int IndentationLevel) = 0;
> +  NodeType Type; // Type of node
>  };
>
>  struct TextNode : public HTMLNode {
> -  TextNode(llvm::StringRef Text, bool Indented)
> -      : Text(Text), Indented(Indented) {}
> +  TextNode(const Twine &Text, bool Indented = true)
> +      : HTMLNode(NodeType::NODE_TEXT), Text(Text.str()),
> Indented(Indented) {}
>
>    std::string Text; // Content of node
>    bool Indented; // Indicates if an indentation must be rendered before
> the text
> @@ -75,7 +76,8 @@ struct TextNode : public HTMLNode {
>
>  struct TagNode : public HTMLNode {
>    TagNode(HTMLTag Tag)
> -      : Tag(Tag), InlineChildren(Tag.HasInlineChildren()),
> +      : HTMLNode(NodeType::NODE_TAG), Tag(Tag),
> +        InlineChildren(Tag.HasInlineChildren()),
>          SelfClosing(Tag.IsSelfClosing()) {}
>    TagNode(HTMLTag Tag, const Twine &Text) : TagNode(Tag) {
>      Children.emplace_back(
> @@ -121,6 +123,7 @@ bool HTMLTag::IsSelfClosing() const {
>    case HTMLTag::TAG_P:
>    case HTMLTag::TAG_UL:
>    case HTMLTag::TAG_LI:
> +  case HTMLTag::TAG_A:
>      return false;
>    }
>    llvm_unreachable("Unhandled HTMLTag::TagType");
> @@ -134,6 +137,7 @@ bool HTMLTag::HasInlineChildren() const
>    case HTMLTag::TAG_H2:
>    case HTMLTag::TAG_H3:
>    case HTMLTag::TAG_LI:
> +  case HTMLTag::TAG_A:
>      return true;
>    case HTMLTag::TAG_DIV:
>    case HTMLTag::TAG_P:
> @@ -163,6 +167,8 @@ llvm::SmallString<16> HTMLTag::ToString(
>      return llvm::SmallString<16>("ul");
>    case HTMLTag::TAG_LI:
>      return llvm::SmallString<16>("li");
> +  case HTMLTag::TAG_A:
> +    return llvm::SmallString<16>("a");
>    }
>    llvm_unreachable("Unhandled HTMLTag::TagType");
>  }
> @@ -185,21 +191,87 @@ void TagNode::Render(llvm::raw_ostream &
>    OS << ">";
>    if (!InlineChildren)
>      OS << "\n";
> -  int ChildrenIndentation = InlineChildren ? 0 : IndentationLevel + 1;
> +  bool NewLineRendered = true;
>    for (const auto &C : Children) {
> +    int ChildrenIndentation =
> +        InlineChildren || !NewLineRendered ? 0 : IndentationLevel + 1;
>      C->Render(OS, ChildrenIndentation);
> -    if (!InlineChildren)
> +    if (!InlineChildren && (C == Children.back() ||
> +                            (C->Type != NodeType::NODE_TEXT ||
> +                             (&C + 1)->get()->Type !=
> NodeType::NODE_TEXT))) {
>        OS << "\n";
> +      NewLineRendered = true;
> +    } else
> +      NewLineRendered = false;
>    }
>    if (!InlineChildren)
>      OS.indent(IndentationLevel * 2);
>    OS << "</" << Tag.ToString() << ">";
>  }
>
> +template <typename Derived, typename Base,
> +          typename = std::enable_if<std::is_base_of<Derived,
> Base>::value>>
> +static void AppendVector(std::vector<Derived> &&New,
> +                         std::vector<Base> &Original) {
> +  std::move(New.begin(), New.end(), std::back_inserter(Original));
> +}
> +
> +// Compute the relative path that names the file path relative to the
> given
> +// directory.
> +static SmallString<128> computeRelativePath(StringRef FilePath,
> +                                            StringRef Directory) {
> +  StringRef Path = FilePath;
> +  while (!Path.empty()) {
> +    if (Directory == Path)
> +      return FilePath.substr(Path.size());
> +    Path = llvm::sys::path::parent_path(Path);
> +  }
> +
> +  StringRef Dir = Directory;
> +  SmallString<128> Result;
> +  while (!Dir.empty()) {
> +    if (Dir == FilePath)
> +      break;
> +    Dir = llvm::sys::path::parent_path(Dir);
> +    llvm::sys::path::append(Result, "..");
> +  }
> +  llvm::sys::path::append(Result, FilePath.substr(Dir.size()));
> +  return Result;
> +}
> +
>  // HTML generation
>
> +static std::unique_ptr<TagNode> genLink(const Twine &Text, const Twine
> &Link) {
> +  auto LinkNode = llvm::make_unique<TagNode>(HTMLTag::TAG_A, Text);
> +  LinkNode->Attributes.try_emplace("href", Link.str());
> +  return LinkNode;
> +}
> +
> +static std::unique_ptr<HTMLNode> genTypeReference(const Reference &Type,
> +                                                  StringRef
> CurrentDirectory) {
> +  if (Type.Path.empty())
> +    return llvm::make_unique<TextNode>(Type.Name);
> +  llvm::SmallString<128> Path =
> +      computeRelativePath(Type.Path, CurrentDirectory);
> +  llvm::sys::path::append(Path, Type.Name + ".html");
> +  return genLink(Type.Name, Path);
> +}
> +
> +static std::vector<std::unique_ptr<HTMLNode>>
> +genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs,
> +                 const StringRef &CurrentDirectory) {
> +  std::vector<std::unique_ptr<HTMLNode>> Out;
> +  for (const auto &R : Refs) {
> +    if (&R != Refs.begin())
> +      Out.emplace_back(llvm::make_unique<TextNode>(", "));
> +    Out.emplace_back(genTypeReference(R, CurrentDirectory));
> +  }
> +  return Out;
> +}
> +
>  static std::vector<std::unique_ptr<TagNode>> genHTML(const EnumInfo &I);
> -static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo
> &I);
> +static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo
> &I,
> +                                                     StringRef
> ParentInfoDir);
>
>  static std::vector<std::unique_ptr<TagNode>>
>  genEnumsBlock(const std::vector<EnumInfo> &Enums) {
> @@ -229,7 +301,8 @@ genEnumMembersBlock(const llvm::SmallVec
>  }
>
>  static std::vector<std::unique_ptr<TagNode>>
> -genFunctionsBlock(const std::vector<FunctionInfo> &Functions) {
> +genFunctionsBlock(const std::vector<FunctionInfo> &Functions,
> +                  StringRef ParentInfoDir) {
>    if (Functions.empty())
>      return {};
>
> @@ -238,14 +311,15 @@ genFunctionsBlock(const std::vector<Func
>    Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_DIV));
>    auto &DivBody = Out.back();
>    for (const auto &F : Functions) {
> -    std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(F);
> +    std::vector<std::unique_ptr<TagNode>> Nodes = genHTML(F,
> ParentInfoDir);
>      AppendVector(std::move(Nodes), DivBody->Children);
>    }
>    return Out;
>  }
>
>  static std::vector<std::unique_ptr<TagNode>>
> -genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4>
> &Members) {
> +genRecordMembersBlock(const llvm::SmallVector<MemberTypeInfo, 4> &Members,
> +                      StringRef ParentInfoDir) {
>    if (Members.empty())
>      return {};
>
> @@ -257,8 +331,11 @@ genRecordMembersBlock(const llvm::SmallV
>      std::string Access = getAccess(M.Access);
>      if (Access != "")
>        Access = Access + " ";
> -    ULBody->Children.emplace_back(llvm::make_unique<TagNode>(
> -        HTMLTag::TAG_LI, Access + M.Type.Name + " " + M.Name));
> +    auto LIBody = llvm::make_unique<TagNode>(HTMLTag::TAG_LI);
> +    LIBody->Children.emplace_back(llvm::make_unique<TextNode>(Access));
> +    LIBody->Children.emplace_back(genTypeReference(M.Type,
> ParentInfoDir));
> +    LIBody->Children.emplace_back(llvm::make_unique<TextNode>(" " +
> M.Name));
> +    ULBody->Children.emplace_back(std::move(LIBody));
>    }
>    return Out;
>  }
> @@ -346,25 +423,35 @@ static std::vector<std::unique_ptr<TagNo
>    return Out;
>  }
>
> -static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo
> &I) {
> +static std::vector<std::unique_ptr<TagNode>> genHTML(const FunctionInfo
> &I,
> +                                                     StringRef
> ParentInfoDir) {
>    std::vector<std::unique_ptr<TagNode>> Out;
>    Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_H3, I.Name));
>
> -  std::string Buffer;
> -  llvm::raw_string_ostream Stream(Buffer);
> -  for (const auto &P : I.Params) {
> -    if (&P != I.Params.begin())
> -      Stream << ", ";
> -    Stream << P.Type.Name + " " + P.Name;
> -  }
> +  Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P));
> +  auto &FunctionHeader = Out.back();
>
>    std::string Access = getAccess(I.Access);
>    if (Access != "")
> -    Access = Access + " ";
> +    FunctionHeader->Children.emplace_back(
> +        llvm::make_unique<TextNode>(Access + " "));
> +  if (I.ReturnType.Type.Name != "") {
> +    FunctionHeader->Children.emplace_back(
> +        genTypeReference(I.ReturnType.Type, ParentInfoDir));
> +    FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>("
> "));
> +  }
> +  FunctionHeader->Children.emplace_back(
> +      llvm::make_unique<TextNode>(I.Name + "("));
>
> -  Out.emplace_back(llvm::make_unique<TagNode>(
> -      HTMLTag::TAG_P, Access + I.ReturnType.Type.Name + " " + I.Name +
> "(" +
> -                          Stream.str() + ")"));
> +  for (const auto &P : I.Params) {
> +    if (&P != I.Params.begin())
> +
> FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(", "));
> +    FunctionHeader->Children.emplace_back(
> +        genTypeReference(P.Type, ParentInfoDir));
> +    FunctionHeader->Children.emplace_back(
> +        llvm::make_unique<TextNode>(" " + P.Name));
> +  }
> +  FunctionHeader->Children.emplace_back(llvm::make_unique<TextNode>(")"));
>
>    if (I.DefLoc)
>      Out.emplace_back(writeFileDefinition(I.DefLoc.getValue()));
> @@ -398,7 +485,7 @@ static std::vector<std::unique_ptr<TagNo
>    AppendVector(std::move(ChildRecords), Out);
>
>    std::vector<std::unique_ptr<TagNode>> ChildFunctions =
> -      genFunctionsBlock(I.ChildFunctions);
> +      genFunctionsBlock(I.ChildFunctions, I.Path);
>    AppendVector(std::move(ChildFunctions), Out);
>    std::vector<std::unique_ptr<TagNode>> ChildEnums =
>        genEnumsBlock(I.ChildEnums);
> @@ -420,29 +507,34 @@ static std::vector<std::unique_ptr<TagNo
>    if (!I.Description.empty())
>      Out.emplace_back(genHTML(I.Description));
>
> -  std::string Parents = genReferenceList(I.Parents);
> -  std::string VParents = genReferenceList(I.VirtualParents);
> +  std::vector<std::unique_ptr<HTMLNode>> Parents =
> +      genReferenceList(I.Parents, I.Path);
> +  std::vector<std::unique_ptr<HTMLNode>> VParents =
> +      genReferenceList(I.VirtualParents, I.Path);
>    if (!Parents.empty() || !VParents.empty()) {
> +    Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P));
> +    auto &PBody = Out.back();
> +    PBody->Children.emplace_back(llvm::make_unique<TextNode>("Inherits
> from "));
>      if (Parents.empty())
> -      Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P,
> -                                                  "Inherits from " +
> VParents));
> +      AppendVector(std::move(VParents), PBody->Children);
>      else if (VParents.empty())
> -      Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_P,
> -                                                  "Inherits from " +
> Parents));
> -    else
> -      Out.emplace_back(llvm::make_unique<TagNode>(
> -          HTMLTag::TAG_P, "Inherits from " + Parents + ", " + VParents));
> +      AppendVector(std::move(Parents), PBody->Children);
> +    else {
> +      AppendVector(std::move(Parents), PBody->Children);
> +      PBody->Children.emplace_back(llvm::make_unique<TextNode>(", "));
> +      AppendVector(std::move(VParents), PBody->Children);
> +    }
>    }
>
>    std::vector<std::unique_ptr<TagNode>> Members =
> -      genRecordMembersBlock(I.Members);
> +      genRecordMembersBlock(I.Members, I.Path);
>    AppendVector(std::move(Members), Out);
>    std::vector<std::unique_ptr<TagNode>> ChildRecords =
>        genReferencesBlock(I.ChildRecords, "Records");
>    AppendVector(std::move(ChildRecords), Out);
>
>    std::vector<std::unique_ptr<TagNode>> ChildFunctions =
> -      genFunctionsBlock(I.ChildFunctions);
> +      genFunctionsBlock(I.ChildFunctions, I.Path);
>    AppendVector(std::move(ChildFunctions), Out);
>    std::vector<std::unique_ptr<TagNode>> ChildEnums =
>        genEnumsBlock(I.ChildEnums);
> @@ -492,7 +584,7 @@ llvm::Error HTMLGenerator::generateDocFo
>    }
>    case InfoType::IT_function: {
>      std::vector<std::unique_ptr<TagNode>> Nodes =
> -        genHTML(*static_cast<clang::doc::FunctionInfo *>(I));
> +        genHTML(*static_cast<clang::doc::FunctionInfo *>(I), "");
>      AppendVector(std::move(Nodes), MainContentNode->Children);
>      break;
>    }
>
> Modified: clang-tools-extra/trunk/clang-doc/MDGenerator.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/MDGenerator.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/MDGenerator.cpp (original)
> +++ clang-tools-extra/trunk/clang-doc/MDGenerator.cpp Fri Jul 12 11:32:00
> 2019
> @@ -28,6 +28,18 @@ static std::string genEmphasis(const Twi
>    return "**" + Text.str() + "**";
>  }
>
> +static std::string
> +genReferenceList(const llvm::SmallVectorImpl<Reference> &Refs) {
> +  std::string Buffer;
> +  llvm::raw_string_ostream Stream(Buffer);
> +  for (const auto &R : Refs) {
> +    if (&R != Refs.begin())
> +      Stream << ", ";
> +    Stream << R.Name;
> +  }
> +  return Stream.str();
> +}
> +
>  static void writeLine(const Twine &Text, raw_ostream &OS) {
>    OS << Text << "\n\n";
>  }
>
> Modified: clang-tools-extra/trunk/clang-doc/Representation.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/Representation.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/Representation.cpp (original)
> +++ clang-tools-extra/trunk/clang-doc/Representation.cpp Fri Jul 12
> 11:32:00 2019
> @@ -118,6 +118,8 @@ void Info::mergeBase(Info &&Other) {
>      USR = Other.USR;
>    if (Name == "")
>      Name = Other.Name;
> +  if (Path == "")
> +    Path = Other.Path;
>    if (Namespace.empty())
>      Namespace = std::move(Other.Namespace);
>    // Unconditionally extend the description, since each decl may have a
> comment.
>
> Modified: clang-tools-extra/trunk/clang-doc/Representation.h
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/Representation.h?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/Representation.h (original)
> +++ clang-tools-extra/trunk/clang-doc/Representation.h Fri Jul 12 11:32:00
> 2019
> @@ -114,8 +114,11 @@ struct CommentInfo {
>  struct Reference {
>    Reference() = default;
>    Reference(llvm::StringRef Name) : Name(Name) {}
> +  Reference(llvm::StringRef Name, StringRef Path) : Name(Name),
> Path(Path) {}
>    Reference(SymbolID USR, StringRef Name, InfoType IT)
>        : USR(USR), Name(Name), RefType(IT) {}
> +  Reference(SymbolID USR, StringRef Name, InfoType IT, StringRef Path)
> +      : USR(USR), Name(Name), RefType(IT), Path(Path) {}
>
>    bool operator==(const Reference &Other) const {
>      return std::tie(USR, Name, RefType) ==
> @@ -127,6 +130,8 @@ struct Reference {
>    InfoType RefType = InfoType::IT_default; // Indicates the type of this
>                                             // Reference (namespace,
> record,
>                                             // function, enum, default).
> +  llvm::SmallString<128> Path; // Path of directory where the clang-doc
> +                               // generated file will be saved
>  };
>
>  // A base struct for TypeInfos
> @@ -134,7 +139,10 @@ struct TypeInfo {
>    TypeInfo() = default;
>    TypeInfo(SymbolID Type, StringRef Field, InfoType IT)
>        : Type(Type, Field, IT) {}
> +  TypeInfo(SymbolID Type, StringRef Field, InfoType IT, StringRef Path)
> +      : Type(Type, Field, IT, Path) {}
>    TypeInfo(llvm::StringRef RefName) : Type(RefName) {}
> +  TypeInfo(llvm::StringRef RefName, StringRef Path) : Type(RefName, Path)
> {}
>
>    bool operator==(const TypeInfo &Other) const { return Type ==
> Other.Type; }
>
> @@ -144,11 +152,13 @@ struct TypeInfo {
>  // Info for field types.
>  struct FieldTypeInfo : public TypeInfo {
>    FieldTypeInfo() = default;
> -  FieldTypeInfo(SymbolID Type, StringRef Field, InfoType IT,
> +  FieldTypeInfo(SymbolID Type, StringRef Field, InfoType IT, StringRef
> Path,
>                  llvm::StringRef Name)
> -      : TypeInfo(Type, Field, IT), Name(Name) {}
> +      : TypeInfo(Type, Field, IT, Path), Name(Name) {}
>    FieldTypeInfo(llvm::StringRef RefName, llvm::StringRef Name)
>        : TypeInfo(RefName), Name(Name) {}
> +  FieldTypeInfo(llvm::StringRef RefName, StringRef Path, llvm::StringRef
> Name)
> +      : TypeInfo(RefName, Path), Name(Name) {}
>
>    bool operator==(const FieldTypeInfo &Other) const {
>      return std::tie(Type, Name) == std::tie(Other.Type, Other.Name);
> @@ -160,12 +170,15 @@ struct FieldTypeInfo : public TypeInfo {
>  // Info for member types.
>  struct MemberTypeInfo : public FieldTypeInfo {
>    MemberTypeInfo() = default;
> -  MemberTypeInfo(SymbolID Type, StringRef Field, InfoType IT,
> +  MemberTypeInfo(SymbolID Type, StringRef Field, InfoType IT, StringRef
> Path,
>                   llvm::StringRef Name, AccessSpecifier Access)
> -      : FieldTypeInfo(Type, Field, IT, Name), Access(Access) {}
> +      : FieldTypeInfo(Type, Field, IT, Path, Name), Access(Access) {}
>    MemberTypeInfo(llvm::StringRef RefName, llvm::StringRef Name,
>                   AccessSpecifier Access)
>        : FieldTypeInfo(RefName, Name), Access(Access) {}
> +  MemberTypeInfo(llvm::StringRef RefName, StringRef Path, llvm::StringRef
> Name,
> +                 AccessSpecifier Access)
> +      : FieldTypeInfo(RefName, Path, Name), Access(Access) {}
>
>    bool operator==(const MemberTypeInfo &Other) const {
>      return std::tie(Type, Name, Access) ==
> @@ -220,6 +233,8 @@ struct Info {
>    llvm::SmallVector<Reference, 4>
>        Namespace; // List of parent namespaces for this decl.
>    std::vector<CommentInfo> Description; // Comment description of this
> decl.
> +  llvm::SmallString<128> Path;          // Path of directory where the
> clang-doc
> +                                        // generated file will be saved
>
>    void mergeBase(Info &&I);
>    bool mergeable(const Info &Other);
>
> Modified: clang-tools-extra/trunk/clang-doc/Serialize.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/Serialize.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/Serialize.cpp (original)
> +++ clang-tools-extra/trunk/clang-doc/Serialize.cpp Fri Jul 12 11:32:00
> 2019
> @@ -24,6 +24,43 @@ SymbolID hashUSR(llvm::StringRef USR) {
>    return llvm::SHA1::hash(arrayRefFromStringRef(USR));
>  }
>
> +template <typename T>
> +static void
> +populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
> +                         const T *D, bool &IsAnonymousNamespace);
> +
> +// A function to extract the appropriate relative path for a given info's
> +// documentation. The path returned is a composite of the parent
> namespaces.
> +//
> +// Example: Given the below, the diretory path for class C info will be
> +// <root>/A/B
> +//
> +// namespace A {
> +// namesapce B {
> +//
> +// class C {};
> +//
> +// }
> +// }
> +llvm::SmallString<128>
> +getInfoRelativePath(const llvm::SmallVectorImpl<doc::Reference>
> &Namespaces) {
> +  std::error_code OK;
> +  llvm::SmallString<128> Path;
> +  for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
> +    llvm::sys::path::append(Path, R->Name);
> +  return Path;
> +}
> +
> +llvm::SmallString<128> getInfoRelativePath(const Decl *D) {
> +  llvm::SmallVector<Reference, 4> Namespaces;
> +  // The third arg in populateParentNamespaces is a boolean passed by
> reference,
> +  // its value is not relevant in here so it's not used anywhere besides
> the
> +  // function call
> +  bool B = true;
> +  populateParentNamespaces(Namespaces, D, B);
> +  return getInfoRelativePath(Namespaces);
> +}
> +
>  class ClangDocCommentVisitor
>      : public ConstCommentVisitor<ClangDocCommentVisitor> {
>  public:
> @@ -203,13 +240,13 @@ static void parseFields(RecordInfo &I, c
>        // valid, as opposed to an assert.
>        if (const auto *N = dyn_cast<EnumDecl>(T)) {
>          I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(),
> -                               InfoType::IT_enum, F->getNameAsString(),
> -                               N->getAccessUnsafe());
> +                               InfoType::IT_enum, getInfoRelativePath(N),
> +                               F->getNameAsString(),
> N->getAccessUnsafe());
>          continue;
>        } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
>          I.Members.emplace_back(getUSRForDecl(T), N->getNameAsString(),
> -                               InfoType::IT_record, F->getNameAsString(),
> -                               N->getAccessUnsafe());
> +                               InfoType::IT_record,
> getInfoRelativePath(N),
> +                               F->getNameAsString(),
> N->getAccessUnsafe());
>          continue;
>        }
>      }
> @@ -228,11 +265,13 @@ static void parseParameters(FunctionInfo
>      if (const auto *T = getDeclForType(P->getOriginalType())) {
>        if (const auto *N = dyn_cast<EnumDecl>(T)) {
>          I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
> -                              InfoType::IT_enum, P->getNameAsString());
> +                              InfoType::IT_enum, getInfoRelativePath(N),
> +                              P->getNameAsString());
>          continue;
>        } else if (const auto *N = dyn_cast<RecordDecl>(T)) {
>          I.Params.emplace_back(getUSRForDecl(N), N->getNameAsString(),
> -                              InfoType::IT_record, P->getNameAsString());
> +                              InfoType::IT_record, getInfoRelativePath(N),
> +                              P->getNameAsString());
>          continue;
>        }
>      }
> @@ -254,14 +293,15 @@ static void parseBases(RecordInfo &I, co
>                               InfoType::IT_record);
>      } else if (const RecordDecl *P = getDeclForType(B.getType()))
>        I.Parents.emplace_back(getUSRForDecl(P), P->getNameAsString(),
> -                             InfoType::IT_record);
> +                             InfoType::IT_record, getInfoRelativePath(P));
>      else
>        I.Parents.emplace_back(B.getType().getAsString());
>    }
>    for (const CXXBaseSpecifier &B : D->vbases()) {
>      if (const auto *P = getDeclForType(B.getType()))
>        I.VirtualParents.emplace_back(getUSRForDecl(P),
> P->getNameAsString(),
> -                                    InfoType::IT_record);
> +                                    InfoType::IT_record,
> +                                    getInfoRelativePath(P));
>      else
>        I.VirtualParents.emplace_back(B.getType().getAsString());
>    }
> @@ -270,14 +310,14 @@ static void parseBases(RecordInfo &I, co
>  template <typename T>
>  static void
>  populateParentNamespaces(llvm::SmallVector<Reference, 4> &Namespaces,
> -                         const T *D, bool &IsAnonymousNamespace) {
> +                         const T *D, bool &IsInAnonymousNamespace) {
>    const auto *DC = dyn_cast<DeclContext>(D);
>    while ((DC = DC->getParent())) {
>      if (const auto *N = dyn_cast<NamespaceDecl>(DC)) {
>        std::string Namespace;
>        if (N->isAnonymousNamespace()) {
>          Namespace = "@nonymous_namespace";
> -        IsAnonymousNamespace = true;
> +        IsInAnonymousNamespace = true;
>        } else
>          Namespace = N->getNameAsString();
>        Namespaces.emplace_back(getUSRForDecl(N), Namespace,
> @@ -324,11 +364,11 @@ static void populateFunctionInfo(Functio
>    populateSymbolInfo(I, D, FC, LineNumber, Filename,
> IsInAnonymousNamespace);
>    if (const auto *T = getDeclForType(D->getReturnType())) {
>      if (dyn_cast<EnumDecl>(T))
> -      I.ReturnType =
> -          TypeInfo(getUSRForDecl(T), T->getNameAsString(),
> InfoType::IT_enum);
> +      I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
> +                              InfoType::IT_enum, getInfoRelativePath(T));
>      else if (dyn_cast<RecordDecl>(T))
> -      I.ReturnType =
> -          TypeInfo(getUSRForDecl(T), T->getNameAsString(),
> InfoType::IT_record);
> +      I.ReturnType = TypeInfo(getUSRForDecl(T), T->getNameAsString(),
> +                              InfoType::IT_record,
> getInfoRelativePath(T));
>    } else {
>      I.ReturnType = TypeInfo(D->getReturnType().getAsString());
>    }
> @@ -347,16 +387,18 @@ emitInfo(const NamespaceDecl *D, const F
>    I->Name = D->isAnonymousNamespace()
>                  ? llvm::SmallString<16>("@nonymous_namespace")
>                  : I->Name;
> +  I->Path = getInfoRelativePath(I->Namespace);
>    if (I->Namespace.empty() && I->USR == SymbolID())
>      return {std::unique_ptr<Info>{std::move(I)}, nullptr};
>
> -  SymbolID ParentUSR = I->Namespace.empty() ? SymbolID() :
> I->Namespace[0].USR;
> -
> -  auto Parent = llvm::make_unique<NamespaceInfo>();
> -  Parent->USR = ParentUSR;
> -  Parent->ChildNamespaces.emplace_back(I->USR, I->Name,
> InfoType::IT_namespace);
> +  auto ParentI = llvm::make_unique<NamespaceInfo>();
> +  ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR;
> +  ParentI->ChildNamespaces.emplace_back(I->USR, I->Name,
> +                                        InfoType::IT_namespace);
> +  if (I->Namespace.empty())
> +    ParentI->Path = getInfoRelativePath(ParentI->Namespace);
>    return {std::unique_ptr<Info>{std::move(I)},
> -          std::unique_ptr<Info>{std::move(Parent)}};
> +          std::unique_ptr<Info>{std::move(ParentI)}};
>  }
>
>  std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
> @@ -378,32 +420,34 @@ emitInfo(const RecordDecl *D, const Full
>      }
>      parseBases(*I, C);
>    }
> +  I->Path = getInfoRelativePath(I->Namespace);
>
>    if (I->Namespace.empty()) {
> -    auto Parent = llvm::make_unique<NamespaceInfo>();
> -    Parent->USR = SymbolID();
> -    Parent->ChildRecords.emplace_back(I->USR, I->Name,
> InfoType::IT_record);
> +    auto ParentI = llvm::make_unique<NamespaceInfo>();
> +    ParentI->USR = SymbolID();
> +    ParentI->ChildRecords.emplace_back(I->USR, I->Name,
> InfoType::IT_record);
> +    ParentI->Path = getInfoRelativePath(ParentI->Namespace);
>      return {std::unique_ptr<Info>{std::move(I)},
> -            std::unique_ptr<Info>{std::move(Parent)}};
> +            std::unique_ptr<Info>{std::move(ParentI)}};
>    }
>
>    switch (I->Namespace[0].RefType) {
>    case InfoType::IT_namespace: {
> -    auto Parent = llvm::make_unique<NamespaceInfo>();
> -    Parent->USR = I->Namespace[0].USR;
> -    Parent->ChildRecords.emplace_back(I->USR, I->Name,
> InfoType::IT_record);
> +    auto ParentI = llvm::make_unique<NamespaceInfo>();
> +    ParentI->USR = I->Namespace[0].USR;
> +    ParentI->ChildRecords.emplace_back(I->USR, I->Name,
> InfoType::IT_record);
>      return {std::unique_ptr<Info>{std::move(I)},
> -            std::unique_ptr<Info>{std::move(Parent)}};
> +            std::unique_ptr<Info>{std::move(ParentI)}};
>    }
>    case InfoType::IT_record: {
> -    auto Parent = llvm::make_unique<RecordInfo>();
> -    Parent->USR = I->Namespace[0].USR;
> -    Parent->ChildRecords.emplace_back(I->USR, I->Name,
> InfoType::IT_record);
> +    auto ParentI = llvm::make_unique<RecordInfo>();
> +    ParentI->USR = I->Namespace[0].USR;
> +    ParentI->ChildRecords.emplace_back(I->USR, I->Name,
> InfoType::IT_record);
>      return {std::unique_ptr<Info>{std::move(I)},
> -            std::unique_ptr<Info>{std::move(Parent)}};
> +            std::unique_ptr<Info>{std::move(ParentI)}};
>    }
>    default:
> -    llvm_unreachable("Invalid reference type");
> +    llvm_unreachable("Invalid reference type for parent namespace");
>    }
>  }
>
> @@ -420,14 +464,16 @@ emitInfo(const FunctionDecl *D, const Fu
>    Func.Access = clang::AccessSpecifier::AS_none;
>
>    // Wrap in enclosing scope
> -  auto I = llvm::make_unique<NamespaceInfo>();
> +  auto ParentI = llvm::make_unique<NamespaceInfo>();
>    if (!Func.Namespace.empty())
> -    I->USR = Func.Namespace[0].USR;
> +    ParentI->USR = Func.Namespace[0].USR;
>    else
> -    I->USR = SymbolID();
> -  I->ChildFunctions.emplace_back(std::move(Func));
> -  // Info es wrapped in its parent scope so it's returned in the second
> position
> -  return {nullptr, std::unique_ptr<Info>{std::move(I)}};
> +    ParentI->USR = SymbolID();
> +  if (Func.Namespace.empty())
> +    ParentI->Path = getInfoRelativePath(ParentI->Namespace);
> +  ParentI->ChildFunctions.emplace_back(std::move(Func));
> +  // Info is wrapped in its parent scope so it's returned in the second
> position
> +  return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
>  }
>
>  std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
> @@ -455,11 +501,13 @@ emitInfo(const CXXMethodDecl *D, const F
>    Func.Access = D->getAccess();
>
>    // Wrap in enclosing scope
> -  auto I = llvm::make_unique<RecordInfo>();
> -  I->USR = ParentUSR;
> -  I->ChildFunctions.emplace_back(std::move(Func));
> +  auto ParentI = llvm::make_unique<RecordInfo>();
> +  ParentI->USR = ParentUSR;
> +  if (Func.Namespace.empty())
> +    ParentI->Path = getInfoRelativePath(ParentI->Namespace);
> +  ParentI->ChildFunctions.emplace_back(std::move(Func));
>    // Info is wrapped in its parent scope so it's returned in the second
> position
> -  return {nullptr, std::unique_ptr<Info>{std::move(I)}};
> +  return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
>  }
>
>  std::pair<std::unique_ptr<Info>, std::unique_ptr<Info>>
> @@ -475,36 +523,38 @@ emitInfo(const EnumDecl *D, const FullCo
>    Enum.Scoped = D->isScoped();
>    parseEnumerators(Enum, D);
>
> -  // Wrap in enclosing scope
> -  if (!Enum.Namespace.empty()) {
> -    switch (Enum.Namespace[0].RefType) {
> -    case InfoType::IT_namespace: {
> -      auto I = llvm::make_unique<NamespaceInfo>();
> -      I->USR = Enum.Namespace[0].USR;
> -      I->ChildEnums.emplace_back(std::move(Enum));
> -      // Info is wrapped in its parent scope so it's returned in the
> second
> -      // position
> -      return {nullptr, std::unique_ptr<Info>{std::move(I)}};
> -    }
> -    case InfoType::IT_record: {
> -      auto I = llvm::make_unique<RecordInfo>();
> -      I->USR = Enum.Namespace[0].USR;
> -      I->ChildEnums.emplace_back(std::move(Enum));
> -      // Info is wrapped in its parent scope so it's returned in the
> second
> -      // position
> -      return {nullptr, std::unique_ptr<Info>{std::move(I)}};
> -    }
> -    default:
> -      break;
> -    }
> +  // Put in global namespace
> +  if (Enum.Namespace.empty()) {
> +    auto ParentI = llvm::make_unique<NamespaceInfo>();
> +    ParentI->USR = SymbolID();
> +    ParentI->ChildEnums.emplace_back(std::move(Enum));
> +    ParentI->Path = getInfoRelativePath(ParentI->Namespace);
> +    // Info is wrapped in its parent scope so it's returned in the second
> +    // position
> +    return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
>    }
>
> -  // Put in global namespace
> -  auto I = llvm::make_unique<NamespaceInfo>();
> -  I->USR = SymbolID();
> -  I->ChildEnums.emplace_back(std::move(Enum));
> -  // Info is wrapped in its parent scope so it's returned in the second
> position
> -  return {nullptr, std::unique_ptr<Info>{std::move(I)}};
> +  // Wrap in enclosing scope
> +  switch (Enum.Namespace[0].RefType) {
> +  case InfoType::IT_namespace: {
> +    auto ParentI = llvm::make_unique<NamespaceInfo>();
> +    ParentI->USR = Enum.Namespace[0].USR;
> +    ParentI->ChildEnums.emplace_back(std::move(Enum));
> +    // Info is wrapped in its parent scope so it's returned in the second
> +    // position
> +    return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
> +  }
> +  case InfoType::IT_record: {
> +    auto ParentI = llvm::make_unique<RecordInfo>();
> +    ParentI->USR = Enum.Namespace[0].USR;
> +    ParentI->ChildEnums.emplace_back(std::move(Enum));
> +    // Info is wrapped in its parent scope so it's returned in the second
> +    // position
> +    return {nullptr, std::unique_ptr<Info>{std::move(ParentI)}};
> +  }
> +  default:
> +    llvm_unreachable("Invalid reference type for parent namespace");
> +  }
>  }
>
>  } // namespace serialize
>
> Modified: clang-tools-extra/trunk/clang-doc/YAMLGenerator.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/YAMLGenerator.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/YAMLGenerator.cpp (original)
> +++ clang-tools-extra/trunk/clang-doc/YAMLGenerator.cpp Fri Jul 12
> 11:32:00 2019
> @@ -113,6 +113,7 @@ static void FieldTypeInfoMapping(IO &IO,
>  static void InfoMapping(IO &IO, Info &I) {
>    IO.mapRequired("USR", I.USR);
>    IO.mapOptional("Name", I.Name, SmallString<16>());
> +  IO.mapOptional("Path", I.Path, SmallString<128>());
>    IO.mapOptional("Namespace", I.Namespace, llvm::SmallVector<Reference,
> 4>());
>    IO.mapOptional("Description", I.Description);
>  }
> @@ -154,6 +155,7 @@ template <> struct MappingTraits<Referen
>      IO.mapOptional("Type", Ref.RefType, InfoType::IT_default);
>      IO.mapOptional("Name", Ref.Name, SmallString<16>());
>      IO.mapOptional("USR", Ref.USR, SymbolID());
> +    IO.mapOptional("Path", Ref.Path, SmallString<128>());
>    }
>  };
>
>
> Modified: clang-tools-extra/trunk/clang-doc/tool/ClangDocMain.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-doc/tool/ClangDocMain.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/clang-doc/tool/ClangDocMain.cpp (original)
> +++ clang-tools-extra/trunk/clang-doc/tool/ClangDocMain.cpp Fri Jul 12
> 11:32:00 2019
> @@ -110,12 +110,13 @@ bool CreateDirectory(const Twine &DirNam
>    return false;
>  }
>
> -// A function to extract the appropriate path name for a given info's
> -// documentation. The path returned is a composite of the parent
> namespaces as
> -// directories plus the decl name as the filename.
> +// A function to extract the appropriate file name for a given info's
> +// documentation. The path returned is a composite of the output
> directory, the
> +// info's relative path and name and the extension. The relative path
> should
> +// have been constructed in the serialization phase.
>  //
> -// Example: Given the below, the <ext> path for class C will be <
> -// root>/A/B/C.<ext>
> +// Example: Given the below, the <ext> path for class C will be
> +// <root>/A/B/C.<ext>
>  //
>  // namespace A {
>  // namesapce B {
> @@ -124,16 +125,14 @@ bool CreateDirectory(const Twine &DirNam
>  //
>  // }
>  // }
> -llvm::Expected<llvm::SmallString<128>>
> -getInfoOutputFile(StringRef Root,
> -                  llvm::SmallVectorImpl<doc::Reference> &Namespaces,
> -                  StringRef Name, StringRef Ext) {
> +llvm::Expected<llvm::SmallString<128>> getInfoOutputFile(StringRef Root,
> +                                                         StringRef
> RelativePath,
> +                                                         StringRef Name,
> +                                                         StringRef Ext) {
>    std::error_code OK;
>    llvm::SmallString<128> Path;
>    llvm::sys::path::native(Root, Path);
> -  for (auto R = Namespaces.rbegin(), E = Namespaces.rend(); R != E; ++R)
> -    llvm::sys::path::append(Path, R->Name);
> -
> +  llvm::sys::path::append(Path, RelativePath);
>    if (CreateDirectory(Path))
>      return llvm::make_error<llvm::StringError>("Unable to create
> directory.\n",
>
> llvm::inconvertibleErrorCode());
> @@ -223,12 +222,11 @@ int main(int argc, const char **argv) {
>      }
>
>      doc::Info *I = Reduced.get().get();
> -
> -    auto InfoPath = getInfoOutputFile(OutDirectory, I->Namespace,
> -                                      I->extractName(), "." + Format);
> +    auto InfoPath = getInfoOutputFile(OutDirectory, I->Path,
> I->extractName(),
> +                                      "." + Format);
>      if (!InfoPath) {
>        llvm::errs() << toString(InfoPath.takeError()) << "\n";
> -      continue;
> +      return 1;
>      }
>      std::error_code FileErr;
>      llvm::raw_fd_ostream InfoOS(InfoPath.get(), FileErr,
> llvm::sys::fs::F_None);
>
> Modified: clang-tools-extra/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp
> (original)
> +++ clang-tools-extra/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp Fri
> Jul 12 11:32:00 2019
> @@ -57,7 +57,7 @@ TEST(HTMLGeneratorTest, emitNamespaceHTM
>    <div>
>      <h3>OneFunction</h3>
>      <p>
> -       OneFunction()
> +      OneFunction()
>      </p>
>    </div>
>    <h2>Enums</h2>
> @@ -73,14 +73,16 @@ TEST(HTMLGeneratorTest, emitNamespaceHTM
>  TEST(HTMLGeneratorTest, emitRecordHTML) {
>    RecordInfo I;
>    I.Name = "r";
> +  I.Path = "X/Y/Z";
>    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.Members.emplace_back("int", "X/Y", "X", AccessSpecifier::AS_private);
>    I.TagType = TagTypeKind::TTK_Class;
> -  I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
> +  I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record,
> +                         llvm::SmallString<128>("path/to"));
>    I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
>
>    I.ChildRecords.emplace_back(EmptySID, "ChildStruct",
> InfoType::IT_record);
> @@ -104,11 +106,13 @@ TEST(HTMLGeneratorTest, emitRecordHTML)
>      Defined at line 10 of test.cpp
>    </p>
>    <p>
> -    Inherits from F, G
> +    Inherits from
> +    <a href="../../../path/to/F.html">F</a>
> +    , G
>    </p>
>    <h2>Members</h2>
>    <ul>
> -    <li>private int X</li>
> +    <li>private <a href="../int.html">int</a> X</li>
>    </ul>
>    <h2>Records</h2>
>    <ul>
> @@ -118,7 +122,7 @@ TEST(HTMLGeneratorTest, emitRecordHTML)
>    <div>
>      <h3>OneFunction</h3>
>      <p>
> -       OneFunction()
> +      OneFunction()
>      </p>
>    </div>
>    <h2>Enums</h2>
> @@ -139,8 +143,8 @@ TEST(HTMLGeneratorTest, emitFunctionHTML
>    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.ReturnType = TypeInfo(EmptySID, "float", InfoType::IT_default,
> "path/to");
> +  I.Params.emplace_back("int", "path/to", "P");
>    I.IsMethod = true;
>    I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
>
> @@ -156,7 +160,10 @@ TEST(HTMLGeneratorTest, emitFunctionHTML
>  <div>
>    <h3>f</h3>
>    <p>
> -    void f(int P)
> +    <a href="path/to/float.html">float</a>
> +     f(
> +    <a href="path/to/int.html">int</a>
> +     P)
>    </p>
>    <p>
>      Defined at line 10 of test.cpp
> @@ -261,8 +268,7 @@ TEST(HTMLGeneratorTest, emitCommentHTML)
>           Brief description.
>        </p>
>        <p>
> -         Extended description that
> -         continues onto the next line.
> +         Extended description that continues onto the next line.
>        </p>
>      </div>
>    </div>
>
> Modified: clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp?rev=365937&r1=365936&r2=365937&view=diff
>
> ==============================================================================
> --- clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp
> (original)
> +++ clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp Fri
> Jul 12 11:32:00 2019
> @@ -25,6 +25,7 @@ std::unique_ptr<Generator> getYAMLGenera
>  TEST(YAMLGeneratorTest, emitNamespaceYAML) {
>    NamespaceInfo I;
>    I.Name = "Namespace";
> +  I.Path = "path/to/A";
>    I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);
>
>    I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace",
> @@ -45,6 +46,7 @@ TEST(YAMLGeneratorTest, emitNamespaceYAM
>        R"raw(---
>  USR:             '0000000000000000000000000000000000000000'
>  Name:            'Namespace'
> +Path:            'path/to/A'
>  Namespace:
>    - Type:            Namespace
>      Name:            'A'
> @@ -69,15 +71,18 @@ ChildEnums:
>  TEST(YAMLGeneratorTest, emitRecordYAML) {
>    RecordInfo I;
>    I.Name = "r";
> +  I.Path = "path/to/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.Members.emplace_back("int", "path/to/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.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, "path/to/F");
> +  I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record,
> +                                "path/to/G");
>
>    I.ChildRecords.emplace_back(EmptySID, "ChildStruct",
> InfoType::IT_record);
>    I.ChildFunctions.emplace_back();
> @@ -95,6 +100,7 @@ TEST(YAMLGeneratorTest, emitRecordYAML)
>        R"raw(---
>  USR:             '0000000000000000000000000000000000000000'
>  Name:            'r'
> +Path:            'path/to/r'
>  Namespace:
>    - Type:            Namespace
>      Name:            'A'
> @@ -108,14 +114,17 @@ TagType:         Class
>  Members:
>    - Type:
>        Name:            'int'
> +      Path:            'path/to/int'
>      Name:            'X'
>      Access:          Private
>  Parents:
>    - Type:            Record
>      Name:            'F'
> +    Path:            'path/to/F'
>  VirtualParents:
>    - Type:            Record
>      Name:            'G'
> +    Path:            'path/to/G'
>  ChildRecords:
>    - Type:            Record
>      Name:            'ChildStruct'
> @@ -139,8 +148,9 @@ TEST(YAMLGeneratorTest, emitFunctionYAML
>    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.ReturnType =
> +      TypeInfo(EmptySID, "void", InfoType::IT_default, "path/to/void");
> +  I.Params.emplace_back("int", "path/to/int", "P");
>    I.IsMethod = true;
>    I.Parent = Reference(EmptySID, "Parent", InfoType::IT_record);
>
> @@ -170,10 +180,12 @@ Parent:
>  Params:
>    - Type:
>        Name:            'int'
> +      Path:            'path/to/int'
>      Name:            'P'
>  ReturnType:
>    Type:
>      Name:            'void'
> +    Path:            'path/to/void'
>  ...
>  )raw";
>    EXPECT_EQ(Expected, Actual.str());
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20190712/5e0f9951/attachment-0001.html>


More information about the cfe-commits mailing list