Skip to content

Commit d9ecaed

Browse files
committed
[clang-tidy] Add new check llvm-prefer-static-over-anonymous-namespace
1 parent e4d0068 commit d9ecaed

10 files changed

+493
-0
lines changed

clang-tools-extra/clang-tidy/llvm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_clang_library(clangTidyLLVMModule STATIC
99
LLVMTidyModule.cpp
1010
PreferIsaOrDynCastInConditionalsCheck.cpp
1111
PreferRegisterOverUnsignedCheck.cpp
12+
PreferStaticOverAnonymousNamespaceCheck.cpp
1213
TwineLocalCheck.cpp
1314

1415
LINK_LIBS

clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "IncludeOrderCheck.h"
1717
#include "PreferIsaOrDynCastInConditionalsCheck.h"
1818
#include "PreferRegisterOverUnsignedCheck.h"
19+
#include "PreferStaticOverAnonymousNamespaceCheck.h"
1920
#include "TwineLocalCheck.h"
2021

2122
namespace clang::tidy {
@@ -34,6 +35,8 @@ class LLVMModule : public ClangTidyModule {
3435
"llvm-prefer-isa-or-dyn-cast-in-conditionals");
3536
CheckFactories.registerCheck<PreferRegisterOverUnsignedCheck>(
3637
"llvm-prefer-register-over-unsigned");
38+
CheckFactories.registerCheck<PreferStaticOverAnonymousNamespaceCheck>(
39+
"llvm-prefer-static-over-anonymous-namespace");
3740
CheckFactories.registerCheck<readability::QualifiedAutoCheck>(
3841
"llvm-qualified-auto");
3942
CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local");
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//===--- PreferStaticOverAnonymousNamespaceCheck.cpp - clang-tidy ---------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "PreferStaticOverAnonymousNamespaceCheck.h"
10+
#include "clang/ASTMatchers/ASTMatchFinder.h"
11+
12+
using namespace clang::ast_matchers;
13+
14+
namespace clang::tidy::llvm_check {
15+
16+
namespace {
17+
18+
AST_MATCHER(NamedDecl, isInMacro) {
19+
return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID();
20+
}
21+
22+
AST_MATCHER(VarDecl, isLocalVariable) { return Node.isLocalVarDecl(); }
23+
24+
} // namespace
25+
26+
PreferStaticOverAnonymousNamespaceCheck::
27+
PreferStaticOverAnonymousNamespaceCheck(StringRef Name,
28+
ClangTidyContext *Context)
29+
: ClangTidyCheck(Name, Context),
30+
AllowVariableDeclarations(Options.get("AllowVariableDeclarations", true)),
31+
AllowMemberFunctionsInClass(
32+
Options.get("AllowMemberFunctionsInClass", true)) {}
33+
34+
void PreferStaticOverAnonymousNamespaceCheck::storeOptions(
35+
ClangTidyOptions::OptionMap &Opts) {
36+
Options.store(Opts, "AllowVariableDeclarations", AllowVariableDeclarations);
37+
Options.store(Opts, "AllowMemberFunctionsInClass",
38+
AllowMemberFunctionsInClass);
39+
}
40+
41+
void PreferStaticOverAnonymousNamespaceCheck::registerMatchers(
42+
MatchFinder *Finder) {
43+
const auto IsDefinitionInAnonymousNamespace =
44+
allOf(unless(isExpansionInSystemHeader()), isInAnonymousNamespace(),
45+
unless(isInMacro()), isDefinition());
46+
47+
if (AllowMemberFunctionsInClass) {
48+
Finder->addMatcher(functionDecl(IsDefinitionInAnonymousNamespace,
49+
unless(hasParent(cxxRecordDecl())))
50+
.bind("function"),
51+
this);
52+
} else
53+
Finder->addMatcher(
54+
functionDecl(IsDefinitionInAnonymousNamespace).bind("function"), this);
55+
56+
if (!AllowVariableDeclarations)
57+
Finder->addMatcher(varDecl(IsDefinitionInAnonymousNamespace,
58+
unless(isLocalVariable()), unless(parmVarDecl()))
59+
.bind("var"),
60+
this);
61+
}
62+
63+
void PreferStaticOverAnonymousNamespaceCheck::check(
64+
const MatchFinder::MatchResult &Result) {
65+
66+
if (const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("function")) {
67+
if (Func->isCXXClassMember())
68+
diag(Func->getLocation(),
69+
"place definition of method %0 outside of an anonymous namespace")
70+
<< Func;
71+
else if (Func->isStatic())
72+
diag(Func->getLocation(),
73+
"place static function %0 outside of an anonymous namespace")
74+
<< Func;
75+
else
76+
diag(Func->getLocation(),
77+
"function %0 is declared in an anonymous namespace; "
78+
"prefer using 'static' for restricting visibility")
79+
<< Func;
80+
return;
81+
}
82+
83+
if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) {
84+
if (Var->getStorageClass() == SC_Static)
85+
diag(Var->getLocation(),
86+
"place static variable %0 outside of an anonymous namespace")
87+
<< Var;
88+
else
89+
diag(Var->getLocation(),
90+
"variable %0 is declared in an anonymous namespace; "
91+
"prefer using 'static' for restricting visibility")
92+
<< Var;
93+
}
94+
}
95+
96+
} // namespace clang::tidy::llvm_check
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===--- PreferStaticOverAnonymousNamespaceCheck.h - clang-tidy -*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_PREFERSTATICOVERANONYMOUSNAMESPACECHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_PREFERSTATICOVERANONYMOUSNAMESPACECHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::llvm_check {
15+
16+
/// Finds function and variable declarations inside anonymous namespace and
17+
/// suggests replacing them with ``static`` declarations.
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/llvm/prefer-static-over-anonymous-namespace.html
21+
class PreferStaticOverAnonymousNamespaceCheck : public ClangTidyCheck {
22+
public:
23+
PreferStaticOverAnonymousNamespaceCheck(StringRef Name,
24+
ClangTidyContext *Context);
25+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
26+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
27+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
28+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
29+
return LangOpts.CPlusPlus;
30+
}
31+
std::optional<TraversalKind> getCheckTraversalKind() const override {
32+
return TK_IgnoreUnlessSpelledInSource;
33+
}
34+
35+
private:
36+
const bool AllowVariableDeclarations;
37+
const bool AllowMemberFunctionsInClass;
38+
};
39+
40+
} // namespace clang::tidy::llvm_check
41+
42+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_PREFERSTATICOVERANONYMOUSNAMESPACECHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ New checks
142142
Finds unscoped (non-class) ``enum`` declarations and suggests using
143143
``enum class`` instead.
144144

