Skip to content

Commit 2f4a804

Browse files
authored
[clang-tidy] Add new check llvm-prefer-static-over-anonymous-namespace (#142839)
Finds function and variable declarations inside anonymous namespace and suggests replacing them with ``static`` declarations. The check will enforce that [restrict-visibility](https://llvm.org/docs/CodingStandards.html#restrict-visibility) rule in LLVM Coding Standards is followed correctly (by adding `static` to functions instead of putting them in anonimous namespace). The check has additional levels of "strictness" represented by Options. By default, the check works in the most relaxed way by giving warning only for methods and functions defined in anonymous namespaces. Also, It finds `static` functions that are placed inside anonymous namespace - there is no point in keeping them inside.
1 parent 593fd44 commit 2f4a804

File tree

8 files changed

+427
-0
lines changed

8 files changed

+427
-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: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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(
49+
functionDecl(IsDefinitionInAnonymousNamespace,
50+
unless(anyOf(hasParent(cxxRecordDecl()),
51+
hasParent(functionTemplateDecl(
52+
hasParent(cxxRecordDecl()))))))
53+
.bind("function"),
54+
this);
55+
} else {
56+
Finder->addMatcher(
57+
functionDecl(IsDefinitionInAnonymousNamespace).bind("function"), this);
58+
}
59+
60+
if (!AllowVariableDeclarations)
61+
Finder->addMatcher(varDecl(IsDefinitionInAnonymousNamespace,
62+
unless(isLocalVariable()), unless(parmVarDecl()))
63+
.bind("var"),
64+
this);
65+
}
66+
67+
void PreferStaticOverAnonymousNamespaceCheck::check(
68+
const MatchFinder::MatchResult &Result) {
69+
70+
if (const auto *Func = Result.Nodes.getNodeAs<FunctionDecl>("function")) {
71+
if (Func->isCXXClassMember())
72+
diag(Func->getLocation(),
73+
"place definition of method %0 outside of an anonymous namespace")
74+
<< Func;
75+
else if (Func->isStatic())
76+
diag(Func->getLocation(),
77+
"place static function %0 outside of an anonymous namespace")
78+
<< Func;
79+
else
80+
diag(Func->getLocation(),
81+
"function %0 is declared in an anonymous namespace; "
82+
"prefer using 'static' for restricting visibility")
83+
<< Func;
84+
return;
85+
}
86+
87+
if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) {
88+
if (Var->getStorageClass() == SC_Static)
89+
diag(Var->getLocation(),
90+
"place static variable %0 outside of an anonymous namespace")
91+
<< Var;
92+
else
93+
diag(Var->getLocation(),
94+
"variable %0 is declared in an anonymous namespace; "
95+
"prefer using 'static' for restricting visibility")
96+
<< Var;
97+
}
98+
}
99+
100+
} // 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 the ``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`.

0 commit comments

Comments
 (0)