diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -3036,6 +3036,18 @@ +
Checks that a call expression or a constructor call expression has +at least the specified number of arguments (including absent default arguments). + +Example matches f(0, 0) and g(0, 0, 0) (matcher = callExpr(argumentCountAtLeast(2))) + void f(int x, int y); + void g(int x, int y, int z); + f(0, 0); + g(0, 0, 0); +
Checks that a call expression or a constructor call expression has a specific number of arguments (including absent default arguments). @@ -3693,6 +3705,18 @@
Checks that a call expression or a constructor call expression has +at least the specified number of arguments (including absent default arguments). + +Example matches f(0, 0) and g(0, 0, 0) (matcher = callExpr(argumentCountAtLeast(2))) + void f(int x, int y); + void g(int x, int y, int z); + f(0, 0); + g(0, 0, 0); +
Checks that a call expression or a constructor call expression has a specific number of arguments (including absent default arguments). @@ -3703,6 +3727,18 @@
Checks that a call expression or a constructor call expression has +at least the specified number of arguments (including absent default arguments). + +Example matches f(0, 0) and g(0, 0, 0) (matcher = callExpr(argumentCountAtLeast(2))) + void f(int x, int y); + void g(int x, int y, int z); + f(0, 0); + g(0, 0, 0); +
Checks that a call expression or a constructor call expression has a specific number of arguments (including absent default arguments). @@ -4860,6 +4896,18 @@
Checks that a call expression or a constructor call expression has +at least the specified number of arguments (including absent default arguments). + +Example matches f(0, 0) and g(0, 0, 0) (matcher = callExpr(argumentCountAtLeast(2))) + void f(int x, int y); + void g(int x, int y, int z); + f(0, 0); + g(0, 0, 0); +
Checks that a call expression or a constructor call expression has a specific number of arguments (including absent default arguments). 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 @@ -4431,6 +4431,33 @@ return NumArgs == N; } +/// Checks that a call expression or a constructor call expression has at least +/// the specified number of arguments (including absent default arguments). +/// +/// Example matches f(0, 0) and g(0, 0, 0) +/// (matcher = callExpr(argumentCountAtLeast(2))) +/// \code +/// void f(int x, int y); +/// void g(int x, int y, int z); +/// f(0, 0); +/// g(0, 0, 0); +/// \endcode +AST_POLYMORPHIC_MATCHER_P(argumentCountAtLeast, + AST_POLYMORPHIC_SUPPORTED_TYPES( + CallExpr, CXXConstructExpr, + CXXUnresolvedConstructExpr, ObjCMessageExpr), + unsigned, N) { + unsigned NumArgs = Node.getNumArgs(); + if (!Finder->isTraversalIgnoringImplicitNodes()) + return NumArgs >= N; + while (NumArgs) { + if (!isa(Node.getArg(NumArgs - 1))) + break; + --NumArgs; + } + return NumArgs >= N; +} + /// Matches the n'th argument of a call expression or a constructor /// call expression. /// 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 @@ -135,6 +135,7 @@ REGISTER_MATCHER(anyOf); REGISTER_MATCHER(anything); REGISTER_MATCHER(argumentCountIs); + REGISTER_MATCHER(argumentCountAtLeast); REGISTER_MATCHER(arraySubscriptExpr); REGISTER_MATCHER(arrayType); REGISTER_MATCHER(asString); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -1640,6 +1640,95 @@ cxxConversionDecl(isExplicit()))); } +TEST_P(ASTMatchersTest, ArgumentCountAtLeast_CallExpr) { + StatementMatcher Call2PlusArgs = callExpr(argumentCountAtLeast(2)); + + EXPECT_TRUE(notMatches("void x(void) { x(); }", Call2PlusArgs)); + EXPECT_TRUE(notMatches("void x(int) { x(0); }", Call2PlusArgs)); + EXPECT_TRUE(matches("void x(int, int) { x(0, 0); }", Call2PlusArgs)); + EXPECT_TRUE(matches("void x(int, int, int) { x(0, 0, 0); }", Call2PlusArgs)); + + if (!GetParam().isCXX()) { + return; + } + + EXPECT_TRUE( + notMatches("void x(int = 1) { x(); }", traverse(TK_AsIs, Call2PlusArgs))); + EXPECT_TRUE(matches("void x(int, int = 1) { x(0); }", + traverse(TK_AsIs, Call2PlusArgs))); + EXPECT_TRUE(matches("void x(int, int = 1, int = 1) { x(0); }", + traverse(TK_AsIs, Call2PlusArgs))); + EXPECT_TRUE(matches("void x(int, int, int = 1) { x(0, 0); }", + traverse(TK_AsIs, Call2PlusArgs))); + EXPECT_TRUE(matches("void x(int, int, int, int = 1) { x(0, 0, 0); }", + traverse(TK_AsIs, Call2PlusArgs))); + + EXPECT_TRUE( + notMatches("void x(int = 1) { x(); }", + traverse(TK_IgnoreUnlessSpelledInSource, Call2PlusArgs))); + EXPECT_TRUE( + notMatches("void x(int, int = 1) { x(0); }", + traverse(TK_IgnoreUnlessSpelledInSource, Call2PlusArgs))); + EXPECT_TRUE( + notMatches("void x(int, int = 1, int = 1) { x(0); }", + traverse(TK_IgnoreUnlessSpelledInSource, Call2PlusArgs))); + EXPECT_TRUE(matches("void x(int, int, int = 1) { x(0, 0); }", + traverse(TK_IgnoreUnlessSpelledInSource, Call2PlusArgs))); + EXPECT_TRUE(matches("void x(int, int, int, int = 1) { x(0, 0, 0); }", + traverse(TK_IgnoreUnlessSpelledInSource, Call2PlusArgs))); +} + +TEST_P(ASTMatchersTest, ArgumentCountAtLeast_CallExpr_CXX) { + if (!GetParam().isCXX()) { + return; + } + + StatementMatcher Call2PlusArgs = callExpr(argumentCountAtLeast(2)); + EXPECT_TRUE(notMatches("class X { void x() { x(); } };", Call2PlusArgs)); + EXPECT_TRUE(notMatches("class X { void x(int) { x(0); } };", Call2PlusArgs)); + EXPECT_TRUE( + matches("class X { void x(int, int) { x(0, 0); } };", Call2PlusArgs)); + EXPECT_TRUE(matches("class X { void x(int, int, int) { x(0, 0, 0); } };", + Call2PlusArgs)); + + EXPECT_TRUE(notMatches("class X { void x(int = 1) { x(0); } };", + traverse(TK_AsIs, Call2PlusArgs))); + EXPECT_TRUE(matches("class X { void x(int, int = 1) { x(0); } };", + traverse(TK_AsIs, Call2PlusArgs))); + EXPECT_TRUE(matches("class X { void x(int, int = 1, int = 1) { x(0); } };", + traverse(TK_AsIs, Call2PlusArgs))); + EXPECT_TRUE(matches("class X { void x(int, int, int = 1) { x(0, 0); } };", + traverse(TK_AsIs, Call2PlusArgs))); + EXPECT_TRUE( + matches("class X { void x(int, int, int, int = 1) { x(0, 0, 0); } };", + traverse(TK_AsIs, Call2PlusArgs))); + + EXPECT_TRUE( + notMatches("class X { void x(int = 1) { x(0); } };", + traverse(TK_IgnoreUnlessSpelledInSource, Call2PlusArgs))); + EXPECT_TRUE( + notMatches("class X { void x(int, int = 1) { x(0); } };", + traverse(TK_IgnoreUnlessSpelledInSource, Call2PlusArgs))); + EXPECT_TRUE( + notMatches("class X { void x(int, int = 1, int = 1) { x(0); } };", + traverse(TK_IgnoreUnlessSpelledInSource, Call2PlusArgs))); + EXPECT_TRUE(matches("class X { void x(int, int, int = 1) { x(0, 0); } };", + traverse(TK_IgnoreUnlessSpelledInSource, Call2PlusArgs))); + EXPECT_TRUE( + matches("class X { void x(int, int, int, int = 1) { x(0, 0, 0); } };", + traverse(TK_IgnoreUnlessSpelledInSource, Call2PlusArgs))); + + EXPECT_TRUE( + notMatches("class X { static void x() { x(); } };", Call2PlusArgs)); + EXPECT_TRUE( + notMatches("class X { static void x(int) { x(0); } };", Call2PlusArgs)); + EXPECT_TRUE(matches("class X { static void x(int, int) { x(0, 0); } };", + Call2PlusArgs)); + EXPECT_TRUE( + matches("class X { static void x(int, int, int) { x(0, 0, 0); } };", + Call2PlusArgs)); +} + TEST_P(ASTMatchersTest, ArgumentCountIs_CallExpr) { StatementMatcher Call1Arg = callExpr(argumentCountIs(1));