diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -2593,6 +2593,29 @@ +
Matches any of the NodeMatchers with InnerMatchers nested within + +Given + if (true); + for (; true; ); +with the matcher + mapAnyOf(ifStmt, forStmt).with( + hasCondition(cxxBoolLiteralExpr(equals(true))) + ).bind("trueCond") +matches the if and the for. It is equivalent to: + auto trueCond = hasCondition(cxxBoolLiteralExpr(equals(true))); + anyOf( + ifStmt(trueCond).bind("trueCond"), + forStmt(trueCond).bind("trueCond") + ); + +The with() chain-call accepts zero or more matchers which are combined +as-if with allOf() in each of the node matchers. +Usable as: Any Matcher +
Matches if the provided matcher does not match. @@ -2980,7 +3003,7 @@- Matcher<CXXDependentScopeMemberExpr> hasMemberName std::string N Matches template-dependent, but known, member names + Matches template-dependent, but known, member names. In template declarations, dependent members are not resolved and so can not be matched to particular named declarations. diff --git a/clang/docs/tools/dump_ast_matchers.py b/clang/docs/tools/dump_ast_matchers.py --- a/clang/docs/tools/dump_ast_matchers.py +++ b/clang/docs/tools/dump_ast_matchers.py @@ -119,8 +119,15 @@ ids[name] += 1 args = unify_arguments(args) result_type = unify_type(result_type) + + docs_result_type = esc('Matcher<%s>' % result_type); + + if name == 'mapAnyOf': + args = "nodeMatcherFunction..." + docs_result_type = "unspecified" + matcher_html = TD_TEMPLATE % { - 'result': esc('Matcher<%s>' % result_type), + 'result': docs_result_type, 'name': name, 'args': esc(args), 'comment': esc(strip_doxygen(comment)), @@ -135,7 +142,7 @@ # exclude known narrowing matchers that also take other matchers as # arguments. elif ('Matcher<' not in args or - name in ['allOf', 'anyOf', 'anything', 'unless']): + name in ['allOf', 'anyOf', 'anything', 'unless', 'mapAnyOf']): dict = narrowing_matchers lookup = result_type + name + esc(args) else: diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -847,6 +847,12 @@ TK, InnerMatcher); } +template+internal::Matcher ::Type> +traverse(TraversalKind TK, const internal::MapAnyOfHelper &InnerMatcher) { + return traverse(TK, InnerMatcher.with()); +} + /// Matches expressions that match InnerMatcher after any implicit AST /// nodes are stripped off. /// @@ -2693,6 +2699,36 @@ UnaryExprOrTypeTraitExpr> unaryExprOrTypeTraitExpr; +/// Matches any of the \p NodeMatchers with InnerMatchers nested within +/// +/// Given +/// \code +/// if (true); +/// for (; true; ); +/// \endcode +/// with the matcher +/// \code +/// mapAnyOf(ifStmt, forStmt).with( +/// hasCondition(cxxBoolLiteralExpr(equals(true))) +/// ).bind("trueCond") +/// \endcode +/// matches the \c if and the \c for. It is equivalent to: +/// \code +/// auto trueCond = hasCondition(cxxBoolLiteralExpr(equals(true))); +/// anyOf( +/// ifStmt(trueCond).bind("trueCond"), +/// forStmt(trueCond).bind("trueCond") +/// ); +/// \endcode +/// +/// The with() chain-call accepts zero or more matchers which are combined +/// as-if with allOf() in each of the node matchers. +/// Usable as: Any Matcher +template +auto mapAnyOf(internal::VariadicDynCastAllOfMatcher const &...) { + return internal::MapAnyOfHelper (); +} + /// Matches unary expressions that have a specific type of argument. /// /// Given diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1987,6 +1987,68 @@ llvm::Regex::RegexFlags Flags, StringRef MatcherID); +template +constexpr auto applyMatcherImpl(F &&f, Tuple &&args, + std::index_sequence ) { + return std::forward (f)(std::get(std::forward (args))...); +} + +template +constexpr auto applyMatcher(F &&f, Tuple &&args) { + return applyMatcherImpl( + std::forward (f), std::forward (args), + std::make_index_sequence< + std::tuple_size ::type>::value>()); +} + +template +struct GetCladeImpl { + using Type = Head; +}; +template +struct GetCladeImpl + : GetCladeImpl ::value, + typename Tail::head, typename Tail::tail> {}; + +template +struct GetClade : GetCladeImpl {}; + +template +struct MapAnyOfMatcherImpl { + + template + BindableMatcher + operator()(InnerMatchers &&... InnerMatcher) const { + // TODO: Use std::apply from c++17 + return VariadicAllOfMatcher ()(applyMatcher( + internal::VariadicOperatorMatcherFunc< + 0, std::numeric_limits ::max()>{ + internal::DynTypedMatcher::VO_AnyOf}, + applyMatcher( + [&](auto... Matcher) { + return std::make_tuple(Matcher( + std::forward (InnerMatcher)...)...); + }, + std::tuple< + VariadicDynCastAllOfMatcher ...>()))); + } +}; + +template +using MapAnyOfMatcher = + MapAnyOfMatcherImpl ::Type, + MatcherTypes...>; + +template struct MapAnyOfHelper { + using CladeType = typename GetClade ::Type; + + MapAnyOfMatcher with; + + operator BindableMatcher () const { return with(); } + + Matcher bind(StringRef ID) const { return with().bind(ID); } +}; + } // namespace internal } // namespace ast_matchers diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -465,6 +465,59 @@ cxxCatchStmt(anyOf(hasDescendant(varDecl()), isCatchAll())))); } +TEST_P(ASTMatchersTest, MapAnyOf) { + if (!GetParam().isCXX()) { + return; + } + + StringRef Code = R"cpp( +void F() { + if (true) {} + for ( ; false; ) {} +} +)cpp"; + + auto trueExpr = cxxBoolLiteral(equals(true)); + auto falseExpr = cxxBoolLiteral(equals(false)); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(ifStmt, forStmt).with(hasCondition(trueExpr))))); + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(ifStmt, forStmt).with(hasCondition(falseExpr))))); + + Code = R"cpp( +void func(bool b) {} +struct S { + S(bool b) {} +}; +void F() { + func(false); + S s(true); +} +)cpp"; + EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(callExpr, cxxConstructExpr) + .with(hasArgument(0, trueExpr))))); + EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(callExpr, cxxConstructExpr) + .with(hasArgument(0, falseExpr))))); + + EXPECT_TRUE( + matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(callExpr, cxxConstructExpr) + .with(hasArgument(0, expr()), + hasDeclaration(functionDecl()))))); + + EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(callExpr, cxxConstructExpr)))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + mapAnyOf(callExpr, cxxConstructExpr).bind("call")))); +} + TEST_P(ASTMatchersTest, IsDerivedFrom) { if (!GetParam().isCXX()) { return;