diff --git a/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.h b/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.h --- a/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.h +++ b/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEANONYMOUSNAMESPACECHECK_H #include "../ClangTidyCheck.h" +#include "../utils/FileExtensionsUtils.h" namespace clang { namespace tidy { @@ -18,19 +19,29 @@ /// Warns when using 'static' functions or variables at global scope, and /// suggests moving them to an anonymous namespace. /// +/// The check supports these options: +/// - `HeaderFileExtensions`: a semicolon-separated list of filename +/// extensions of header files (The filename extension should not contain +/// "." prefix). ";h;hh;hpp;hxx" by default. +/// +/// For extension-less header files, using an empty string or leaving an +/// empty string between ";" if there are other filename extensions. +/// /// For the user-facing documentation see: /// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-anonymous-namespace.html class UseAnonymousNamespaceCheck : public ClangTidyCheck { public: - UseAnonymousNamespaceCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + UseAnonymousNamespaceCheck(StringRef Name, ClangTidyContext *Context); bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus; } + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; private: + const StringRef RawStringHeaderFileExtensions; + utils::FileExtensionsSet HeaderFileExtensions; template void processMatch(const T *MatchedDecl); }; diff --git a/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.cpp b/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.cpp --- a/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.cpp +++ b/clang-tools-extra/clang-tidy/misc/UseAnonymousNamespaceCheck.cpp @@ -21,6 +21,15 @@ return Node.getStorageClass() == SC_Static; } +AST_POLYMORPHIC_MATCHER_P(isInHeaderFile, + AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, + VarDecl), + utils::FileExtensionsSet, HeaderFileExtensions) { + return utils::isExpansionLocInHeaderFile( + Node.getBeginLoc(), Finder->getASTContext().getSourceManager(), + HeaderFileExtensions); +} + AST_MATCHER(FunctionDecl, isMemberFunction) { return llvm::isa(&Node); } @@ -31,6 +40,24 @@ } } // namespace +UseAnonymousNamespaceCheck::UseAnonymousNamespaceCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + RawStringHeaderFileExtensions(Options.getLocalOrGlobal( + "HeaderFileExtensions", utils::defaultHeaderFileExtensions())) { + if (!utils::parseFileExtensions(RawStringHeaderFileExtensions, + HeaderFileExtensions, + utils::defaultFileExtensionDelimiters())) { + this->configurationDiag("Invalid header file extension: '%0'") + << RawStringHeaderFileExtensions; + } +} + +void UseAnonymousNamespaceCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "HeaderFileExtensions", RawStringHeaderFileExtensions); +} + template void UseAnonymousNamespaceCheck::processMatch(const T *MatchedDecl) { StringRef Type = llvm::isa(MatchedDecl) ? "variable" : "function"; @@ -42,12 +69,15 @@ void UseAnonymousNamespaceCheck::registerMatchers(MatchFinder *Finder) { Finder->addMatcher( functionDecl(isStatic(), - unless(anyOf(isInAnonymousNamespace(), isMemberFunction()))) + unless(anyOf(isInHeaderFile(HeaderFileExtensions), + isInAnonymousNamespace(), isMemberFunction()))) .bind("func"), this); Finder->addMatcher( - varDecl(isStatic(), unless(anyOf(isInAnonymousNamespace(), - isStaticLocal(), isStaticDataMember()))) + varDecl(isStatic(), + unless(anyOf(isInHeaderFile(HeaderFileExtensions), + isInAnonymousNamespace(), isStaticLocal(), + isStaticDataMember(), hasType(isConstQualified())))) .bind("var"), this); } diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/use-anonymous-namespace.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/use-anonymous-namespace.rst --- a/clang-tools-extra/docs/clang-tidy/checks/misc/use-anonymous-namespace.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/use-anonymous-namespace.rst @@ -11,6 +11,14 @@ keep C compatibility [1]. ``static`` is an overloaded term with different meanings in different contexts, so it can create confusion. +The following uses of ``static`` will *not* be diagnosed: + +* Functions or variables in header files, since anonymous namespaces in headers + is considered an antipattern. Allowed header file extensions can be configured + via the `HeaderFileExtensions` option (see below). +* ``const`` or ``constexpr`` variables, since they already have implicit internal + linkage in C++. + Examples: .. code-block:: c++ @@ -25,4 +33,14 @@ int x; } // namespace +Options +------- + +.. option:: HeaderFileExtensions + + A semicolon-separated list of filename extensions of header files (the filename + extensions should not include "." prefix). Default is ";h;hh;hpp;hxx". + For extension-less header files, using an empty string or leaving an + empty string between ";" if there are other filename extensions. + [1] `Undeprecating static `_ diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/use-anonymous-namespace.h b/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/use-anonymous-namespace.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/Inputs/use-anonymous-namespace.h @@ -0,0 +1,3 @@ +// Should not warn here, do not require anonymous namespaces in headers +static int gv{123}; +static void gf(){} diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/use-anonymous-namespace.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/use-anonymous-namespace.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/misc/use-anonymous-namespace.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-anonymous-namespace.cpp @@ -1,4 +1,5 @@ -// RUN: %check_clang_tidy %s misc-use-anonymous-namespace %t +// RUN: %check_clang_tidy %s misc-use-anonymous-namespace %t -- -header-filter=.* -- -I%S/Inputs +#include "use-anonymous-namespace.h" static void f1(); // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: function 'f1' declared 'static', move to anonymous namespace instead [misc-use-anonymous-namespace] @@ -41,3 +42,7 @@ { static int x; } + +// OK +static const int v8{123}; +static constexpr int v9{123};