[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
Wed Mar 11 10:30:25 PDT 2026
https://github.com/irishrover updated https://github.com/llvm/llvm-project/pull/183921
>From 5d517060b6dc37b1568b5af332e763b81ad35eba 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 1dc4cc9856549..f03c9c008389a 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 2a00a7200ee76..832af0c26ef70 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
@@ -365,6 +365,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 c6a2182af238655f16544447f50e046e305edd8b 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 | 15 ++++-
.../checkers/modernize/use-string-view.cpp | 55 ++++++++++++++++++-
2 files changed, 67 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseStringViewCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseStringViewCheck.cpp
index f03c9c008389a..638b02c175576 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,7 +32,18 @@ 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)) {
+ 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) {
if (SeenFunctions.insert(FD->getCanonicalDecl()).second) {
UniqueSignatures++;
if (UniqueSignatures > 1)
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 832af0c26ef70..a43e846eea40d 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
@@ -211,6 +211,21 @@ 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"; }
+}
+
// ==========================================================
// Negative tests
// ==========================================================
@@ -365,13 +380,51 @@ 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"; }
+}
+
struct TemplateString {
static constexpr char* val = "TEMPLATE";
template<typename T>
More information about the cfe-commits
mailing list