[libTooling] Add `ifBound`, `elseBranch` RangeSelector combinators.
authorYitzhak Mandelbaum <yitzhakm@google.com>
Fri, 20 Sep 2019 17:11:03 +0000 (17:11 +0000)
committerYitzhak Mandelbaum <yitzhakm@google.com>
Fri, 20 Sep 2019 17:11:03 +0000 (17:11 +0000)
Summary:
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.

Reviewers: gribozavr

Subscribers: cfe-commits

Tags: #clang

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

llvm-svn: 372410

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

index b117e4d..e5fe051 100644 (file)
@@ -79,10 +79,19 @@ RangeSelector statements(std::string ID);
 // (all source between the braces).
 RangeSelector initListElements(std::string ID);
 
+/// Given an \IfStmt (bound to \p ID), selects the range of the else branch,
+/// starting from the \c else keyword.
+RangeSelector elseBranch(std::string ID);
+
 /// Selects the range from which `S` was expanded (possibly along with other
 /// 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);
 } // namespace tooling
 } // namespace clang
 
index 768c02e..ae55698 100644 (file)
@@ -219,6 +219,9 @@ RangeSelector tooling::name(std::string ID) {
 }
 
 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 @@ RangeSelector tooling::initListElements(std::string ID) {
   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 @@ RangeSelector tooling::expansion(RangeSelector S) {
     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 38c15be..58ce63c 100644 (file)
@@ -520,6 +520,35 @@ TEST(RangeSelectorTest, ElementsOpErrors) {
                        Failed<StringError>(withTypeErrorMessage("stmt")));
 }
 
+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(
@@ -546,4 +575,29 @@ TEST(RangeSelectorTest, ExpansionOpPartial) {
                        HasValue("BADDECL(x * x)"));
 }
 
+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"));
+}
+
 } // namespace