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

via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 12 13:55:15 PDT 2019


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


More information about the cfe-commits mailing list