diff --git a/clang/lib/Tooling/NodeIntrospection.cpp b/clang/lib/Tooling/NodeIntrospection.cpp --- a/clang/lib/Tooling/NodeIntrospection.cpp +++ b/clang/lib/Tooling/NodeIntrospection.cpp @@ -13,6 +13,9 @@ #include "clang/Tooling/NodeIntrospection.h" #include "clang/AST/AST.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" namespace clang { @@ -49,6 +52,56 @@ return Result; } +/// lexicographical 3 way comparison. +static int compareStringArray(llvm::ArrayRef LHS, + llvm::ArrayRef RHS) { + // Most accessors don't accept any arguments + if (LLVM_LIKELY(LHS.empty() && RHS.empty())) + return 0; + auto LI = LHS.begin(), LE = LHS.end(), RI = RHS.begin(), RE = RHS.end(); + for (; LI != LE && RI != RE; ++LI, ++RI) { + if (auto Cmp = LI->compare(*RI)) + return Cmp; + } + if (LI == LE && RI == RE) + return 0; + // Return -1 if Left contains less items, 1 if more items. + return LI == LE ? -1 : 1; +} + +/// A 3 way comparison of what LHS and RHS would look like when formatted as a +/// cpp string. +static int compareLocationCall(const LocationCall &LHS, + const LocationCall &RHS) { + // Expand the call stacks into a vector so we can traverse it in reverse + // order. + llvm::SmallVector Left, Right; + for (const LocationCall *Item = &LHS; Item; Item = Item->on()) + Left.push_back(Item); + for (const LocationCall *Item = &RHS; Item; Item = Item->on()) + Right.push_back(Item); + + auto LI = Left.rbegin(), LE = Left.rend(), RI = Right.rbegin(); + for (; LI != LE; ++LI, ++RI) { + // If we haven't returned up to here, both call stacks are the same up the + // here are the same. If we detect that RI is at the end, that means the + // rights last item was a SourceLocation/Range accessor. Therefore the + // preceding item in left should also return a Loc/Range and it should be + // the last item in its stack. + assert(RI != Right.rend()); + if (auto Strcmp = (*LI)->name().compare((*RI)->name())) + return Strcmp; + if (auto ArgCmp = compareStringArray((*LI)->args(), (*RI)->args())) + return ArgCmp; + // As both calls are the same, they should both either return by value or + // both return by pointer. + assert((*LI)->returnsPointer() == (*RI)->returnsPointer()); + } + // As before but flipped. + assert(RI == Right.rend()); + return 0; +} + namespace internal { bool RangeLessThan::operator()( std::pair const &LHS, @@ -66,15 +119,13 @@ else if (LHS.first.getEnd() != RHS.first.getEnd()) return false; - return LocationCallFormatterCpp::format(*LHS.second) < - LocationCallFormatterCpp::format(*RHS.second); + return compareLocationCall(*LHS.second, *RHS.second) < 0; } bool RangeLessThan::operator()( std::pair const &LHS, std::pair const &RHS) const { if (LHS.first == RHS.first) - return LocationCallFormatterCpp::format(*LHS.second) < - LocationCallFormatterCpp::format(*RHS.second); + return compareLocationCall(*LHS.second, *RHS.second) < 0; return LHS.first < RHS.first; } } // namespace internal