Index: clang-tools-extra/clang-doc/HTMLGenerator.cpp =================================================================== --- clang-tools-extra/clang-doc/HTMLGenerator.cpp +++ clang-tools-extra/clang-doc/HTMLGenerator.cpp @@ -211,13 +211,43 @@ // directory. static SmallString<128> computeRelativePath(StringRef FilePath, StringRef Directory) { + // If the directory is empty, the relative path to the file will be its + // complete path. + if (Directory.empty()) + return FilePath; + + // The relative path is an empty path if both dirs are the same. + if (FilePath == Directory) + return {}; + + // This will handle the cases where FilePath is a dir inside of Directory, + // example: + // Filepath = "A/B/C" + // Directory = "A/B" + // The resulting path can be calculated by looking through the parent + // directories of FilePath until one of them matches Directory. At this point + // the new relative path is determined by removing Directory from FilePath. StringRef Path = FilePath; while (!Path.empty()) { if (Directory == Path) - return FilePath.substr(Path.size()); + return FilePath.substr(Directory.size() + 1); Path = llvm::sys::path::parent_path(Path); } + // This will handle any other case. + // The relative path will always be at least one parent directory (..) and a + // part of the FilePath. + // The loop will look for the parent directory of Directory until it matches + // FilePath or until the are no more parent directories. For each parent + // directory before exiting the loop a ".." is required. The part + // of FilePath that's not in the last parent directory of Directory stored is + // then appended to the multiple ".."'s stored. + // FIXME: In a case where FilePath and Directory share some directories but + // Directory is not a dir inside of FilePath, example: + // Filepath = "A/B/D" + // Directory = "A/B/C" + // The resulting path is "../../../A/B/D" instead of a "../D". It is correct + // but it would be better to have the shorter version. StringRef Dir = Directory; SmallString<128> Result; while (!Dir.empty()) { @@ -271,8 +301,8 @@ } static std::unique_ptr -genTypeReference(const Reference &Type, StringRef CurrentDirectory, - llvm::Optional JumpToSection = None) { +genReference(const Reference &Type, StringRef CurrentDirectory, + llvm::Optional JumpToSection = None) { if (Type.Path.empty() && !Type.IsInGlobalNamespace) { if (!JumpToSection) return llvm::make_unique(Type.Name); @@ -296,7 +326,7 @@ for (const auto &R : Refs) { if (&R != Refs.begin()) Out.emplace_back(llvm::make_unique(", ")); - Out.emplace_back(genTypeReference(R, CurrentDirectory)); + Out.emplace_back(genReference(R, CurrentDirectory)); } return Out; } @@ -368,7 +398,7 @@ Access = Access + " "; auto LIBody = llvm::make_unique(HTMLTag::TAG_LI); LIBody->Children.emplace_back(llvm::make_unique(Access)); - LIBody->Children.emplace_back(genTypeReference(M.Type, ParentInfoDir)); + LIBody->Children.emplace_back(genReference(M.Type, ParentInfoDir)); LIBody->Children.emplace_back(llvm::make_unique(" " + M.Name)); ULBody->Children.emplace_back(std::move(LIBody)); } @@ -377,7 +407,7 @@ static std::vector> genReferencesBlock(const std::vector &References, - llvm::StringRef Title) { + llvm::StringRef Title, StringRef ParentPath) { if (References.empty()) return {}; @@ -386,9 +416,11 @@ Out.back()->Attributes.try_emplace("id", Title); Out.emplace_back(llvm::make_unique(HTMLTag::TAG_UL)); auto &ULBody = Out.back(); - for (const auto &R : References) - ULBody->Children.emplace_back( - llvm::make_unique(HTMLTag::TAG_LI, R.Name)); + for (const auto &R : References) { + auto LiNode = llvm::make_unique(HTMLTag::TAG_LI); + LiNode->Children.emplace_back(genReference(R, ParentPath)); + ULBody->Children.emplace_back(std::move(LiNode)); + } return Out; } @@ -437,9 +469,9 @@ Out.emplace_back(llvm::make_unique(HTMLTag::TAG_SPAN)); auto &SpanBody = Out.back(); if (!Index.JumpToSection) - SpanBody->Children.emplace_back(genTypeReference(Index, InfoPath)); + SpanBody->Children.emplace_back(genReference(Index, InfoPath)); else - SpanBody->Children.emplace_back(genTypeReference( + SpanBody->Children.emplace_back(genReference( Index, InfoPath, StringRef{Index.JumpToSection.getValue()})); } if (Index.Children.empty()) @@ -536,7 +568,7 @@ llvm::make_unique(Access + " ")); if (I.ReturnType.Type.Name != "") { FunctionHeader->Children.emplace_back( - genTypeReference(I.ReturnType.Type, ParentInfoDir)); + genReference(I.ReturnType.Type, ParentInfoDir)); FunctionHeader->Children.emplace_back(llvm::make_unique(" ")); } FunctionHeader->Children.emplace_back( @@ -545,8 +577,7 @@ for (const auto &P : I.Params) { if (&P != I.Params.begin()) FunctionHeader->Children.emplace_back(llvm::make_unique(", ")); - FunctionHeader->Children.emplace_back( - genTypeReference(P.Type, ParentInfoDir)); + FunctionHeader->Children.emplace_back(genReference(P.Type, ParentInfoDir)); FunctionHeader->Children.emplace_back( llvm::make_unique(" " + P.Name)); } @@ -577,10 +608,10 @@ Out.emplace_back(genHTML(I.Description)); std::vector> ChildNamespaces = - genReferencesBlock(I.ChildNamespaces, "Namespaces"); + genReferencesBlock(I.ChildNamespaces, "Namespaces", I.Path); AppendVector(std::move(ChildNamespaces), Out); std::vector> ChildRecords = - genReferencesBlock(I.ChildRecords, "Records"); + genReferencesBlock(I.ChildRecords, "Records", I.Path); AppendVector(std::move(ChildRecords), Out); std::vector> ChildFunctions = @@ -639,7 +670,7 @@ genRecordMembersBlock(I.Members, I.Path); AppendVector(std::move(Members), Out); std::vector> ChildRecords = - genReferencesBlock(I.ChildRecords, "Records"); + genReferencesBlock(I.ChildRecords, "Records", I.Path); AppendVector(std::move(ChildRecords), Out); std::vector> ChildFunctions = Index: clang-tools-extra/clang-doc/Representation.h =================================================================== --- clang-tools-extra/clang-doc/Representation.h +++ clang-tools-extra/clang-doc/Representation.h @@ -131,6 +131,9 @@ std::tie(Other.USR, Other.Name, Other.RefType); } + bool mergeable(const Reference &Other); + void merge(Reference &&I); + SymbolID USR = SymbolID(); // Unique identifer for referenced decl SmallString<16> Name; // Name of type (possibly unresolved). InfoType RefType = InfoType::IT_default; // Indicates the type of this Index: clang-tools-extra/clang-doc/Representation.cpp =================================================================== --- clang-tools-extra/clang-doc/Representation.cpp +++ clang-tools-extra/clang-doc/Representation.cpp @@ -54,13 +54,15 @@ return -1; } -// For References, we don't need to actually merge them, we just don't want -// duplicates. void reduceChildren(std::vector &Children, std::vector &&ChildrenToMerge) { for (auto &ChildToMerge : ChildrenToMerge) { - if (getChildIndexIfExists(Children, ChildToMerge) == -1) + int mergeIdx = getChildIndexIfExists(Children, ChildToMerge); + if (mergeIdx == -1) { Children.push_back(std::move(ChildToMerge)); + continue; + } + Children[mergeIdx].merge(std::move(ChildToMerge)); } } @@ -112,6 +114,20 @@ } } +bool Reference::mergeable(const Reference &Other) { + return RefType == Other.RefType && USR == Other.USR; +} + +void Reference::merge(Reference &&Other) { + assert(mergeable(Other)); + if (Name.empty()) + Name = Other.Name; + if (Path.empty()) + Path = Other.Path; + if (!IsInGlobalNamespace) + IsInGlobalNamespace = Other.IsInGlobalNamespace; +} + void Info::mergeBase(Info &&Other) { assert(mergeable(Other)); if (USR == EmptySID) Index: clang-tools-extra/clang-doc/Serialize.cpp =================================================================== --- clang-tools-extra/clang-doc/Serialize.cpp +++ clang-tools-extra/clang-doc/Serialize.cpp @@ -392,8 +392,8 @@ auto ParentI = llvm::make_unique(); ParentI->USR = I->Namespace.empty() ? SymbolID() : I->Namespace[0].USR; - ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, - InfoType::IT_namespace); + ParentI->ChildNamespaces.emplace_back(I->USR, I->Name, InfoType::IT_namespace, + getInfoRelativePath(I->Namespace)); if (I->Namespace.empty()) ParentI->Path = getInfoRelativePath(ParentI->Namespace); return {std::unique_ptr{std::move(I)}, @@ -424,7 +424,8 @@ if (I->Namespace.empty()) { auto ParentI = llvm::make_unique(); ParentI->USR = SymbolID(); - ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record); + ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record, + getInfoRelativePath(I->Namespace)); ParentI->Path = getInfoRelativePath(ParentI->Namespace); return {std::unique_ptr{std::move(I)}, std::unique_ptr{std::move(ParentI)}}; @@ -434,14 +435,16 @@ case InfoType::IT_namespace: { auto ParentI = llvm::make_unique(); ParentI->USR = I->Namespace[0].USR; - ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record); + ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record, + getInfoRelativePath(I->Namespace)); return {std::unique_ptr{std::move(I)}, std::unique_ptr{std::move(ParentI)}}; } case InfoType::IT_record: { auto ParentI = llvm::make_unique(); ParentI->USR = I->Namespace[0].USR; - ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record); + ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record, + getInfoRelativePath(I->Namespace)); return {std::unique_ptr{std::move(I)}, std::unique_ptr{std::move(ParentI)}}; } Index: clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp =================================================================== --- clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp +++ clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp @@ -39,8 +39,9 @@ I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace", - InfoType::IT_namespace); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); + InfoType::IT_namespace, "Namespace"); + I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, + "Namespace"); I.ChildFunctions.emplace_back(); I.ChildFunctions.back().Name = "OneFunction"; I.ChildEnums.emplace_back(); @@ -100,11 +101,15 @@

