[clang-tools-extra] [clang-tidy] Skip overloaded functions in modernize-use-string-view (PR #183921)
Zinovy Nis via cfe-commits
cfe-commits at lists.llvm.org
Sun Apr 5 00:51:46 PDT 2026
https://github.com/irishrover updated https://github.com/llvm/llvm-project/pull/183921
>From 69736c0da2dfe630870d8798fb5b8e19557f6b0e Mon Sep 17 00:00:00 2001
From: Zinovy Nis <zinovy.nis at gmail.com>
Date: Sat, 28 Feb 2026 17:33:50 +0300
Subject: [PATCH 1/2] [clang-tidy] Skip overloaded functions in
modernize-use-string-view
---
.../modernize/UseStringViewCheck.cpp | 24 +++++++++++++++++++
.../checkers/modernize/use-string-view.cpp | 7 ++++++
2 files changed, 31 insertions(+)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStringViewCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStringViewCheck.cpp
index f5a51c0baba33..4d71a4a0ceb18 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStringViewCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStringViewCheck.cpp
@@ -21,6 +21,29 @@ using namespace clang::ast_matchers;
namespace clang::tidy::modernize {
+namespace {
+AST_MATCHER(FunctionDecl, isOverloaded) {
+ const DeclarationName Name = Node.getDeclName();
+ // Skip lambda-like functions
+ if (Name.isEmpty())
+ return false;
+ const DeclContext *DC = Node.getDeclContext();
+ auto LookupResult = DC->lookup(Name);
+ size_t UniqueSignatures = 0;
+ llvm::SmallPtrSet<const FunctionDecl *, 2> SeenFunctions;
+ for (NamedDecl *ND : LookupResult) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
+ if (SeenFunctions.insert(FD->getCanonicalDecl()).second) {
+ UniqueSignatures++;
+ if (UniqueSignatures > 1)
+ return true;
+ }
+ }
+ }
+ return false;
+}
+} // namespace
+
static constexpr StringRef StringViewClassKey = "string";
static constexpr StringRef WStringViewClassKey = "wstring";
static constexpr StringRef U8StringViewClassKey = "u8string";
@@ -81,6 +104,7 @@ void UseStringViewCheck::registerMatchers(MatchFinder *Finder) {
functionDecl(
isDefinition(),
unless(anyOf(VirtualOrOperator, IgnoredFunctionsMatcher,
+ isOverloaded(),
ast_matchers::isExplicitTemplateSpecialization())),
returns(IsStdString), hasDescendant(returnStmt()),
unless(hasDescendant(returnStmt(hasReturnValue(unless(
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-string-view.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-string-view.cpp
index 26a72c1c242e1..81d779859f8bd 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-string-view.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-string-view.cpp
@@ -357,6 +357,13 @@ std::string lambda() {
}();
}
+namespace overloads {
+std::string dbl2str(double f);
+std::string overload(int) { return "int"; }
+std::string overload(double f) { return "f=" + dbl2str(f); }
+std::string overload(std::string) { return "string"; }
+}
+
struct TemplateString {
static constexpr char* val = "TEMPLATE";
template<typename T>
>From c1fa16220ab5683d9a2e22d8908a4ffd675affdc Mon Sep 17 00:00:00 2001
From: Zinovy Nis <zinovy.nis at gmail.com>
Date: Thu, 5 Mar 2026 21:38:56 +0300
Subject: [PATCH 2/2] Extend the tests
---
.../modernize/UseStringViewCheck.cpp | 23 +++--
.../checks/modernize/use-string-view.rst | 1 +
.../checkers/modernize/use-string-view.cpp | 86 ++++++++++++++++++-
3 files changed, 102 insertions(+), 8 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStringViewCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStringViewCheck.cpp
index 4d71a4a0ceb18..29e5bdb65632e 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseStringViewCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseStringViewCheck.cpp
@@ -24,7 +24,7 @@ namespace clang::tidy::modernize {
namespace {
AST_MATCHER(FunctionDecl, isOverloaded) {
const DeclarationName Name = Node.getDeclName();
- // Skip lambda-like functions
+ // Sanity check
if (Name.isEmpty())
return false;
const DeclContext *DC = Node.getDeclContext();
@@ -32,12 +32,21 @@ AST_MATCHER(FunctionDecl, isOverloaded) {
size_t UniqueSignatures = 0;
llvm::SmallPtrSet<const FunctionDecl *, 2> SeenFunctions;
for (NamedDecl *ND : LookupResult) {
- if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
- if (SeenFunctions.insert(FD->getCanonicalDecl()).second) {
- UniqueSignatures++;
- if (UniqueSignatures > 1)
- return true;
- }
+ const FunctionDecl *FD = nullptr;
+ if (const auto *Func = dyn_cast<FunctionDecl>(ND)) {
+ // Regular functions
+ FD = Func;
+ } else if (const auto *USD = dyn_cast<UsingShadowDecl>(ND)) {
+ // Overloads via "using ns::func_name"
+ FD = dyn_cast<FunctionDecl>(USD->getTargetDecl());
+ } else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(ND)) {
+ // Templated functions
+ FD = FTD->getTemplatedDecl();
+ }
+ if (FD && SeenFunctions.insert(FD->getCanonicalDecl()).second) {
+ UniqueSignatures++;
+ if (UniqueSignatures > 1)
+ return true;
}
}
return false;
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-string-view.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-string-view.rst
index 157bb1f52092e..c72a0480c0eb8 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-string-view.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-string-view.rst
@@ -83,6 +83,7 @@ Limitations
* ``auto Trailing() -> std::string { return "Trailing"; }`` warns, doesn't fix
* returnings from lambda
* complicated macro and templated code
+ * overloaded functions and methods
In some cases the fixed code will not compile due to lack of conversion from
``std::string_view`` to ``std::string``. It can be fixed (preferably) by
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-string-view.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-string-view.cpp
index 81d779859f8bd..07c7eaff4a4c3 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-string-view.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-string-view.cpp
@@ -203,6 +203,44 @@ MyString<wchar_t> aliasedWChar() {
return L"aliasedWChar";
}
+namespace overload_funcs_redeclared {
+std::basic_string<char> overload(int);
+std::string overload(int);
+std::string overload(int) { return "int"; }
+// CHECK-MESSAGES:[[@LINE-1]]:1: warning: consider using 'std::string_view' to avoid unnecessary copying and allocations [modernize-use-string-view]
+// CHECK-FIXES: std::string_view overload(int) { return "int"; }
+}
+
+namespace overload_non_func {
+struct overload {};
+std::string overload(int) { return "int"; }
+// CHECK-MESSAGES:[[@LINE-1]]:1: warning: consider using 'std::string_view' to avoid unnecessary copying and allocations [modernize-use-string-view]
+// CHECK-FIXES: std::string_view overload(int) { return "int"; }
+}
+
+namespace overload_with_inline {
+ inline namespace inline_namespace {
+ std::string overload(int) { return "int"; }
+// CHECK-MESSAGES:[[@LINE-1]]:5: warning: consider using 'std::string_view' to avoid unnecessary copying and allocations [modernize-use-string-view]
+// CHECK-FIXES: std::string_view overload(int) { return "int"; }
+ }
+ inline namespace regular_namespace {
+ std::string overload(int) { return "int"; }
+// CHECK-MESSAGES:[[@LINE-1]]:5: warning: consider using 'std::string_view' to avoid unnecessary copying and allocations [modernize-use-string-view]
+// CHECK-FIXES: std::string_view overload(int) { return "int"; }
+ }
+}
+
+namespace overload_with_outer {
+namespace overload_with_templates {
+ template <typename T>
+ std::string overload(T) { return "T"; }
+ std::string overload(std::string) { return "string"; }
+}
+using overload_with_templates::overload;
+std::string overload(char*) { return "char*"; }
+}
+
// ==========================================================
// Negative tests
// ==========================================================
@@ -357,13 +395,59 @@ std::string lambda() {
}();
}
-namespace overloads {
+namespace overload_funcs {
std::string dbl2str(double f);
+// Skip overloaded functions
std::string overload(int) { return "int"; }
+// Because of this overload (non-literal return) the fix should not be applied
std::string overload(double f) { return "f=" + dbl2str(f); }
std::string overload(std::string) { return "string"; }
}
+namespace overload_methods {
+struct Foo {
+ // Skip overloaded methods
+ std::string overload(int) { return "int"; }
+ std::string overload(double f) { return "double"; }
+ std::string overload(std::string) { return "string"; }
+};
+}
+
+namespace overload_methods_nested_classes {
+struct Bar {
+ std::string overload(int) { return "int"; }
+ std::string overload(std::string) { return "string"; }
+
+ struct FooBar {
+ std::string overload(char*) { return "char*"; }
+ std::string overload(double f) { return "double"; }
+ };
+};
+}
+
+namespace overload_methods_nested_namespaces {
+namespace foo {
+ std::string overload(int) { return "int"; }
+ std::string overload(std::string) { return "string"; }
+}
+using foo::overload;
+std::string overload(char*) { return "char*"; }
+}
+
+namespace overload_methods_templated {
+ template <typename T>
+ std::string overload(T value) { return "T";}
+ std::string overload(int value) { return "int"; }
+}
+
+namespace two_overloads_with_inline {
+ inline namespace inline_namespace {
+ std::string overload(int) { return "int"; }
+ std::string overload(double) { return "double"; }
+ }
+ std::string overload(int) { return "int"; }
+}
+
struct TemplateString {
static constexpr char* val = "TEMPLATE";
template<typename T>
More information about the cfe-commits
mailing list