wgml updated this revision to Diff 165658.
wgml marked 12 inline comments as done.
wgml added a comment.

Adjusted to review comments.


https://reviews.llvm.org/D52136

Files:
  clang-tidy/modernize/CMakeLists.txt
  clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp
  clang-tidy/modernize/ConcatNestedNamespacesCheck.h
  clang-tidy/modernize/ModernizeTidyModule.cpp
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/modernize-concat-nested-namespaces.rst
  test/clang-tidy/modernize-concat-nested-namespaces.cpp

Index: test/clang-tidy/modernize-concat-nested-namespaces.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/modernize-concat-nested-namespaces.cpp
@@ -0,0 +1,161 @@
+// RUN: %check_clang_tidy %s modernize-concat-nested-namespaces %t -- -- -std=c++17
+
+namespace n1 {}
+
+namespace n2 {
+namespace n3 {
+void t();
+}
+namespace n4 {
+void t();
+}
+} // namespace n2
+
+namespace n5 {
+inline namespace n6 {
+void t();
+}
+} // namespace n5
+
+namespace n7 {
+void t();
+
+namespace n8 {
+void t();
+}
+} // namespace n7
+
+namespace n9 {
+namespace n10 {
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: Nested namespaces can be concatenated [modernize-concat-nested-namespaces]
+// CHECK-FIXES: namespace n9::n10
+void t();
+} // namespace n10
+} // namespace n9
+// CHECK-FIXES: }
+
+namespace n11 {
+namespace n12 {
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: Nested namespaces can be concatenated [modernize-concat-nested-namespaces]
+// CHECK-FIXES: namespace n11::n12
+namespace n13 {
+void t();
+}
+namespace n14 {
+void t();
+}
+} // namespace n12
+} // namespace n11
+// CHECK-FIXES: }
+
+namespace n15 {
+namespace n16 {
+void t();
+}
+
+inline namespace n17 {
+void t();
+}
+
+namespace n18 {
+namespace n19 {
+namespace n20 {
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: Nested namespaces can be concatenated [modernize-concat-nested-namespaces]
+// CHECK-FIXES: namespace n18::n19::n20
+void t();
+} // namespace n20
+} // namespace n19
+} // namespace n18
+// CHECK-FIXES: }
+
+namespace n21 {
+void t();
+}
+} // namespace n15
+
+namespace n22 {
+namespace {
+void t();
+}
+} // namespace n22
+
+namespace n23 {
+namespace {
+namespace n24 {
+namespace n25 {
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: Nested namespaces can be concatenated [modernize-concat-nested-namespaces]
+// CHECK-FIXES: namespace n24::n25
+void t();
+} // namespace n25
+} // namespace n24
+// CHECK-FIXES: }
+} // namespace
+} // namespace n23
+
+namespace n26::n27 {
+namespace n28 {
+namespace n29::n30 {
+// CHECK-MESSAGES: :[[@LINE-3]]:1: warning: Nested namespaces can be concatenated [modernize-concat-nested-namespaces]
+// CHECK-FIXES: namespace n26::n27::n28::n29::n30
+void t() {}
+} // namespace n29::n30
+} // namespace n28
+} // namespace n26::n27
+// CHECK-FIXES: }
+
+namespace n31 {
+namespace n32 {}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: Nested namespaces can be concatenated [modernize-concat-nested-namespaces]
+} // namespace n31
+// CHECK-FIXES-EMPTY
+
+namespace n33 {
+namespace n34 {
+namespace n35 {}
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: Nested namespaces can be concatenated [modernize-concat-nested-namespaces]
+} // namespace n34
+// CHECK-FIXES-EMPTY
+namespace n36 {
+void t();
+}
+} // namespace n33
+
+namespace n37::n38 {
+void t();
+}
+
+#define IEXIST
+namespace n39 {
+namespace n40 {
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: Nested namespaces can be concatenated [modernize-concat-nested-namespaces]
+// CHECK-FIXES: namespace n39::n40
+#ifdef IEXIST
+void t() {}
+#endif
+} // namespace n40
+} // namespace n39
+// CHECK-FIXES: }
+
+namespace n41 {
+namespace n42 {
+// CHECK-MESSAGES: :[[@LINE-2]]:1: warning: Nested namespaces can be concatenated [modernize-concat-nested-namespaces]
+// CHECK-FIXES: namespace n41::n42
+#ifdef IDONTEXIST
+void t() {}
+#endif
+} // namespace n42
+} // namespace n41
+// CHECK-FIXES: }
+
+int main() {
+  n26::n27::n28::n29::n30::t();
+#ifdef IEXIST
+  n39::n40::t();
+#endif
+
+#ifdef IDONTEXIST
+  n41::n42::t();
+#endif
+
+  return 0;
+}
Index: docs/clang-tidy/checks/modernize-concat-nested-namespaces.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/modernize-concat-nested-namespaces.rst
@@ -0,0 +1,49 @@
+.. title:: clang-tidy - modernize-concat-nested-namespaces
+
+modernize-concat-nested-namespaces
+==================================
+
+Checks for use of nested namespaces in a form of ``namespace a { namespace b { ... } }``
+and offers change to syntax introduced in C++17: ``namespace a::b { ... }``.
+Inlined namespaces are not modified.
+
+For example:
+
+.. code-block:: c++
+
+  namspace n1 {
+  namespace n2 {
+  void t();
+  }
+  }
+
+  namespace n3 {
+  namespace n4 {
+  namespace n5 {
+  void t();
+  }
+  }
+  namespace n6 {
+  namespace n7 {
+  void t();
+  }
+  }
+  }
+
+Will be modified to:
+
+.. code-block:: c++
+
+  namspace n1::n2 {
+  void t();
+  }
+
+  namespace n3 {
+  namespace n4::n5 {
+  void t();
+  }
+  namespace n6::n7 {
+  void t();
+  }
+  }
+
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -9,8 +9,8 @@
    abseil-no-internal-dependencies
    abseil-no-namespace
    abseil-redundant-strcat-calls
