Index: clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp +++ clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp @@ -207,26 +207,44 @@ 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); - } +// Compute the relative path from an Origin directory to a Destination directory +static SmallString<128> computeRelativePath(StringRef Destination, + StringRef Origin) { + // If Origin is empty, the relative path to the Destination is its complete + // path. + if (Origin.empty()) + return Destination; - StringRef Dir = Directory; - SmallString<128> Result; - while (!Dir.empty()) { - if (Dir == FilePath) - break; - Dir = llvm::sys::path::parent_path(Dir); + // The relative path is an empty path if both directories are the same. + if (Destination == Origin) + return {}; + + // These iterators iterate through each of their parent directories + llvm::sys::path::const_iterator FileI = llvm::sys::path::begin(Destination); + llvm::sys::path::const_iterator FileE = llvm::sys::path::end(Destination); + llvm::sys::path::const_iterator DirI = llvm::sys::path::begin(Origin); + llvm::sys::path::const_iterator DirE = llvm::sys::path::end(Origin); + // Advance both iterators until the paths differ. Example: + // Destination = A/B/C/D + // Origin = A/B/E/F + // FileI will point to C and DirI to E. The directories behind them is the + // directory they share (A/B). + while (FileI != FileE && DirI != DirE && *FileI == *DirI) { + ++FileI; + ++DirI; + } + SmallString<128> Result; // This will hold the resulting path. + // Result has to go up one directory for each of the remaining directories in + // Origin + while (DirI != DirE) { llvm::sys::path::append(Result, ".."); + ++DirI; + } + // Result has to append each of the remaining directories in Destination + while (FileI != FileE) { + llvm::sys::path::append(Result, *FileI); + ++FileI; } - llvm::sys::path::append(Result, FilePath.substr(Dir.size())); return Result; } @@ -271,8 +289,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 +314,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; } @@ -372,7 +390,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)); } @@ -381,7 +399,7 @@ static std::vector> genReferencesBlock(const std::vector &References, - llvm::StringRef Title) { + llvm::StringRef Title, StringRef ParentPath) { if (References.empty()) return {}; @@ -390,9 +408,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; } @@ -461,9 +481,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()) @@ -567,7 +587,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( @@ -576,8 +596,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)); } @@ -614,10 +633,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 = @@ -682,7 +701,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/trunk/clang-doc/Representation.h =================================================================== --- clang-tools-extra/trunk/clang-doc/Representation.h +++ clang-tools-extra/trunk/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/trunk/clang-doc/Representation.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/Representation.cpp +++ clang-tools-extra/trunk/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/trunk/clang-doc/Serialize.cpp =================================================================== --- clang-tools-extra/trunk/clang-doc/Serialize.cpp +++ clang-tools-extra/trunk/clang-doc/Serialize.cpp @@ -394,8 +394,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)}, @@ -427,7 +427,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)}}; @@ -437,14 +438,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/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp +++ clang-tools-extra/trunk/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(); @@ -215,7 +221,9 @@

Records

Functions

Index: clang-tools-extra/trunk/unittests/clang-doc/MergeTest.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clang-doc/MergeTest.cpp +++ clang-tools-extra/trunk/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/trunk/unittests/clang-doc/SerializeTest.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clang-doc/SerializeTest.cpp +++ clang-tools-extra/trunk/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/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp =================================================================== --- clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp +++ clang-tools-extra/trunk/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'