tom-anders updated this revision to Diff 475939.
tom-anders added a comment.

Fix clangd::Annotations interface, now also has pointWithPayload etc.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D137909/new/

https://reviews.llvm.org/D137909

Files:
  clang-tools-extra/clangd/unittests/Annotations.cpp
  clang-tools-extra/clangd/unittests/Annotations.h
  clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp
  clang-tools-extra/clangd/unittests/tweaks/TweakTesting.h
  llvm/include/llvm/Testing/Support/Annotations.h
  llvm/lib/Testing/Support/Annotations.cpp
  llvm/unittests/Support/AnnotationsTest.cpp

Index: llvm/unittests/Support/AnnotationsTest.cpp
===================================================================
--- llvm/unittests/Support/AnnotationsTest.cpp
+++ llvm/unittests/Support/AnnotationsTest.cpp
@@ -25,10 +25,21 @@
       arg, result_listener);
 }
 
-llvm::Annotations::Range range(size_t Begin, size_t End) {
+llvm::Annotations::Point
+point(size_t Position, llvm::Optional<llvm::StringRef> Payload = llvm::None) {
+  llvm::Annotations::Point P;
+  P.Position = Position;
+  P.Payload = Payload;
+  return P;
+}
+
+llvm::Annotations::Range
+range(size_t Begin, size_t End,
+      llvm::Optional<llvm::StringRef> Payload = llvm::None) {
   llvm::Annotations::Range R;
   R.Begin = Begin;
   R.End = End;
+  R.Payload = Payload;
   return R;
 }
 
@@ -105,6 +116,32 @@
               ElementsAre(range(8, 9), range(7, 10), range(1, 10)));
 }
 
+TEST(AnnotationsTest, Payload) {
+  // A single unnamed point or range with empty payload
+  EXPECT_EQ(llvm::Annotations("a$^b").pointWithPayload(),
+            point(1u, llvm::None));
+  EXPECT_EQ(llvm::Annotations("a$[[b]]cdef").range(), range(1, 2, llvm::None));
+
+  // A single unnamed point or range with payload.
+  EXPECT_EQ(llvm::Annotations("a$(foo)^b").pointWithPayload(),
+            point(1u, {"foo"}));
+  EXPECT_EQ(llvm::Annotations("a$(foo)[[b]]cdef").range(),
+            range(1, 2, {"foo"}));
+
+  // A single named point or range with payload
+  EXPECT_EQ(llvm::Annotations("a$name(foo)^b").pointWithPayload("name"),
+            point(1u, {"foo"}));
+  EXPECT_EQ(llvm::Annotations("a$name(foo)[[b]]cdef").range("name"),
+            range(1, 2, {"foo"}));
+
+  // Multiple named points with payload.
+  llvm::Annotations Annotated("a$p1(p1)^bcd$p2(p2)^123$p1^345");
+  EXPECT_THAT(Annotated.points(), IsEmpty());
+  EXPECT_THAT(Annotated.pointsWithPayload("p1"),
+              ElementsAre(point(1u, {"p1"}), point(7u, llvm::None)));
+  EXPECT_EQ(Annotated.pointWithPayload("p2"), point(4u, {"p2"}));
+}
+
 TEST(AnnotationsTest, Named) {
   // A single named point or range.
   EXPECT_EQ(llvm::Annotations("a$foo^b").point("foo"), 1u);
@@ -144,6 +181,8 @@
   EXPECT_DEATH(llvm::Annotations("ff[[fdfd"), "unmatched \\[\\[");
   EXPECT_DEATH(llvm::Annotations("ff[[fdjsfjd]]xxx]]"), "unmatched \\]\\]");
   EXPECT_DEATH(llvm::Annotations("ff$fdsfd"), "unterminated \\$name");
+  EXPECT_DEATH(llvm::Annotations("ff$("), "unterminated payload");
+  EXPECT_DEATH(llvm::Annotations("ff$name("), "unterminated payload");
 #endif
 }
 } // namespace
