Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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
- Loading branch information
Showing
8 changed files
with
378 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 113 additions & 0 deletions
113
clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
41 changes: 41 additions & 0 deletions
41
clang-tools-extra/clang-tidy/modernize/ConcatNestedNamespacesCheck.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
//===--- 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/SmallString.h" | ||
#include "llvm/ADT/SmallVector.h" | ||
|
||
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>; | ||
using NamespaceString = llvm::SmallString<40>; | ||
|
||
void reportDiagnostic(const SourceRange &FrontReplacement, | ||
const SourceRange &BackReplacement); | ||
NamespaceString concatNamespaces(); | ||
NamespaceContextVec Namespaces; | ||
}; | ||
} // namespace modernize | ||
} // namespace tidy | ||
} // namespace clang | ||
|
||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_CONCATNESTEDNAMESPACESCHECK_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
49 changes: 49 additions & 0 deletions
49
clang-tools-extra/docs/clang-tidy/checks/modernize-concat-nested-namespaces.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
.. title:: clang-tidy - modernize-concat-nested-namespaces | ||
|
||
modernize-concat-nested-namespaces | ||
================================== | ||
|
||
Checks for use of nested namespaces such as ``namespace a { namespace b { ... } }`` | ||
and suggests changing to the more concise syntax introduced in C++17: ``namespace a::b { ... }``. | ||
Inline namespaces are not modified. | ||
|
||
For example: | ||
|
||
.. code-block:: c++ | ||
|
||
namespace 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++ | ||
|
||
namespace n1::n2 { | ||
void t(); | ||
} | ||
|
||
namespace n3 { | ||
namespace n4::n5 { | ||
void t(); | ||
} | ||
namespace n6::n7 { | ||
void t(); | ||
} | ||
} | ||
|
Oops, something went wrong.