Index: docs/LibASTMatchersReference.html =================================================================== --- docs/LibASTMatchersReference.html +++ docs/LibASTMatchersReference.html @@ -4224,6 +4224,11 @@ +Matcher<clang::CXXMemberCallExpr>invokedAtTypeclang::ast_matchers::DeclarationMatcher InnerMatcher +
Overloaded to match the type's declaration.
+
+ + Matcher<internal::Matcher<Decl>>isInstantiated
Matches declarations that are template instantiations or are inside
 template instantiations.
@@ -4814,12 +4819,15 @@
 matched by a given matcher.
 
 Given
-  struct X { int m; };
-  void f(X x) { x.m; m; }
-memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))))))
-  matches "x.m" and "m"
-with hasObjectExpression(...)
-  matching "x" and the implicit object expression of "m" which has type X*.
+  struct X {
+    int m;
+    int f(X x) { x.m; return m; }
+  };
+memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))))
+  matches "x.m", but not "m"
+memberExpr(hasObjectExpression(hasType(pointsTo(
+     cxxRecordDecl(hasName("X"))))))
+  matches "m", but not "x.m"
 
@@ -4857,16 +4865,39 @@ Matcher<CXXMemberCallExpr>onImplicitObjectArgumentMatcher<Expr> InnerMatcher -

+
Matches on the implicit object argument of a member call expression. Unlike
+`on`, matches the argument directly without stripping away anything.
+
+Given
+  class Y { public: void m(); };
+  Y g();
+  class X : public Y { void g(); };
+  void z(Y y, X x) { y.m(); x.m(); x.g(); (g()).m(); }
+cxxMemberCallExpr(onImplicitObjectArgument(hasType(
+    cxxRecordDecl(hasName("Y")))))
+  matches `y.m()`, `x.m()` and (g()).m(), but not `x.g()`.
+cxxMemberCallExpr(on(callExpr()))
+  does not match `(g()).m()`, because the parens are not ignored.
+
+FIXME: Overload to allow directly matching types?
+
Matcher<CXXMemberCallExpr>onMatcher<Expr> InnerMatcher -
Matches on the implicit object argument of a member call expression.
+
Matches on the implicit object argument of a member call expression, after
+stripping off any parentheses or implicit casts.
 
