[clang] 4c48ea6 - [ASTMatchers] extract public matchers from const-analysis into own patch
Jonas Toth via cfe-commits
cfe-commits at lists.llvm.org
Sat Jan 11 10:21:35 PST 2020
Author: Jonas Toth
Date: 2020-01-11T19:21:03+01:00
New Revision: 4c48ea68e491cb42f1b5d43ffba89f6a7f0dadc4
URL: https://github.com/llvm/llvm-project/commit/4c48ea68e491cb42f1b5d43ffba89f6a7f0dadc4
DIFF: https://github.com/llvm/llvm-project/commit/4c48ea68e491cb42f1b5d43ffba89f6a7f0dadc4.diff
LOG: [ASTMatchers] extract public matchers from const-analysis into own patch
Summary:
The analysis for const-ness of local variables required a view generally useful
matchers that are extracted into its own patch.
They are `decompositionDecl` and `forEachArgumentWithParamType`, that works
for calls through function pointers as well.
Reviewers: aaron.ballman
Reviewed By: aaron.ballman
Subscribers: cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D72505
Added:
Modified:
clang/docs/LibASTMatchersReference.html
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/lib/ASTMatchers/Dynamic/Registry.cpp
clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 5bb181b04d3a..099462f17d2b 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -635,6 +635,30 @@ <h2 id="decl-matchers">Node Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DecompositionDecl.html">DecompositionDecl</a>></td><td class="name" onclick="toggle('decompositionDecl0')"><a name="decompositionDecl0Anchor">decompositionDecl</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DecompositionDecl.html">DecompositionDecl</a>>...</td></tr>
+<tr><td colspan="4" class="doc" id="decompositionDecl0"><pre>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};
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DecompositionDecl.html">DecompositionDecl</a>></td><td class="name" onclick="toggle('decompositionDecl0')"><a name="decompositionDecl0Anchor">decompositionDecl</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1DecompositionDecl.html">DecompositionDecl</a>>...</td></tr>
+<tr><td colspan="4" class="doc" id="decompositionDecl0"><pre>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};
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifierLoc.html">NestedNameSpecifierLoc</a>></td><td class="name" onclick="toggle('nestedNameSpecifierLoc0')"><a name="nestedNameSpecifierLoc0Anchor">nestedNameSpecifierLoc</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifierLoc.html">NestedNameSpecifierLoc</a>>...</td></tr>
<tr><td colspan="4" class="doc" id="nestedNameSpecifierLoc0"><pre>Same as nestedNameSpecifier but matches NestedNameSpecifierLoc.
</pre></td></tr>
@@ -4969,6 +4993,60 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>></td><td class="name" onclick="toggle('forEachArgumentWithParamType1')"><a name="forEachArgumentWithParamType1Anchor">forEachArgumentWithParamType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> ArgMatcher, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>> ParamMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="forEachArgumentWithParamType1"><pre>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
diff erence 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
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>></td><td class="name" onclick="toggle('forEachArgumentWithParamType1')"><a name="forEachArgumentWithParamType1Anchor">forEachArgumentWithParamType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> ArgMatcher, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>> ParamMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="forEachArgumentWithParamType1"><pre>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
diff erence 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
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXConstructExpr.html">CXXConstructExpr</a>></td><td class="name" onclick="toggle('hasAnyArgument1')"><a name="hasAnyArgument1Anchor">hasAnyArgument</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasAnyArgument1"><pre>Matches any argument of a call expression or a constructor call
expression, or an ObjC-message-send expression.
@@ -5447,6 +5525,60 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html">CallExpr</a>></td><td class="name" onclick="toggle('forEachArgumentWithParamType0')"><a name="forEachArgumentWithParamType0Anchor">forEachArgumentWithParamType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> ArgMatcher, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>> ParamMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="forEachArgumentWithParamType0"><pre>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
diff erence 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
+</pre></td></tr>
+
+
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html">CallExpr</a>></td><td class="name" onclick="toggle('forEachArgumentWithParamType0')"><a name="forEachArgumentWithParamType0Anchor">forEachArgumentWithParamType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> ArgMatcher, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>> ParamMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="forEachArgumentWithParamType0"><pre>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
diff erence 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
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html">CallExpr</a>></td><td class="name" onclick="toggle('hasAnyArgument0')"><a name="hasAnyArgument0Anchor">hasAnyArgument</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasAnyArgument0"><pre>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
index 9a5888b7572b..d223e9a0477d 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -310,6 +310,18 @@ AST_POLYMORPHIC_MATCHER_P(isExpansionInFileMatching,
/// \endcode
extern const internal::VariadicAllOfMatcher<Decl> decl;
+/// 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> decompositionDecl;
+
/// Matches a declaration of a linkage specification.
///
/// Given
@@ -4194,6 +4206,104 @@ AST_POLYMORPHIC_MATCHER_P2(forEachArgumentWithParam,
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
diff erence 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<Expr>, ArgMatcher,
+ internal::Matcher<QualType>, 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<CallExpr>(&Node)) {
+ if (const auto *Value =
+ dyn_cast_or_null<ValueDecl>(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<FunctionProtoType>();
+
+ if (QT->isMemberFunctionPointerType()) {
+ const auto *MP = QT->getAs<MemberPointerType>();
+ assert(MP &&
+ "Must be member-pointer if its a memberfunctionpointer");
+ FProto = MP->getPointeeType()->getAs<FunctionProtoType>();
+ 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 any parameter of a function or an ObjC method declaration or a
/// block.
///
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 1c0930c5983a..bfd7906559af 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -196,6 +196,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(cxxUnresolvedConstructExpr);
REGISTER_MATCHER(decayedType);
REGISTER_MATCHER(decl);
+ REGISTER_MATCHER(decompositionDecl);
REGISTER_MATCHER(declCountIs);
REGISTER_MATCHER(declRefExpr);
REGISTER_MATCHER(declStmt);
@@ -220,6 +221,7 @@ RegistryMaps::RegistryMaps() {
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
index 23447baf912b..c9911379623e 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -735,6 +735,172 @@ TEST(ForEachArgumentWithParam, HandlesBoundNodesForNonMatches) {
std::make_unique<VerifyIdIsBoundTo<VarDecl>>("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<VerifyIdIsBoundTo<QualType>>("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<VerifyIdIsBoundTo<QualType>>("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<VerifyIdIsBoundTo<QualType>>(
+ "type")));
+ EXPECT_TRUE(
+ matchAndVerifyResultTrue("void f(int i) { int y; f(y); }", CallExpr,
+ std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>(
+ "arg")));
+
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void f(int i, int j) { int y; f(y, y); }", CallExpr,
+ std::make_unique<VerifyIdIsBoundTo<QualType>>("type", 2)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void f(int i, int j) { int y; f(y, y); }", CallExpr,
+ std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("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<VerifyIdIsBoundTo<QualType>>("type")));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "struct C {"
+ " C(int i) {}"
+ "};"
+ "int y = 0;"
+ "C Obj(y);",
+ ConstructExpr,
+ std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("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<VerifyIdIsBoundTo<VarDecl>>("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<VerifyIdIsBoundTo<QualType>>("type")));
+ EXPECT_TRUE(
+ matchAndVerifyResultTrue("void f(int i) {"
+ "void (*f_ptr)(int) = f; int y; f_ptr(y); }",
+ CallExpr,
+ std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("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<VerifyIdIsBoundTo<QualType>>("type")));
+ EXPECT_TRUE(
+ matchAndVerifyResultTrue(S, CallExpr,
+ std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg")));
+}
+
TEST(QualType, hasCanonicalType) {
EXPECT_TRUE(notMatches("typedef int &int_ref;"
"int a;"
More information about the cfe-commits
mailing list