145+
- New :doc:`llvm-prefer-static-over-anonymous-namespace
146+
<clang-tidy/checks/llvm/prefer-static-over-anonymous-namespace>` check.
147+
148+
Finds function and variable declarations inside anonymous namespace and
149+
suggests replacing them with ``static`` declarations.
150+
145151
- New :doc:`modernize-use-scoped-lock
146152
<clang-tidy/checks/modernize/use-scoped-lock>` check.
147153

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ Clang-Tidy Checks
250250
:doc:`llvm-namespace-comment <llvm/namespace-comment>`,
251251
:doc:`llvm-prefer-isa-or-dyn-cast-in-conditionals <llvm/prefer-isa-or-dyn-cast-in-conditionals>`, "Yes"
252252
:doc:`llvm-prefer-register-over-unsigned <llvm/prefer-register-over-unsigned>`, "Yes"
253+
:doc:`llvm-prefer-static-over-anonymous-namespace <llvm/prefer-static-over-anonymous-namespace>`,
253254
:doc:`llvm-twine-local <llvm/twine-local>`, "Yes"
254255
:doc:`llvmlibc-callee-namespace <llvmlibc/callee-namespace>`,
255256
:doc:`llvmlibc-implementation-in-namespace <llvmlibc/implementation-in-namespace>`,
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
.. title:: clang-tidy - llvm-prefer-static-over-anonymous-namespace
2+
3+
llvm-prefer-static-over-anonymous-namespace
4+
===========================================
5+
6+
Finds function and variable declarations inside anonymous namespace and
7+
suggests replacing them with ``static`` declarations.
8+
9+
The `LLVM Coding Standards <https://llvm.org/docs/CodingStandards.html#restrict-visibility>`_
10+
recommend keeping anonymous namespaces as small as possible and only use them
11+
for class declarations. For functions and variables, ``static`` specifier
12+
should be preferred for restricting visibility.
13+
14+
For example non-compliant code:
15+
16+
.. code-block:: c++
17+
18+
namespace {
19+
20+
class StringSort {
21+
public:
22+
StringSort(...)
23+
bool operator<(const char *RHS) const;
24+
};
25+
26+
// warning: place method definition outside of an anonymous namespace
27+
bool StringSort::operator<(const char *RHS) const {}
28+
29+
// warning: prefer using 'static' for restricting visibility
30+
void runHelper() {}
31+
32+
// warning: prefer using 'static' for restricting visibility
33+
int myVariable = 42;
34+
35+
}
36+
37+
Should become:
38+
39+
.. code-block:: c++
40+
41+
// Small anonymous namespace for class declaration
42+
namespace {
43+
44+
class StringSort {
45+
public:
46+
StringSort(...)
47+
bool operator<(const char *RHS) const;
48+
};
49+
50+
}
51+
52+
// placed method definition outside of the anonymous namespace
53+
bool StringSort::operator<(const char *RHS) const {}
54+
55+
// used 'static' instead of an anonymous namespace
56+
static void runHelper() {}
57+
58+
// used 'static' instead of an anonymous namespace
59+
static int myVariable = 42;
60+
61+
62+
Options
63+
-------
64+
65+
.. option:: AllowVariableDeclarations
66+
67+
When `true`, allow variable declarations to be in anonymous namespace.
68+
Default value is `true`.
69+
70+
.. option:: AllowMemberFunctionsInClass
71+
72+
When `true`, only methods defined in anonymous namespace outside of the
73+
corresponding class will be warned. Default value is `true`.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: %check_clang_tidy %s llvm-prefer-static-over-anonymous-namespace %t -- \
2+
// RUN: -config="{CheckOptions: { \
3+
// RUN: llvm-prefer-static-over-anonymous-namespace.AllowMemberFunctionsInClass: false }, \
4+
// RUN: }" -- -fno-delayed-template-parsing
5+
6+
namespace {
7+
8+
class MyClass {
9+
public:
10+
MyClass() {}
11+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: place definition of method 'MyClass' outside of an anonymous namespace
12+
MyClass(const MyClass&) = default;
13+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: place definition of method 'MyClass' outside of an anonymous namespace
14+
MyClass& operator=(const MyClass&) = default;
15+
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: place definition of method 'operator=' outside of an anonymous namespace
16+
MyClass& operator=(MyClass&&) { return *this; };
17+
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: place definition of method 'operator=' outside of an anonymous namespace
18+
bool operator<(const MyClass&) const { return true; };
19+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: place definition of method 'operator<' outside of an anonymous namespace
20+
void memberFunction();
21+
void memberDefinedInClass() {}
22+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: place definition of method 'memberDefinedInClass' outside of an anonymous namespace
23+
};
24+
25+
void MyClass::memberFunction() {}
26+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: place definition of method 'memberFunction' outside of an anonymous namespace
27+
28+
template<typename T>
29+
class TemplateClass {
30+
public:
31+
TemplateClass() {};
32+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: place definition of method 'TemplateClass<T>' outside of an anonymous namespace
33+
TemplateClass(const TemplateClass&) = default;
34+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: place definition of method 'TemplateClass<T>' outside of an anonymous namespace
35+
TemplateClass& operator=(const TemplateClass&) = default;
36+
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: place definition of method 'operator=' outside of an anonymous namespace
37+
TemplateClass& operator=(TemplateClass&&) { return *this; };
38+
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: place definition of method 'operator=' outside of an anonymous namespace
39+
bool operator<(const TemplateClass&) const { return true; };
40+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: place definition of method 'operator<' outside of an anonymous namespace
41+
void memberFunc();
42+
T getValue() const { return {}; };
43+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: place definition of method 'getValue' outside of an anonymous namespace
44+
void memberDefinedInClass() {}
45+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: place definition of method 'memberDefinedInClass' outside of an anonymous namespace
46+
};
47+
48+
template<typename T>
49+
void TemplateClass<T>::memberFunc() {}
50+
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: place definition of method 'memberFunc' outside of an anonymous namespace
51+
52+
class OuterClass {
53+
public:
54+
class NestedClass {
55+
public:
56+
void nestedMemberFunc();
57+
void nestedMemberDefinedInClass() {}
58+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: place definition of method 'nestedMemberDefinedInClass' outside of an anonymous namespace
59+
};
60+
};
61+
62+
void OuterClass::NestedClass::nestedMemberFunc() {}
63+
// CHECK-MESSAGES: :[[@LINE-1]]:31: warning: place definition of method 'nestedMemberFunc' outside of an anonymous namespace
64+
65+
} // namespace
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %check_clang_tidy %s llvm-prefer-static-over-anonymous-namespace %t -- \
2+
// RUN: -config="{CheckOptions: { \
3+
// RUN: llvm-prefer-static-over-anonymous-namespace.AllowVariableDeclarations: false }, \
4+
// RUN: }" -- -fno-delayed-template-parsing
5+
6+
namespace {
7+
8+
void regularFunction(int param) {
9+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'regularFunction' is declared in an anonymous namespace;
10+
11+
int Variable = 42;
12+
auto Lambda = []() { return 42; };
13+
static int StaticVariable = 42;
14+
}
15+
16+
int globalVariable = 42;
17+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'globalVariable' is declared in an anonymous namespace;
18+
19+
static int staticVariable = 42;
20+
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: place static variable 'staticVariable' outside of an anonymous namespace
21+
22+
typedef int MyInt;
23+
const MyInt myGlobalVariable = 42;
24+
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: variable 'myGlobalVariable' is declared in an anonymous namespace;
25+
26+
template<typename T>
27+
constexpr T Pi = T(3.1415926);
28+
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: variable 'Pi' is declared in an anonymous namespace;
29+
30+
void (*funcPtr)() = nullptr;
31+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: variable 'funcPtr' is declared in an anonymous namespace;
32+
33+
auto lambda = []() { return 42; };
34+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: variable 'lambda' is declared in an anonymous namespace;
35+
36+
class MyClass {
37+
int member;
38+
};
39+
40+
MyClass instance;
41+
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: variable 'instance' is declared in an anonymous namespace;
42+
43+
MyClass* instancePtr = nullptr;
44+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: variable 'instancePtr' is declared in an anonymous namespace;
45+
46+
MyClass& instanceRef = instance;
47+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: variable 'instanceRef' is declared in an anonymous namespace;
48+
49+
class OtherClass{
50+
void method() {
51+
MyClass instance;
52+
MyClass* instancePtr = nullptr;
53+
MyClass& instanceRef = instance;
54+
}
55+
MyClass member;
56+
MyClass* memberPtr = nullptr;
57+
MyClass& memberRef = member;
58+
};
59+
60+
} // namespace

0 commit comments

Comments
 (0)