forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			111 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			111 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- 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 <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) {
 | 
						|
  // 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 (!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
 |