From 2ecd583adaae907d88e4f544ed62939a8103e968 Mon Sep 17 00:00:00 2001 From: Yitzhak Mandelbaum Date: Fri, 20 Sep 2019 17:11:03 +0000 Subject: [PATCH] [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 --- .../clang/Tooling/Refactoring/RangeSelector.h | 9 ++++ lib/Tooling/Refactoring/RangeSelector.cpp | 24 +++++++++ unittests/Tooling/RangeSelectorTest.cpp | 54 +++++++++++++++++++ 3 files changed, 87 insertions(+) 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(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(std::move(ID)); +} + RangeSelector tooling::expansion(RangeSelector S) { return [S](const MatchResult &Result) -> Expected { Expected 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(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 -- 2.40.0