bkramer created this revision.
bkramer added a reviewer: alexfh.
bkramer added a subscriber: cfe-commits.

This is prone to ODR violations and generally frowned upon in many
codebases (e.g. LLVM). The checker flags definitions, variables and
classes in the global namespace. Common false positives like extern "C"
functions are filtered, symbols with internal linkage or hidden
visibility are not warned on either.

On LLVM most of the instances are helper functions that should be just
made static or globals that belong into a namespace.

https://reviews.llvm.org/D23130

Files:
  clang-tidy/misc/CMakeLists.txt
  clang-tidy/misc/GlobalNamespaceCheck.cpp
  clang-tidy/misc/GlobalNamespaceCheck.h
  clang-tidy/misc/MiscTidyModule.cpp
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/misc-global-namespace.rst
  test/clang-tidy/misc-global-namespace.cpp

Index: test/clang-tidy/misc-global-namespace.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/misc-global-namespace.cpp
@@ -0,0 +1,38 @@
+// RUN: %check_clang_tidy %s misc-global-namespace %t
+
+void f() {}
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: definition for 'f' in the global
+// namespace
+class F {};
+// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: definition for 'F' in the global
+// namespace
+int i;
+// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: definition for 'i' in the global
+// namespace
+
+// No warnings below.
+void g();
+
+extern "C" void h() {}
+
+extern "C" {
+void j() {}
+}
+
+struct Clike {
+  int i;
+};
+
+void *operator new(__SIZE_TYPE__, int) { return 0; }
+void operator delete(void *, int) {}
+
+__attribute__((visibility("hidden"))) void k() {}
+static void l() {}
+namespace {
+void m() {}
+}
+namespace x {
+void n() {}
+}
+
+int main() {}
Index: docs/clang-tidy/checks/misc-global-namespace.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/misc-global-namespace.rst
@@ -0,0 +1,7 @@
+.. title:: clang-tidy - misc-global-namespace
+
+misc-global-namespace
+=====================
+
+Finds definitions in the global namespace. Those definitions are prone to ODR
+conflicts.
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -59,6 +59,7 @@
    misc-definitions-in-headers
    misc-fold-init-type
    misc-forward-declaration-namespace
+   misc-global-namespace
    misc-inaccurate-erase
    misc-incorrect-roundings
    misc-inefficient-algorithm
Index: clang-tidy/misc/MiscTidyModule.cpp
===================================================================
--- clang-tidy/misc/MiscTidyModule.cpp
+++ clang-tidy/misc/MiscTidyModule.cpp
@@ -12,18 +12,18 @@
 #include "../ClangTidyModuleRegistry.h"
 #include "ArgumentCommentCheck.h"
 #include "AssertSideEffectCheck.h"
-#include "MisplacedConstCheck.h"
-#include "UnconventionalAssignOperatorCheck.h"
 #include "BoolPointerImplicitConversionCheck.h"
 #include "DanglingHandleCheck.h"
 #include "DefinitionsInHeadersCheck.h"
 #include "FoldInitTypeCheck.h"
 #include "ForwardDeclarationNamespaceCheck.h"
+#include "GlobalNamespaceCheck.h"
 #include "InaccurateEraseCheck.h"
 #include "IncorrectRoundings.h"
 #include "InefficientAlgorithmCheck.h"
 #include "MacroParenthesesCheck.h"
 #include "MacroRepeatedSideEffectsCheck.h"
+#include "MisplacedConstCheck.h"
 #include "MisplacedWideningCastCheck.h"
 #include "MoveConstantArgumentCheck.h"
 #include "MoveConstructorInitCheck.h"
@@ -44,6 +44,7 @@
 #include "SuspiciousStringCompareCheck.h"
 #include "SwappedArgumentsCheck.h"
 #include "ThrowByValueCatchByReferenceCheck.h"
+#include "UnconventionalAssignOperatorCheck.h"
 #include "UndelegatedConstructor.h"
 #include "UniqueptrResetReleaseCheck.h"
 #include "UnusedAliasDeclsCheck.h"
