clang-tools  15.0.0git
ConcatNestedNamespacesCheck.cpp
Go to the documentation of this file.
1 //===--- ConcatNestedNamespacesCheck.cpp - clang-tidy----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13 #include <algorithm>
14 
15 namespace clang {
16 namespace tidy {
17 namespace modernize {
18 
19 static bool locationsInSameFile(const SourceManager &Sources,
20  SourceLocation Loc1, SourceLocation Loc2) {
21  return Loc1.isFileID() && Loc2.isFileID() &&
22  Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
23 }
24 
25 static bool anonymousOrInlineNamespace(const NamespaceDecl &ND) {
26  return ND.isAnonymousNamespace() || ND.isInlineNamespace();
27 }
28 
29 static bool singleNamedNamespaceChild(const NamespaceDecl &ND) {
30  NamespaceDecl::decl_range Decls = ND.decls();
31  if (std::distance(Decls.begin(), Decls.end()) != 1)
32  return false;
33 
34  const auto *ChildNamespace = dyn_cast<const NamespaceDecl>(*Decls.begin());
35  return ChildNamespace && !anonymousOrInlineNamespace(*ChildNamespace);
36 }
37 
38 static bool alreadyConcatenated(std::size_t NumCandidates,
39  const SourceRange &ReplacementRange,
40  const SourceManager &Sources,
41  const LangOptions &LangOpts) {
42  // FIXME: This logic breaks when there is a comment with ':'s in the middle.
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 
65  ast_matchers::MatchFinder *Finder) {
66  Finder->addMatcher(ast_matchers::namespaceDecl().bind("namespace"), this);
67 }
68 
69 void ConcatNestedNamespacesCheck::reportDiagnostic(
70  const SourceRange &FrontReplacement, const SourceRange &BackReplacement) {
71  diag(Namespaces.front()->getBeginLoc(),
72  "nested namespaces can be concatenated", DiagnosticIDs::Warning)
73  << FixItHint::CreateReplacement(FrontReplacement, concatNamespaces())
74  << FixItHint::CreateReplacement(BackReplacement, "}");
75 }
76 
78  const ast_matchers::MatchFinder::MatchResult &Result) {
79  const NamespaceDecl &ND = *Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
80  const SourceManager &Sources = *Result.SourceManager;
81 
82  if (!locationsInSameFile(Sources, ND.getBeginLoc(), ND.getRBraceLoc()))
83  return;
84 
86  return;
87 
88  Namespaces.push_back(&ND);
89 
91  return;
92 
93  SourceRange FrontReplacement(Namespaces.front()->getBeginLoc(),
94  Namespaces.back()->getLocation());
95  SourceRange BackReplacement(Namespaces.back()->getRBraceLoc(),
96  Namespaces.front()->getRBraceLoc());
97 
98  if (!alreadyConcatenated(Namespaces.size(), FrontReplacement, Sources,
99  getLangOpts()))
100  reportDiagnostic(FrontReplacement, BackReplacement);
101 
102  Namespaces.clear();
103 }
104 
105 } // namespace modernize
106 } // namespace tidy
107 } // namespace clang
clang::tidy::modernize::singleNamedNamespaceChild
static bool singleNamedNamespaceChild(const NamespaceDecl &ND)
Definition: ConcatNestedNamespacesCheck.cpp:29
clang::tidy::modernize::anonymousOrInlineNamespace
static bool anonymousOrInlineNamespace(const NamespaceDecl &ND)
Definition: ConcatNestedNamespacesCheck.cpp:25
clang::tidy::cppcoreguidelines::getSourceText
static std::string getSourceText(const CXXDestructorDecl &Destructor)
Definition: VirtualClassDestructorCheck.cpp:112
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:419
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:25
clang::tidy::modernize::locationsInSameFile
static bool locationsInSameFile(const SourceManager &Sources, SourceLocation Loc1, SourceLocation Loc2)
Definition: ConcatNestedNamespacesCheck.cpp:19
ConcatNestedNamespacesCheck.h
clang::tidy::modernize::alreadyConcatenated
static bool alreadyConcatenated(std::size_t NumCandidates, const SourceRange &ReplacementRange, const SourceManager &Sources, const LangOptions &LangOpts)
Definition: ConcatNestedNamespacesCheck.cpp:38
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
LangOpts
const LangOptions * LangOpts
Definition: ExtractFunction.cpp:366
clang::tidy::modernize::ConcatNestedNamespacesCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: ConcatNestedNamespacesCheck.cpp:64
clang::tidy::modernize::ConcatNestedNamespacesCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: ConcatNestedNamespacesCheck.cpp:77
Warning
constexpr static llvm::SourceMgr::DiagKind Warning
Definition: ConfigCompile.cpp:586