namespace Namespace

Namespaces

Records

Functions

@@ -137,7 +142,8 @@ I.Parents.emplace_back(EmptySID, "F", InfoType::IT_record, PathTo); I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); + I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, + "X/Y/Z/r"); I.ChildFunctions.emplace_back(); I.ChildFunctions.back().Name = "OneFunction"; I.ChildEnums.emplace_back(); @@ -210,7 +216,9 @@

Records

Functions

Index: clang-tools-extra/unittests/clang-doc/MergeTest.cpp =================================================================== --- clang-tools-extra/unittests/clang-doc/MergeTest.cpp +++ clang-tools-extra/unittests/clang-doc/MergeTest.cpp @@ -87,7 +87,7 @@ One.Parents.emplace_back(EmptySID, "F", InfoType::IT_record); One.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); - One.ChildRecords.emplace_back(NonEmptySID, "ChildStruct", + One.ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct", InfoType::IT_record); One.ChildFunctions.emplace_back(); One.ChildFunctions.back().Name = "OneFunction"; @@ -104,8 +104,8 @@ Two.TagType = TagTypeKind::TTK_Class; - Two.ChildRecords.emplace_back(EmptySID, "OtherChildStruct", - InfoType::IT_record); + Two.ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct", + InfoType::IT_record, "path"); Two.ChildFunctions.emplace_back(); Two.ChildFunctions.back().Name = "TwoFunction"; Two.ChildEnums.emplace_back(); @@ -127,10 +127,8 @@ Expected->Parents.emplace_back(EmptySID, "F", InfoType::IT_record); Expected->VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record); - Expected->ChildRecords.emplace_back(NonEmptySID, "ChildStruct", - InfoType::IT_record); - Expected->ChildRecords.emplace_back(EmptySID, "OtherChildStruct", - InfoType::IT_record); + Expected->ChildRecords.emplace_back(NonEmptySID, "SharedChildStruct", + InfoType::IT_record, "path"); Expected->ChildFunctions.emplace_back(); Expected->ChildFunctions.back().Name = "OneFunction"; Expected->ChildFunctions.back().USR = NonEmptySID; Index: clang-tools-extra/unittests/clang-doc/SerializeTest.cpp =================================================================== --- clang-tools-extra/unittests/clang-doc/SerializeTest.cpp +++ clang-tools-extra/unittests/clang-doc/SerializeTest.cpp @@ -407,12 +407,14 @@ RecordInfo *ParentB = InfoAsRecord(Infos[3].get()); RecordInfo ExpectedParentB(EmptySID); - ExpectedParentB.ChildRecords.emplace_back(EmptySID, "B", InfoType::IT_record); + ExpectedParentB.ChildRecords.emplace_back(EmptySID, "B", InfoType::IT_record, + "A"); CheckRecordInfo(&ExpectedParentB, ParentB); NamespaceInfo *ParentC = InfoAsNamespace(Infos[7].get()); NamespaceInfo ExpectedParentC(EmptySID); - ExpectedParentC.ChildRecords.emplace_back(EmptySID, "C", InfoType::IT_record); + ExpectedParentC.ChildRecords.emplace_back(EmptySID, "C", InfoType::IT_record, + "@nonymous_namespace"); CheckNamespaceInfo(&ExpectedParentC, ParentC); } @@ -431,7 +433,7 @@ NamespaceInfo *ParentB = InfoAsNamespace(Infos[3].get()); NamespaceInfo ExpectedParentB(EmptySID); ExpectedParentB.ChildNamespaces.emplace_back(EmptySID, "B", - InfoType::IT_namespace); + InfoType::IT_namespace, "A"); CheckNamespaceInfo(&ExpectedParentB, ParentB); } Index: clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp =================================================================== --- clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp +++ clang-tools-extra/unittests/clang-doc/YAMLGeneratorTest.cpp @@ -29,8 +29,9 @@ I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); I.ChildNamespaces.emplace_back(EmptySID, "ChildNamespace", - InfoType::IT_namespace); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); + InfoType::IT_namespace, "path/to/A/Namespace"); + I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, + "path/to/A/Namespace"); I.ChildFunctions.emplace_back(); I.ChildFunctions.back().Name = "OneFunction"; I.ChildEnums.emplace_back(); @@ -53,9 +54,11 @@ ChildNamespaces: - Type: Namespace Name: 'ChildNamespace' + Path: 'path/to/A/Namespace' ChildRecords: - Type: Record Name: 'ChildStruct' + Path: 'path/to/A/Namespace' ChildFunctions: - USR: '0000000000000000000000000000000000000000' Name: 'OneFunction' @@ -71,7 +74,7 @@ TEST(YAMLGeneratorTest, emitRecordYAML) { RecordInfo I; I.Name = "r"; - I.Path = "path/to/r"; + I.Path = "path/to/A"; I.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); I.DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}); @@ -85,7 +88,8 @@ I.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record, "path/to/G"); - I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record); + I.ChildRecords.emplace_back(EmptySID, "ChildStruct", InfoType::IT_record, + "path/to/A/r"); I.ChildFunctions.emplace_back(); I.ChildFunctions.back().Name = "OneFunction"; I.ChildEnums.emplace_back(); @@ -101,7 +105,7 @@ R"raw(--- USR: '0000000000000000000000000000000000000000' Name: 'r' -Path: 'path/to/r' +Path: 'path/to/A' Namespace: - Type: Namespace Name: 'A' @@ -129,6 +133,7 @@ ChildRecords: - Type: Record Name: 'ChildStruct' + Path: 'path/to/A/r' ChildFunctions: - USR: '0000000000000000000000000000000000000000' Name: 'OneFunction'