Index: llvm/lib/Testing/Support/Annotations.cpp
===================================================================
--- llvm/lib/Testing/Support/Annotations.cpp
+++ llvm/lib/Testing/Support/Annotations.cpp
@@ -28,27 +28,36 @@
     require(Assertion, Msg, Text);
   };
   llvm::Optional<llvm::StringRef> Name;
-  llvm::SmallVector<std::pair<llvm::StringRef, size_t>, 8> OpenRanges;
+  llvm::Optional<llvm::StringRef> Payload;
+  struct OpenRange {
+    llvm::StringRef Name;
+    size_t Begin;
+    llvm::Optional<llvm::StringRef> Payload;
+  };
+  llvm::SmallVector<OpenRange, 8> OpenRanges;
 
   Code.reserve(Text.size());
   while (!Text.empty()) {
     if (Text.consume_front("^")) {
-      Points[Name.value_or("")].push_back(Code.size());
+      Points[Name.value_or("")].push_back({Code.size(), Payload});
       Name = llvm::None;
+      Payload = llvm::None;
       continue;
     }
     if (Text.consume_front("[[")) {
-      OpenRanges.emplace_back(Name.value_or(""), Code.size());
+      OpenRanges.push_back({Name.value_or(""), Code.size(), Payload});
       Name = llvm::None;
+      Payload = llvm::None;
       continue;
     }
     Require(!Name, "$name should be followed by ^ or [[");
     if (Text.consume_front("]]")) {
       Require(!OpenRanges.empty(), "unmatched ]]");
       Range R;
-      R.Begin = OpenRanges.back().second;
+      R.Begin = OpenRanges.back().Begin;
       R.End = Code.size();
-      Ranges[OpenRanges.back().first].push_back(R);
+      R.Payload = OpenRanges.back().Payload;
+      Ranges[OpenRanges.back().Name].push_back(R);
       OpenRanges.pop_back();
       continue;
     }
@@ -56,6 +65,13 @@
       Name =
           Text.take_while([](char C) { return llvm::isAlnum(C) || C == '_'; });
       Text = Text.drop_front(Name->size());
+
+      if (Text.consume_front("(")) {
+        Payload = Text.take_while([](char C) { return C != ')'; });
+        Require(Text.size() > Payload->size(), "unterminated payload");
+        Text = Text.drop_front(Payload->size() + 1);
+      }
+
       continue;
     }
     Code.push_back(Text.front());
@@ -66,6 +82,10 @@
 }
 
 size_t Annotations::point(llvm::StringRef Name) const {
+  return pointWithPayload(Name).Position;
+}
+
+Annotations::Point Annotations::pointWithPayload(llvm::StringRef Name) const {
   auto I = Points.find(Name);
   require(I != Points.end() && I->getValue().size() == 1,
           "expected exactly one point", Code);
@@ -73,14 +93,34 @@
 }
 
 std::vector<size_t> Annotations::points(llvm::StringRef Name) const {
+  auto Pts = pointsWithPayload(Name);
+
+  std::vector<size_t> Positions;
+  Positions.reserve(Pts.size());
+  llvm::transform(Pts, std::back_inserter(Positions),
+                  [](const Point &P) { return P.Position; });
+  return Positions;
+}
+
+std::vector<Annotations::Point>
+Annotations::pointsWithPayload(llvm::StringRef Name) const {
   auto I = Points.find(Name);
   if (I == Points.end())
     return {};
   return {I->getValue().begin(), I->getValue().end()};
 }
 
