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.jame...@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++. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits