summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYitzhak Mandelbaum <yitzhakm@google.com>2019-09-20 17:11:03 +0000
committerYitzhak Mandelbaum <yitzhakm@google.com>2019-09-20 17:11:03 +0000
commit2ecd583adaae907d88e4f544ed62939a8103e968 (patch)
tree56cf60cdc5649431a4bdc9c8f640f9798728cf94
parent5765299b76e9098d2c84c69178b4ccfdb6c23bc0 (diff)
downloadclang-2ecd583adaae907d88e4f544ed62939a8103e968.tar.gz
[libTooling] Add `ifBound`, `elseBranch` RangeSelector combinators.
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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@372410 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Tooling/Refactoring/RangeSelector.h9
-rw-r--r--lib/Tooling/Refactoring/RangeSelector.cpp24
-rw-r--r--unittests/Tooling/RangeSelectorTest.cpp54
3 files changed, 87 insertions, 0 deletions
diff --git a/include/clang/Tooling/Refactoring/RangeSelector.h b/include/clang/Tooling/Refactoring/RangeSelector.h
index b117e4d82a..e5fe051413 100644
--- a/include/clang/Tooling/Refactoring/RangeSelector.h
+++ b/include/clang/Tooling/Refactoring/RangeSelector.h
@@ -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
diff --git a/lib/Tooling/Refactoring/RangeSelector.cpp b/lib/Tooling/Refactoring/RangeSelector.cpp
index 768c02e227..ae55698189 100644
--- a/lib/Tooling/Refactoring/RangeSelector.cpp
+++ b/lib/Tooling/Refactoring/RangeSelector.cpp
@@ -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);
+ };
+}
diff --git a/unittests/Tooling/RangeSelectorTest.cpp b/unittests/Tooling/RangeSelectorTest.cpp
index 38c15be00c..58ce63cbd7 100644
--- a/unittests/Tooling/RangeSelectorTest.cpp
+++ b/unittests/Tooling/RangeSelectorTest.cpp
@@ -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