diff options
author | Jonas Toth <jonas.toth@gmail.com> | 2018-09-25 18:12:28 +0000 |
---|---|---|
committer | Jonas Toth <jonas.toth@gmail.com> | 2018-09-25 18:12:28 +0000 |
commit | d1bd01c3183f537cbf63a71b8303a656b4c6cb65 (patch) | |
tree | 9816a0d6c6d61b66641093ea089ea619d8ad2dd3 /clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp | |
parent | 02d600d267e5012eaabdf0e20afaf2dd7fb9b2e2 (diff) | |
download | bcm5719-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.cpp | 113 |
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 |