diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -649,6 +649,30 @@ +
Matches decomposition-declarations. + +Examples matches the declaration node with foo and bar, but not +number. +(matcher = declStmt(has(decompositionDecl()))) + + int number = 42; + auto [foo, bar] = std::make_pair{42, 42}; +
Matches decomposition-declarations. + +Examples matches the declaration node with foo and bar, but not +number. +(matcher = declStmt(has(decompositionDecl()))) + + int number = 42; + auto [foo, bar] = std::make_pair{42, 42}; +
Same as nestedNameSpecifier but matches NestedNameSpecifierLoc.
Matches all arguments and their respective types for a CallExpr or +CXXConstructExpr. It is very similar to forEachArgumentWithParam but +it works on calls through function pointers as well. + +The difference is, that function pointers do not provide access to a +ParmVarDecl, but only the QualType for each argument. + +Given + void f(int i); + int y; + f(y); + void (*f_ptr)(int) = f; + f_ptr(y); +callExpr( + forEachArgumentWithParamType( + declRefExpr(to(varDecl(hasName("y")))), + qualType(isInteger()).bind("type) +)) + matches f(y) and f_ptr(y) +with declRefExpr(...) + matching int y +and qualType(...) + matching int +
Matches all arguments and their respective types for a CallExpr or +CXXConstructExpr. It is very similar to forEachArgumentWithParam but +it works on calls through function pointers as well. + +The difference is, that function pointers do not provide access to a +ParmVarDecl, but only the QualType for each argument. + +Given + void f(int i); + int y; + f(y); + void (*f_ptr)(int) = f; + f_ptr(y); +callExpr( + forEachArgumentWithParamType( + declRefExpr(to(varDecl(hasName("y")))), + qualType(isInteger()).bind("type) +)) + matches f(y) and f_ptr(y) +with declRefExpr(...) + matching int y +and qualType(...) + matching int +
Matches any argument of a call expression or a constructor call expression, or an ObjC-message-send expression. @@ -5850,6 +5928,60 @@
Matches all arguments and their respective types for a CallExpr or +CXXConstructExpr. It is very similar to forEachArgumentWithParam but +it works on calls through function pointers as well. + +The difference is, that function pointers do not provide access to a +ParmVarDecl, but only the QualType for each argument. + +Given + void f(int i); + int y; + f(y); + void (*f_ptr)(int) = f; + f_ptr(y); +callExpr( + forEachArgumentWithParamType( + declRefExpr(to(varDecl(hasName("y")))), + qualType(isInteger()).bind("type) +)) + matches f(y) and f_ptr(y) +with declRefExpr(...) + matching int y +and qualType(...) + matching int +
Matches all arguments and their respective types for a CallExpr or +CXXConstructExpr. It is very similar to forEachArgumentWithParam but +it works on calls through function pointers as well. + +The difference is, that function pointers do not provide access to a +ParmVarDecl, but only the QualType for each argument. + +Given + void f(int i); + int y; + f(y); + void (*f_ptr)(int) = f; + f_ptr(y); +callExpr( + forEachArgumentWithParamType( + declRefExpr(to(varDecl(hasName("y")))), + qualType(isInteger()).bind("type) +)) + matches f(y) and f_ptr(y) +with declRefExpr(...) + matching int y +and qualType(...) + matching int +
Matches any argument of a call expression or a constructor call expression, or an ObjC-message-send expression. 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 @@ -334,6 +334,19 @@ /// \endcode extern const internal::VariadicAllOfMatcherdecl; +/// Matches decomposition-declarations. +/// +/// Examples matches the declaration node with \c foo and \c bar, but not +/// \c number. +/// (matcher = declStmt(has(decompositionDecl()))) +/// +/// \code +/// int number = 42; +/// auto [foo, bar] = std::make_pair{42, 42}; +/// \endcode +extern const internal::VariadicAllOfMatcher + decompositionDecl; + /// Matches a declaration of a linkage specification. /// /// Given @@ -4349,6 +4362,103 @@ return Matched; } +/// Matches all arguments and their respective types for a \c CallExpr or +/// \c CXXConstructExpr. It is very similar to \c forEachArgumentWithParam but +/// it works on calls through function pointers as well. +/// +/// The difference is, that function pointers do not provide access to a +/// \c ParmVarDecl, but only the \c QualType for each argument. +/// +/// Given +/// \code +/// void f(int i); +/// int y; +/// f(y); +/// void (*f_ptr)(int) = f; +/// f_ptr(y); +/// \endcode +/// callExpr( +/// forEachArgumentWithParamType( +/// declRefExpr(to(varDecl(hasName("y")))), +/// qualType(isInteger()).bind("type) +/// )) +/// matches f(y) and f_ptr(y) +/// with declRefExpr(...) +/// matching int y +/// and qualType(...) +/// matching int +AST_POLYMORPHIC_MATCHER_P2(forEachArgumentWithParamType, + AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr, + CXXConstructExpr), + internal::Matcher , ArgMatcher, + internal::Matcher , ParamMatcher) { + BoundNodesTreeBuilder Result; + // The first argument of an overloaded member operator is the implicit object + // argument of the method which should not be matched against a parameter, so + // we skip over it here. + BoundNodesTreeBuilder Matches; + unsigned ArgIndex = cxxOperatorCallExpr(callee(cxxMethodDecl())) + .matches(Node, Finder, &Matches) + ? 1 + : 0; + + const FunctionProtoType *FProto = nullptr; + + if (const auto *Call = dyn_cast (&Node)) { + if (const auto *Value = + dyn_cast_or_null (Call->getCalleeDecl())) { + QualType QT = Value->getType().getCanonicalType(); + + // This does not necessarily lead to a `FunctionProtoType`, + // e.g. K&R functions do not have a function prototype. + if (QT->isFunctionPointerType()) + FProto = QT->getPointeeType()->getAs (); + + if (QT->isMemberFunctionPointerType()) { + const auto *MP = QT->getAs (); + assert(MP && "Must be member-pointer if its a memberfunctionpointer"); + FProto = MP->getPointeeType()->getAs (); + assert(FProto && + "The call must have happened through a member function " + "pointer"); + } + } + } + + int ParamIndex = 0; + bool Matched = false; + + for (; ArgIndex < Node.getNumArgs(); ++ArgIndex, ++ParamIndex) { + BoundNodesTreeBuilder ArgMatches(*Builder); + if (ArgMatcher.matches(*(Node.getArg(ArgIndex)->IgnoreParenCasts()), Finder, + &ArgMatches)) { + BoundNodesTreeBuilder ParamMatches(ArgMatches); + + // This test is cheaper compared to the big matcher in the next if. + // Therefore, please keep this order. + if (FProto) { + QualType ParamType = FProto->getParamType(ParamIndex); + if (ParamMatcher.matches(ParamType, Finder, &ParamMatches)) { + Result.addMatch(ParamMatches); + Matched = true; + continue; + } + } + if (expr(anyOf(cxxConstructExpr(hasDeclaration(cxxConstructorDecl( + hasParameter(ParamIndex, hasType(ParamMatcher))))), + callExpr(callee(functionDecl( + hasParameter(ParamIndex, hasType(ParamMatcher))))))) + .matches(Node, Finder, &ParamMatches)) { + Result.addMatch(ParamMatches); + Matched = true; + continue; + } + } + } + *Builder = std::move(Result); + return Matched; +} + /// Matches the ParmVarDecl nodes that are at the N'th position in the parameter /// list. The parameter list could be that of either a block, function, or /// objc-method. diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -202,6 +202,7 @@ REGISTER_MATCHER(cxxUnresolvedConstructExpr); REGISTER_MATCHER(decayedType); REGISTER_MATCHER(decl); + REGISTER_MATCHER(decompositionDecl); REGISTER_MATCHER(declCountIs); REGISTER_MATCHER(declRefExpr); REGISTER_MATCHER(declStmt); @@ -227,6 +228,7 @@ REGISTER_MATCHER(floatLiteral); REGISTER_MATCHER(forEach); REGISTER_MATCHER(forEachArgumentWithParam); + REGISTER_MATCHER(forEachArgumentWithParamType); REGISTER_MATCHER(forEachConstructorInitializer); REGISTER_MATCHER(forEachDescendant); REGISTER_MATCHER(forEachOverridden); diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -741,6 +741,164 @@ std::make_unique >("v", 4))); } +TEST(ForEachArgumentWithParamType, ReportsNoFalsePositives) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + TypeMatcher IntType = qualType(isInteger()).bind("type"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParamType(ArgumentY, IntType)); + + // IntParam does not match. + EXPECT_TRUE(notMatches("void f(int* i) { int* y; f(y); }", CallExpr)); + // ArgumentY does not match. + EXPECT_TRUE(notMatches("void f(int i) { int x; f(x); }", CallExpr)); +} + +TEST(ForEachArgumentWithParamType, MatchesCXXMemberCallExpr) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + TypeMatcher IntType = qualType(isInteger()).bind("type"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParamType(ArgumentY, IntType)); + EXPECT_TRUE(matchAndVerifyResultTrue( + "struct S {" + " const S& operator[](int i) { return *this; }" + "};" + "void f(S S1) {" + " int y = 1;" + " S1[y];" + "}", + CallExpr, std::make_unique >("type", 1))); + + StatementMatcher CallExpr2 = + callExpr(forEachArgumentWithParamType(ArgumentY, IntType)); + EXPECT_TRUE(matchAndVerifyResultTrue( + "struct S {" + " static void g(int i);" + "};" + "void f() {" + " int y = 1;" + " S::g(y);" + "}", + CallExpr2, std::make_unique >("type", 1))); +} + +TEST(ForEachArgumentWithParamType, MatchesCallExpr) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + TypeMatcher IntType = qualType(isInteger()).bind("type"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParamType(ArgumentY, IntType)); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f(int i) { int y; f(y); }", CallExpr, + std::make_unique >("type"))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f(int i) { int y; f(y); }", CallExpr, + std::make_unique >("arg"))); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f(int i, int j) { int y; f(y, y); }", CallExpr, + std::make_unique >("type", 2))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f(int i, int j) { int y; f(y, y); }", CallExpr, + std::make_unique >("arg", 2))); +} + +TEST(ForEachArgumentWithParamType, MatchesConstructExpr) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + TypeMatcher IntType = qualType(isInteger()).bind("type"); + StatementMatcher ConstructExpr = + cxxConstructExpr(forEachArgumentWithParamType(ArgumentY, IntType)); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "struct C {" + " C(int i) {}" + "};" + "int y = 0;" + "C Obj(y);", + ConstructExpr, std::make_unique >("type"))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "struct C {" + " C(int i) {}" + "};" + "int y = 0;" + "C Obj(y);", + ConstructExpr, std::make_unique >("arg"))); +} + +TEST(ForEachArgumentWithParamType, HandlesKandRFunctions) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + TypeMatcher IntType = qualType(isInteger()).bind("type"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParamType(ArgumentY, IntType)); + + EXPECT_TRUE(matchesC("void f();\n" + "void call_it(void) { int x, y; f(x, y); }\n" + "void f(a, b) int a, b; {}\n" + "void call_it2(void) { int x, y; f(x, y); }", + CallExpr)); +} + +TEST(ForEachArgumentWithParamType, HandlesBoundNodesForNonMatches) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "void g(int i, int j) {" + " int a;" + " int b;" + " int c;" + " g(a, 0);" + " g(a, b);" + " g(0, b);" + "}", + functionDecl( + forEachDescendant(varDecl().bind("v")), + forEachDescendant(callExpr(forEachArgumentWithParamType( + declRefExpr(to(decl(equalsBoundNode("v")))), qualType())))), + std::make_unique >("v", 4))); +} + +TEST(ForEachArgumentWithParamType, MatchesFunctionPtrCalls) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + TypeMatcher IntType = qualType(builtinType()).bind("type"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParamType(ArgumentY, IntType)); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f(int i) {" + "void (*f_ptr)(int) = f; int y; f_ptr(y); }", + CallExpr, std::make_unique >("type"))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f(int i) {" + "void (*f_ptr)(int) = f; int y; f_ptr(y); }", + CallExpr, std::make_unique >("arg"))); +} + +TEST(ForEachArgumentWithParamType, MatchesMemberFunctionPtrCalls) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + TypeMatcher IntType = qualType(builtinType()).bind("type"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParamType(ArgumentY, IntType)); + + StringRef S = "struct A {\n" + " int f(int i) { return i + 1; }\n" + " int (A::*x)(int);\n" + "};\n" + "void f() {\n" + " int y = 42;\n" + " A a;\n" + " a.x = &A::f;\n" + " (a.*(a.x))(y);\n" + "}"; + EXPECT_TRUE(matchAndVerifyResultTrue( + S, CallExpr, std::make_unique >("type"))); + EXPECT_TRUE(matchAndVerifyResultTrue( + S, CallExpr, std::make_unique >("arg"))); +} + TEST(QualType, hasCanonicalType) { EXPECT_TRUE(notMatches("typedef int &int_ref;" "int a;"