-const llvm::StringMap<llvm::SmallVector<size_t, 1>> &
-Annotations::all_points() const {
+llvm::StringMap<llvm::SmallVector<size_t, 1>> Annotations::all_points() const {
+  llvm::StringMap<llvm::SmallVector<size_t, 1>> Result;
+  for (const auto &Name : Points.keys()) {
+    auto Pts = points(Name);
+    Result[Name] = {Pts.begin(), Pts.end()};
+  }
+  return Result;
+}
+
+const llvm::StringMap<llvm::SmallVector<Annotations::Point, 1>> &
+Annotations::all_points_and_payloads() const {
   return Points;
 }
 
@@ -106,5 +146,10 @@
 
 llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &O,
                                     const llvm::Annotations::Range &R) {
-  return O << llvm::formatv("[{0}, {1})", R.Begin, R.End);
+  return O << llvm::formatv("[{0}, {1}) ({2})", R.Begin, R.End, R.Payload);
+}
+
+llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &O,
+                                    const llvm::Annotations::Point &P) {
+  return O << llvm::formatv("{0} ({1})", P.Position, P.Payload);
 }
Index: llvm/include/llvm/Testing/Support/Annotations.h
===================================================================
--- llvm/include/llvm/Testing/Support/Annotations.h
+++ llvm/include/llvm/Testing/Support/Annotations.h
@@ -8,6 +8,7 @@
 #ifndef LLVM_TESTING_SUPPORT_ANNOTATIONS_H
 #define LLVM_TESTING_SUPPORT_ANNOTATIONS_H
 
+#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
@@ -24,7 +25,9 @@
 ///       int complete() { x.pri^ }         // ^ indicates a point
 ///       void err() { [["hello" == 42]]; } // [[this is a range]]
 ///       $definition^class Foo{};          // points can be named: "definition"
+///       $definition(foo)^class Foo{};     // and/or contain a payload: "foo"
 ///       $fail[[static_assert(false, "")]] // ranges can be named too: "fail"
+///       $fail(foo)[[]]                    // and/or contain a payload: "foo"
 ///    )cpp");
 ///
 ///    StringRef Code = Example.code();             // annotations stripped.
@@ -35,6 +38,9 @@
 /// Points/ranges are coordinated into `code()` which is stripped of
 /// annotations.
 ///
+/// Names consist of only alphanumeric characters or '_'.
+/// Payloads can contain any character expect '(' and ')'.
+///
 /// Ranges may be nested (and points can be inside ranges), but there's no way
 /// to define general overlapping ranges.
 ///
@@ -52,9 +58,11 @@
   struct Range {
     size_t Begin = 0;
     size_t End = 0;
+    llvm::Optional<llvm::StringRef> Payload;
 
     friend bool operator==(const Range &L, const Range &R) {
-      return std::tie(L.Begin, L.End) == std::tie(R.Begin, R.End);
+      return std::tie(L.Begin, L.End, L.Payload) ==
+             std::tie(R.Begin, R.End, R.Payload);
     }
     friend bool operator!=(const Range &L, const Range &R) { return !(L == R); }
   };
@@ -66,16 +74,38 @@
   /// All points and ranges are relative to this stripped text.
   llvm::StringRef code() const { return Code; }
 
+  struct Point {
+    size_t Position;
+    llvm::Optional<llvm::StringRef> Payload;
+
+    friend bool operator==(const Point &L, const Point &R) {
+      return std::tie(L.Position, L.Payload) == std::tie(R.Position, R.Payload);
+    }
+    friend bool operator!=(const Point &L, const Point &R) { return !(L == R); }
+  };
+
   /// Returns the position of the point marked by ^ (or $name^) in the text.
   /// Crashes if there isn't exactly one.
   size_t point(llvm::StringRef Name = "") const;
+  /// Returns the position of the point marked by ^ (or $name(payload)^) in the
+  /// text containing its payload (or llvm::None if there is no payload).
+  /// Crashes if there isn't exactly one.
+  Point pointWithPayload(llvm::StringRef Name = "") const;
   /// Returns the position of all points marked by ^ (or $name^) in the text.
   /// Order matches the order within the text.
   std::vector<size_t> points(llvm::StringRef Name = "") const;