@@ -62,6 +63,7 @@
     CheckFactories.registerCheck<ArgumentCommentCheck>("misc-argument-comment");
     CheckFactories.registerCheck<AssertSideEffectCheck>(
         "misc-assert-side-effect");
+    CheckFactories.registerCheck<GlobalNamespaceCheck>("misc-global-namespace");
     CheckFactories.registerCheck<MisplacedConstCheck>(
         "misc-misplaced-const");
     CheckFactories.registerCheck<UnconventionalAssignOperatorCheck>(
Index: clang-tidy/misc/GlobalNamespaceCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/misc/GlobalNamespaceCheck.h
@@ -0,0 +1,36 @@
+//===--- GlobalNamespaceCheck.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_MISC_GLOBAL_NAMESPACE_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_GLOBAL_NAMESPACE_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// Finds definitions in the global namespace. Those definitions are prone to
+/// ODR conflicts.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/misc-global-namespace.html
+class GlobalNamespaceCheck : public ClangTidyCheck {
+public:
+  GlobalNamespaceCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_GLOBAL_NAMESPACE_H
Index: clang-tidy/misc/GlobalNamespaceCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/misc/GlobalNamespaceCheck.cpp
@@ -0,0 +1,78 @@
+//===--- GlobalNamespaceCheck.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 "GlobalNamespaceCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+void GlobalNamespaceCheck::registerMatchers(MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  Finder->addMatcher(
+      namedDecl(hasDeclContext(translationUnitDecl()),
+                anyOf(functionDecl(isDefinition(), unless(isDeleted()),
+                                   unless(isExternC())),
+                      varDecl(isDefinition()), cxxRecordDecl(isDefinition())))
+          .bind("decl"),
+      this);
+}
+
+void GlobalNamespaceCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<NamedDecl>("decl");
+
+  // Ignore decls with internal linkage. Hidden visibility can still lead to ODR
+  // violations but cannot cross DSO boundaries, so it's often used to separate
+  // namespaces, ignore those too.
+  LinkageInfo LV = MatchedDecl->getLinkageAndVisibility();
+  if (!isExternallyVisible(LV.getLinkage()) ||
+      LV.getVisibility() == HiddenVisibility)
+    return;
+
+  if (const auto *VDecl = dyn_cast<VarDecl>(MatchedDecl)) {
+    // extern "C" globals need to be in the global namespace.
+    if (VDecl->isExternC())
+      return;
+  }
+
+  if (const auto *RDecl = dyn_cast<CXXRecordDecl>(MatchedDecl)) {
+    // Ignore C-like records. Those are often used in C wrappers and don't
+    // contain anything with linkage.
+    if (RDecl->isCLike())
+      return;
+  }
+
+  if (const auto *FDecl = dyn_cast<FunctionDecl>(MatchedDecl)) {
+    // main() should be in the global namespace.
+    if (FDecl->isMain())
+      return;
+
+    // Ignore overloaded operators.
+    // FIXME: Should this only ignore global operator new/delete?
+    if (FDecl->isOverloadedOperator())
+      return;
+  }
+
+  // FIXME: If we want to be really fancy we could move declaration-less
+  // definitions into a global namespace or make them static with a FixIt.
+  diag(MatchedDecl->getLocation(), "definition for %0 in the global namespace; "
+                                   "move it into a namespace or give it "
+                                   "internal linkage to avoid ODR conflicts")
+      << MatchedDecl;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/misc/CMakeLists.txt
===================================================================
--- clang-tidy/misc/CMakeLists.txt
+++ clang-tidy/misc/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_clang_library(clangTidyMiscModule
   ArgumentCommentCheck.cpp
   AssertSideEffectCheck.cpp
+  GlobalNamespaceCheck.cpp
   MisplacedConstCheck.cpp
   UnconventionalAssignOperatorCheck.cpp
   BoolPointerImplicitConversionCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to