summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp
diff options
context:
space:
mode:
authorJonas Toth <jonas.toth@gmail.com>2018-09-25 18:12:28 +0000
committerJonas Toth <jonas.toth@gmail.com>2018-09-25 18:12:28 +0000
commitd1bd01c3183f537cbf63a71b8303a656b4c6cb65 (patch)
tree9816a0d6c6d61b66641093ea089ea619d8ad2dd3 /clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp
parent02d600d267e5012eaabdf0e20afaf2dd7fb9b2e2 (diff)
downloadbcm5719-llvm-d1bd01c3183f537cbf63a71b8303a656b4c6cb65.tar.gz
bcm5719-llvm-d1bd01c3183f537cbf63a71b8303a656b4c6cb65.zip
[clang-tidy] Add modernize-concat-nested-namespaces check
Summary: Finds instances of namespaces concatenated using explicit syntax, such as `namespace a { namespace b { [...] }}` and offers fix to glue it to `namespace a::b { [...] }`. Properly handles `inline` and unnamed namespaces. ~~Also, detects empty blocks in nested namespaces and offers to remove them.~~ Test with common use cases included. I ran the check against entire llvm repository. Except for expected `nested namespace definitions only available with -std=c++17 or -std=gnu++17` warnings I noticed no issues when the check was performed. Example: ``` namespace a { namespace b { void test(); }} ``` can become ``` namespace a::b { void test(); } ``` Patch by wgml! Reviewers: alexfh, aaron.ballman, hokein Reviewed By: aaron.ballman Subscribers: JonasToth, Eugene.Zelenko, lebedev.ri, mgorny, xazax.hun, cfe-commits Tags: #clang-tools-extra Differential Revision: https://reviews.llvm.org/D52136 llvm-svn: 343000
Diffstat (limited to 'clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp')
-rw-r--r--clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp113
1 files changed, 113 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp b/clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp
new file mode 100644
index 00000000000..bef85f7b357
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp
@@ -0,0 +1,113 @@
+//===--- 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 <algorithm>
+#include <iterator>
+
+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) {
+ 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) {
+ CharSourceRange TextRange =
+ Lexer::getAsCharRange(ReplacementRange, Sources, LangOpts);
+ StringRef CurrentNamespacesText =
+ Lexer::getSourceText(TextRange, Sources, LangOpts);
+ return CurrentNamespacesText.count(':') == (NumCandidates - 1) * 2;
+}
+
+ConcatNestedNamespacesCheck::NamespaceString
+ConcatNestedNamespacesCheck::concatNamespaces() {
+ NamespaceString Result("namespace ");
+ Result.append(Namespaces.front()->getName());
+
+ std::for_each(std::next(Namespaces.begin()), Namespaces.end(),
+ [&Result](const NamespaceDecl *ND) {
+ Result.append("::");
+ Result.append(ND->getName());
+ });
+
+ return Result;
+}
+
+void ConcatNestedNamespacesCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus17)
+ return;
+
+ Finder->addMatcher(ast_matchers::namespaceDecl().bind("namespace"), this);
+}
+
+void ConcatNestedNamespacesCheck::reportDiagnostic(
+ const SourceRange &FrontReplacement, const SourceRange &BackReplacement) {
+ diag(Namespaces.front()->getBeginLoc(),
+ "nested namespaces can be concatenated", DiagnosticIDs::Warning)
+ << FixItHint::CreateReplacement(FrontReplacement, concatNamespaces())
+ << FixItHint::CreateReplacement(BackReplacement, "}");
+}
+
+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;
+
+ SourceRange FrontReplacement(Namespaces.front()->getBeginLoc(),
+ Namespaces.back()->getLocation());
+ SourceRange BackReplacement(Namespaces.back()->getRBraceLoc(),
+ Namespaces.front()->getRBraceLoc());
+
+ if (!alreadyConcatenated(Namespaces.size(), FrontReplacement, Sources,
+ getLangOpts()))
+ reportDiagnostic(FrontReplacement, BackReplacement);
+
+ Namespaces.clear();
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
OpenPOWER on IntegriCloud