ymandel created this revision.
ymandel added a reviewer: gribozavr.
Herald added a project: clang.

Adds two new combinators and corresponding tests to the RangeSelector library.

- `ifBound` -- conditional evaluation of range-selectors, based on whether a 
given node id is bound in the match.
- `elseBranch` -- selects the source range of the else and its statement.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D67621

Files:
  clang/include/clang/Tooling/Refactoring/RangeSelector.h
  clang/lib/Tooling/Refactoring/RangeSelector.cpp
  clang/unittests/Tooling/RangeSelectorTest.cpp

Index: clang/unittests/Tooling/RangeSelectorTest.cpp
===================================================================
--- clang/unittests/Tooling/RangeSelectorTest.cpp
+++ clang/unittests/Tooling/RangeSelectorTest.cpp
@@ -520,6 +520,60 @@
                        Failed<StringError>(withTypeErrorMessage("stmt")));
 }
 
+TEST(RangeSelectorTest, IfBoundOpBound) {
+  StringRef Code = R"cc(
+    int f() {
+      return 3 + 5;
+    }
+  )cc";
+  StringRef ID = "id", Op = "op";
+  TestMatch Match =
+      matchCode(Code, binaryOperator(hasLHS(expr().bind(ID))).bind(Op));
+  EXPECT_THAT_EXPECTED(select(ifBound(ID, node(ID), node(Op)), Match),
+                       HasValue("3"));
+}
+
+TEST(RangeSelectorTest, IfBoundOpUnbound) {
+  StringRef Code = R"cc(
+    int f() {
+      return 3 + 5;
+    }
+  )cc";
+  StringRef ID = "id", Op = "op";
+  TestMatch Match = matchCode(Code, binaryOperator().bind(Op));
+  EXPECT_THAT_EXPECTED(select(ifBound(ID, node(ID), node(Op)), Match),
+                       HasValue("3 + 5"));
+}
+
+TEST(RangeSelectorTest, ElseBranchOpSingleStatement) {
+  StringRef Code = R"cc(
+    int f() {
+      int x = 0;
+      if (true) x = 3;
+      else x = 4;
+      return x + 5;
+    }
+  )cc";
+  StringRef ID = "id";
+  TestMatch Match = matchCode(Code, ifStmt().bind(ID));
+  EXPECT_THAT_EXPECTED(select(elseBranch(ID), Match), HasValue("else x = 4;"));
+}
+
+TEST(RangeSelectorTest, ElseBranchOpCompoundStatement) {
+  StringRef Code = R"cc(
+    int f() {
+      int x = 0;
+      if (true) x = 3;
+      else { x = 4; }
+      return x + 5;
+    }
+  )cc";
+  StringRef ID = "id";
+  TestMatch Match = matchCode(Code, ifStmt().bind(ID));
+  EXPECT_THAT_EXPECTED(select(elseBranch(ID), Match),
+                       HasValue("else { x = 4; }"));
+}
+
 // Tests case where the matched node is the complete expanded text.
 TEST(RangeSelectorTest, ExpansionOp) {
   StringRef Code = R"cc(
Index: clang/lib/Tooling/Refactoring/RangeSelector.cpp
===================================================================
--- clang/lib/Tooling/Refactoring/RangeSelector.cpp
+++ clang/lib/Tooling/Refactoring/RangeSelector.cpp
@@ -219,6 +219,9 @@
 }
 
 namespace {
+// FIXME: make this available in the public API for users to easily create their
+// own selectors.
+
 // Creates a selector from a range-selection function \p Func, which selects a
 // range that is relative to a bound node id.  \c T is the node type expected by
 // \p Func.
@@ -286,6 +289,19 @@
   return RelativeSelector<InitListExpr, getElementsRange>(std::move(ID));
 }
 
+namespace {
+// Returns the range of the else branch, including the `else` keyword.
+CharSourceRange getElseRange(const MatchResult &Result, const IfStmt &S) {
+  return maybeExtendRange(
+      CharSourceRange::getTokenRange(S.getElseLoc(), S.getEndLoc()),
+      tok::TokenKind::semi, *Result.Context);
+}
+} // namespace
+
+RangeSelector tooling::elseBranch(std::string ID) {
+  return RelativeSelector<IfStmt, getElseRange>(std::move(ID));
+}
+
 RangeSelector tooling::expansion(RangeSelector S) {
   return [S](const MatchResult &Result) -> Expected<CharSourceRange> {
     Expected<CharSourceRange> SRange = S(Result);
@@ -294,3 +310,11 @@
     return Result.SourceManager->getExpansionRange(*SRange);
   };
 }
+
+RangeSelector tooling::ifBound(std::string ID, RangeSelector TrueSelector,
+                               RangeSelector FalseSelector) {
+  return [=](const MatchResult &Result) {
+    auto &Map = Result.Nodes.getMap();
+    return (Map.find(ID) != Map.end() ? TrueSelector : FalseSelector)(Result);
+  };
+}
Index: clang/include/clang/Tooling/Refactoring/RangeSelector.h
===================================================================
--- clang/include/clang/Tooling/Refactoring/RangeSelector.h
+++ clang/include/clang/Tooling/Refactoring/RangeSelector.h
@@ -83,6 +83,15 @@
 /// source), if `S` is an expansion, and `S` itself, otherwise.  Corresponds to
 /// `SourceManager::getExpansionRange`.
 RangeSelector expansion(RangeSelector S);
+
+/// Chooses between the two selectors, based on whether \p ID is bound in the
+/// match.
+RangeSelector ifBound(std::string ID, RangeSelector TrueSelector,
+                      RangeSelector FalseSelector);
+
+/// Gets the range of the else branch of an if statement, starting from the
+/// \c else keyword.  \param ID must be bound to an `ifStmt`.
+RangeSelector elseBranch(std::string ID);
 } // namespace tooling
 } // namespace clang
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to