Author: diegoastiazaran Date: Mon Aug 12 11:42:46 2019 New Revision: 368602
URL: http://llvm.org/viewvc/llvm-project?rev=368602&view=rev Log: [clang-doc] Generate HTML links for children namespaces/records Path is now stored in the references to the child while serializing, then this path is used to generate the relative path in the HTML generator. Now some references have paths and some don't so in the reducing phase, references are now properly merged checking for empty attributes. Tests added for HTML and YAML generators, merging and serializing. computeRelativePath function had a bug when the filepath is part of the given directory; it returned a path that starts with a separator. This has been fixed. Differential Revision: https://reviews.llvm.org/D65987 Modified: clang-tools-extra/trunk/clang-doc/HTMLGenerator.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/unittests/clang-doc/HTMLGeneratorTest.cpp clang-tools-extra/trunk/unittests/clang-doc/MergeTest.cpp clang-tools-extra/trunk/unittests/clang-doc/SerializeTest.cpp clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp 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=368602&r1=368601&r2=368602&view=diff ============================================================================== --- clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp (original) +++ clang-tools-extra/trunk/clang-doc/HTMLGenerator.cpp Mon Aug 12 11:42:46 2019 @@ -207,26 +207,44 @@ static void AppendVector(std::vector<Der 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); +// 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; + + // 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<TagNode> genLink( } static std::unique_ptr<HTMLNode> -genTypeReference(const Reference &Type, StringRef CurrentDirectory, - llvm::Optional<StringRef> JumpToSection = None) { +genReference(const Reference &Type, StringRef CurrentDirectory, + llvm::Optional<StringRef> JumpToSection = None) { if (Type.Path.empty() && !Type.IsInGlobalNamespace) { if (!JumpToSection) return llvm::make_unique<TextNode>(Type.Name); @@ -296,7 +314,7 @@ genReferenceList(const llvm::SmallVector for (const auto &R : Refs) { if (&R != Refs.begin()) Out.emplace_back(llvm::make_unique<TextNode>(", ")); - Out.emplace_back(genTypeReference(R, CurrentDirectory)); + Out.emplace_back(genReference(R, CurrentDirectory)); } return Out; } @@ -372,7 +390,7 @@ genRecordMembersBlock(const llvm::SmallV Access = Access + " "; 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(genReference(M.Type, ParentInfoDir)); LIBody->Children.emplace_back(llvm::make_unique<TextNode>(" " + M.Name)); ULBody->Children.emplace_back(std::move(LIBody)); } @@ -381,7 +399,7 @@ genRecordMembersBlock(const llvm::SmallV static std::vector<std::unique_ptr<TagNode>> genReferencesBlock(const std::vector<Reference> &References, - llvm::StringRef Title) { + llvm::StringRef Title, StringRef ParentPath) { if (References.empty()) return {}; @@ -390,9 +408,11 @@ genReferencesBlock(const std::vector<Ref Out.back()->Attributes.try_emplace("id", Title); Out.emplace_back(llvm::make_unique<TagNode>(HTMLTag::TAG_UL)); auto &ULBody = Out.back(); - for (const auto &R : References) - ULBody->Children.emplace_back( - llvm::make_unique<TagNode>(HTMLTag::TAG_LI, R.Name)); + for (const auto &R : References) { + auto LiNode = llvm::make_unique<TagNode>(HTMLTag::TAG_LI); + LiNode->Children.emplace_back(genReference(R, ParentPath)); + ULBody->Children.emplace_back(std::move(LiNode)); + } return Out; } @@ -461,9 +481,9 @@ static std::vector<std::unique_ptr<TagNo Out.emplace_back(llvm::make_unique<TagNode>(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 @@ genHTML(const FunctionInfo &I, const Cla llvm::make_unique<TextNode>(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<TextNode>(" ")); } FunctionHeader->Children.emplace_back( @@ -576,8 +596,7 @@ genHTML(const FunctionInfo &I, const Cla 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(genReference(P.Type, ParentInfoDir)); FunctionHeader->Children.emplace_back( llvm::make_unique<TextNode>(" " + P.Name)); } @@ -614,10 +633,10 @@ genHTML(const NamespaceInfo &I, Index &I Out.emplace_back(genHTML(I.Description)); std::vector<std::unique_ptr<TagNode>> ChildNamespaces = - genReferencesBlock(I.ChildNamespaces, "Namespaces"); + genReferencesBlock(I.ChildNamespaces, "Namespaces", I.Path); AppendVector(std::move(ChildNamespaces), Out); std::vector<std::unique_ptr<TagNode>> ChildRecords = - genReferencesBlock(I.ChildRecords, "Records"); + genReferencesBlock(I.ChildRecords, "Records", I.Path); AppendVector(std::move(ChildRecords), Out); std::vector<std::unique_ptr<TagNode>> ChildFunctions = @@ -682,7 +701,7 @@ genHTML(const RecordInfo &I, Index &Info genRecordMembersBlock(I.Members, I.Path); AppendVector(std::move(Members), Out); std::vector<std::unique_ptr<TagNode>> ChildRecords = - genReferencesBlock(I.ChildRecords, "Records"); + genReferencesBlock(I.ChildRecords, "Records", I.Path); AppendVector(std::move(ChildRecords), Out); std::vector<std::unique_ptr<TagNode>> ChildFunctions = 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=368602&r1=368601&r2=368602&view=diff ============================================================================== --- clang-tools-extra/trunk/clang-doc/Representation.cpp (original) +++ clang-tools-extra/trunk/clang-doc/Representation.cpp Mon Aug 12 11:42:46 2019 @@ -54,13 +54,15 @@ int getChildIndexIfExists(std::vector<T> return -1; } -// For References, we don't need to actually merge them, we just don't want -// duplicates. void reduceChildren(std::vector<Reference> &Children, std::vector<Reference> &&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 @@ mergeInfos(std::vector<std::unique_ptr<I } } +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) 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=368602&r1=368601&r2=368602&view=diff ============================================================================== --- clang-tools-extra/trunk/clang-doc/Representation.h (original) +++ clang-tools-extra/trunk/clang-doc/Representation.h Mon Aug 12 11:42:46 2019 @@ -131,6 +131,9 @@ struct Reference { 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 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=368602&r1=368601&r2=368602&view=diff ============================================================================== --- clang-tools-extra/trunk/clang-doc/Serialize.cpp (original) +++ clang-tools-extra/trunk/clang-doc/Serialize.cpp Mon Aug 12 11:42:46 2019 @@ -394,8 +394,8 @@ emitInfo(const NamespaceDecl *D, const F 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); + 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<Info>{std::move(I)}, @@ -427,7 +427,8 @@ emitInfo(const RecordDecl *D, const Full if (I->Namespace.empty()) { auto ParentI = llvm::make_unique<NamespaceInfo>(); 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<Info>{std::move(I)}, std::unique_ptr<Info>{std::move(ParentI)}}; @@ -437,14 +438,16 @@ emitInfo(const RecordDecl *D, const Full case InfoType::IT_namespace: { auto ParentI = llvm::make_unique<NamespaceInfo>(); 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<Info>{std::move(I)}, std::unique_ptr<Info>{std::move(ParentI)}}; } case 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); + ParentI->ChildRecords.emplace_back(I->USR, I->Name, InfoType::IT_record, + getInfoRelativePath(I->Namespace)); return {std::unique_ptr<Info>{std::move(I)}, std::unique_ptr<Info>{std::move(ParentI)}}; } 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=368602&r1=368601&r2=368602&view=diff ============================================================================== --- clang-tools-extra/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp (original) +++ clang-tools-extra/trunk/unittests/clang-doc/HTMLGeneratorTest.cpp Mon Aug 12 11:42:46 2019 @@ -39,8 +39,9 @@ TEST(HTMLGeneratorTest, emitNamespaceHTM 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 @@ TEST(HTMLGeneratorTest, emitNamespaceHTM <h1>namespace Namespace</h1> <h2 id="Namespaces">Namespaces</h2> <ul> - <li>ChildNamespace</li> + <li> + <a href="Namespace/ChildNamespace.html">ChildNamespace</a> + </li> </ul> <h2 id="Records">Records</h2> <ul> - <li>ChildStruct</li> + <li> + <a href="Namespace/ChildStruct.html">ChildStruct</a> + </li> </ul> <h2 id="Functions">Functions</h2> <div> @@ -137,7 +142,8 @@ TEST(HTMLGeneratorTest, emitRecordHTML) 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 @@ TEST(HTMLGeneratorTest, emitRecordHTML) </ul> <h2 id="Records">Records</h2> <ul> - <li>ChildStruct</li> + <li> + <a href="r/ChildStruct.html">ChildStruct</a> + </li> </ul> <h2 id="Functions">Functions</h2> <div> Modified: clang-tools-extra/trunk/unittests/clang-doc/MergeTest.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-doc/MergeTest.cpp?rev=368602&r1=368601&r2=368602&view=diff ============================================================================== --- clang-tools-extra/trunk/unittests/clang-doc/MergeTest.cpp (original) +++ clang-tools-extra/trunk/unittests/clang-doc/MergeTest.cpp Mon Aug 12 11:42:46 2019 @@ -87,7 +87,7 @@ TEST(MergeTest, mergeRecordInfos) { 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 @@ TEST(MergeTest, mergeRecordInfos) { 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 @@ TEST(MergeTest, mergeRecordInfos) { 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; Modified: clang-tools-extra/trunk/unittests/clang-doc/SerializeTest.cpp URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clang-doc/SerializeTest.cpp?rev=368602&r1=368601&r2=368602&view=diff ============================================================================== --- clang-tools-extra/trunk/unittests/clang-doc/SerializeTest.cpp (original) +++ clang-tools-extra/trunk/unittests/clang-doc/SerializeTest.cpp Mon Aug 12 11:42:46 2019 @@ -407,12 +407,14 @@ TEST(SerializeTest, emitChildRecords) { 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 @@ TEST(SerializeTest, emitChildNamespaces) 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); } 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=368602&r1=368601&r2=368602&view=diff ============================================================================== --- clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp (original) +++ clang-tools-extra/trunk/unittests/clang-doc/YAMLGeneratorTest.cpp Mon Aug 12 11:42:46 2019 @@ -29,8 +29,9 @@ TEST(YAMLGeneratorTest, emitNamespaceYAM 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 @@ Namespace: 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 @@ ChildEnums: 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 @@ TEST(YAMLGeneratorTest, emitRecordYAML) 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 @@ TEST(YAMLGeneratorTest, emitRecordYAML) R"raw(--- USR: '0000000000000000000000000000000000000000' Name: 'r' -Path: 'path/to/r' +Path: 'path/to/A' Namespace: - Type: Namespace Name: 'A' @@ -129,6 +133,7 @@ VirtualParents: ChildRecords: - Type: Record Name: 'ChildStruct' + Path: 'path/to/A/r' ChildFunctions: - USR: '0000000000000000000000000000000000000000' Name: 'OneFunction' _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits