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
// (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
}
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.
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);
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);
+ };
+}
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(
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