r361955 - [LibTooling] Add `before` and `after` selectors for selecting point-ranges relative to nodes.

Yitzhak Mandelbaum via cfe-commits cfe-commits at lists.llvm.org
Wed May 29 05:40:36 PDT 2019


Author: ymandel
Date: Wed May 29 05:40:36 2019
New Revision: 361955

URL: http://llvm.org/viewvc/llvm-project?rev=361955&view=rev
Log:
[LibTooling] Add `before` and `after` selectors for selecting point-ranges relative to nodes.

Summary:
The `before` and `after` selectors allow users to specify a zero-length range --
a point -- at the relevant location in an AST-node's source.  Point ranges can
be useful, for example, to insert a change using an API that takes a range to be
modified (e.g. `tooling::change()`).

Reviewers: ilya-biryukov

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D62419

Modified:
    cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h
    cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp
    cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp

Modified: cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h?rev=361955&r1=361954&r2=361955&view=diff
==============================================================================
--- cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h (original)
+++ cfe/trunk/include/clang/Tooling/Refactoring/RangeSelector.h Wed May 29 05:40:36 2019
@@ -37,6 +37,15 @@ RangeSelector range(RangeSelector Begin,
 /// Convenience version of \c range where end-points are bound nodes.
 RangeSelector range(std::string BeginID, std::string EndID);
 
+/// Selects the (empty) range [B,B) when \p Selector selects the range [B,E).
+RangeSelector before(RangeSelector Selector);
+
+/// Selects the the point immediately following \p Selector. That is, the
+/// (empty) range [E,E), when \p Selector selects either
+/// * the CharRange [B,E) or
+/// * the TokenRange [B,E'] where the token at E' spans the range [E,E').
+RangeSelector after(RangeSelector Selector);
+
 /// Selects a node, including trailing semicolon (for non-expression
 /// statements). \p ID is the node's binding in the match result.
 RangeSelector node(std::string ID);

Modified: cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp?rev=361955&r1=361954&r2=361955&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring/RangeSelector.cpp Wed May 29 05:40:36 2019
@@ -104,6 +104,28 @@ static SourceLocation findOpenParen(cons
   return findPreviousTokenKind(EndLoc, SM, LangOpts, tok::TokenKind::l_paren);
 }
 
+RangeSelector tooling::before(RangeSelector Selector) {
+  return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
+    Expected<CharSourceRange> SelectedRange = Selector(Result);
+    if (!SelectedRange)
+      return SelectedRange.takeError();
+    return CharSourceRange::getCharRange(SelectedRange->getBegin());
+  };
+}
+
+RangeSelector tooling::after(RangeSelector Selector) {
+  return [Selector](const MatchResult &Result) -> Expected<CharSourceRange> {
+    Expected<CharSourceRange> SelectedRange = Selector(Result);
+    if (!SelectedRange)
+      return SelectedRange.takeError();
+    if (SelectedRange->isCharRange())
+      return CharSourceRange::getCharRange(SelectedRange->getEnd());
+    return CharSourceRange::getCharRange(Lexer::getLocForEndOfToken(
+        SelectedRange->getEnd(), 0, Result.Context->getSourceManager(),
+        Result.Context->getLangOpts()));
+  };
+}
+
 RangeSelector tooling::node(std::string ID) {
   return [ID](const MatchResult &Result) -> Expected<CharSourceRange> {
     Expected<DynTypedNode> Node = getNode(Result.Nodes, ID);

Modified: cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp?rev=361955&r1=361954&r2=361955&view=diff
==============================================================================
--- cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp (original)
+++ cfe/trunk/unittests/Tooling/RangeSelectorTest.cpp Wed May 29 05:40:36 2019
@@ -21,13 +21,15 @@ using namespace tooling;
 using namespace ast_matchers;
 
 namespace {
-using ::testing::AllOf;
-using ::testing::HasSubstr;
-using MatchResult = MatchFinder::MatchResult;
 using ::llvm::Expected;
 using ::llvm::Failed;
 using ::llvm::HasValue;
 using ::llvm::StringError;
+using ::testing::AllOf;
+using ::testing::HasSubstr;
+using ::testing::Property;
+
+using MatchResult = MatchFinder::MatchResult;
 
 struct TestMatch {
   // The AST unit from which `result` is built. We bundle it because it backs
@@ -117,6 +119,55 @@ TEST(RangeSelectorTest, UnboundNode) {
                        Failed<StringError>(withUnboundNodeMessage()));
 }
 
+MATCHER_P(EqualsCharSourceRange, Range, "") {
+  return Range.getAsRange() == arg.getAsRange() &&
+         Range.isTokenRange() == arg.isTokenRange();
+}
+
+// FIXME: here and elsewhere: use llvm::Annotations library to explicitly mark
+// points and ranges of interest, enabling more readable tests.
+TEST(RangeSelectorTest, BeforeOp) {
+  StringRef Code = R"cc(
+    int f(int x, int y, int z) { return 3; }
+    int g() { return f(/* comment */ 3, 7 /* comment */, 9); }
+  )cc";
+  StringRef Call = "call";
+  TestMatch Match = matchCode(Code, callExpr().bind(Call));
+  const auto* E = Match.Result.Nodes.getNodeAs<Expr>(Call);
+  assert(E != nullptr);
+  auto ExprBegin = E->getSourceRange().getBegin();
+  EXPECT_THAT_EXPECTED(
+      before(node(Call))(Match.Result),
+      HasValue(EqualsCharSourceRange(
+          CharSourceRange::getCharRange(ExprBegin, ExprBegin))));
+}
+
+TEST(RangeSelectorTest, AfterOp) {
+  StringRef Code = R"cc(
+    int f(int x, int y, int z) { return 3; }
+    int g() { return f(/* comment */ 3, 7 /* comment */, 9); }
+  )cc";
+  StringRef Call = "call";
+  TestMatch Match = matchCode(Code, callExpr().bind(Call));
+  const auto* E = Match.Result.Nodes.getNodeAs<Expr>(Call);
+  assert(E != nullptr);
+  const SourceRange Range = E->getSourceRange();
+  // The end token, a right paren, is one character wide, so advance by one,
+  // bringing us to the semicolon.
+  const SourceLocation SemiLoc = Range.getEnd().getLocWithOffset(1);
+  const auto ExpectedAfter = CharSourceRange::getCharRange(SemiLoc, SemiLoc);
+
+  // Test with a char range.
+  auto CharRange = CharSourceRange::getCharRange(Range.getBegin(), SemiLoc);
+  EXPECT_THAT_EXPECTED(after(charRange(CharRange))(Match.Result),
+                       HasValue(EqualsCharSourceRange(ExpectedAfter)));
+
+  // Test with a token range.
+  auto TokenRange = CharSourceRange::getTokenRange(Range);
+  EXPECT_THAT_EXPECTED(after(charRange(TokenRange))(Match.Result),
+                       HasValue(EqualsCharSourceRange(ExpectedAfter)));
+}
+
 TEST(RangeSelectorTest, RangeOp) {
   StringRef Code = R"cc(
     int f(int x, int y, int z) { return 3; }




More information about the cfe-commits mailing list