Index: include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -2871,6 +2871,48 @@ *Node.getParamDecl(N), Finder, Builder)); } +/// \brief Matches all arguments and their respective ParmVarDecl. +/// +/// Given +/// \code +/// void f(int i); +/// int y; +/// f(y); +/// \endcode +/// callExpr(declRefExpr(to(varDecl(hasName("y")))), +/// parmVarDecl(hasType(isInteger()))) +/// matches f(y); +/// with declRefExpr(...) +/// matching int y +/// and parmVarDecl(...) +/// matching int i +AST_POLYMORPHIC_MATCHER_P2(forEachArgumentWithParam, + AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr, + CXXConstructExpr), + internal::Matcher, ArgMatcher, + internal::Matcher, ParamMatcher) { + BoundNodesTreeBuilder Result; + int ParamIndex = 0; + bool Matched = false; + for (const Expr *Arg : Node.arguments()) { + BoundNodesTreeBuilder ArgMatches(*Builder); + if (ArgMatcher.matches(*Arg->IgnoreParenCasts(), Finder, &ArgMatches)) { + BoundNodesTreeBuilder ParamMatches(ArgMatches); + if (expr(anyOf(cxxConstructExpr(hasDeclaration(cxxConstructorDecl( + hasParameter(ParamIndex, ParamMatcher)))), + callExpr(callee(functionDecl( + hasParameter(ParamIndex, ParamMatcher)))))) + .matches(Node, Finder, &ParamMatches)) { + Result.addMatch(ParamMatches); + Matched = true; + } + } + ++ParamIndex; + } + *Builder = std::move(Result); + return Matched; +} + /// \brief Matches any parameter of a function declaration. /// /// Does not match the 'this' parameter of a method. Index: lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- lib/ASTMatchers/Dynamic/Registry.cpp +++ lib/ASTMatchers/Dynamic/Registry.cpp @@ -173,6 +173,7 @@ REGISTER_MATCHER(fieldDecl); REGISTER_MATCHER(floatLiteral); REGISTER_MATCHER(forEach); + REGISTER_MATCHER(forEachArgumentWithParam); REGISTER_MATCHER(forEachConstructorInitializer); REGISTER_MATCHER(forEachDescendant); REGISTER_MATCHER(forEachSwitchCase); Index: unittests/ASTMatchers/ASTMatchersTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.cpp +++ unittests/ASTMatchers/ASTMatchersTest.cpp @@ -1608,6 +1608,59 @@ EXPECT_TRUE(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY)); } +TEST(Matcher, ForEachArgumentWithParam) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + DeclarationMatcher IntParam = parmVarDecl(hasType(isInteger())).bind("param"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParam(ArgumentY, IntParam)); + + // IntParam does not match. + EXPECT_FALSE(matches("void f(int* i) { int* y; f(y); }", CallExpr)); + // ArgumentY does not match. + EXPECT_FALSE(matches("void f(int i) { int x; f(x); }", CallExpr)); + + EXPECT_TRUE( + matchAndVerifyResultTrue("void f(int i) { int y; f(y); }", CallExpr, + new VerifyIdIsBoundTo("param"))); + EXPECT_TRUE( + matchAndVerifyResultTrue("void f(int i) { int y; f(y); }", CallExpr, + new VerifyIdIsBoundTo("arg"))); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f(int i, int j) { int y; f(y, y); }", CallExpr, + new VerifyIdIsBoundTo("param", 2))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f(int i, int j) { int y; f(y, y); }", CallExpr, + new VerifyIdIsBoundTo("arg", 2))); + + StatementMatcher ConstructExpr = + cxxConstructExpr(forEachArgumentWithParam(ArgumentY, IntParam)); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "struct C {" + " C(int i) {}" + "};" + "int y = 0;" + "C Obj(y);", + ConstructExpr, new VerifyIdIsBoundTo("param"))); + + 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(forEachArgumentWithParam( + declRefExpr(to(decl(equalsBoundNode("v")))), parmVarDecl())))), + new VerifyIdIsBoundTo("v", 4))); +} + TEST(Matcher, ArgumentCount) { StatementMatcher Call1Arg = callExpr(argumentCountIs(1));