-Example matches y.x()
-  (matcher = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y"))))))
-  class Y { public: void x(); };
-  void z() { Y y; y.x(); }
+Given
+  class Y { public: void m(); };
+  Y g();
+  class X : public Y {};
+  void z(Y y, X x) { y.m(); (g()).m(); x.m(); }
+cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y")))))
+  matches `y.m()`, `(g()).m()` and `x.m()`.
+cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X")))))
+  matches `x.m()`.
+cxxMemberCallExpr(on(callExpr()))
+  matches `(g()).m()`
 
 FIXME: Overload to allow directly matching types?
 
@@ -4878,8 +4909,20 @@ Matcher<CXXMemberCallExpr>thisPointerTypeMatcher<QualType> InnerMatcher -
Matches if the expression's type either matches the specified
-matcher, or is a pointer to a type that matches the InnerMatcher.
+
Matches if the type of the expression's implicit object argument either
+matches the InnerMatcher, or is a pointer to a type that matches the
+InnerMatcher.
+
+Given
+  class Y { public: void m(); };
+  class X : public Y { void g(); };
+  void z() { Y y; y.m(); Y *p; p->m(); X x; x.m(); x.g(); }
+cxxMemberCallExpr(thisPointerType(hasDeclaration(
+    cxxRecordDecl(hasName("Y")))))
+  matches `y.m()`, `p->m()` and `x.m()`.
+cxxMemberCallExpr(thisPointerType(hasDeclaration(
+    cxxRecordDecl(hasName("X")))))
+  matches `x.g()`.
 
@@ -5988,12 +6031,15 @@ matched by a given matcher. Given - struct X { int m; }; - void f(X x) { x.m; m; } -memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X"))))))) - matches "x.m" and "m" -with hasObjectExpression(...) - matching "x" and the implicit object expression of "m" which has type X*. + struct X { + int m; + int f(X x) { x.m; return m; } + }; +memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X"))))) + matches "x.m", but not "m" +memberExpr(hasObjectExpression(hasType(pointsTo( + cxxRecordDecl(hasName("X")))))) + matches "m", but not "x.m"
@@ -6809,12 +6855,15 @@ matched by a given matcher. Given - struct X { int m; }; - void f(X x) { x.m; m; } -memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X"))))))) - matches "x.m" and "m" -with hasObjectExpression(...) - matching "x" and the implicit object expression of "m" which has type X*. + struct X { + int m; + int f(X x) { x.m; return m; } + }; +memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X"))))) + matches "x.m", but not "m" +memberExpr(hasObjectExpression(hasType(pointsTo( + cxxRecordDecl(hasName("X")))))) + matches "m", but not "x.m"
@@ -6956,6 +7005,27 @@ +Matcher<clang::CXXMemberCallExpr>invokedAtTypeclang::ast_matchers::Matcher<clang::QualType> InnerMatcher +
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()`.
+
+ + Matcher<internal::BindableMatcher<NestedNameSpecifierLoc>>locMatcher<NestedNameSpecifier> InnerMatcher
Matches NestedNameSpecifierLocs for which the given inner
 NestedNameSpecifier-matcher matches.
Index: include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -2887,14 +2887,22 @@
          InnerMatcher.matches(*UnderlyingDecl, Finder, Builder);
 }
 
-/// Matches on the implicit object argument of a member call expression.
+/// Matches on the implicit object argument of a member call expression, after
+/// stripping off any parentheses or implicit casts.
 ///
-/// Example matches y.x()
-///   (matcher = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y"))))))
+/// Given
 /// \code
-///   class Y { public: void x(); };
-///   void z() { Y y; y.x(); }
-/// \endcode
+///   class Y { public: void m(); };
+///   Y g();
+///   class X : public Y {};
+///   void z(Y y, X x) { y.m(); (g()).m(); x.m(); }
+/// \endcode
+/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y")))))
+///   matches `y.m()`, `(g()).m()` and `x.m()`.
+/// cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X")))))
+///   matches `x.m()`.
+/// cxxMemberCallExpr(on(callExpr()))
+///   matches `(g()).m()`
 ///
 /// FIXME: Overload to allow directly matching types?
 AST_MATCHER_P(CXXMemberCallExpr, on, internal::Matcher,
@@ -3254,6 +3262,23 @@
       .matches(Node, Finder, Builder);
 }
 
+/// Matches on the implicit object argument of a member call expression. Unlike
+/// `on`, matches the argument directly without stripping away anything.
+///
+/// Given
+/// \code
+///   class Y { public: void m(); };
+///   Y g();
+///   class X : public Y { void g(); };
+///   void z(Y y, X x) { y.m(); x.m(); x.g(); (g()).m(); }
+/// \endcode
+/// cxxMemberCallExpr(onImplicitObjectArgument(hasType(
+///     cxxRecordDecl(hasName("Y")))))
+///   matches `y.m()`, `x.m()` and (g()).m(), but not `x.g()`.
+/// cxxMemberCallExpr(on(callExpr()))
+///   does not match `(g()).m()`, because the parens are not ignored.
+///
+/// FIXME: Overload to allow directly matching types?
 AST_MATCHER_P(CXXMemberCallExpr, onImplicitObjectArgument,
               internal::Matcher, InnerMatcher) {
   const Expr *ExprNode = Node.getImplicitObjectArgument();
@@ -3261,8 +3286,22 @@
           InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
 
-/// Matches if the expression's type either matches the specified
-/// matcher, or is a pointer to a type that matches the InnerMatcher.
+/// Matches if the type of the expression's implicit object argument either
+/// matches the InnerMatcher, or is a pointer to a type that matches the
+/// InnerMatcher.
+///
+/// Given
+/// \code
+///   class Y { public: void m(); };
+///   class X : public Y { void g(); };
+///   void z() { Y y; y.m(); Y *p; p->m(); X x; x.m(); x.g(); }
+/// \endcode
+/// cxxMemberCallExpr(thisPointerType(hasDeclaration(
+///     cxxRecordDecl(hasName("Y")))))
+///   matches `y.m()`, `p->m()` and `x.m()`.
+/// cxxMemberCallExpr(thisPointerType(hasDeclaration(
+///     cxxRecordDecl(hasName("X")))))
+///   matches `x.g()`.
 AST_MATCHER_P_OVERLOAD(CXXMemberCallExpr, thisPointerType,
                        internal::Matcher, InnerMatcher, 0) {
   return onImplicitObjectArgument(
@@ -3278,6 +3317,45 @@
       .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()`.
