From e810e95e4bb908d1c8844e2c6f7da999732cabc9 Mon Sep 17 00:00:00 2001
From: Stephen Kelly
Matches nodes which can be used with binary operators. + +The code + var1 != var2; +might be represented in the clang AST as a binaryOperator or a +cxxOperatorCallExpr, depending on + +* whether the types of var1 and var2 are fundamental (binaryOperator) or at + least one is a class type (cxxOperatorCallExpr) +* whether the code appears in a template declaration, if at least one of the + vars is a dependent-type (binaryOperator) + +This matcher elides details in places where the matchers for the nodes are +compatible. + +Given + binaryOperation( + hasOperatorName("!="), + hasLHS(expr().bind("lhs")), + hasRHS(expr().bind("rhs")) + ) +matches each use of "!=" in: + struct S{ + bool operator!=(const S&) const; + }; + + void foo() + { + 1 != 2; + S() != S(); + } + + template<typename T> + void templ() + { + 1 != 2; + T() != S(); + } +
Matches if any of the given matchers matches. diff --git a/clang/docs/tools/dump_ast_matchers.py b/clang/docs/tools/dump_ast_matchers.py index d3aff1284f6..18afbdd36c6 100755 --- a/clang/docs/tools/dump_ast_matchers.py +++ b/clang/docs/tools/dump_ast_matchers.py @@ -379,6 +379,14 @@ Flags can be combined with '|' example \"IgnoreCase | BasicRegex\" add_matcher('*', name, 'Matcher<*>, ..., Matcher<*>', comment) return + m = re.match( + r"""^.*MapAnyOfMatcher<.*>\s* + ([a-zA-Z]*);$""", + declaration, flags=re.X) + if m: + name = m.groups()[0] + add_matcher('*', name, 'Matcher<*>...Matcher<*>', comment) + return # Parse free standing matcher functions, like: # MatcherName(Matcher InnerMatcher) { diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index e81ac4cb7bf..8140e20c9ca 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -1971,6 +1971,8 @@ extern const internal::VariadicDynCastAllOfMatcher /// ostream &o; int b = 1, c = 1; /// o << b << c; /// \endcode +/// See also the binaryOperation() matcher for more-general matching of binary +/// uses of this AST node. extern const internal::VariadicDynCastAllOfMatcher cxxOperatorCallExpr; @@ -2393,6 +2395,7 @@ extern const internal::VariadicDynCastAllOfMatcher stmtExpr; /// \code /// !(a || b) /// \endcode +/// See also the binaryOperation() matcher for more-general matching. extern const internal::VariadicDynCastAllOfMatcher binaryOperator; @@ -2729,6 +2732,53 @@ auto mapAnyOf(internal::VariadicDynCastAllOfMatcher const &...) { return internal::MapAnyOfHelper (); } +/// Matches nodes which can be used with binary operators. +/// +/// The code +/// \code +/// var1 != var2; +/// \endcode +/// might be represented in the clang AST as a binaryOperator or a +/// cxxOperatorCallExpr, depending on +/// +/// * whether the types of var1 and var2 are fundamental (binaryOperator) or at +/// least one is a class type (cxxOperatorCallExpr) +/// * whether the code appears in a template declaration, if at least one of the +/// vars is a dependent-type (binaryOperator) +/// +/// This matcher elides details in places where the matchers for the nodes are +/// compatible. +/// +/// Given +/// \code +/// binaryOperation( +/// hasOperatorName("!="), +/// hasLHS(expr().bind("lhs")), +/// hasRHS(expr().bind("rhs")) +/// ) +/// \endcode +/// matches each use of "!=" in: +/// \code +/// struct S{ +/// bool operator!=(const S&) const; +/// }; +/// +/// void foo() +/// { +/// 1 != 2; +/// S() != S(); +/// } +/// +/// template +/// void templ() +/// { +/// 1 != 2; +/// T() != S(); +/// } +/// \endcode +extern const internal::MapAnyOfMatcher + binaryOperation; + /// Matches unary expressions that have a specific type of argument. /// /// Given diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp index eb0fffcc3c3..36917cf0db4 100644 --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -919,6 +919,8 @@ const internal::VariadicDynCastAllOfMatcher atomicExpr; const internal::VariadicDynCastAllOfMatcher stmtExpr; const internal::VariadicDynCastAllOfMatcher binaryOperator; +const internal::MapAnyOfMatcher + binaryOperation; const internal::VariadicDynCastAllOfMatcher unaryOperator; const internal::VariadicDynCastAllOfMatcher conditionalOperator; diff --git a/clang/lib/ASTMatchers/Dynamic/Marshallers.h b/clang/lib/ASTMatchers/Dynamic/Marshallers.h index f53df1f6528..23e26dcd9db 100644 --- a/clang/lib/ASTMatchers/Dynamic/Marshallers.h +++ b/clang/lib/ASTMatchers/Dynamic/Marshallers.h @@ -925,6 +925,50 @@ private: const StringRef MatcherName; }; +template +class MapAnyOfMatcherDescriptor : public MatcherDescriptor { + std::vector Funcs; + +public: + MapAnyOfMatcherDescriptor(StringRef MatcherName) + : Funcs{DynCastAllOfMatcherDescriptor( + ast_matchers::internal::VariadicDynCastAllOfMatcher {}, + MatcherName)...} {} + + VariantMatcher create(SourceRange NameRange, ArrayRef Args, + Diagnostics *Error) const override { + std::vector InnerArgs; + + for (auto const &F : Funcs) { + InnerArgs.push_back(F.create(NameRange, Args, Error)); + if (!Error->errors().empty()) + return {}; + } + return VariantMatcher::SingleMatcher( + ast_matchers::internal::BindableMatcher ( + VariantMatcher::VariadicOperatorMatcher( + ast_matchers::internal::DynTypedMatcher::VO_AnyOf, + std::move(InnerArgs)) + .getTypedMatcher ())); + } + + bool isVariadic() const override { return true; } + unsigned getNumArgs() const override { return 0; } + + void getArgKinds(ASTNodeKind ThisKind, unsigned, + std::vector &Kinds) const override { + Kinds.push_back(ThisKind); + } + + bool isConvertibleTo(ASTNodeKind Kind, unsigned *Specificity, + ASTNodeKind *LeastDerivedKind) const override { + return llvm::all_of(Funcs, [=](const auto &F) { + return F.isConvertibleTo(Kind, Specificity, LeastDerivedKind); + }); + } +}; + /// Helper functions to select the appropriate marshaller functions. /// They detect the number of arguments, arguments types and return type. @@ -1029,6 +1073,14 @@ std::unique_ptr makeMatcherAutoMarshall( MinCount, MaxCount, Func.Op, MatcherName); } +template +std::unique_ptr makeMatcherAutoMarshall( + ast_matchers::internal::MapAnyOfMatcherImpl , + StringRef MatcherName) { + return std::make_unique >( + MatcherName); +} + } // namespace internal } // namespace dynamic } // namespace ast_matchers diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp index 390d883dc5e..9dc92a881cb 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -143,6 +143,7 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(autoreleasePoolStmt) REGISTER_MATCHER(binaryConditionalOperator); REGISTER_MATCHER(binaryOperator); + REGISTER_MATCHER(binaryOperation); REGISTER_MATCHER(blockDecl); REGISTER_MATCHER(blockExpr); REGISTER_MATCHER(blockPointerType); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index 9e011892f6e..ae57383273e 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -470,6 +470,10 @@ TEST_P(ASTMatchersTest, MapAnyOf) { return; } + if (GetParam().hasDelayedTemplateParsing()) { + return; + } + StringRef Code = R"cpp( void F() { if (true) {} @@ -556,6 +560,15 @@ void opFree() if (s1 != s2) return; } + +template +void templ() +{ + T s1; + T s2; + if (s1 != s2) + return; +} )cpp"; EXPECT_TRUE(matches( @@ -669,6 +682,38 @@ void opFree() .with(hasAnyOperatorName("==", "!="), forFunction(functionDecl(hasName("opFree"))))))); + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + binaryOperation( + hasOperatorName("!="), + forFunction(functionDecl(hasName("binop"))), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + binaryOperation( + hasOperatorName("!="), + forFunction(functionDecl(hasName("opMem"))), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + binaryOperation( + hasOperatorName("!="), + forFunction(functionDecl(hasName("opFree"))), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); + + EXPECT_TRUE(matches( + Code, traverse(TK_IgnoreUnlessSpelledInSource, + binaryOperation( + hasOperatorName("!="), + forFunction(functionDecl(hasName("templ"))), + hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), + hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); + Code = R"cpp( struct HasOpBangMem { -- 2.11.0