Index: cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h +++ cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h @@ -2871,6 +2871,57 @@ *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; + // 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; + int ParamIndex = 0; + bool Matched = false; + for (; ArgIndex < Node.getNumArgs(); ++ArgIndex) { + BoundNodesTreeBuilder ArgMatches(*Builder); + if (ArgMatcher.matches(*(Node.getArg(ArgIndex)->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: cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp +++ cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp @@ -174,6 +174,7 @@ REGISTER_MATCHER(fieldDecl); REGISTER_MATCHER(floatLiteral); REGISTER_MATCHER(forEach); + REGISTER_MATCHER(forEachArgumentWithParam); REGISTER_MATCHER(forEachConstructorInitializer); REGISTER_MATCHER(forEachDescendant); REGISTER_MATCHER(forEachSwitchCase); Index: cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp =================================================================== --- cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp +++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -1608,6 +1608,91 @@ EXPECT_TRUE(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY)); } +TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) { + 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)); +} + +TEST(ForEachArgumentWithParam, MatchesCXXMemberCallExpr) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + DeclarationMatcher IntParam = parmVarDecl(hasType(isInteger())).bind("param"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParam(ArgumentY, IntParam)); + EXPECT_TRUE(matchAndVerifyResultTrue( + "struct S {" + " const S& operator[](int i) { return *this; }" + "};" + "void f(S S1) {" + " int y = 1;" + " S1[y];" + "}", + CallExpr, new VerifyIdIsBoundTo("param", 1))); +} + +TEST(ForEachArgumentWithParam, MatchesCallExpr) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + DeclarationMatcher IntParam = parmVarDecl(hasType(isInteger())).bind("param"); + StatementMatcher CallExpr = + callExpr(forEachArgumentWithParam(ArgumentY, IntParam)); + + 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))); +} + +TEST(ForEachArgumentWithParam, MatchesConstructExpr) { + StatementMatcher ArgumentY = + declRefExpr(to(varDecl(hasName("y")))).bind("arg"); + DeclarationMatcher IntParam = parmVarDecl(hasType(isInteger())).bind("param"); + StatementMatcher ConstructExpr = + cxxConstructExpr(forEachArgumentWithParam(ArgumentY, IntParam)); + + EXPECT_TRUE(matchAndVerifyResultTrue( + "struct C {" + " C(int i) {}" + "};" + "int y = 0;" + "C Obj(y);", + ConstructExpr, new VerifyIdIsBoundTo("param"))); +} + +TEST(ForEachArgumentWithParam, 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(forEachArgumentWithParam( + declRefExpr(to(decl(equalsBoundNode("v")))), parmVarDecl())))), + new VerifyIdIsBoundTo("v", 4))); +} + TEST(Matcher, ArgumentCount) { StatementMatcher Call1Arg = callExpr(argumentCountIs(1));