-   abseil-string-find-startswith
    abseil-str-cat-append
+   abseil-string-find-startswith
    android-cloexec-accept
    android-cloexec-accept4
    android-cloexec-creat
@@ -172,6 +172,7 @@
    misc-unused-parameters
    misc-unused-using-decls
    modernize-avoid-bind
+   modernize-concat-nested-namespaces
    modernize-deprecated-headers
    modernize-loop-convert
    modernize-make-shared
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -72,7 +72,7 @@
 
 - New :doc:`abseil-no-internal-dependencies
   <clang-tidy/checks/abseil-no-internal-dependencies>` check.
-  
+
   Gives a warning if code using Abseil depends on internal details.
 
 - New :doc:`abseil-no-namespace
@@ -90,9 +90,16 @@
 - New :doc:`abseil-str-cat-append
   <clang-tidy/checks/abseil-str-cat-append>` check.
 
-  Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests 
+  Flags uses of ``absl::StrCat()`` to append to a ``std::string``. Suggests
   ``absl::StrAppend()`` should be used instead.
 
+- New :doc:`modernize-concat-nested-namespaces
+  <clang-tidy/checks/modernize-concat-nested-namespaces>` check.
+
+  Checks for uses of nested namespaces in the form of
+  ``namespace a { namespace b { ... }}`` and offers change to
+  syntax introduced in C++17 standard: ``namespace a::b { ... }``.
+
 - New :doc:`readability-magic-numbers
   <clang-tidy/checks/readability-magic-numbers>` check.
 
Index: clang-tidy/modernize/ModernizeTidyModule.cpp
===================================================================
--- clang-tidy/modernize/ModernizeTidyModule.cpp
+++ clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -11,6 +11,7 @@
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
 #include "AvoidBindCheck.h"
+#include "ConcatNestedNamespacesCheck.h"
 #include "DeprecatedHeadersCheck.h"
 #include "LoopConvertCheck.h"
 #include "MakeSharedCheck.h"
@@ -46,6 +47,8 @@
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
     CheckFactories.registerCheck<AvoidBindCheck>("modernize-avoid-bind");
+    CheckFactories.registerCheck<ConcatNestedNamespacesCheck>(
+        "modernize-concat-nested-namespaces");
     CheckFactories.registerCheck<DeprecatedHeadersCheck>(
         "modernize-deprecated-headers");
     CheckFactories.registerCheck<LoopConvertCheck>("modernize-loop-convert");
Index: clang-tidy/modernize/ConcatNestedNamespacesCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/modernize/ConcatNestedNamespacesCheck.h
@@ -0,0 +1,39 @@
+//===--- ConcatNestedNamespacesCheck.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_MODERNIZE_CONCATNESTEDNAMESPACESCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CONCATNESTEDNAMESPACESCHECK_H
+
+#include "../ClangTidy.h"
+#include "llvm/ADT/SmallVector.h"
+#include <string>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+class ConcatNestedNamespacesCheck : public ClangTidyCheck {
+public:
+  ConcatNestedNamespacesCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  using NamespaceContextVec = llvm::SmallVector<const NamespaceDecl *, 6>;
+
+  void reportDiagnostic(const SourceManager &Sources);
+  static std::string concatNamespaces(const NamespaceContextVec &Namespaces);
+  NamespaceContextVec Namespaces;
+};
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CONCATNESTEDNAMESPACESCHECK_H
Index: clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp
@@ -0,0 +1,117 @@
+//===--- ConcatNestedNamespacesCheck.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 "ConcatNestedNamespacesCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <iterator>
+#include <sstream>
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static bool locationsInSameFile(const SourceManager &Sources,
+                                SourceLocation Loc1, SourceLocation Loc2) {
+  return Loc1.isFileID() && Loc2.isFileID() &&
+         Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
+}
+
+static bool anonymousOrInlineNamespace(const NamespaceDecl &ND) {
+  return ND.isAnonymousNamespace() || ND.isInlineNamespace();
+}
+
+static bool singleNamedNamespaceChild(const NamespaceDecl &ND) {
+  const NamespaceDecl::decl_range Decls = ND.decls();
+  if (std::distance(Decls.begin(), Decls.end()) != 1)
+    return false;
+
+  const auto *ChildNamespace = dyn_cast<const NamespaceDecl>(*Decls.begin());
+  return ChildNamespace && !anonymousOrInlineNamespace(*ChildNamespace);
+}
+
+static bool alreadyConcatenated(std::size_t NumCandidates,
+                                const SourceRange &ReplacementRange,
+                                const SourceManager &Sources,
+                                const LangOptions &LangOpts) {
+  const CharSourceRange TextRange =
+      Lexer::getAsCharRange(ReplacementRange, Sources, LangOpts);
+  const StringRef CurrentNamespacesText =
+      Lexer::getSourceText(TextRange, Sources, LangOpts);
+  return CurrentNamespacesText.count(':') == (NumCandidates - 1) * 2;
+}
+
+std::string ConcatNestedNamespacesCheck::concatNamespaces(
+    const NamespaceContextVec &Namespaces) {
+  std::ostringstream Result;
+  bool First = true;
+  for (const NamespaceDecl *NS : Namespaces) {
+    Result << (First ? "namespace " : "::") << NS->getNameAsString();
+    First = false;
+  }
+
+  return Result.str();
+}
+
+void ConcatNestedNamespacesCheck::registerMatchers(
+    ast_matchers::MatchFinder *Finder) {
+  if (!getLangOpts().CPlusPlus17)
+    return;
+
+  Finder->addMatcher(ast_matchers::namespaceDecl().bind("namespace"), this);
+}
+
+void ConcatNestedNamespacesCheck::reportDiagnostic(
+    const SourceManager &Sources) {
+  SourceRange FrontReplacement(Namespaces.front()->getBeginLoc(),
+                               Namespaces.back()->getLocation());
+  SourceRange BackReplacement(Namespaces.back()->getRBraceLoc(),
+                              Namespaces.front()->getRBraceLoc());
+
+  if (!alreadyConcatenated(Namespaces.size(), FrontReplacement, Sources,
+                           getLangOpts())) {
+    diag(Namespaces.front()->Decl::getBeginLoc(),
+         "Nested namespaces can be concatenated", DiagnosticIDs::Warning)
+        << FixItHint::CreateReplacement(FrontReplacement,
+                                        concatNamespaces(Namespaces))
+        << FixItHint::CreateReplacement(BackReplacement, std::string("}"));
+  }
+  Namespaces.clear();
+}
+
+void ConcatNestedNamespacesCheck::check(
+    const ast_matchers::MatchFinder::MatchResult &Result) {
+  const NamespaceDecl &ND = *Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
+  const SourceManager &Sources = *Result.SourceManager;
+
+  if (!locationsInSameFile(Sources, ND.getBeginLoc(), ND.getRBraceLoc()))
+    return;
+
+  if (!Sources.isInMainFile(ND.getBeginLoc()))
+    return;
+
+  if (anonymousOrInlineNamespace(ND))
+    return;
+
+  Namespaces.push_back(&ND);
+
+  if (singleNamedNamespaceChild(ND))
+    return;
+
+  if (Namespaces.size() == 1) {
+    Namespaces.clear();
+    return;
+  }
+
+  reportDiagnostic(Sources);
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/modernize/CMakeLists.txt
===================================================================
--- clang-tidy/modernize/CMakeLists.txt
+++ clang-tidy/modernize/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 add_clang_library(clangTidyModernizeModule
   AvoidBindCheck.cpp
+  ConcatNestedNamespacesCheck.cpp
   DeprecatedHeadersCheck.cpp
   LoopConvertCheck.cpp
   LoopConvertUtils.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to