[clang] 0d6e125 - Add new matchers for dependent names in templates
Stephen Kelly via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 6 13:03:56 PST 2020
Author: Stephen Kelly
Date: 2020-11-06T21:03:20Z
New Revision: 0d6e1251d79d07a03d8cf528e3537d41d4285680
URL: https://github.com/llvm/llvm-project/commit/0d6e1251d79d07a03d8cf528e3537d41d4285680
DIFF: https://github.com/llvm/llvm-project/commit/0d6e1251d79d07a03d8cf528e3537d41d4285680.diff
LOG: Add new matchers for dependent names in templates
Differential Revision: https://reviews.llvm.org/D90767
Added:
Modified:
clang/docs/LibASTMatchersReference.html
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/lib/ASTMatchers/Dynamic/Registry.cpp
clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 14dec7d972b3..d348bc80e792 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -2543,6 +2543,28 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXDependentScopeMemberExpr.html">CXXDependentScopeMemberExpr</a>></td><td class="name" onclick="toggle('hasMemberName0')"><a name="hasMemberName0Anchor">hasMemberName</a></td><td>std::string N</td></tr>
+<tr><td colspan="4" class="doc" id="hasMemberName0"><pre>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()`
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXDependentScopeMemberExpr.html">CXXDependentScopeMemberExpr</a>></td><td class="name" onclick="toggle('isArrow2')"><a name="isArrow2Anchor">isArrow</a></td><td></td></tr>
<tr><td colspan="4" class="doc" id="isArrow2"><pre>Matches member expressions that are called with '->' as opposed
to '.'.
@@ -2569,6 +2591,42 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXDependentScopeMemberExpr.html">CXXDependentScopeMemberExpr</a>></td><td class="name" onclick="toggle('memberHasSameNameAsBoundNode0')"><a name="memberHasSameNameAsBoundNode0Anchor">memberHasSameNameAsBoundNode</a></td><td>std::string BindingID</td></tr>
+<tr><td colspan="4" class="doc" id="memberHasSameNameAsBoundNode0"><pre>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
+ at code
+cxxDependentScopeMemberExpr(
+ hasObjectExpression(declRefExpr(hasType(templateSpecializationType(
+ hasDeclaration(classTemplateDecl(has(cxxRecordDecl(has(
+ cxxMethodDecl(hasName("mem")).bind("templMem")
+ )))))
+ )))),
+ memberHasSameNameAsBoundNode("templMem")
+ )
+ at 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
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>></td><td class="name" onclick="toggle('isConst0')"><a name="isConst0Anchor">isConst</a></td><td></td></tr>
<tr><td colspan="4" class="doc" id="isConst0"><pre>Matches if the given method declaration is const.
@@ -2866,6 +2924,16 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXUnresolvedConstructExpr.html">CXXUnresolvedConstructExpr</a>></td><td class="name" onclick="toggle('argumentCountIs2')"><a name="argumentCountIs2Anchor">argumentCountIs</a></td><td>unsigned N</td></tr>
+<tr><td colspan="4" class="doc" id="argumentCountIs2"><pre>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);
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html">CallExpr</a>></td><td class="name" onclick="toggle('argumentCountIs0')"><a name="argumentCountIs0Anchor">argumentCountIs</a></td><td>unsigned N</td></tr>
<tr><td colspan="4" class="doc" id="argumentCountIs0"><pre>Checks that a call expression or a constructor call expression has
a specific number of arguments (including absent default arguments).
@@ -3923,8 +3991,8 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCMessageExpr.html">ObjCMessageExpr</a>></td><td class="name" onclick="toggle('argumentCountIs2')"><a name="argumentCountIs2Anchor">argumentCountIs</a></td><td>unsigned N</td></tr>
-<tr><td colspan="4" class="doc" id="argumentCountIs2"><pre>Checks that a call expression or a constructor call expression has
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCMessageExpr.html">ObjCMessageExpr</a>></td><td class="name" onclick="toggle('argumentCountIs3')"><a name="argumentCountIs3Anchor">argumentCountIs</a></td><td>unsigned N</td></tr>
+<tr><td colspan="4" class="doc" id="argumentCountIs3"><pre>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 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CXXUnresolvedConstructExpr.html">CXXUnresolvedConstructExpr</a>></td><td class="name" onclick="toggle('hasArgument2')"><a name="hasArgument2Anchor">hasArgument</a></td><td>unsigned N, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasArgument2"><pre>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); }
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html">CallExpr</a>></td><td class="name" onclick="toggle('callee1')"><a name="callee1Anchor">callee</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>> InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="callee1"><pre>Matches if the call expression's callee's declaration matches the
given matcher.
@@ -7180,8 +7258,8 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
</pre></td></tr>
-<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCMessageExpr.html">ObjCMessageExpr</a>></td><td class="name" onclick="toggle('hasArgument2')"><a name="hasArgument2Anchor">hasArgument</a></td><td>unsigned N, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
-<tr><td colspan="4" class="doc" id="hasArgument2"><pre>Matches the n'th argument of a call expression or a constructor
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1ObjCMessageExpr.html">ObjCMessageExpr</a>></td><td class="name" onclick="toggle('hasArgument3')"><a name="hasArgument3Anchor">hasArgument</a></td><td>unsigned N, Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasArgument3"><pre>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
index 73f898a08365..fea7b83904e9 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2835,6 +2835,80 @@ extern const internal::VariadicFunction<
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 <typename T>
+/// struct S {
+/// void mem();
+/// };
+/// template <typename T>
+/// void x() {
+/// S<T> 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 <typename T>
+/// struct S {
+/// void mem();
+/// };
+/// template <typename T>
+/// void x() {
+/// S<T> 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<NamedDecl>()) {
+ if (!isa<FieldDecl, CXXMethodDecl, VarDecl>(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
index 00e6abbb477b..3374ccc86181 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -302,6 +302,7 @@ RegistryMaps::RegistryMaps() {
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 @@ RegistryMaps::RegistryMaps() {
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
index d37b53e8f114..61dc1a9ee939 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1629,6 +1629,95 @@ TEST_P(ASTMatchersTest, ArgumentCountIs_CXXConstructExpr) {
Constructor1Arg));
}
+TEST(ASTMatchersTest, NamesMember_CXXDependentScopeMemberExpr) {
+
+ // Member functions:
+ {
+ auto Code = "template <typename T> struct S{ void mem(); }; template "
+ "<typename T> void x() { S<T> 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 <typename T> struct S{ int mem; }; template "
+ "<typename T> void x() { S<T> 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 <typename T> struct S{ static int mem; }; template "
+ "<typename T> void x() { S<T> 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 <typename T>
+struct S {
+ bool operator==(int) const { return true; }
+};
+
+template <typename T>
+void func(T t) {
+ S<T> s;
+ s.operator==(1);
+}
+)cpp";
+
+ EXPECT_TRUE(matches(
+ Code, cxxDependentScopeMemberExpr(hasMemberName("operator=="))));
+ }
+
+ // other named decl:
+ {
+ auto Code = "template <typename T> struct S{ static int mem; }; struct "
+ "mem{}; template "
+ "<typename T> void x() { S<T> 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 <typename T> struct S{}; template <typename T> void "
More information about the cfe-commits
mailing list