This revision was landed with ongoing or failed builds. This revision was automatically updated to reflect the committed changes. Closed by commit rGa7101450a42e: [ASTMatchers] Add mapAnyOf matcher (authored by stephenkelly).
Changed prior to commit: https://reviews.llvm.org/D94127?vs=316712&id=317168#toc Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D94127/new/ https://reviews.llvm.org/D94127 Files: clang/docs/LibASTMatchersReference.html clang/docs/tools/dump_ast_matchers.py clang/include/clang/ASTMatchers/ASTMatchers.h clang/include/clang/ASTMatchers/ASTMatchersInternal.h clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ 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; Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -1987,6 +1987,68 @@ llvm::Regex::RegexFlags Flags, StringRef MatcherID); +template <typename F, typename Tuple, std::size_t... I> +constexpr auto applyMatcherImpl(F &&f, Tuple &&args, + std::index_sequence<I...>) { + return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...); +} + +template <typename F, typename Tuple> +constexpr auto applyMatcher(F &&f, Tuple &&args) { + return applyMatcherImpl( + std::forward<F>(f), std::forward<Tuple>(args), + std::make_index_sequence< + std::tuple_size<typename std::decay<Tuple>::type>::value>()); +} + +template <typename T, bool IsBaseOf, typename Head, typename Tail> +struct GetCladeImpl { + using Type = Head; +}; +template <typename T, typename Head, typename Tail> +struct GetCladeImpl<T, false, Head, Tail> + : GetCladeImpl<T, std::is_base_of<typename Tail::head, T>::value, + typename Tail::head, typename Tail::tail> {}; + +template <typename T, typename... U> +struct GetClade : GetCladeImpl<T, false, T, AllNodeBaseTypes> {}; + +template <typename CladeType, typename... MatcherTypes> +struct MapAnyOfMatcherImpl { + + template <typename... InnerMatchers> + BindableMatcher<CladeType> + operator()(InnerMatchers &&... InnerMatcher) const { + // TODO: Use std::apply from c++17 + return VariadicAllOfMatcher<CladeType>()(applyMatcher( + internal::VariadicOperatorMatcherFunc< + 0, std::numeric_limits<unsigned>::max()>{ + internal::DynTypedMatcher::VO_AnyOf}, + applyMatcher( + [&](auto... Matcher) { + return std::make_tuple(Matcher( + std::forward<decltype(InnerMatcher)>(InnerMatcher)...)...); + }, + std::tuple< + VariadicDynCastAllOfMatcher<CladeType, MatcherTypes>...>()))); + } +}; + +template <typename... MatcherTypes> +using MapAnyOfMatcher = + MapAnyOfMatcherImpl<typename GetClade<MatcherTypes...>::Type, + MatcherTypes...>; + +template <typename... MatcherTypes> struct MapAnyOfHelper { + using CladeType = typename GetClade<MatcherTypes...>::Type; + + MapAnyOfMatcher<MatcherTypes...> with; + + operator BindableMatcher<CladeType>() const { return with(); } + + Matcher<CladeType> bind(StringRef ID) const { return with().bind(ID); } +}; + } // namespace internal } // namespace ast_matchers Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -847,6 +847,12 @@ TK, InnerMatcher); } +template <typename... T> +internal::Matcher<typename internal::GetClade<T...>::Type> +traverse(TraversalKind TK, const internal::MapAnyOfHelper<T...> &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 <typename T, typename... U> +auto mapAnyOf(internal::VariadicDynCastAllOfMatcher<T, U> const &...) { + return internal::MapAnyOfHelper<U...>(); +} + /// Matches unary expressions that have a specific type of argument. /// /// Given Index: clang/docs/tools/dump_ast_matchers.py =================================================================== --- clang/docs/tools/dump_ast_matchers.py +++ 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 = "<em>unspecified</em>" + 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: Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -2593,6 +2593,29 @@ </pre></td></tr> +<tr><td><em>unspecified</em></td><td class="name" onclick="toggle('mapAnyOf0')"><a name="mapAnyOf0Anchor">mapAnyOf</a></td><td>nodeMatcherFunction...</td></tr> +<tr><td colspan="4" class="doc" id="mapAnyOf0"><pre>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 +</pre></td></tr> + + <tr><td>Matcher<*></td><td class="name" onclick="toggle('unless0')"><a name="unless0Anchor">unless</a></td><td>Matcher<*></td></tr> <tr><td colspan="4" class="doc" id="unless0"><pre>Matches if the provided matcher does not match. @@ -2980,7 +3003,7 @@ <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXDependentScopeMemberExpr.html">CXXDependentScopeMemberExpr</a>></td><td class="name" onclick="toggle('hasMemberName0')"><a name="hasMemberName0Anchor">hasMemberName</a></td><td>std::string N</td></tr> -<tr><td colspan="4" class="doc" id="hasMemberName0"><pre>Matches template-dependent, but known, member names +<tr><td colspan="4" class="doc" id="hasMemberName0"><pre>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.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits