Index: clang-tools-extra/clang-doc/Representation.h =================================================================== --- clang-tools-extra/clang-doc/Representation.h +++ clang-tools-extra/clang-doc/Representation.h @@ -46,6 +46,41 @@ CommentInfo() = default; CommentInfo(CommentInfo &Other) = delete; CommentInfo(CommentInfo &&Other) = default; + CommentInfo &operator=(CommentInfo &&Other) = default; + + bool operator==(const CommentInfo &Other) const { + auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName, + SelfClosing, Explicit, AttrKeys, AttrValues, Args); + auto SecondCI = + std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction, + Other.ParamName, Other.CloseName, Other.SelfClosing, + Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args); + + if (FirstCI != SecondCI || Children.size() != Other.Children.size()) + return false; + + return std::equal(Children.begin(), Children.end(), Other.Children.begin(), + llvm::deref{}); + } + + bool operator<(const CommentInfo &Other) const { + auto FirstCI = std::tie(Kind, Text, Name, Direction, ParamName, CloseName, + SelfClosing, Explicit, AttrKeys, AttrValues, Args); + auto SecondCI = + std::tie(Other.Kind, Other.Text, Other.Name, Other.Direction, + Other.ParamName, Other.CloseName, Other.SelfClosing, + Other.Explicit, Other.AttrKeys, Other.AttrValues, Other.Args); + + if (FirstCI < SecondCI || + (FirstCI == SecondCI && Children.size() < Other.Children.size())) + return true; + + if (FirstCI > SecondCI || Children.size() > Other.Children.size()) + return false; + + return std::equal(Children.begin(), Children.end(), Other.Children.begin(), + llvm::deref{}); + } SmallString<16> Kind; // Kind of comment (FullComment, ParagraphComment, TextComment, @@ -148,6 +183,11 @@ std::tie(Other.LineNumber, Other.Filename); } + bool operator<(const Location &Other) const { + return std::tie(LineNumber, Filename) < + std::tie(Other.LineNumber, Other.Filename); + } + int LineNumber; // Line number of this Location. SmallString<32> Filename; // File for this Location. }; Index: clang-tools-extra/clang-doc/Representation.cpp =================================================================== --- clang-tools-extra/clang-doc/Representation.cpp +++ clang-tools-extra/clang-doc/Representation.cpp @@ -122,6 +122,9 @@ // Unconditionally extend the description, since each decl may have a comment. std::move(Other.Description.begin(), Other.Description.end(), std::back_inserter(Description)); + std::sort(Description.begin(), Description.end()); + auto Last = std::unique(Description.begin(), Description.end()); + Description.erase(Last, Description.end()); } bool Info::mergeable(const Info &Other) { @@ -134,6 +137,9 @@ DefLoc = std::move(Other.DefLoc); // Unconditionally extend the list of locations, since we want all of them. std::move(Other.Loc.begin(), Other.Loc.end(), std::back_inserter(Loc)); + std::sort(Loc.begin(), Loc.end()); + auto Last = std::unique(Loc.begin(), Loc.end()); + Loc.erase(Last, Loc.end()); mergeBase(std::move(Other)); } 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 @@ -159,15 +159,37 @@ One.IsMethod = true; One.Parent = Reference(EmptySID, "Parent", InfoType::IT_namespace); + One.Description.emplace_back(); + auto OneFullComment = &One.Description.back(); + OneFullComment->Kind = "FullComment"; + auto OneParagraphComment = llvm::make_unique(); + OneParagraphComment->Kind = "ParagraphComment"; + auto OneTextComment = llvm::make_unique(); + OneTextComment->Kind = "TextComment"; + OneTextComment->Text = "This is a text comment."; + OneParagraphComment->Children.push_back(std::move(OneTextComment)); + OneFullComment->Children.push_back(std::move(OneParagraphComment)); + FunctionInfo Two; Two.Name = "f"; Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace); - Two.Loc.emplace_back(20, llvm::SmallString<16>{"test.cpp"}); + Two.Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); Two.ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); Two.Params.emplace_back("int", "P"); + Two.Description.emplace_back(); + auto TwoFullComment = &Two.Description.back(); + TwoFullComment->Kind = "FullComment"; + auto TwoParagraphComment = llvm::make_unique(); + TwoParagraphComment->Kind = "ParagraphComment"; + auto TwoTextComment = llvm::make_unique(); + TwoTextComment->Kind = "TextComment"; + TwoTextComment->Text = "This is a text comment."; + TwoParagraphComment->Children.push_back(std::move(TwoTextComment)); + TwoFullComment->Children.push_back(std::move(TwoParagraphComment)); + std::vector> Infos; Infos.emplace_back(llvm::make_unique(std::move(One))); Infos.emplace_back(llvm::make_unique(std::move(Two))); @@ -178,13 +200,23 @@ Expected->DefLoc = Location(10, llvm::SmallString<16>{"test.cpp"}); Expected->Loc.emplace_back(12, llvm::SmallString<16>{"test.cpp"}); - Expected->Loc.emplace_back(20, llvm::SmallString<16>{"test.cpp"}); Expected->ReturnType = TypeInfo(EmptySID, "void", InfoType::IT_default); Expected->Params.emplace_back("int", "P"); Expected->IsMethod = true; Expected->Parent = Reference(EmptySID, "Parent", InfoType::IT_namespace); + Expected->Description.emplace_back(); + auto ExpectedFullComment = &Expected->Description.back(); + ExpectedFullComment->Kind = "FullComment"; + auto ExpectedParagraphComment = llvm::make_unique(); + ExpectedParagraphComment->Kind = "ParagraphComment"; + auto ExpectedTextComment = llvm::make_unique(); + ExpectedTextComment->Kind = "TextComment"; + ExpectedTextComment->Text = "This is a text comment."; + ExpectedParagraphComment->Children.push_back(std::move(ExpectedTextComment)); + ExpectedFullComment->Children.push_back(std::move(ExpectedParagraphComment)); + auto Actual = mergeInfos(Infos); assert(Actual); CheckFunctionInfo(InfoAsFunction(Expected.get()),