1 //===--- ConcatNestedNamespacesCheck.cpp - clang-tidy----------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "ConcatNestedNamespacesCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include <algorithm> 14 #include <iterator> 15 16 namespace clang { 17 namespace tidy { 18 namespace modernize { 19 20 static bool locationsInSameFile(const SourceManager &Sources, 21 SourceLocation Loc1, SourceLocation Loc2) { 22 return Loc1.isFileID() && Loc2.isFileID() && 23 Sources.getFileID(Loc1) == Sources.getFileID(Loc2); 24 } 25 26 static bool anonymousOrInlineNamespace(const NamespaceDecl &ND) { 27 return ND.isAnonymousNamespace() || ND.isInlineNamespace(); 28 } 29 30 static bool singleNamedNamespaceChild(const NamespaceDecl &ND) { 31 NamespaceDecl::decl_range Decls = ND.decls(); 32 if (std::distance(Decls.begin(), Decls.end()) != 1) 33 return false; 34 35 const auto *ChildNamespace = dyn_cast<const NamespaceDecl>(*Decls.begin()); 36 return ChildNamespace && !anonymousOrInlineNamespace(*ChildNamespace); 37 } 38 39 static bool alreadyConcatenated(std::size_t NumCandidates, 40 const SourceRange &ReplacementRange, 41 const SourceManager &Sources, 42 const LangOptions &LangOpts) { 43 CharSourceRange TextRange = 44 Lexer::getAsCharRange(ReplacementRange, Sources, LangOpts); 45 StringRef CurrentNamespacesText = 46 Lexer::getSourceText(TextRange, Sources, LangOpts); 47 return CurrentNamespacesText.count(':') == (NumCandidates - 1) * 2; 48 } 49 50 ConcatNestedNamespacesCheck::NamespaceString 51 ConcatNestedNamespacesCheck::concatNamespaces() { 52 NamespaceString Result("namespace "); 53 Result.append(Namespaces.front()->getName()); 54 55 std::for_each(std::next(Namespaces.begin()), Namespaces.end(), 56 [&Result](const NamespaceDecl *ND) { 57 Result.append("::"); 58 Result.append(ND->getName()); 59 }); 60 61 return Result; 62 } 63 64 void ConcatNestedNamespacesCheck::registerMatchers( 65 ast_matchers::MatchFinder *Finder) { 66 if (!getLangOpts().CPlusPlus17) 67 return; 68 69 Finder->addMatcher(ast_matchers::namespaceDecl().bind("namespace"), this); 70 } 71 72 void ConcatNestedNamespacesCheck::reportDiagnostic( 73 const SourceRange &FrontReplacement, const SourceRange &BackReplacement) { 74 diag(Namespaces.front()->getBeginLoc(), 75 "nested namespaces can be concatenated", DiagnosticIDs::Warning) 76 << FixItHint::CreateReplacement(FrontReplacement, concatNamespaces()) 77 << FixItHint::CreateReplacement(BackReplacement, "}"); 78 } 79 80 void ConcatNestedNamespacesCheck::check( 81 const ast_matchers::MatchFinder::MatchResult &Result) { 82 const NamespaceDecl &ND = *Result.Nodes.getNodeAs<NamespaceDecl>("namespace"); 83 const SourceManager &Sources = *Result.SourceManager; 84 85 if (!locationsInSameFile(Sources, ND.getBeginLoc(), ND.getRBraceLoc())) 86 return; 87 88 if (!Sources.isInMainFile(ND.getBeginLoc())) 89 return; 90 91 if (anonymousOrInlineNamespace(ND)) 92 return; 93 94 Namespaces.push_back(&ND); 95 96 if (singleNamedNamespaceChild(ND)) 97 return; 98 99 SourceRange FrontReplacement(Namespaces.front()->getBeginLoc(), 100 Namespaces.back()->getLocation()); 101 SourceRange BackReplacement(Namespaces.back()->getRBraceLoc(), 102 Namespaces.front()->getRBraceLoc()); 103 104 if (!alreadyConcatenated(Namespaces.size(), FrontReplacement, Sources, 105 getLangOpts())) 106 reportDiagnostic(FrontReplacement, BackReplacement); 107 108 Namespaces.clear(); 109 } 110 111 } // namespace modernize 112 } // namespace tidy 113 } // namespace clang 114