+/// cxxMemberCallExpr(invokedAtType(hasDeclaration(
+///     cxxRecordDecl(hasName("X")))))
+///   matches `x.m()` and `p->m()`.
+AST_MATCHER_P_OVERLOAD(clang::CXXMemberCallExpr, invokedAtType,
+                       clang::ast_matchers::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(clang::CXXMemberCallExpr, invokedAtType,
+                       clang::ast_matchers::DeclarationMatcher, InnerMatcher,
+                       1) {
+  using ::clang::ast_matchers::on;
+  using ::clang::ast_matchers::anyOf;
+  using ::clang::ast_matchers::hasType;
+  using ::clang::ast_matchers::pointsTo;
+  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.
 ///
@@ -4969,13 +5047,16 @@
 ///
 /// Given
 /// \code
-///   struct X { int m; };
-///   void f(X x) { x.m; m; }
+///   struct X {
+///     int m;
+///     int f(X x) { x.m; return m; }
+///   };
 /// \endcode
-/// memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))))))
-///   matches "x.m" and "m"
-/// with hasObjectExpression(...)
-///   matching "x" and the implicit object expression of "m" which has type X*.
+/// memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))))
+///   matches "x.m", but not "m"
+/// memberExpr(hasObjectExpression(hasType(pointsTo(
+//      cxxRecordDecl(hasName("X"))))))
+///   matches "m", but not "x.m"
 AST_POLYMORPHIC_MATCHER_P(
     hasObjectExpression,
     AST_POLYMORPHIC_SUPPORTED_TYPES(MemberExpr, UnresolvedMemberExpr,
Index: unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
===================================================================
--- unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -470,6 +470,124 @@
 
 }
 
+TEST(MatcherCXXMemberCallExpr, On) {
+  auto MatchesType = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X")))));
+  EXPECT_TRUE(matches(
+      R"cc(
+        struct Y {
+          void m();
+        };
+        struct X : public Y {};
+        void z(X x) { x.m(); }
+      )cc",
+      MatchesType));
+  EXPECT_TRUE(notMatches(
+      R"cc(
+        struct Y {
+          void m();
+        };
+        void z(Y y) { y.m(); }
+      )cc",
+      MatchesType));
+
+  // Parens are ignored.
+  auto MatchesCall = cxxMemberCallExpr(on(callExpr()));
+  EXPECT_TRUE(matches(
+      R"cc(
+        struct Y {
+          void m();
+        };
+        Y g();
+        void z(Y y) { (g()).m(); }
+      )cc",
+      MatchesCall));
+}
+
+TEST(MatcherCXXMemberCallExpr, OnImplicitObjectArgument) {
+  auto Snippet1 = R"cc(
+    struct Y {
+      void m();
+    };
+    void z(Y y) { y.m(); }
+  )cc";
+  auto Snippet2 = R"cc(
+    struct Y {
+      void m();
+    };
+    struct X : public Y {};
+    void z(X x) { x.m(); }
+  )cc";
+  auto MatchesY = cxxMemberCallExpr(
+      onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("Y")))));
+  EXPECT_TRUE(matches(Snippet1, MatchesY));
+  EXPECT_TRUE(matches(Snippet2, MatchesY));
+
+  auto MatchesX = cxxMemberCallExpr(
+      onImplicitObjectArgument(hasType(cxxRecordDecl(hasName("X")))));
+  EXPECT_TRUE(notMatches(Snippet2, MatchesX));
+
+  // Parens are not ignored.
+  auto MatchesCall = cxxMemberCallExpr(onImplicitObjectArgument(callExpr()));
+  EXPECT_TRUE(notMatches(
+      R"cc(
+        struct Y {
+          void m();
+        };
+        Y g();
+        void z(Y y) { (g()).m(); }
+      )cc",
+      MatchesCall));
+}
+
+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(Matcher, HasObjectExpr) {
+  auto M = memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))));
+  EXPECT_TRUE(matches(
+      R"cc(
+        struct X {
+          int m;
+          int f(X x) { return x.m; }
+        };
+      )cc",
+      M));
+  EXPECT_TRUE(notMatches(
+      R"cc(
+        struct X {
+          int m;
+          int f(X x) { return m; }
+        };
+      )cc",
+      M));
+}
+
 TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) {
   StatementMatcher ArgumentY =
     declRefExpr(to(varDecl(hasName("y")))).bind("arg");