Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -4224,6 +4224,11 @@ +
Overloaded to match the type's declaration. +
Matches declarations that are template instantiations or are inside template instantiations. @@ -6956,6 +6961,27 @@
Matches if the type of the object expression (as written, without any +implicit casts) either matches the InnerMatcher, or is a pointer to a +type that matches the InnerMatcher. Differs from `thisPointerType` in that +it matches against the type of the written object expression, which could be +a subclass of the thisPointerType (like when the invoked member is +inherited). + +Given + class Y { public: void m(); }; + class X : public Y {}; + void z() { Y y; y.m(); X x; x.m(); X *p; p->m(); } +cxxMemberCallExpr(invokedAtType(hasDeclaration( + cxxRecordDecl(hasName("Y"))))) + matches `y.m()`. +cxxMemberCallExpr(invokedAtType(hasDeclaration( + cxxRecordDecl(hasName("X"))))) + matches `x.m()` and `p->m()`. +
Matches NestedNameSpecifierLocs for which the given inner NestedNameSpecifier-matcher matches. Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -3278,6 +3278,40 @@ .matches(Node, Finder, Builder); } +/// Matches if the type of the object expression (as written, without any +/// implicit casts) either matches the InnerMatcher, or is a pointer to a +/// type that matches the InnerMatcher. Differs from `thisPointerType` in that +/// it matches against the type of the written object expression, which could be +/// a subclass of the thisPointerType (like when the invoked member is +/// inherited). +/// +/// Given +/// \code +/// class Y { public: void m(); }; +/// class X : public Y {}; +/// void z() { Y y; y.m(); X x; x.m(); X *p; p->m(); } +/// \endcode +/// cxxMemberCallExpr(invokedAtType(hasDeclaration( +/// cxxRecordDecl(hasName("Y"))))) +/// matches `y.m()` (while `thisPointerType` would match all three calls.) +/// +/// cxxMemberCallExpr(invokedAtType(hasDeclaration( +/// cxxRecordDecl(hasName("X"))))) +/// matches `x.m()` and `p->m()` (while `thisPointerType` would not match any +/// of the calls, because `this`'s type is `Y` in those calls). +AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, invokedAtType, + internal::Matcher, InnerMatcher, 0) { + return on(anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher)))) + .matches(Node, Finder, Builder); +} + +/// Overloaded to match the type's declaration. +AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, invokedAtType, DeclarationMatcher, + InnerMatcher, 1) { + return on(anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher)))) + .matches(Node, Finder, Builder); +} + /// Matches a DeclRefExpr that refers to a declaration that matches the /// specified matcher. /// Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -470,6 +470,35 @@ } +TEST(MatcherCXXMemberCallExpr, InvokedAtType) { + auto M = cxxMemberCallExpr(invokedAtType(cxxRecordDecl(hasName("Y")))); + EXPECT_TRUE(matches( + R"cc( + struct Y { + void m(); + }; + void z(Y y) { y.m(); } + )cc", + M)); + EXPECT_TRUE(matches( + R"cc( + struct Y { + void m(); + }; + void z(Y *y) { y->m(); } + )cc", + M)); + EXPECT_TRUE(notMatches( + R"cc( + struct Y { + void m(); + }; + struct X : public Y {}; + void z(X x) { x.m(); } + )cc", + M)); +} + TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) { StatementMatcher ArgumentY = declRefExpr(to(varDecl(hasName("y")))).bind("arg");