Add forEachArgumentWithParam AST matcher.

The new matcher allows users to provide a matcher for both the argument
of a CallExpr/CxxConstructExpr a well as the ParmVarDecl of the
argument.

Patch by Felix Berger.

Differential Revision: http://reviews.llvm.org/D13845

llvm-svn: 258042
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTest.cpp
index cd18df8..46392dc 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTest.cpp
+++ b/clang/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<ParmVarDecl>("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<ParmVarDecl>("param")));
+  EXPECT_TRUE(
+      matchAndVerifyResultTrue("void f(int i) { int y; f(y); }", CallExpr,
+                               new VerifyIdIsBoundTo<DeclRefExpr>("arg")));
+
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      "void f(int i, int j) { int y; f(y, y); }", CallExpr,
+      new VerifyIdIsBoundTo<ParmVarDecl>("param", 2)));
+  EXPECT_TRUE(matchAndVerifyResultTrue(
+      "void f(int i, int j) { int y; f(y, y); }", CallExpr,
+      new VerifyIdIsBoundTo<DeclRefExpr>("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<ParmVarDecl>("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<VarDecl>("v", 4)));
+}
+
 TEST(Matcher, ArgumentCount) {
   StatementMatcher Call1Arg = callExpr(argumentCountIs(1));