123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- //===--- ConcatNestedNamespacesCheck.cpp - clang-tidy----------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "ConcatNestedNamespacesCheck.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include "clang/Lex/Lexer.h"
- #include <algorithm>
- namespace clang::tidy::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) {
- // FIXME: This logic breaks when there is a comment with ':'s in the middle.
- 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) {
- 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 (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 clang::tidy::modernize
|