Index: docs/LibASTMatchersReference.html =================================================================== --- docs/LibASTMatchersReference.html +++ 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.
@@ -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"
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?
+Matches on the implicit object argument of a member call expression. +@@ -4878,8 +4909,20 @@ 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?- Matcher<CXXMemberCallExpr> thisPointerType Matcher<QualType> InnerMatcher @@ -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" Matches if the expression's type either matches the specified -matcher, or is a pointer to a type that matches the InnerMatcher. +@@ -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" 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()`.
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: 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");