summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp
diff options
context:
space:
mode:
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