diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -2543,6 +2543,28 @@ +
Matches template-dependent, but known, member names + +In template declarations, dependent members are not resolved and so can +not be matched to particular named declarations. + +This matcher allows to match on the known name of members. + +Given + template <typename T> + struct S { + void mem(); + }; + template <typename T> + void x() { + S<T> s; + s.mem(); + } +cxxDependentScopeMemberExpr(hasMemberName("mem")) matches `s.mem()` +
Matches member expressions that are called with '->' as opposed to '.'. @@ -2569,6 +2591,42 @@
Matches template-dependent, but known, member names against an already-bound +node + +In template declarations, dependent members are not resolved and so can +not be matched to particular named declarations. + +This matcher allows to match on the name of already-bound VarDecl, FieldDecl +and CXXMethodDecl nodes. + +Given + template <typename T> + struct S { + void mem(); + }; + template <typename T> + void x() { + S<T> s; + s.mem(); + } +The matcher +@code +cxxDependentScopeMemberExpr( + hasObjectExpression(declRefExpr(hasType(templateSpecializationType( + hasDeclaration(classTemplateDecl(has(cxxRecordDecl(has( + cxxMethodDecl(hasName("mem")).bind("templMem") + ))))) + )))), + memberHasSameNameAsBoundNode("templMem") + ) +@endcode +first matches and binds the @c mem member of the @c S template, then +compares its name to the usage in @c s.mem() in the @c x function template +
Matches if the given method declaration is const. @@ -2866,6 +2924,16 @@
Checks that a call expression or a constructor call expression has +a specific number of arguments (including absent default arguments). + +Example matches f(0, 0) (matcher = callExpr(argumentCountIs(2))) + void f(int x, int y); + f(0, 0); +
Checks that a call expression or a constructor call expression has a specific number of arguments (including absent default arguments). @@ -3923,8 +3991,8 @@
Checks that a call expression or a constructor call expression has ++ Matcher<ObjCMessageExpr> argumentCountIs unsigned N + Checks that a call expression or a constructor call expression has a specific number of arguments (including absent default arguments). Example matches f(0, 0) (matcher = callExpr(argumentCountIs(2))) @@ -5865,6 +5933,16 @@+ Matcher<CXXUnresolvedConstructExpr> hasArgument unsigned N, Matcher<Expr> InnerMatcher + + Matches the n'th argument of a call expression or a constructor +call expression. + +Example matches y in x(y) + (matcher = callExpr(hasArgument(0, declRefExpr()))) + void x(int) { int y; x(y); } +Matcher<CallExpr> callee Matcher<Decl> InnerMatcher - Matches if the call expression's callee's declaration matches the given matcher. @@ -7180,8 +7258,8 @@- Matcher<ObjCMessageExpr> hasArgument unsigned N, Matcher<Expr> InnerMatcher Matches the n'th argument of a call expression or a constructor ++ Matcher<ObjCMessageExpr> hasArgument unsigned N, Matcher<Expr> InnerMatcher Matches the n'th argument of a call expression or a constructor call expression. Example matches y in x(y) diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -2835,6 +2835,80 @@ StringRef, internal::hasAnyOverloadedOperatorNameFunc> hasAnyOverloadedOperatorName; +/// Matches template-dependent, but known, member names. +/// +/// In template declarations, dependent members are not resolved and so can +/// not be matched to particular named declarations. +/// +/// This matcher allows to match on the known name of members. +/// +/// Given +/// \code +/// template+/// struct S { +/// void mem(); +/// }; +/// template +/// void x() { +/// S s; +/// s.mem(); +/// } +/// \endcode +/// \c cxxDependentScopeMemberExpr(hasMemberName("mem")) matches `s.mem()` +AST_MATCHER_P(CXXDependentScopeMemberExpr, hasMemberName, std::string, N) { + return Node.getMember().getAsString() == N; +} + +/// Matches template-dependent, but known, member names against an already-bound +/// node +/// +/// In template declarations, dependent members are not resolved and so can +/// not be matched to particular named declarations. +/// +/// This matcher allows to match on the name of already-bound VarDecl, FieldDecl +/// and CXXMethodDecl nodes. +/// +/// Given +/// \code +/// template +/// struct S { +/// void mem(); +/// }; +/// template +/// void x() { +/// S s; +/// s.mem(); +/// } +/// \endcode +/// The matcher +/// @code +/// \c cxxDependentScopeMemberExpr( +/// hasObjectExpression(declRefExpr(hasType(templateSpecializationType( +/// hasDeclaration(classTemplateDecl(has(cxxRecordDecl(has( +/// cxxMethodDecl(hasName("mem")).bind("templMem") +/// ))))) +/// )))), +/// memberHasSameNameAsBoundNode("templMem") +/// ) +/// @endcode +/// first matches and binds the @c mem member of the @c S template, then +/// compares its name to the usage in @c s.mem() in the @c x function template +AST_MATCHER_P(CXXDependentScopeMemberExpr, memberHasSameNameAsBoundNode, + std::string, BindingID) { + auto MemberName = Node.getMember().getAsString(); + + return Builder->removeBindings( + [this, MemberName](const BoundNodesMap &Nodes) { + const auto &BN = Nodes.getNode(this->BindingID); + if (const auto *ND = BN.get ()) { + if (!isa (ND)) + return true; + return ND->getName() != MemberName; + } + return true; + }); +} + /// Matches C++ classes that are directly or indirectly derived from a class /// matching \c Base, or Objective-C classes that directly or indirectly /// subclass a class matching \c Base. diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -302,6 +302,7 @@ REGISTER_MATCHER(hasLocalStorage); REGISTER_MATCHER(hasLoopInit); REGISTER_MATCHER(hasLoopVariable); + REGISTER_MATCHER(hasMemberName); REGISTER_MATCHER(hasMethod); REGISTER_MATCHER(hasName); REGISTER_MATCHER(hasNullSelector); @@ -443,6 +444,7 @@ REGISTER_MATCHER(materializeTemporaryExpr); REGISTER_MATCHER(member); REGISTER_MATCHER(memberExpr); + REGISTER_MATCHER(memberHasSameNameAsBoundNode); REGISTER_MATCHER(memberPointerType); REGISTER_MATCHER(namedDecl); REGISTER_MATCHER(namesType); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -17,7 +17,6 @@ namespace clang { namespace ast_matchers { - TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesInFile) { StringRef input = R"cc( #define MY_MACRO(a) (4 + (a)) @@ -1629,6 +1628,95 @@ Constructor1Arg)); } +TEST(ASTMatchersTest, NamesMember_CXXDependentScopeMemberExpr) { + + // Member functions: + { + auto Code = "template struct S{ void mem(); }; template " + " void x() { S s; s.mem(); }"; + + EXPECT_TRUE(matches( + Code, + cxxDependentScopeMemberExpr( + hasObjectExpression(declRefExpr(hasType(templateSpecializationType( + hasDeclaration(classTemplateDecl(has(cxxRecordDecl( + has(cxxMethodDecl(hasName("mem")).bind("templMem")))))))))), + memberHasSameNameAsBoundNode("templMem")))); + + EXPECT_TRUE( + matches(Code, cxxDependentScopeMemberExpr(hasMemberName("mem")))); + } + + // Member variables: + { + auto Code = "template struct S{ int mem; }; template " + " void x() { S s; s.mem; }"; + + EXPECT_TRUE( + matches(Code, cxxDependentScopeMemberExpr(hasMemberName("mem")))); + + EXPECT_TRUE(matches( + Code, + cxxDependentScopeMemberExpr( + hasObjectExpression(declRefExpr(hasType(templateSpecializationType( + hasDeclaration(classTemplateDecl(has(cxxRecordDecl( + has(fieldDecl(hasName("mem")).bind("templMem")))))))))), + memberHasSameNameAsBoundNode("templMem")))); + } + + // static member variables: + { + auto Code = "template struct S{ static int mem; }; template " + " void x() { S s; s.mem; }"; + + EXPECT_TRUE( + matches(Code, cxxDependentScopeMemberExpr(hasMemberName("mem")))); + + EXPECT_TRUE(matches( + Code, + cxxDependentScopeMemberExpr( + hasObjectExpression(declRefExpr(hasType(templateSpecializationType( + hasDeclaration(classTemplateDecl(has(cxxRecordDecl( + has(varDecl(hasName("mem")).bind("templMem")))))))))), + memberHasSameNameAsBoundNode("templMem")))); + } + { + auto Code = R"cpp( +template +struct S { + bool operator==(int) const { return true; } +}; + +template +void func(T t) { + S s; + s.operator==(1); +} +)cpp"; + + EXPECT_TRUE(matches( + Code, cxxDependentScopeMemberExpr(hasMemberName("operator==")))); + } + + // other named decl: + { + auto Code = "template struct S{ static int mem; }; struct " + "mem{}; template " + " void x() { S s; s.mem; }"; + + EXPECT_TRUE(matches( + Code, + translationUnitDecl(has(cxxRecordDecl(hasName("mem"))), + hasDescendant(cxxDependentScopeMemberExpr())))); + + EXPECT_FALSE(matches( + Code, + translationUnitDecl(has(cxxRecordDecl(hasName("mem")).bind("templMem")), + hasDescendant(cxxDependentScopeMemberExpr( + memberHasSameNameAsBoundNode("templMem")))))); + } +} + TEST(ASTMatchersTest, ArgumentCountIs_CXXUnresolvedConstructExpr) { const auto *Code = "template struct S{}; template void "