From b7d665bf49d20faf9bb22f782503391dcc361cab Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 16 Nov 2025 23:56:06 +0300 Subject: [PATCH 01/19] Implementation template from DeepSeek --- .../clang-tidy/misc/CMakeLists.txt | 1 + .../clang-tidy/misc/MiscTidyModule.cpp | 3 + .../misc/ShadowedNamespaceFunctionCheck.cpp | 131 ++++++++++++++++++ .../misc/ShadowedNamespaceFunctionCheck.h | 39 ++++++ clang-tools-extra/docs/ReleaseNotes.rst | 5 + .../docs/clang-tidy/checks/list.rst | 2 + .../misc/shadowed-namespace-function.rst | 6 + .../misc/shadowed-namespace-function.cpp | 13 ++ 8 files changed, 200 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt index 6214ee92927f2..d18af237283ff 100644 --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -34,6 +34,7 @@ add_clang_library(clangTidyMiscModule STATIC NonPrivateMemberVariablesInClassesCheck.cpp OverrideWithDifferentVisibilityCheck.cpp RedundantExpressionCheck.cpp + ShadowedNamespaceFunctionCheck.cpp StaticAssertCheck.cpp ThrowByValueCatchByReferenceCheck.cpp UnconventionalAssignOperatorCheck.cpp diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp index 347fa2a82e43c..2706fe647fe14 100644 --- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -24,6 +24,7 @@ #include "NonPrivateMemberVariablesInClassesCheck.h" #include "OverrideWithDifferentVisibilityCheck.h" #include "RedundantExpressionCheck.h" +#include "ShadowedNamespaceFunctionCheck.h" #include "StaticAssertCheck.h" #include "ThrowByValueCatchByReferenceCheck.h" #include "UnconventionalAssignOperatorCheck.h" @@ -65,6 +66,8 @@ class MiscModule : public ClangTidyModule { "misc-non-private-member-variables-in-classes"); CheckFactories.registerCheck( "misc-redundant-expression"); + CheckFactories.registerCheck( + "misc-shadowed-namespace-function"); CheckFactories.registerCheck("misc-static-assert"); CheckFactories.registerCheck( "misc-throw-by-value-catch-by-reference"); diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp new file mode 100644 index 0000000000000..d9fcfccfb85fb --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ShadowedNamespaceFunctionCheck.h" +#include "../utils/FixItHintUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" + +using namespace clang; +using namespace clang::ast_matchers; +using namespace clang::tidy; + +namespace clang { +namespace tidy { +namespace misc { + +void ShadowedNamespaceFunctionCheck::registerMatchers(MatchFinder *Finder) { + // Simple matcher for all function definitions + Finder->addMatcher( + functionDecl( + isDefinition() + ).bind("func"), + this + ); +} + +void ShadowedNamespaceFunctionCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *Func = Result.Nodes.getNodeAs("func"); + + if (!Func || !Result.SourceManager) + return; + + // Skip if not in global namespace + const DeclContext *DC = Func->getDeclContext(); + if (!DC->isTranslationUnit()) + return; + + // Skip templates, static functions, main, etc. + if (Func->isTemplated() || Func->isStatic() || + Func->getName() == "main" || Func->isImplicit()) + return; + + std::string FuncName = Func->getNameAsString(); + if (FuncName.empty()) + return; + + ASTContext *Context = Result.Context; + + // Look for functions with the same name in namespaces + const FunctionDecl *ShadowedFunc = nullptr; + const NamespaceDecl *ShadowedNamespace = nullptr; + + // Traverse all declarations in the translation unit + for (const auto *Decl : Context->getTranslationUnitDecl()->decls()) { + if (const auto *NS = dyn_cast(Decl)) { + findShadowedInNamespace(NS, Func, FuncName, ShadowedFunc, ShadowedNamespace); + if (ShadowedFunc) break; + } + } + + if (!ShadowedFunc || !ShadowedNamespace) + return; + + // Generate warning message + std::string NamespaceName = ShadowedNamespace->getNameAsString(); + auto Diag = diag(Func->getLocation(), + "free function %0 shadows %1::%2") + << Func->getDeclName() + << NamespaceName + << ShadowedFunc->getDeclName(); + + // Generate fixit hint to add namespace qualification + SourceLocation NameLoc = Func->getLocation(); + if (NameLoc.isValid()) { + std::string Fix = NamespaceName + "::"; + Diag << FixItHint::CreateInsertion(NameLoc, Fix); + } + + // Note: Also show where the shadowed function is declared + diag(ShadowedFunc->getLocation(), + "function %0 declared here", DiagnosticIDs::Note) + << ShadowedFunc->getDeclName(); +} + +void ShadowedNamespaceFunctionCheck::findShadowedInNamespace( + const NamespaceDecl *NS, + const FunctionDecl *GlobalFunc, + const std::string &GlobalFuncName, + const FunctionDecl *&ShadowedFunc, + const NamespaceDecl *&ShadowedNamespace) { + + // Skip anonymous namespaces + if (NS->isAnonymousNamespace()) + return; + + for (const auto *Decl : NS->decls()) { + // Check nested namespaces + if (const auto *NestedNS = dyn_cast(Decl)) { + findShadowedInNamespace(NestedNS, GlobalFunc, GlobalFuncName, + ShadowedFunc, ShadowedNamespace); + if (ShadowedFunc) return; + } + + // Check functions + if (const auto *Func = dyn_cast(Decl)) { + // Skip if it's the same function, templates, or definitions + if (Func == GlobalFunc || Func->isTemplated() || + Func->isThisDeclarationADefinition()) + continue; + + if (Func->getNameAsString() == GlobalFuncName) { + ShadowedFunc = Func; + ShadowedNamespace = NS; + return; + } + } + } +} + +} // namespace misc +} // namespace tidy +} // namespace clang + diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h new file mode 100644 index 0000000000000..e3383f6f06332 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SHADOWEDNAMESPACEFUNCTIONCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SHADOWEDNAMESPACEFUNCTIONCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::misc { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/misc/shadowed-namespace-function.html +class ShadowedNamespaceFunctionCheck : public ClangTidyCheck { +public: + ShadowedNamespaceFunctionCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } +private: + void findShadowedInNamespace(const NamespaceDecl *NS, + const FunctionDecl *GlobalFunc, + const std::string &GlobalFuncName, + const FunctionDecl *&ShadowedFunc, + const NamespaceDecl *&ShadowedNamespace); +}; + +} // namespace clang::tidy::misc + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SHADOWEDNAMESPACEFUNCTIONCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 666865cfb2fcd..33a6919c2f9c8 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -226,6 +226,11 @@ New checks Finds virtual function overrides with different visibility than the function in the base class. +- New :doc:`misc-shadowed-namespace-function + ` check. + + FIXME: Write a short description. + - New :doc:`readability-redundant-parentheses ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index e2875604af72b..4e8b752968e38 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -277,6 +277,8 @@ Clang-Tidy Checks :doc:`misc-non-private-member-variables-in-classes `, :doc:`misc-override-with-different-visibility `, :doc:`misc-redundant-expression `, "Yes" + :doc:`misc-shadowed-namespace-function `, "Yes" + :doc:`misc-shadowed-namespace-function `, "Yes" :doc:`misc-static-assert `, "Yes" :doc:`misc-throw-by-value-catch-by-reference `, :doc:`misc-unconventional-assign-operator `, diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst new file mode 100644 index 0000000000000..46367e56563cc --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst @@ -0,0 +1,6 @@ +.. title:: clang-tidy - misc-shadowed-namespace-function + +misc-shadowed-namespace-function +================================ + +FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp new file mode 100644 index 0000000000000..9f7af24cb4240 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp @@ -0,0 +1,13 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +void f1(); +namespace foo { + void f0(); + void f1(); +} +void f0() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f0' shadows foo::f0 [misc-shadowed-namespace-function] +// CHECK-FIXES: void foo::f0() {} +void f1() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f1' shadows foo::f1 [misc-shadowed-namespace-function] +// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes From e9de03d1438e7bfd59a6b89c7695421ea74c9583 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Sun, 16 Nov 2025 23:56:19 +0300 Subject: [PATCH 02/19] fix unit-test --- .../clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp | 6 +++--- .../checkers/misc/shadowed-namespace-function.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp index d9fcfccfb85fb..b006c5ef44763 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp @@ -72,14 +72,14 @@ void ShadowedNamespaceFunctionCheck::check( // Generate warning message std::string NamespaceName = ShadowedNamespace->getNameAsString(); auto Diag = diag(Func->getLocation(), - "free function %0 shadows %1::%2") + "free function %0 shadows '%1::%2'") << Func->getDeclName() << NamespaceName - << ShadowedFunc->getDeclName(); + << ShadowedFunc->getDeclName().getAsString(); // Generate fixit hint to add namespace qualification SourceLocation NameLoc = Func->getLocation(); - if (NameLoc.isValid()) { + if (NameLoc.isValid() && !Func->getPreviousDecl()) { std::string Fix = NamespaceName + "::"; Diag << FixItHint::CreateInsertion(NameLoc, Fix); } diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp index 9f7af24cb4240..920556cca0381 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp @@ -6,8 +6,8 @@ namespace foo { void f1(); } void f0() {} -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f0' shadows foo::f0 [misc-shadowed-namespace-function] +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f0' shadows 'foo::f0' [misc-shadowed-namespace-function] // CHECK-FIXES: void foo::f0() {} void f1() {} -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f1' shadows foo::f1 [misc-shadowed-namespace-function] +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f1' shadows 'foo::f1' [misc-shadowed-namespace-function] // CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes From 199ecec95322a37b95d8138bdd3df296241a34e4 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 00:03:28 +0300 Subject: [PATCH 03/19] Docs written by DeepSeek --- .../misc/shadowed-namespace-function.rst | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst index 46367e56563cc..b31455cfa0c31 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst @@ -3,4 +3,44 @@ misc-shadowed-namespace-function ================================ -FIXME: Describe what patterns does the check detect and why. Give examples. +Detects free functions in the global namespace that shadow functions declared +in other namespaces. This check helps prevent accidental shadowing of namespace +functions, which can lead to confusion about which function is being called and +potential linking errors. + +Examples +-------- + +.. code-block:: c++ + + namespace utils { + void process(); + void calculate(); + } + + // Warning: free function shadows utils::process + void process() {} + + // No warning - static function + static void calculate() {} + +The check will suggest adding the appropriate namespace qualification: + +.. code-block:: diff + + - void process() {} + + void utils::process() {} + +Options +------- + +None + +Limitations +----------- + +- Does not warn about functions in anonymous namespaces +- Does not warn about template functions +- Does not warn about static functions or member functions +- Does not warn about the ``main`` function +- Only considers functions declared before the global definition From 8018941598da61c57338f80407b8fa39ded3a923 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 00:11:33 +0300 Subject: [PATCH 04/19] short description by DeepSeek --- .../clang-tidy/misc/ShadowedNamespaceFunctionCheck.h | 2 +- clang-tools-extra/docs/ReleaseNotes.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h index e3383f6f06332..61f4cf8ed1c49 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h @@ -13,7 +13,7 @@ namespace clang::tidy::misc { -/// FIXME: Write a short description. +/// Detects free functions in global namespace that shadow functions from other namespaces. /// /// For the user-facing documentation see: /// https://clang.llvm.org/extra/clang-tidy/checks/misc/shadowed-namespace-function.html diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 33a6919c2f9c8..a1482d89b6042 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -229,7 +229,7 @@ New checks - New :doc:`misc-shadowed-namespace-function ` check. - FIXME: Write a short description. + Detects free functions in global namespace that shadow functions from other namespaces. - New :doc:`readability-redundant-parentheses ` check. From 320f6c765fe6288d2956d4b4498edf2d2d76b93f Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 00:12:49 +0300 Subject: [PATCH 05/19] fix list.rst --- clang-tools-extra/docs/clang-tidy/checks/list.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 4e8b752968e38..473d82033758f 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -278,7 +278,6 @@ Clang-Tidy Checks :doc:`misc-override-with-different-visibility `, :doc:`misc-redundant-expression `, "Yes" :doc:`misc-shadowed-namespace-function `, "Yes" - :doc:`misc-shadowed-namespace-function `, "Yes" :doc:`misc-static-assert `, "Yes" :doc:`misc-throw-by-value-catch-by-reference `, :doc:`misc-unconventional-assign-operator `, From 5994fc0d21f12fd6bdb629409329599eb2974165 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 00:45:46 +0300 Subject: [PATCH 06/19] more tests && fix the doc --- .../misc/ShadowedNamespaceFunctionCheck.cpp | 2 +- .../checks/misc/shadowed-namespace-function.rst | 1 - ...owed-namespace-function-anonymous-namespace.cpp | 11 +++++++++++ .../misc/shadowed-namespace-function-nested.cpp | 13 +++++++++++++ .../misc/shadowed-namespace-function-static.cpp | 9 +++++++++ .../misc/shadowed-namespace-function-template.cpp | 14 ++++++++++++++ 6 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-anonymous-namespace.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-nested.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-static.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp index b006c5ef44763..b0324ab79aab5 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp @@ -70,7 +70,7 @@ void ShadowedNamespaceFunctionCheck::check( return; // Generate warning message - std::string NamespaceName = ShadowedNamespace->getNameAsString(); + std::string NamespaceName = ShadowedNamespace->getQualifiedNameAsString(); auto Diag = diag(Func->getLocation(), "free function %0 shadows '%1::%2'") << Func->getDeclName() diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst index b31455cfa0c31..b82317fd0a748 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst @@ -43,4 +43,3 @@ Limitations - Does not warn about template functions - Does not warn about static functions or member functions - Does not warn about the ``main`` function -- Only considers functions declared before the global definition diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-anonymous-namespace.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-anonymous-namespace.cpp new file mode 100644 index 0000000000000..1689843464ae1 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-anonymous-namespace.cpp @@ -0,0 +1,11 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +namespace { void f1(); } +namespace foo { + void f0(); + void f1(); +} +namespace { +void f0() {} +void f1() {} +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-nested.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-nested.cpp new file mode 100644 index 0000000000000..41b130624bbaa --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-nested.cpp @@ -0,0 +1,13 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +void f1(); +namespace foo::foo2::foo3 { + void f0(); + void f1(); +} +void f0() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f0' shadows 'foo::foo2::foo3::f0' [misc-shadowed-namespace-function] +// CHECK-FIXES: void foo::foo2::foo3::f0() {} +void f1() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f1' shadows 'foo::foo2::foo3::f1' [misc-shadowed-namespace-function] +// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-static.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-static.cpp new file mode 100644 index 0000000000000..d1371c044d112 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-static.cpp @@ -0,0 +1,9 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +static void f1(); +namespace foo { + void f0(); + void f1(); +} +static void f0() {} +static void f1() {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp new file mode 100644 index 0000000000000..48924fcdb932d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp @@ -0,0 +1,14 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +template +void f1(); +namespace foo { + template + void f0(); + template + void f1(); +} +template +void f0() {} +template +void f1() {} From 4112759aea418b33204f8f19f571657c1f53ffdf Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 00:53:00 +0300 Subject: [PATCH 07/19] fix invalid fixit for correctly defined function --- .../misc/ShadowedNamespaceFunctionCheck.cpp | 3 +++ .../misc/shadowed-namespace-function-good.cpp | 9 +++++++++ .../misc/shadowed-namespace-function-good2.cpp | 11 +++++++++++ 3 files changed, 23 insertions(+) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good2.cpp diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp index b0324ab79aab5..ca095771c477a 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp @@ -69,6 +69,9 @@ void ShadowedNamespaceFunctionCheck::check( if (!ShadowedFunc || !ShadowedNamespace) return; + if (ShadowedFunc->getDefinition()) + return; + // Generate warning message std::string NamespaceName = ShadowedNamespace->getQualifiedNameAsString(); auto Diag = diag(Func->getLocation(), diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good.cpp new file mode 100644 index 0000000000000..17abae5e943b6 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good.cpp @@ -0,0 +1,9 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +void f1(); +namespace foo { + void f0() {} + void f1() {} +} +void f0() {} +void f1() {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good2.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good2.cpp new file mode 100644 index 0000000000000..b3218afa60e9d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good2.cpp @@ -0,0 +1,11 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +void f1(); +namespace foo { + void f0(); + void f1(); +} +void foo::f0() {} +void foo::f1() {} +void f0() {} +void f1() {} From 89029b766d465aeda8bcdc21a6b092bf73239630 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 00:57:41 +0300 Subject: [PATCH 08/19] more unit-test --- .../checkers/misc/shadowed-namespace-function-main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-main.cpp diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-main.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-main.cpp new file mode 100644 index 0000000000000..4321486ba7101 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-main.cpp @@ -0,0 +1,6 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +namespace foo { + int main(); +} +int main() {} From 34dc72a0bd3a34bed5b55703f189550599b4ac0d Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 01:48:47 +0300 Subject: [PATCH 09/19] refactoring --- .../misc/ShadowedNamespaceFunctionCheck.cpp | 33 +++++++++---------- .../misc/ShadowedNamespaceFunctionCheck.h | 10 +++--- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp index ca095771c477a..669d6793e23cd 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp @@ -45,14 +45,14 @@ void ShadowedNamespaceFunctionCheck::check( // Skip templates, static functions, main, etc. if (Func->isTemplated() || Func->isStatic() || - Func->getName() == "main" || Func->isImplicit()) + Func->isMain() || Func->isImplicit()) return; - std::string FuncName = Func->getNameAsString(); + const std::string FuncName = Func->getNameAsString(); if (FuncName.empty()) return; - ASTContext *Context = Result.Context; + const ASTContext *Context = Result.Context; // Look for functions with the same name in namespaces const FunctionDecl *ShadowedFunc = nullptr; @@ -61,7 +61,7 @@ void ShadowedNamespaceFunctionCheck::check( // Traverse all declarations in the translation unit for (const auto *Decl : Context->getTranslationUnitDecl()->decls()) { if (const auto *NS = dyn_cast(Decl)) { - findShadowedInNamespace(NS, Func, FuncName, ShadowedFunc, ShadowedNamespace); + std::tie(ShadowedFunc, ShadowedNamespace) = findShadowedInNamespace(NS, Func, FuncName); if (ShadowedFunc) break; } } @@ -73,7 +73,7 @@ void ShadowedNamespaceFunctionCheck::check( return; // Generate warning message - std::string NamespaceName = ShadowedNamespace->getQualifiedNameAsString(); + const std::string NamespaceName = ShadowedNamespace->getQualifiedNameAsString(); auto Diag = diag(Func->getLocation(), "free function %0 shadows '%1::%2'") << Func->getDeclName() @@ -81,9 +81,9 @@ void ShadowedNamespaceFunctionCheck::check( << ShadowedFunc->getDeclName().getAsString(); // Generate fixit hint to add namespace qualification - SourceLocation NameLoc = Func->getLocation(); + const SourceLocation NameLoc = Func->getLocation(); if (NameLoc.isValid() && !Func->getPreviousDecl()) { - std::string Fix = NamespaceName + "::"; + const std::string Fix = NamespaceName + "::"; Diag << FixItHint::CreateInsertion(NameLoc, Fix); } @@ -93,23 +93,21 @@ void ShadowedNamespaceFunctionCheck::check( << ShadowedFunc->getDeclName(); } -void ShadowedNamespaceFunctionCheck::findShadowedInNamespace( +std::pair ShadowedNamespaceFunctionCheck::findShadowedInNamespace( const NamespaceDecl *NS, const FunctionDecl *GlobalFunc, - const std::string &GlobalFuncName, - const FunctionDecl *&ShadowedFunc, - const NamespaceDecl *&ShadowedNamespace) { + const std::string &GlobalFuncName) { // Skip anonymous namespaces if (NS->isAnonymousNamespace()) - return; + return {nullptr, nullptr}; for (const auto *Decl : NS->decls()) { // Check nested namespaces if (const auto *NestedNS = dyn_cast(Decl)) { - findShadowedInNamespace(NestedNS, GlobalFunc, GlobalFuncName, - ShadowedFunc, ShadowedNamespace); - if (ShadowedFunc) return; + auto [ShadowedFunc, ShadowedNamespace] = findShadowedInNamespace(NestedNS, GlobalFunc, GlobalFuncName); + if (ShadowedFunc) + return {ShadowedFunc, ShadowedNamespace}; } // Check functions @@ -120,12 +118,11 @@ void ShadowedNamespaceFunctionCheck::findShadowedInNamespace( continue; if (Func->getNameAsString() == GlobalFuncName) { - ShadowedFunc = Func; - ShadowedNamespace = NS; - return; + return {Func, NS}; } } } + return {nullptr, nullptr}; } } // namespace misc diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h index 61f4cf8ed1c49..594ab73dc18ab 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_SHADOWEDNAMESPACEFUNCTIONCHECK_H #include "../ClangTidyCheck.h" +#include namespace clang::tidy::misc { @@ -27,11 +28,10 @@ class ShadowedNamespaceFunctionCheck : public ClangTidyCheck { return LangOpts.CPlusPlus; } private: - void findShadowedInNamespace(const NamespaceDecl *NS, - const FunctionDecl *GlobalFunc, - const std::string &GlobalFuncName, - const FunctionDecl *&ShadowedFunc, - const NamespaceDecl *&ShadowedNamespace); + std::pair + findShadowedInNamespace(const NamespaceDecl *NS, + const FunctionDecl *GlobalFunc, + const std::string &GlobalFuncName); }; } // namespace clang::tidy::misc From a2490841477c7c494801617c41818e4c800b1439 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 03:07:56 +0300 Subject: [PATCH 10/19] Fix FP --- .../misc/ShadowedNamespaceFunctionCheck.cpp | 17 +++++++++++++++-- .../checks/misc/shadowed-namespace-function.rst | 1 + .../misc/shadowed-namespace-function-good3.cpp | 9 +++++++++ .../misc/shadowed-namespace-function-good4.cpp | 9 +++++++++ .../misc/shadowed-namespace-function-using.cpp | 17 +++++++++++++++++ .../shadowed-namespace-function-variadic.cpp | 9 +++++++++ 6 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good3.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good4.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-using.cpp create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp index 669d6793e23cd..9633bf6c3353f 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp @@ -12,6 +12,7 @@ #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "llvm/ADT/STLExtras.h" using namespace clang; using namespace clang::ast_matchers; @@ -21,6 +22,18 @@ namespace clang { namespace tidy { namespace misc { +static bool hasSameParameters(const FunctionDecl *Func1, const FunctionDecl *Func2) { + if (Func1->param_size() != Func2->param_size()) + return false; + + return llvm::all_of_zip( + Func1->parameters(), Func2->parameters(), + [](const ParmVarDecl *Param1, const ParmVarDecl *Param2) { + return Param1->getType().getCanonicalType() == + Param2->getType().getCanonicalType(); + }); +} + void ShadowedNamespaceFunctionCheck::registerMatchers(MatchFinder *Finder) { // Simple matcher for all function definitions Finder->addMatcher( @@ -45,7 +58,7 @@ void ShadowedNamespaceFunctionCheck::check( // Skip templates, static functions, main, etc. if (Func->isTemplated() || Func->isStatic() || - Func->isMain() || Func->isImplicit()) + Func->isMain() || Func->isImplicit() || Func->isVariadic()) return; const std::string FuncName = Func->getNameAsString(); @@ -117,7 +130,7 @@ std::pair ShadowedNamespaceFunction Func->isThisDeclarationADefinition()) continue; - if (Func->getNameAsString() == GlobalFuncName) { + if (Func->getNameAsString() == GlobalFuncName && !Func->isVariadic() && hasSameParameters(Func, GlobalFunc) && Func->getReturnType().getCanonicalType() == GlobalFunc->getReturnType().getCanonicalType()) { return {Func, NS}; } } diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst index b82317fd0a748..04755b21dbd87 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst @@ -42,4 +42,5 @@ Limitations - Does not warn about functions in anonymous namespaces - Does not warn about template functions - Does not warn about static functions or member functions +- Does not warn about variadic functions - Does not warn about the ``main`` function diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good3.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good3.cpp new file mode 100644 index 0000000000000..998f0ff472b69 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good3.cpp @@ -0,0 +1,9 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +void f1(int); +namespace foo { + void f0(short); + void f1(unsigned); +} +void f0(char) {} +void f1(int) {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good4.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good4.cpp new file mode 100644 index 0000000000000..d6fb377edf586 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good4.cpp @@ -0,0 +1,9 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +int f1(); +namespace foo { + char f0(); + unsigned f1(); +} +short f0() { return {}; } +int f1() { return {}; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-using.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-using.cpp new file mode 100644 index 0000000000000..42222e3ba9032 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-using.cpp @@ -0,0 +1,17 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +using my_int = int; +using my_short = short; +using my_short2 = short; + +my_int f1(my_short2); +namespace foo { + int f0(short); + int f1(short); +} +my_int f0(my_short) {} +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: free function 'f0' shadows 'foo::f0' [misc-shadowed-namespace-function] +// CHECK-FIXES: my_int foo::f0(my_short) {} +my_int f1(my_short) {} +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: free function 'f1' shadows 'foo::f1' [misc-shadowed-namespace-function] +// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp new file mode 100644 index 0000000000000..794c1cb629df5 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp @@ -0,0 +1,9 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +void f1(...); +namespace foo { + void f0(...); + void f1(...); +} +void f0(...) {} +void f1(...) {} From b1ffd923b596ddf480e9892b0e0f7bd1dd6fa422 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 03:12:34 +0300 Subject: [PATCH 11/19] more unit-tests --- .../misc/shadowed-namespace-function-macro.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-macro.cpp diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-macro.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-macro.cpp new file mode 100644 index 0000000000000..540ae9112a21b --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-macro.cpp @@ -0,0 +1,16 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + +#define VOID_F0 void f0 +#define VOID_F1_BRACES_BODY void f1() {} + +void f1(); +namespace foo { + void f0(); + void f1(); +} +VOID_F0() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: free function 'f0' shadows 'foo::f0' [misc-shadowed-namespace-function] +// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes +VOID_F1_BRACES_BODY +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: free function 'f1' shadows 'foo::f1' [misc-shadowed-namespace-function] +// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes From 1589ed566dd0a105982f62aeecfca9393aad8f55 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 03:16:31 +0300 Subject: [PATCH 12/19] lint --- .../clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp index 9633bf6c3353f..ea5f03f342b78 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp @@ -9,18 +9,16 @@ #include "ShadowedNamespaceFunctionCheck.h" #include "../utils/FixItHintUtils.h" #include "clang/AST/ASTContext.h" -#include "clang/ASTMatchers/ASTMatchers.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/ASTMatchers/ASTMatchers.h" #include "llvm/ADT/STLExtras.h" using namespace clang; using namespace clang::ast_matchers; using namespace clang::tidy; -namespace clang { -namespace tidy { -namespace misc { +namespace clang::tidy::misc { static bool hasSameParameters(const FunctionDecl *Func1, const FunctionDecl *Func2) { if (Func1->param_size() != Func2->param_size()) @@ -138,7 +136,5 @@ std::pair ShadowedNamespaceFunction return {nullptr, nullptr}; } -} // namespace misc -} // namespace tidy -} // namespace clang +} // namespace clang::tidy::misc From 3b36a438b1a42e3c76b0e5305dbdc22fc2c5b4d1 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 03:17:09 +0300 Subject: [PATCH 13/19] format --- .../misc/ShadowedNamespaceFunctionCheck.cpp | 54 +++++++++---------- .../misc/ShadowedNamespaceFunctionCheck.h | 4 +- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp index ea5f03f342b78..0dd9ad0de357f 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp @@ -20,7 +20,8 @@ using namespace clang::tidy; namespace clang::tidy::misc { -static bool hasSameParameters(const FunctionDecl *Func1, const FunctionDecl *Func2) { +static bool hasSameParameters(const FunctionDecl *Func1, + const FunctionDecl *Func2) { if (Func1->param_size() != Func2->param_size()) return false; @@ -34,18 +35,13 @@ static bool hasSameParameters(const FunctionDecl *Func1, const FunctionDecl *Fun void ShadowedNamespaceFunctionCheck::registerMatchers(MatchFinder *Finder) { // Simple matcher for all function definitions - Finder->addMatcher( - functionDecl( - isDefinition() - ).bind("func"), - this - ); + Finder->addMatcher(functionDecl(isDefinition()).bind("func"), this); } void ShadowedNamespaceFunctionCheck::check( const MatchFinder::MatchResult &Result) { const auto *Func = Result.Nodes.getNodeAs("func"); - + if (!Func || !Result.SourceManager) return; @@ -55,8 +51,8 @@ void ShadowedNamespaceFunctionCheck::check( return; // Skip templates, static functions, main, etc. - if (Func->isTemplated() || Func->isStatic() || - Func->isMain() || Func->isImplicit() || Func->isVariadic()) + if (Func->isTemplated() || Func->isStatic() || Func->isMain() || + Func->isImplicit() || Func->isVariadic()) return; const std::string FuncName = Func->getNameAsString(); @@ -72,8 +68,10 @@ void ShadowedNamespaceFunctionCheck::check( // Traverse all declarations in the translation unit for (const auto *Decl : Context->getTranslationUnitDecl()->decls()) { if (const auto *NS = dyn_cast(Decl)) { - std::tie(ShadowedFunc, ShadowedNamespace) = findShadowedInNamespace(NS, Func, FuncName); - if (ShadowedFunc) break; + std::tie(ShadowedFunc, ShadowedNamespace) = + findShadowedInNamespace(NS, Func, FuncName); + if (ShadowedFunc) + break; } } @@ -84,11 +82,10 @@ void ShadowedNamespaceFunctionCheck::check( return; // Generate warning message - const std::string NamespaceName = ShadowedNamespace->getQualifiedNameAsString(); - auto Diag = diag(Func->getLocation(), - "free function %0 shadows '%1::%2'") - << Func->getDeclName() - << NamespaceName + const std::string NamespaceName = + ShadowedNamespace->getQualifiedNameAsString(); + auto Diag = diag(Func->getLocation(), "free function %0 shadows '%1::%2'") + << Func->getDeclName() << NamespaceName << ShadowedFunc->getDeclName().getAsString(); // Generate fixit hint to add namespace qualification @@ -99,16 +96,16 @@ void ShadowedNamespaceFunctionCheck::check( } // Note: Also show where the shadowed function is declared - diag(ShadowedFunc->getLocation(), - "function %0 declared here", DiagnosticIDs::Note) + diag(ShadowedFunc->getLocation(), "function %0 declared here", + DiagnosticIDs::Note) << ShadowedFunc->getDeclName(); } -std::pair ShadowedNamespaceFunctionCheck::findShadowedInNamespace( - const NamespaceDecl *NS, - const FunctionDecl *GlobalFunc, +std::pair +ShadowedNamespaceFunctionCheck::findShadowedInNamespace( + const NamespaceDecl *NS, const FunctionDecl *GlobalFunc, const std::string &GlobalFuncName) { - + // Skip anonymous namespaces if (NS->isAnonymousNamespace()) return {nullptr, nullptr}; @@ -116,7 +113,8 @@ std::pair ShadowedNamespaceFunction for (const auto *Decl : NS->decls()) { // Check nested namespaces if (const auto *NestedNS = dyn_cast(Decl)) { - auto [ShadowedFunc, ShadowedNamespace] = findShadowedInNamespace(NestedNS, GlobalFunc, GlobalFuncName); + auto [ShadowedFunc, ShadowedNamespace] = + findShadowedInNamespace(NestedNS, GlobalFunc, GlobalFuncName); if (ShadowedFunc) return {ShadowedFunc, ShadowedNamespace}; } @@ -124,11 +122,14 @@ std::pair ShadowedNamespaceFunction // Check functions if (const auto *Func = dyn_cast(Decl)) { // Skip if it's the same function, templates, or definitions - if (Func == GlobalFunc || Func->isTemplated() || + if (Func == GlobalFunc || Func->isTemplated() || Func->isThisDeclarationADefinition()) continue; - if (Func->getNameAsString() == GlobalFuncName && !Func->isVariadic() && hasSameParameters(Func, GlobalFunc) && Func->getReturnType().getCanonicalType() == GlobalFunc->getReturnType().getCanonicalType()) { + if (Func->getNameAsString() == GlobalFuncName && !Func->isVariadic() && + hasSameParameters(Func, GlobalFunc) && + Func->getReturnType().getCanonicalType() == + GlobalFunc->getReturnType().getCanonicalType()) { return {Func, NS}; } } @@ -137,4 +138,3 @@ std::pair ShadowedNamespaceFunction } } // namespace clang::tidy::misc - diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h index 594ab73dc18ab..8ee09272fffed 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h @@ -14,7 +14,8 @@ namespace clang::tidy::misc { -/// Detects free functions in global namespace that shadow functions from other namespaces. +/// Detects free functions in global namespace that shadow functions from other +/// namespaces. /// /// For the user-facing documentation see: /// https://clang.llvm.org/extra/clang-tidy/checks/misc/shadowed-namespace-function.html @@ -27,6 +28,7 @@ class ShadowedNamespaceFunctionCheck : public ClangTidyCheck { bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus; } + private: std::pair findShadowedInNamespace(const NamespaceDecl *NS, From 1f7748d4eb7146bee27e16f1258d71d000e79a59 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 19:13:43 +0300 Subject: [PATCH 14/19] friend --- .../misc/shadowed-namespace-function.rst | 30 ++++++++++++++----- .../shadowed-namespace-function-friend.cpp | 16 ++++++++++ 2 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-friend.cpp diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst index 04755b21dbd87..c14251000d29c 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst @@ -31,16 +31,32 @@ The check will suggest adding the appropriate namespace qualification: - void process() {} + void utils::process() {} -Options -------- - -None +The check will not warn about: +- Static functions or member functions; +- Functions in anonymous namespaces; +- The ``main`` function. Limitations ----------- -- Does not warn about functions in anonymous namespaces +- Does not warn about friend functions: + +.. code-block:: c++ + + namespace llvm::gsym { + struct MergedFunctionsInfo { + friend bool operator==(const llvm::gsym::MergedFunctionsInfo &LHS, + const llvm::gsym::MergedFunctionsInfo &RHS); + }; + } + + using namespace llvm::gsym; + + bool operator==(const MergedFunctionsInfo &LHS, // no warning in this version + const MergedFunctionsInfo &RHS) { + return LHS.MergedFunctions == RHS.MergedFunctions; + } + - Does not warn about template functions -- Does not warn about static functions or member functions - Does not warn about variadic functions -- Does not warn about the ``main`` function + diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-friend.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-friend.cpp new file mode 100644 index 0000000000000..f70474987922d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-friend.cpp @@ -0,0 +1,16 @@ +// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t + + +namespace foo { struct A; } +void f1(foo::A); + +namespace foo { + struct A{ + friend void f0(A); + friend void f1(A); + }; +} + +// FIXME: provide warning without fixit in these two cases +void f0(foo::A) {} +void f1(foo::A) {} From d6bdb398bde17319da8b7455c96b6709319f41f3 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 19:16:45 +0300 Subject: [PATCH 15/19] fix doc --- .../clang-tidy/checks/misc/shadowed-namespace-function.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst index c14251000d29c..628a6ef190430 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst @@ -32,6 +32,7 @@ The check will suggest adding the appropriate namespace qualification: + void utils::process() {} The check will not warn about: + - Static functions or member functions; - Functions in anonymous namespaces; - The ``main`` function. @@ -45,8 +46,8 @@ Limitations namespace llvm::gsym { struct MergedFunctionsInfo { - friend bool operator==(const llvm::gsym::MergedFunctionsInfo &LHS, - const llvm::gsym::MergedFunctionsInfo &RHS); + friend bool operator==(const MergedFunctionsInfo &LHS, + const MergedFunctionsInfo &RHS); }; } From 51eac24d050f6b26f3b963584ca260ec77e6e7fc Mon Sep 17 00:00:00 2001 From: denzor200 Date: Mon, 17 Nov 2025 19:22:47 +0300 Subject: [PATCH 16/19] important TODOs for future versions of check --- .../checkers/misc/shadowed-namespace-function-template.cpp | 3 +++ .../checkers/misc/shadowed-namespace-function-variadic.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp index 48924fcdb932d..ad99e524b0207 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp @@ -8,6 +8,9 @@ namespace foo { template void f1(); } + +// FIXME: provide warning in these two cases +// FIXME: provide fixit for f0 template void f0() {} template diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp index 794c1cb629df5..cd470d28551c3 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp @@ -5,5 +5,8 @@ namespace foo { void f0(...); void f1(...); } + +// FIXME: warning in these two cases?? +// FIXME: fixit for f0?? void f0(...) {} void f1(...) {} From 532b761e793b957b019eb46d87a7ee5748232045 Mon Sep 17 00:00:00 2001 From: denzor200 Date: Tue, 18 Nov 2025 01:18:44 +0300 Subject: [PATCH 17/19] refactor tests --- ...namespace-function-anonymous-namespace.cpp | 11 -- .../shadowed-namespace-function-friend.cpp | 16 -- .../misc/shadowed-namespace-function-good.cpp | 9 - .../shadowed-namespace-function-good2.cpp | 11 -- .../shadowed-namespace-function-good3.cpp | 9 - .../shadowed-namespace-function-good4.cpp | 9 - .../shadowed-namespace-function-macro.cpp | 16 -- .../misc/shadowed-namespace-function-main.cpp | 6 - .../shadowed-namespace-function-nested.cpp | 13 -- .../shadowed-namespace-function-static.cpp | 9 - .../shadowed-namespace-function-template.cpp | 17 -- .../shadowed-namespace-function-using.cpp | 17 -- .../shadowed-namespace-function-variadic.cpp | 12 -- .../misc/shadowed-namespace-function.cpp | 185 +++++++++++++++++- 14 files changed, 177 insertions(+), 163 deletions(-) delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-anonymous-namespace.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-friend.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good2.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good3.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good4.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-macro.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-main.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-nested.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-static.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-using.cpp delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-anonymous-namespace.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-anonymous-namespace.cpp deleted file mode 100644 index 1689843464ae1..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-anonymous-namespace.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -namespace { void f1(); } -namespace foo { - void f0(); - void f1(); -} -namespace { -void f0() {} -void f1() {} -} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-friend.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-friend.cpp deleted file mode 100644 index f70474987922d..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-friend.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - - -namespace foo { struct A; } -void f1(foo::A); - -namespace foo { - struct A{ - friend void f0(A); - friend void f1(A); - }; -} - -// FIXME: provide warning without fixit in these two cases -void f0(foo::A) {} -void f1(foo::A) {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good.cpp deleted file mode 100644 index 17abae5e943b6..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -void f1(); -namespace foo { - void f0() {} - void f1() {} -} -void f0() {} -void f1() {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good2.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good2.cpp deleted file mode 100644 index b3218afa60e9d..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good2.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -void f1(); -namespace foo { - void f0(); - void f1(); -} -void foo::f0() {} -void foo::f1() {} -void f0() {} -void f1() {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good3.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good3.cpp deleted file mode 100644 index 998f0ff472b69..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good3.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -void f1(int); -namespace foo { - void f0(short); - void f1(unsigned); -} -void f0(char) {} -void f1(int) {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good4.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good4.cpp deleted file mode 100644 index d6fb377edf586..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-good4.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -int f1(); -namespace foo { - char f0(); - unsigned f1(); -} -short f0() { return {}; } -int f1() { return {}; } diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-macro.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-macro.cpp deleted file mode 100644 index 540ae9112a21b..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-macro.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -#define VOID_F0 void f0 -#define VOID_F1_BRACES_BODY void f1() {} - -void f1(); -namespace foo { - void f0(); - void f1(); -} -VOID_F0() {} -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: free function 'f0' shadows 'foo::f0' [misc-shadowed-namespace-function] -// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes -VOID_F1_BRACES_BODY -// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: free function 'f1' shadows 'foo::f1' [misc-shadowed-namespace-function] -// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-main.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-main.cpp deleted file mode 100644 index 4321486ba7101..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -namespace foo { - int main(); -} -int main() {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-nested.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-nested.cpp deleted file mode 100644 index 41b130624bbaa..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-nested.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -void f1(); -namespace foo::foo2::foo3 { - void f0(); - void f1(); -} -void f0() {} -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f0' shadows 'foo::foo2::foo3::f0' [misc-shadowed-namespace-function] -// CHECK-FIXES: void foo::foo2::foo3::f0() {} -void f1() {} -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f1' shadows 'foo::foo2::foo3::f1' [misc-shadowed-namespace-function] -// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-static.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-static.cpp deleted file mode 100644 index d1371c044d112..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-static.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -static void f1(); -namespace foo { - void f0(); - void f1(); -} -static void f0() {} -static void f1() {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp deleted file mode 100644 index ad99e524b0207..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-template.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -template -void f1(); -namespace foo { - template - void f0(); - template - void f1(); -} - -// FIXME: provide warning in these two cases -// FIXME: provide fixit for f0 -template -void f0() {} -template -void f1() {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-using.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-using.cpp deleted file mode 100644 index 42222e3ba9032..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-using.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -using my_int = int; -using my_short = short; -using my_short2 = short; - -my_int f1(my_short2); -namespace foo { - int f0(short); - int f1(short); -} -my_int f0(my_short) {} -// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: free function 'f0' shadows 'foo::f0' [misc-shadowed-namespace-function] -// CHECK-FIXES: my_int foo::f0(my_short) {} -my_int f1(my_short) {} -// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: free function 'f1' shadows 'foo::f1' [misc-shadowed-namespace-function] -// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp deleted file mode 100644 index cd470d28551c3..0000000000000 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function-variadic.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t - -void f1(...); -namespace foo { - void f0(...); - void f1(...); -} - -// FIXME: warning in these two cases?? -// FIXME: fixit for f0?? -void f0(...) {} -void f1(...) {} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp index 920556cca0381..28e2f0c56df54 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/shadowed-namespace-function.cpp @@ -1,13 +1,182 @@ // RUN: %check_clang_tidy %s misc-shadowed-namespace-function %t -void f1(); +void f1_general(); +namespace foo_general { + void f0_general(); + void f1_general(); +} +void f0_general() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f0_general' shadows 'foo_general::f0_general' [misc-shadowed-namespace-function] +// CHECK-FIXES: void foo_general::f0_general() {} +void f1_general() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f1_general' shadows 'foo_general::f1_general' [misc-shadowed-namespace-function] +// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes + +////////////////////////////////////////////////////////////////////////////////////////// + +void f1_variadic(...); +namespace foo_variadic { + void f0_variadic(...); + void f1_variadic(...); +} + +// FIXME: warning in these two cases?? +// FIXME: fixit for f0?? +void f0_variadic(...) {} +void f1_variadic(...) {} + +////////////////////////////////////////////////////////////////////////////////////////// + +using my_int = int; +using my_short = short; +using my_short2 = short; + +my_int f1_using(my_short2); +namespace foo_using { + int f0_using(short); + int f1_using(short); +} +my_int f0_using(my_short) {} +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: free function 'f0_using' shadows 'foo_using::f0_using' [misc-shadowed-namespace-function] +// CHECK-FIXES: my_int foo_using::f0_using(my_short) {} +my_int f1_using(my_short) {} +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: free function 'f1_using' shadows 'foo_using::f1_using' [misc-shadowed-namespace-function] +// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes + +////////////////////////////////////////////////////////////////////////////////////////// + +template +void f1_template(); namespace foo { - void f0(); - void f1(); + template + void f0_template(); + template + void f1_template(); } -void f0() {} -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f0' shadows 'foo::f0' [misc-shadowed-namespace-function] -// CHECK-FIXES: void foo::f0() {} -void f1() {} -// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f1' shadows 'foo::f1' [misc-shadowed-namespace-function] + +// FIXME: provide warning in these two cases +// FIXME: provide fixit for f0 +template +void f0_template() {} +template +void f1_template() {} + +////////////////////////////////////////////////////////////////////////////////////////// + +static void f1_static(); +namespace foo_static { + void f0_static(); + void f1_static(); +} +static void f0_static() {} +static void f1_static() {} + +////////////////////////////////////////////////////////////////////////////////////////// + +void f1_nested(); +namespace foo_nested::foo2::foo3 { + void f0_nested(); + void f1_nested(); +} +void f0_nested() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f0_nested' shadows 'foo_nested::foo2::foo3::f0_nested' [misc-shadowed-namespace-function] +// CHECK-FIXES: void foo_nested::foo2::foo3::f0_nested() {} +void f1_nested() {} +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: free function 'f1_nested' shadows 'foo_nested::foo2::foo3::f1_nested' [misc-shadowed-namespace-function] // CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes + +////////////////////////////////////////////////////////////////////////////////////////// + +namespace foo_main { + int main(); +} +int main() {} + +////////////////////////////////////////////////////////////////////////////////////////// + +#define VOID_F0 void f0_macro +#define VOID_F1_BRACES_BODY void f1_macro() {} + +void f1_macro(); +namespace foo_macro { + void f0_macro(); + void f1_macro(); +} +VOID_F0() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: free function 'f0_macro' shadows 'foo_macro::f0_macro' [misc-shadowed-namespace-function] +// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes +VOID_F1_BRACES_BODY +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: free function 'f1_macro' shadows 'foo_macro::f1_macro' [misc-shadowed-namespace-function] +// CHECK-MESSAGES-NOT: :[[@LINE-2]]:{{.*}}: note: FIX-IT applied suggested code changes + +////////////////////////////////////////////////////////////////////////////////////////// + +void f1_good(); +namespace foo_good { + void f0_good() {} + void f1_good() {} +} +void f0_good() {} +void f1_good() {} + +////////////////////////////////////////////////////////////////////////////////////////// + +void f1_good2(); +namespace foo_good2 { + void f0_good2(); + void f1_good2(); +} +void foo_good2::f0_good2() {} +void foo_good2::f1_good2() {} +void f0_good2() {} +void f1_good2() {} + +////////////////////////////////////////////////////////////////////////////////////////// + +void f1_good3(int); +namespace foo_good3 { + void f0_good3(short); + void f1_good3(unsigned); +} +void f0_good3(char) {} +void f1_good3(int) {} + +////////////////////////////////////////////////////////////////////////////////////////// + +int f1_good4(); +namespace foo_good4 { + char f0_good4(); + unsigned f1_good4(); +} +short f0_good4() { return {}; } +int f1_good4() { return {}; } + +////////////////////////////////////////////////////////////////////////////////////////// + +namespace foo_friend { struct A; } +void f1_friend(foo_friend::A); + +namespace foo_friend { + struct A{ + friend void f0_friend(A); + friend void f1_friend(A); + }; +} + +// FIXME: provide warning without fixit in these two cases +void f0_friend(foo_friend::A) {} +void f1_friend(foo_friend::A) {} + +////////////////////////////////////////////////////////////////////////////////////////// + +namespace { void f1_anon(); } +namespace foo_anon { + void f0_anon(); + void f1_anon(); +} +namespace { +void f0_anon() {} +void f1_anon() {} +} + +////////////////////////////////////////////////////////////////////////////////////////// From 94ebafa18f82090d843e5998daeab339ca52fd1b Mon Sep 17 00:00:00 2001 From: denzor200 Date: Tue, 18 Nov 2025 15:34:30 +0300 Subject: [PATCH 18/19] review --- .../misc/ShadowedNamespaceFunctionCheck.cpp | 94 ++++++++----------- .../misc/ShadowedNamespaceFunctionCheck.h | 6 -- clang-tools-extra/docs/ReleaseNotes.rst | 3 +- .../misc/shadowed-namespace-function.rst | 11 ++- 4 files changed, 46 insertions(+), 68 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp index 0dd9ad0de357f..f7e6f3be5ae5b 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp @@ -33,39 +33,60 @@ static bool hasSameParameters(const FunctionDecl *Func1, }); } +static +std::pair +findShadowedInNamespace( + const NamespaceDecl *NS, const FunctionDecl *GlobalFunc, + const std::string &GlobalFuncName) { + + if (NS->isAnonymousNamespace()) + return {nullptr, nullptr}; + + for (const auto *Decl : NS->decls()) { + // Check nested namespaces + if (const auto *NestedNS = dyn_cast(Decl)) { + auto [ShadowedFunc, ShadowedNamespace] = + findShadowedInNamespace(NestedNS, GlobalFunc, GlobalFuncName); + if (ShadowedFunc) + return {ShadowedFunc, ShadowedNamespace}; + } + + // Check functions + if (const auto *Func = dyn_cast(Decl)) { + if (Func == GlobalFunc || Func->isTemplated() || + Func->isThisDeclarationADefinition()) + continue; + + if (Func->getNameAsString() == GlobalFuncName && !Func->isVariadic() && + hasSameParameters(Func, GlobalFunc) && + Func->getReturnType().getCanonicalType() == + GlobalFunc->getReturnType().getCanonicalType()) { + return {Func, NS}; + } + } + } + return {nullptr, nullptr}; +} + void ShadowedNamespaceFunctionCheck::registerMatchers(MatchFinder *Finder) { - // Simple matcher for all function definitions - Finder->addMatcher(functionDecl(isDefinition()).bind("func"), this); + Finder->addMatcher(functionDecl(isDefinition(), decl(hasDeclContext(translationUnitDecl())), unless( + anyOf(isImplicit(), isVariadic(), isMain(), isStaticStorageClass(), ast_matchers::isTemplateInstantiation()) + )).bind("func"), this); } void ShadowedNamespaceFunctionCheck::check( const MatchFinder::MatchResult &Result) { const auto *Func = Result.Nodes.getNodeAs("func"); - if (!Func || !Result.SourceManager) - return; - - // Skip if not in global namespace - const DeclContext *DC = Func->getDeclContext(); - if (!DC->isTranslationUnit()) - return; - - // Skip templates, static functions, main, etc. - if (Func->isTemplated() || Func->isStatic() || Func->isMain() || - Func->isImplicit() || Func->isVariadic()) - return; - const std::string FuncName = Func->getNameAsString(); if (FuncName.empty()) return; const ASTContext *Context = Result.Context; - // Look for functions with the same name in namespaces const FunctionDecl *ShadowedFunc = nullptr; const NamespaceDecl *ShadowedNamespace = nullptr; - // Traverse all declarations in the translation unit for (const auto *Decl : Context->getTranslationUnitDecl()->decls()) { if (const auto *NS = dyn_cast(Decl)) { std::tie(ShadowedFunc, ShadowedNamespace) = @@ -81,60 +102,21 @@ void ShadowedNamespaceFunctionCheck::check( if (ShadowedFunc->getDefinition()) return; - // Generate warning message const std::string NamespaceName = ShadowedNamespace->getQualifiedNameAsString(); auto Diag = diag(Func->getLocation(), "free function %0 shadows '%1::%2'") << Func->getDeclName() << NamespaceName << ShadowedFunc->getDeclName().getAsString(); - // Generate fixit hint to add namespace qualification const SourceLocation NameLoc = Func->getLocation(); if (NameLoc.isValid() && !Func->getPreviousDecl()) { const std::string Fix = NamespaceName + "::"; Diag << FixItHint::CreateInsertion(NameLoc, Fix); } - // Note: Also show where the shadowed function is declared diag(ShadowedFunc->getLocation(), "function %0 declared here", DiagnosticIDs::Note) << ShadowedFunc->getDeclName(); } -std::pair -ShadowedNamespaceFunctionCheck::findShadowedInNamespace( - const NamespaceDecl *NS, const FunctionDecl *GlobalFunc, - const std::string &GlobalFuncName) { - - // Skip anonymous namespaces - if (NS->isAnonymousNamespace()) - return {nullptr, nullptr}; - - for (const auto *Decl : NS->decls()) { - // Check nested namespaces - if (const auto *NestedNS = dyn_cast(Decl)) { - auto [ShadowedFunc, ShadowedNamespace] = - findShadowedInNamespace(NestedNS, GlobalFunc, GlobalFuncName); - if (ShadowedFunc) - return {ShadowedFunc, ShadowedNamespace}; - } - - // Check functions - if (const auto *Func = dyn_cast(Decl)) { - // Skip if it's the same function, templates, or definitions - if (Func == GlobalFunc || Func->isTemplated() || - Func->isThisDeclarationADefinition()) - continue; - - if (Func->getNameAsString() == GlobalFuncName && !Func->isVariadic() && - hasSameParameters(Func, GlobalFunc) && - Func->getReturnType().getCanonicalType() == - GlobalFunc->getReturnType().getCanonicalType()) { - return {Func, NS}; - } - } - } - return {nullptr, nullptr}; -} - } // namespace clang::tidy::misc diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h index 8ee09272fffed..93dc69f157281 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.h @@ -28,12 +28,6 @@ class ShadowedNamespaceFunctionCheck : public ClangTidyCheck { bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus; } - -private: - std::pair - findShadowedInNamespace(const NamespaceDecl *NS, - const FunctionDecl *GlobalFunc, - const std::string &GlobalFuncName); }; } // namespace clang::tidy::misc diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index a1482d89b6042..42772b5335dd1 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -229,7 +229,8 @@ New checks - New :doc:`misc-shadowed-namespace-function ` check. - Detects free functions in global namespace that shadow functions from other namespaces. + Detects free functions in global namespace that shadow functions from other + namespaces. - New :doc:`readability-redundant-parentheses ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst index 628a6ef190430..5dc8930dde782 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/shadowed-namespace-function.rst @@ -4,9 +4,11 @@ misc-shadowed-namespace-function ================================ Detects free functions in the global namespace that shadow functions declared -in other namespaces. This check helps prevent accidental shadowing of namespace -functions, which can lead to confusion about which function is being called and -potential linking errors. +in other namespaces. + +This check helps prevent accidental shadowing of namespace functions, which can +lead to confusion about which function is being called and potential linking +errors. Examples -------- @@ -59,5 +61,4 @@ Limitations } - Does not warn about template functions -- Does not warn about variadic functions - +- Does not warn about variadic functions. From cd94fa73e75e886a98f4b3b33ddcf9de1d8bd54e Mon Sep 17 00:00:00 2001 From: denzor200 Date: Tue, 18 Nov 2025 15:41:42 +0300 Subject: [PATCH 19/19] format --- .../misc/ShadowedNamespaceFunctionCheck.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp index f7e6f3be5ae5b..b1865ca459088 100644 --- a/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/ShadowedNamespaceFunctionCheck.cpp @@ -33,11 +33,9 @@ static bool hasSameParameters(const FunctionDecl *Func1, }); } -static -std::pair -findShadowedInNamespace( - const NamespaceDecl *NS, const FunctionDecl *GlobalFunc, - const std::string &GlobalFuncName) { +static std::pair +findShadowedInNamespace(const NamespaceDecl *NS, const FunctionDecl *GlobalFunc, + const std::string &GlobalFuncName) { if (NS->isAnonymousNamespace()) return {nullptr, nullptr}; @@ -69,9 +67,13 @@ findShadowedInNamespace( } void ShadowedNamespaceFunctionCheck::registerMatchers(MatchFinder *Finder) { - Finder->addMatcher(functionDecl(isDefinition(), decl(hasDeclContext(translationUnitDecl())), unless( - anyOf(isImplicit(), isVariadic(), isMain(), isStaticStorageClass(), ast_matchers::isTemplateInstantiation()) - )).bind("func"), this); + Finder->addMatcher( + functionDecl(isDefinition(), decl(hasDeclContext(translationUnitDecl())), + unless(anyOf(isImplicit(), isVariadic(), isMain(), + isStaticStorageClass(), + ast_matchers::isTemplateInstantiation()))) + .bind("func"), + this); } void ShadowedNamespaceFunctionCheck::check(