+  /// Returns the position and payload of all points marked by ^ (or
+  /// $name(payload)^) in the text. Order matches the order within the text.
+  std::vector<Point> pointsWithPayload(llvm::StringRef Name = "") const;
   /// Returns the mapping of all names of points marked in the text to their
   /// position. Unnamed points are mapped to the empty string. The positions are
   /// sorted.
-  const llvm::StringMap<llvm::SmallVector<size_t, 1>> &all_points() const;
+  llvm::StringMap<llvm::SmallVector<size_t, 1>> all_points() const;
+  /// Returns the mapping of all names of points marked in the text to their
+  /// position and payload. Unnamed points are mapped to the empty string. The
+  /// positions are sorted.
+  const llvm::StringMap<llvm::SmallVector<Point, 1>> &
+  all_points_and_payloads() const;
 
   /// Returns the location of the range marked by [[ ]] (or $name[[ ]]).
   /// Crashes if there isn't exactly one.
@@ -90,12 +120,14 @@
 
 private:
   std::string Code;
-  llvm::StringMap<llvm::SmallVector<size_t, 1>> Points;
+  llvm::StringMap<llvm::SmallVector<Point, 1>> Points;
   llvm::StringMap<llvm::SmallVector<Range, 1>> Ranges;
 };
 
 llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
                               const llvm::Annotations::Range &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
+                              const llvm::Annotations::Point &R);
 
 } // namespace llvm
 
Index: clang-tools-extra/clangd/unittests/tweaks/TweakTesting.h
===================================================================
--- clang-tools-extra/clangd/unittests/tweaks/TweakTesting.h
+++ clang-tools-extra/clangd/unittests/tweaks/TweakTesting.h
@@ -116,7 +116,8 @@
     auto AST = build(A.code());                                                \
     assert(!A.points().empty() || !A.ranges().empty());                        \
     for (const auto &P : A.points())                                           \
-      EXPECT_EQ(Available, isAvailable(AST, {P, P})) << decorate(A.code(), P); \
+      EXPECT_EQ(Available, isAvailable(AST, {P, P, llvm::None}))               \
+          << decorate(A.code(), P);                                            \
     for (const auto &R : A.ranges())                                           \
       EXPECT_EQ(Available, isAvailable(AST, R)) << decorate(A.code(), R);      \
   } while (0)
Index: clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp
+++ clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp
@@ -53,7 +53,7 @@
   if (A.points().size() != 0) {
     assert(A.ranges().size() == 0 &&
            "both a cursor point and a selection range were specified");
-    return {A.point(), A.point()};
+    return {A.point(), A.point(), llvm::None};
   }
   return A.range();
 }
Index: clang-tools-extra/clangd/unittests/Annotations.h
===================================================================
--- clang-tools-extra/clangd/unittests/Annotations.h
+++ clang-tools-extra/clangd/unittests/Annotations.h
@@ -19,18 +19,30 @@
 namespace clangd {
 
 /// Same as llvm::Annotations, but adjusts functions to LSP-specific types for
-/// positions and ranges.
+/// positions and ranges (extending them with the payload that may be contained
+/// inside llvm::Annotations' points and ranges.
 class Annotations : public llvm::Annotations {
   using Base = llvm::Annotations;
 
 public:
   using llvm::Annotations::Annotations;
 
+  struct PositionWithPayload : clangd::Position {
+    llvm::Optional<llvm::StringRef> Payload;
+  };
   Position point(llvm::StringRef Name = "") const;
+  PositionWithPayload pointWithPayload(llvm::StringRef Name = "") const;
   std::vector<Position> points(llvm::StringRef Name = "") const;
+  std::vector<PositionWithPayload>
+  pointsWithPayload(llvm::StringRef Name = "") const;
 
+  struct RangeWithPayload : clangd::Range {
+    llvm::Optional<llvm::StringRef> Payload;
+  };
   clangd::Range range(llvm::StringRef Name = "") const;
+  RangeWithPayload rangeWithPayload(llvm::StringRef Name = "") const;
   std::vector<clangd::Range> ranges(llvm::StringRef Name = "") const;
+  std::vector<RangeWithPayload> rangesWithPayload(llvm::StringRef Name = "") const;
 };
 
 } // namespace clangd
