ymandel created this revision. Herald added a subscriber: cfe-commits. Assorted changes to matchers for `CXXMemberExpr`:
- fixes the comments on `hasObjectExpression`, - clarifies comments on `thisPointerType` and `on`, - adds comments to `onImplicitObjectArgument` - adds new matcher `invokedAtType`, a variant of `thisPointerType` (like `on` is a variant of `onImplicitObjectArgument`). Repository: rC Clang https://reviews.llvm.org/D56786 Files: docs/LibASTMatchersReference.html include/clang/ASTMatchers/ASTMatchers.h unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
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"); 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<Expr>, @@ -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<Expr>, 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<QualType>, 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<clang::QualType>, + 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: docs/LibASTMatchersReference.html =================================================================== --- docs/LibASTMatchersReference.html +++ docs/LibASTMatchersReference.html @@ -4224,6 +4224,11 @@ </pre></td></tr> +<tr><td>Matcher<clang::CXXMemberCallExpr></td><td class="name" onclick="toggle('invokedAtType1')"><a name="invokedAtType1Anchor">invokedAtType</a></td><td>clang::ast_matchers::DeclarationMatcher InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="invokedAtType1"><pre>Overloaded to match the type's declaration. +</pre></td></tr> + + <tr><td>Matcher<internal::Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>>></td><td class="name" onclick="toggle('isInstantiated0')"><a name="isInstantiated0Anchor">isInstantiated</a></td><td></td></tr> <tr><td colspan="4" class="doc" id="isInstantiated0"><pre>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" </pre></td></tr> @@ -4857,16 +4865,39 @@ <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXMemberCallExpr.html">CXXMemberCallExpr</a>></td><td class="name" onclick="toggle('onImplicitObjectArgument0')"><a name="onImplicitObjectArgument0Anchor">onImplicitObjectArgument</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="onImplicitObjectArgument0"><pre></pre></td></tr> +<tr><td colspan="4" class="doc" id="onImplicitObjectArgument0"><pre>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? +</pre></td></tr> <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXMemberCallExpr.html">CXXMemberCallExpr</a>></td><td class="name" onclick="toggle('on0')"><a name="on0Anchor">on</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="on0"><pre>Matches on the implicit object argument of a member call expression. +<tr><td colspan="4" class="doc" id="on0"><pre>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? </pre></td></tr> @@ -4878,8 +4909,20 @@ <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXMemberCallExpr.html">CXXMemberCallExpr</a>></td><td class="name" onclick="toggle('thisPointerType0')"><a name="thisPointerType0Anchor">thisPointerType</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>> InnerMatcher</td></tr> -<tr><td colspan="4" class="doc" id="thisPointerType0"><pre>Matches if the expression's type either matches the specified -matcher, or is a pointer to a type that matches the InnerMatcher. +<tr><td colspan="4" class="doc" id="thisPointerType0"><pre>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()`. </pre></td></tr> @@ -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" </pre></td></tr> @@ -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" </pre></td></tr> @@ -6956,6 +7005,27 @@ </pre></td></tr> +<tr><td>Matcher<clang::CXXMemberCallExpr></td><td class="name" onclick="toggle('invokedAtType0')"><a name="invokedAtType0Anchor">invokedAtType</a></td><td>clang::ast_matchers::Matcher<clang::QualType> InnerMatcher</td></tr> +<tr><td colspan="4" class="doc" id="invokedAtType0"><pre>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()`. +</pre></td></tr> + + <tr><td>Matcher<internal::BindableMatcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifierLoc.html">NestedNameSpecifierLoc</a>>></td><td class="name" onclick="toggle('loc1')"><a name="loc1Anchor">loc</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifier.html">NestedNameSpecifier</a>> InnerMatcher</td></tr> <tr><td colspan="4" class="doc" id="loc1"><pre>Matches NestedNameSpecifierLocs for which the given inner NestedNameSpecifier-matcher matches.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits