Index: clang-tidy/abseil/AbseilTidyModule.cpp =================================================================== --- clang-tidy/abseil/AbseilTidyModule.cpp +++ clang-tidy/abseil/AbseilTidyModule.cpp @@ -9,6 +9,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "AnonymousEnclosedAliasesCheck.h" #include "DurationAdditionCheck.h" #include "DurationComparisonCheck.h" #include "DurationConversionCastCheck.h" @@ -33,6 +34,8 @@ class AbseilModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "abseil-anonymous-enclosed-aliases"); CheckFactories.registerCheck( "abseil-duration-addition"); CheckFactories.registerCheck( Index: clang-tidy/abseil/AnonymousEnclosedAliasesCheck.h =================================================================== --- /dev/null +++ clang-tidy/abseil/AnonymousEnclosedAliasesCheck.h @@ -0,0 +1,40 @@ +//===--- AnonymousEnclosedAliasesCheck.h - clang-tidy -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_ANONYMOUSENCLOSEDALIASESCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_ANONYMOUSENCLOSEDALIASESCHECK_H + +#include "../ClangTidy.h" +#include + +namespace clang { +namespace tidy { +namespace abseil { + +/// Detecting incorrect practice of putting using declarations outside an +/// anonymous namespace when there exists one. +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/ +/// abseil-anonymous-enclosed-aliases.html +class AnonymousEnclosedAliasesCheck : public ClangTidyCheck { +public: + AnonymousEnclosedAliasesCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +private: + const NamespaceDecl* AnonymousNamespaceDecl; + std::vector MatchedUsingDecls; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_ANONYMOUSENCLOSEDALIASESCHECK_H Index: clang-tidy/abseil/AnonymousEnclosedAliasesCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/abseil/AnonymousEnclosedAliasesCheck.cpp @@ -0,0 +1,79 @@ +//===--- AnonymousEnclosedAliasesCheck.cpp - clang-tidy -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AnonymousEnclosedAliasesCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +void AnonymousEnclosedAliasesCheck::registerMatchers(MatchFinder *Finder) { + // We try to match two types of nodes: + // 1. anonymous namespace declarations, + // 2. using declarations that are not inside an anonymous namespace and + // not inside any other scope. + Finder->addMatcher( + namespaceDecl(allOf(namespaceDecl(isAnonymous()), + namespaceDecl(isExpansionInMainFile()))) + .bind("anonymous_namespace"), + this); + Finder->addMatcher( + usingDecl( + allOf(usingDecl(isExpansionInMainFile()), + usingDecl(unless(hasAncestor(functionDecl()))), + usingDecl(unless(hasAncestor(cxxRecordDecl()))), + usingDecl(unless(hasAncestor(namespaceDecl(isAnonymous())))))) + .bind("using_decl"), + this); +} + +void AnonymousEnclosedAliasesCheck::check( + const MatchFinder::MatchResult &Result) { + + const auto *MatchedUsingDecl = + Result.Nodes.getNodeAs("using_decl"); + // If a potential using declaration is matched, + if (MatchedUsingDecl) { + // and if an anonymous namespace declaration has already been found, + // the matched using declaration is a target, and we print out + // the diagnostics for it. Otherwise, we add the using declaration + // to the vector containing all candidate using declarations. + if (AnonymousNamespaceDecl) { + diag(MatchedUsingDecl->getLocation(), + "using declaration %0 should be in the anonymous namespace. " + "Use discretion when moving using declarations as it might " + "necessitate moving lines containing relevant aliases.") + << MatchedUsingDecl; + } else { + MatchedUsingDecls.push_back(MatchedUsingDecl); + } + return; + } + // Otherwise, an anonymous namespace declaration is matched. In this case, + // all the previously matched namespace declarations in the vector + // CurrentUsingDecl are our targets, and we print out the + // diagnostics for all of them. + AnonymousNamespaceDecl = + Result.Nodes.getNodeAs("anonymous_namespace"); + for (const auto *CurrentUsingDecl : MatchedUsingDecls) { + diag(CurrentUsingDecl->getLocation(), + "using declaration %0 should be in the anonymous namespace. " + "Use discretion when moving using declarations as it might " + "necessitate moving lines containing relevant aliases.") + << CurrentUsingDecl; + } +} + +} // namespace abseil +} // namespace tidy +} // namespace clang Index: clang-tidy/abseil/CMakeLists.txt =================================================================== --- clang-tidy/abseil/CMakeLists.txt +++ clang-tidy/abseil/CMakeLists.txt @@ -2,6 +2,7 @@ add_clang_library(clangTidyAbseilModule AbseilTidyModule.cpp + AnonymousEnclosedAliasesCheck.cpp DurationAdditionCheck.cpp DurationComparisonCheck.cpp DurationConversionCastCheck.cpp Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -67,6 +67,12 @@ Improvements to clang-tidy -------------------------- +- New :doc:`abseil-anonymous-enclosed-aliases + ` check. + + Finds instances of using declarations not in an anonymous namespace + when there exists one. + - New :doc:`abseil-duration-addition ` check. Index: docs/clang-tidy/checks/abseil-anonymous-enclosed-aliases.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/abseil-anonymous-enclosed-aliases.rst @@ -0,0 +1,20 @@ +.. title:: clang-tidy - abseil-anonymous-enclosed-aliases + +abseil-anonymous-enclosed-aliases +================================= + +Finds using declarations outside of anonymous namespaces, and +suggests those declarations be moved to that namespace. + +Example: +.. code-block:: c++ + + namespace foo { + + using something; // should be inside the anonymous namespace below + + namespace { + + } // anonymous namespace + + } // foo Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -4,6 +4,7 @@ ================= .. toctree:: + abseil-anonymous-enclosed-aliases abseil-duration-addition abseil-duration-comparison abseil-duration-conversion-cast Index: test/clang-tidy/abseil-anonymous-enclosed-aliases.cpp =================================================================== --- /dev/null +++ test/clang-tidy/abseil-anonymous-enclosed-aliases.cpp @@ -0,0 +1,56 @@ +// RUN: %check_clang_tidy %s abseil-anonymous-enclosed-aliases %t +namespace bar { + +class A {}; +class B {}; + +} // namespace bar + +namespace foo1 { + +// CHECK-MESSAGES: :[[@LINE+4]]:12: warning: using declaration 'A' should +// be in the anonymous namespace. Use discretion when moving using declarations +// as it might necessitate moving lines containing relevant aliases. +// [abseil-anonymous-enclosed-aliases] +using bar::A; +void f(A a); + +namespace {} // anonymous namespace + +} // namespace foo1 + +namespace foo2 { + +namespace { + +// This is okay +using ::bar::B; + +} // anonymous namespace + +void g(B b); + +} // namespace foo2 + +// Check should not be triggered below when the using declaration is at +// function or class (instead of namespace) scope. +namespace outer { + +int fun_scope() { + using ::bar::A; + return 0; +} // function scope + +class Base { +public: + void f(); +}; // class scope + +class Derived : public Base { +public: + using Base::f; +}; // class scope + +namespace {} // anonymous namespace + +} // namespace outer \ No newline at end of file