[clang-tools-extra] [clang-tidy] Fix false negatives in performance-faster-string-find with libstdc++ (PR #185559)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 9 23:11:29 PDT 2026
https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/185559
>From cb8e9fdb2c62fc3dfa471ca6cf3392d259c9cb4d Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Sun, 8 Mar 2026 17:50:48 +0800
Subject: [PATCH 1/4] [clang-tidy] Fix false negatives in
performance-faster-string-find with libstdc++
---
.../performance/FasterStringFindCheck.cpp | 15 ++---------
.../performance/FasterStringFindCheck.h | 3 +++
clang-tools-extra/docs/ReleaseNotes.rst | 9 ++++---
.../performance/faster-string-find.cpp | 26 ++++++++++++++++++-
4 files changed, 36 insertions(+), 17 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp b/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp
index 52a4d70e15265..8a96d41944748 100644
--- a/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp
+++ b/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp
@@ -42,16 +42,6 @@ makeCharacterLiteral(const StringLiteral *Literal) {
return Result;
}
-namespace {
-
-AST_MATCHER_FUNCTION(ast_matchers::internal::Matcher<Expr>,
- hasSubstitutedType) {
- return hasType(qualType(anyOf(substTemplateTypeParmType(),
- hasDescendant(substTemplateTypeParmType()))));
-}
-
-} // namespace
-
FasterStringFindCheck::FasterStringFindCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
@@ -77,9 +67,8 @@ void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) {
callee(functionDecl(InterestingStringFunction).bind("func")),
anyOf(argumentCountIs(1), argumentCountIs(2)),
hasArgument(0, SingleChar),
- on(expr(hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
- recordDecl(hasAnyName(StringLikeClasses)))))),
- unless(hasSubstitutedType())))),
+ on(expr(hasType(hasUnqualifiedDesugaredType(recordType(
+ hasDeclaration(recordDecl(hasAnyName(StringLikeClasses))))))))),
this);
}
diff --git a/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.h b/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.h
index 74067c1f5792d..f5d36d805498e 100644
--- a/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.h
+++ b/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.h
@@ -28,6 +28,9 @@ class FasterStringFindCheck : public ClangTidyCheck {
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index cf8dd0dba9f12..8f4bb6e247ca8 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -231,9 +231,12 @@ Changes in existing checks
- Improved the ignore list to correctly handle ``typedef`` and ``enum``.
- Improved :doc:`performance-faster-string-find
- <clang-tidy/checks/performance/faster-string-find>` check to
- analyze calls to the ``starts_with``, ``ends_with``, and ``contains``
- string member functions.
+ <clang-tidy/checks/performance/faster-string-find>` check:
+
+ - Analyze calls to the ``starts_with``, ``ends_with``, and ``contains``
+ string member functions.
+
+ - Fixes false negatives when using ``std::set`` from ``libstdc++``.
- Improved :doc:`performance-inefficient-vector-operation
<clang-tidy/checks/performance/inefficient-vector-operation>` check by
diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
index 83824c62494e7..55f52643d89ab 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
@@ -38,6 +38,16 @@ struct basic_string_view {
typedef basic_string_view<char> string_view;
typedef basic_string_view<wchar_t> wstring_view;
+
+template <typename T> struct set {
+ struct iterator {
+ const T& operator*() const;
+ iterator& operator++();
+ bool operator!=(const iterator&) const;
+ };
+ iterator begin() const;
+ iterator end() const;
+};
} // namespace std
namespace llvm {
@@ -143,9 +153,14 @@ int FindTemplateDependant(T value) {
}
template <typename T>
int FindTemplateNotDependant(T pos) {
+ // Ignored since the type of `pos` is dependent, the call cannot be completely resolved without instantiating the template.
return std::string().find("A", pos);
+}
+template <typename T>
+int FindTemplateNotDependant2() {
+ return std::string().find("A");
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: 'find' called with a string literal
- // CHECK-FIXES: return std::string().find('A', pos);
+ // CHECK-FIXES: return std::string().find('A');
}
int FindStr() {
@@ -160,3 +175,12 @@ int Macros() {
// CHECK-MESSAGES: [[@LINE-1]]:10: warning: 'find' called with a string literal
// CHECK-MESSAGES: [[@LINE-2]]:37: warning: 'find' called with a string literal
}
+
+void IteratorInLibStdCXX() {
+ std::set<std::string> s;
+ for (const auto &str : s) {
+ str.find("a");
+ // CHECK-MESSAGES: [[@LINE-1]]:14: warning: 'find' called with a string literal
+ // CHECK-FIXES: str.find('a');
+ }
+}
>From 2dd086b744d6bf89aa8996a16ec49e5a26f1c0ec Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Tue, 10 Mar 2026 10:51:11 +0800
Subject: [PATCH 2/4] ~
---
.../test/clang-tidy/checkers/performance/faster-string-find.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
index bcc500fc047d3..f358042d426a0 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
@@ -5,6 +5,7 @@
// RUN: '::llvm::StringRef;'}}"
#include <string>
+// Mock for libstdc++
namespace std {
template <typename T> struct set {
struct iterator {
>From e4583e0d629a0cfb7df65896903994535c319115 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Tue, 10 Mar 2026 11:03:05 +0800
Subject: [PATCH 3/4] fixup fixup fixup
---
.../clang-tidy/performance/FasterStringFindCheck.cpp | 5 ++---
.../clang-tidy/checkers/performance/faster-string-find.cpp | 3 +--
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp b/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp
index 96ad60bb2fdd4..1d9325166e341 100644
--- a/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp
+++ b/clang-tools-extra/clang-tidy/performance/FasterStringFindCheck.cpp
@@ -58,9 +58,8 @@ void FasterStringFindCheck::registerMatchers(MatchFinder *Finder) {
const auto SingleChar =
ignoringParenCasts(stringLiteral(hasSize(1)).bind("literal"));
- const auto StringExpr =
- expr(hasType(hasUnqualifiedDesugaredType(recordType(
- hasDeclaration(recordDecl(hasAnyName(StringLikeClasses)))))));
+ const auto StringExpr = expr(hasType(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(recordDecl(hasAnyName(StringLikeClasses)))))));
const auto InterestingStringFunction = hasAnyName(
"find", "rfind", "find_first_of", "find_first_not_of", "find_last_of",
diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
index f358042d426a0..dbb4fb8c1b0ad 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
@@ -5,7 +5,6 @@
// RUN: '::llvm::StringRef;'}}"
#include <string>
-// Mock for libstdc++
namespace std {
template <typename T> struct set {
struct iterator {
@@ -139,7 +138,7 @@ template <typename T>
int FindTemplateNotDependant2() {
return std::string().find("A");
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: 'find' called with a string literal
- // CHECK-FIXES: return std::string().find('A');
+ // CHECK-FIXES: return std::string().find('A');
}
int FindStr() {
>From f909b404d324bb4c020743ec91da812d5707ecb3 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Tue, 10 Mar 2026 14:11:08 +0800
Subject: [PATCH 4/4] try again
---
.../performance/faster-string-find.cpp | 33 +++++++------------
1 file changed, 11 insertions(+), 22 deletions(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp b/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
index dbb4fb8c1b0ad..63b229ccbb8e6 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/performance/faster-string-find.cpp
@@ -1,22 +1,10 @@
-// RUN: %check_clang_tidy %s performance-faster-string-find %t
+// RUN: %check_clang_tidy %s performance-faster-string-find %t -- -- -fno-delayed-template-parsing
// RUN: %check_clang_tidy -check-suffix=CUSTOM %s performance-faster-string-find %t -- \
// RUN: -config="{CheckOptions: \
// RUN: {performance-faster-string-find.StringLikeClasses: \
-// RUN: '::llvm::StringRef;'}}"
+// RUN: '::llvm::StringRef;'}}" -- -fno-delayed-template-parsing
#include <string>
-namespace std {
-template <typename T> struct set {
- struct iterator {
- const T& operator*() const;
- iterator& operator++();
- bool operator!=(const iterator&) const;
- };
- iterator begin() const;
- iterator end() const;
-};
-} // namespace std
-
namespace llvm {
struct StringRef {
int find(const char *) const;
@@ -27,6 +15,10 @@ struct NotStringRef {
int find(const char *);
};
+template <typename T> struct Wrapper {
+ T value;
+};
+
void StringFind() {
std::string Str;
@@ -138,7 +130,7 @@ template <typename T>
int FindTemplateNotDependant2() {
return std::string().find("A");
// CHECK-MESSAGES: [[@LINE-1]]:29: warning: 'find' called with a string literal
- // CHECK-FIXES: return std::string().find('A');
+ // CHECK-FIXES: return std::string().find('A');
}
int FindStr() {
@@ -154,11 +146,8 @@ int Macros() {
// CHECK-MESSAGES: [[@LINE-2]]:37: warning: 'find' called with a string literal
}
-void IteratorInLibStdCXX() {
- std::set<std::string> s;
- for (const auto &str : s) {
- str.find("a");
- // CHECK-MESSAGES: [[@LINE-1]]:14: warning: 'find' called with a string literal
- // CHECK-FIXES: str.find('a');
- }
+void SubstitutedTemplateType() {
+ Wrapper<std::string>().value.find("a");
+ // CHECK-MESSAGES: [[@LINE-1]]:37: warning: 'find' called with a string literal
+ // CHECK-FIXES: Wrapper<std::string>().value.find('a');
}
More information about the cfe-commits
mailing list