Index: clang-tools-extra/clangd/unittests/Annotations.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/Annotations.cpp
+++ clang-tools-extra/clangd/unittests/Annotations.cpp
@@ -13,38 +13,73 @@
 namespace clangd {
 
 Position Annotations::point(llvm::StringRef Name) const {
-  return offsetToPosition(code(), Base::point(Name));
+  return pointWithPayload(Name);
+}
+
+Annotations::PositionWithPayload
+Annotations::pointWithPayload(llvm::StringRef Name) const {
+  auto BasePoint = Base::pointWithPayload(Name);
+  return {offsetToPosition(code(), BasePoint.Position), BasePoint.Payload};
 }
 
 std::vector<Position> Annotations::points(llvm::StringRef Name) const {
-  auto Offsets = Base::points(Name);
+  auto Pts = pointsWithPayload(Name);
+  std::vector<Position> Result;
+  Result.reserve(Pts.size());
+  llvm::transform(
+      Pts, std::back_inserter(Result),
+      [](const PositionWithPayload &P) { return static_cast<Position>(P); });
+  return Result;
+}
+
+std::vector<Annotations::PositionWithPayload>
+Annotations::pointsWithPayload(llvm::StringRef Name) const {
+  auto BasePoints = Base::pointsWithPayload(Name);
 
-  std::vector<Position> Ps;
-  Ps.reserve(Offsets.size());
-  for (size_t O : Offsets)
-    Ps.push_back(offsetToPosition(code(), O));
+  std::vector<PositionWithPayload> Ps;
+  Ps.reserve(BasePoints.size());
+  for (Point P : BasePoints)
+    Ps.push_back({offsetToPosition(code(), P.Position), P.Payload});
 
   return Ps;
 }
 
-static clangd::Range toLSPRange(llvm::StringRef Code, Annotations::Range R) {
+static clangd::Range toLSPRange(llvm::StringRef Code,
+                                llvm::Annotations::Range R) {
   clangd::Range LSPRange;
   LSPRange.start = offsetToPosition(Code, R.Begin);
   LSPRange.end = offsetToPosition(Code, R.End);
   return LSPRange;
 }
 
-clangd::Range Annotations::range(llvm::StringRef Name) const {
-  return toLSPRange(code(), Base::range(Name));
+Range Annotations::range(llvm::StringRef Name) const {
+  return rangeWithPayload(Name);
+}
+
+Annotations::RangeWithPayload
+Annotations::rangeWithPayload(llvm::StringRef Name) const {
+  auto BaseRange = Base::range(Name);
+  return {toLSPRange(code(), BaseRange), BaseRange.Payload};
+}
+
+std::vector<Range> Annotations::ranges(llvm::StringRef Name) const {
+  auto Rs = rangesWithPayload(Name);
+  std::vector<clangd::Range> Result;
+  Result.reserve(Rs.size());
+  llvm::transform(
+      Rs, std::back_inserter(Result),
+      [](const RangeWithPayload &P) { return static_cast<clangd::Range>(P); });
+  return Result;
 }
 
-std::vector<clangd::Range> Annotations::ranges(llvm::StringRef Name) const {
+std::vector<Annotations::RangeWithPayload>
+Annotations::rangesWithPayload(llvm::StringRef Name) const {
   auto OffsetRanges = Base::ranges(Name);
 
-  std::vector<clangd::Range> Rs;
+  std::vector<RangeWithPayload> Rs;
   Rs.reserve(OffsetRanges.size());
-  for (Annotations::Range R : OffsetRanges)
-    Rs.push_back(toLSPRange(code(), R));
+  for (llvm::Annotations::Range R : OffsetRanges)
+    Rs.push_back({toLSPRange(code(), R), R.Payload});
 
   return Rs;
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to