[clang] [ASMMatchers] Extend hasName matcher when matching templates (PR #100349)
Nathan James via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 24 04:42:16 PDT 2024
https://github.com/njames93 created https://github.com/llvm/llvm-project/pull/100349
Allow users to match all record instantiations by using <> as a wildcard.
With
```cpp
template <typename T>
struct Foo {
void Bar();
};
```
The following code:
```cpp
Foo<int>{}.Bar();
```
Will match against:
```
callExpr(callee(cxxMethodDecl(hasName("Bar"))))
callExpr(callee(cxxMethodDecl(hasName("Foo<int>::Bar"))))
callExpr(callee(cxxMethodDecl(hasName("Foo<>::Bar"))))
```
>From ee05cd6a775d1dce12036757d1f2c90c7baa65fc Mon Sep 17 00:00:00 2001
From: Nathan James <n.james93 at hotmail.co.uk>
Date: Wed, 24 Jul 2024 12:38:37 +0100
Subject: [PATCH] [ASMMatchers] Extend hasName matcher when matching templates
Allow users to match all record instantiations by using <> as a wildcard
---
clang/include/clang/ASTMatchers/ASTMatchers.h | 16 ++++++++++
clang/lib/ASTMatchers/ASTMatchersInternal.cpp | 30 +++++++++++++++++--
.../ASTMatchers/ASTMatchersNarrowingTest.cpp | 24 +++++++++++++++
3 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index ca44c3ee08565..4fa04f3af5909 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -3076,6 +3076,22 @@ inline internal::BindableMatcher<Stmt> sizeOfExpr(
/// \code
/// namespace a { namespace b { class X; } }
/// \endcode
+///
+/// Qualified names in templated classes can be matched explicitly or implicity
+/// by specifying the template type or using empty angle brackets to match any
+/// template.
+///
+/// Example matches:
+/// - callExpr(callee(functionDecl(hasName("Foo<int>::Bar"))))
+/// - callExpr(callee(functionDecl(hasName("Foo<>::Bar"))))
+/// \code
+/// template<typename T> class Foo{
+/// static void Bar();
+/// };
+/// void Func() {
+/// Foo<int>::Bar();
+/// }
+/// \endcode
inline internal::Matcher<NamedDecl> hasName(StringRef Name) {
return internal::Matcher<NamedDecl>(
new internal::HasNameMatcher({std::string(Name)}));
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index bf87b1aa0992a..6d1d2507de4b7 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -638,6 +638,30 @@ bool HasNameMatcher::matchesNodeFullFast(const NamedDecl &Node) const {
return Patterns.foundMatch(/*AllowFullyQualified=*/true);
}
+static std::optional<StringRef> consumePatternBack(StringRef Pattern, StringRef Target){
+ while(!Pattern.empty()){
+ auto Index = Pattern.rfind("<>");
+ if (Index == StringRef::npos){
+ if (Target.consume_back(Pattern)) return Target;
+ return {};
+ }
+ auto Suffix = Pattern.substr(Index + 1);
+ if (!Target.consume_back(Suffix)) return {};
+ auto BracketCount = 1;
+ while (BracketCount) {
+ if (Target.empty()) return {};
+ switch(Target.back()){
+ case '<': --BracketCount; break;
+ case '>': ++BracketCount; break;
+ default: break;
+ }
+ Target = Target.drop_back();
+ }
+ Pattern = Pattern.take_front(Index);
+ }
+ return Target;
+}
+
bool HasNameMatcher::matchesNodeFullSlow(const NamedDecl &Node) const {
const bool SkipUnwrittenCases[] = {false, true};
for (bool SkipUnwritten : SkipUnwrittenCases) {
@@ -653,10 +677,10 @@ bool HasNameMatcher::matchesNodeFullSlow(const NamedDecl &Node) const {
for (const StringRef Pattern : Names) {
if (Pattern.starts_with("::")) {
- if (FullName == Pattern)
+ if (auto Result = consumePatternBack(Pattern, FullName); Result && Result->empty()){
return true;
- } else if (FullName.ends_with(Pattern) &&
- FullName.drop_back(Pattern.size()).ends_with("::")) {
+ }
+ } else if (auto Result = consumePatternBack(Pattern, FullName); Result && Result->ends_with("::")) {
return true;
}
}
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index f26140675fd46..03e5fb3562dca 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -2664,6 +2664,30 @@ TEST_P(ASTMatchersTest, HasName_QualifiedStringMatchesThroughLinkage) {
EXPECT_TRUE(notMatches(code, functionDecl(hasName("::test"))));
}
+TEST_P(ASTMatchersTest, HasName_TemplateStrip) {
+ if (!GetParam().isCXX()) {
+ return;
+ }
+
+ StringRef Code = "template<typename T> struct Foo{void Bar() const;}; void Func() { Foo<int> Item; Item.Bar(); }";
+
+ EXPECT_TRUE(matches(Code, callExpr(callee(cxxMethodDecl(hasName("Bar"))))));
+ EXPECT_TRUE(matches(Code, callExpr(callee(cxxMethodDecl(hasName("Foo<int>::Bar"))))));
+ EXPECT_TRUE(matches(Code, callExpr(callee(cxxMethodDecl(hasName("::Foo<int>::Bar"))))));
+ EXPECT_TRUE(matches(Code, callExpr(callee(cxxMethodDecl(hasName("::Foo<>::Bar"))))));
+ EXPECT_TRUE(notMatches(Code, callExpr(callee(cxxMethodDecl(hasName("::Foo<::Bar"))))));
+ EXPECT_TRUE(notMatches(Code, callExpr(callee(cxxMethodDecl(hasName("::Foo<long>::Bar"))))));
+
+ if (GetParam().isCXX11OrLater()){
+ Code = "template<typename T> struct Foo{void Bar() const;}; void Func() { Foo<Foo<int>> Item; Item.Bar(); }";
+ EXPECT_TRUE(matches(Code, callExpr(callee(cxxMethodDecl(hasName("Bar"))))));
+ EXPECT_TRUE(matches(Code, callExpr(callee(cxxMethodDecl(hasName("Foo<>::Bar"))))));
+ EXPECT_TRUE(matches(Code, callExpr(callee(cxxMethodDecl(hasName("::Foo<>::Bar"))))));
+ EXPECT_TRUE(matches(Code, callExpr(callee(cxxMethodDecl(hasName("Foo<Foo<>>::Bar"))))));
+ EXPECT_TRUE(matches(Code, callExpr(callee(cxxMethodDecl(hasName("::Foo<Foo<>>::Bar"))))));
+ }
+}
+
TEST_P(ASTMatchersTest, HasAnyName) {
if (!GetParam().isCXX()) {
// FIXME: Add a test for `hasAnyName()` that does not depend on C++.
More information about the cfe-commits
mailing list