11#include "clang/AST/ASTContext.h"
12#include "clang/AST/Decl.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Basic/SourceLocation.h"
20 SourceLocation Loc1, SourceLocation Loc2) {
21 return Loc1.isFileID() && Loc2.isFileID() &&
22 Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
26 const SourceManager &Sources,
27 const LangOptions &LangOpts) {
28 CharSourceRange TextRange = Lexer::getAsCharRange(Range, Sources, LangOpts);
29 return Lexer::getSourceText(TextRange, Sources, LangOpts);
32std::optional<SourceRange>
34 const LangOptions &LangOpts)
const {
36 std::optional<Token> Tok =
38 back()->getLocation(), SM, LangOpts);
41 while (Tok->getKind() != tok::TokenKind::l_brace) {
47 return SourceRange{front()->getBeginLoc(), Tok->getEndLoc()};
50 return SourceRange{front()->getBeginLoc(), back()->getLocation()};
54 return SourceRange{front()->getRBraceLoc(), front()->getRBraceLoc()};
57 const LangOptions &LangOpts)
const {
59 SourceLocation Loc = front()->getRBraceLoc();
60 std::optional<Token> Tok =
64 if (Tok->getKind() != tok::TokenKind::comment)
66 SourceRange TokRange = SourceRange{Tok->getLocation(), Tok->getEndLoc()};
72 constexpr size_t L =
sizeof(
"//") - 1U;
73 if (TokText.take_front(L) ==
"//" &&
74 TokText.drop_front(L).trim() != CloseComment)
76 return SourceRange{front()->getRBraceLoc(), Tok->getEndLoc()};
80 for (
const NamespaceDecl *ND : *
this) {
81 if (ND->isInlineNamespace())
82 Str.append(
"inline ");
83 Str.append(ND->getName());
97 if (ND.isAnonymousNamespace() || !ND.attrs().empty())
99 if (getLangOpts().CPlusPlus20) {
101 bool IsFirstNS = IsChild || !Namespaces.empty();
102 return ND.isInlineNamespace() && !IsFirstNS;
104 return ND.isInlineNamespace();
108 const NamespaceDecl &ND)
const {
109 NamespaceDecl::decl_range Decls = ND.decls();
110 if (std::distance(Decls.begin(), Decls.end()) != 1)
113 const auto *ChildNamespace = dyn_cast<const NamespaceDecl>(*Decls.begin());
118 ast_matchers::MatchFinder *Finder) {
119 Finder->addMatcher(ast_matchers::namespaceDecl().bind(
"namespace"),
this);
122void ConcatNestedNamespacesCheck::reportDiagnostic(
123 const SourceManager &SM,
const LangOptions &LangOpts) {
124 DiagnosticBuilder DB =
125 diag(Namespaces.front().front()->getBeginLoc(),
126 "nested namespaces can be concatenated", DiagnosticIDs::Warning);
128 SmallVector<SourceRange, 6> Fronts;
129 Fronts.reserve(Namespaces.size() - 1U);
130 SmallVector<SourceRange, 6> Backs;
131 Backs.reserve(Namespaces.size());
133 for (
const NS &ND : Namespaces) {
134 std::optional<SourceRange> SR =
135 ND.getCleanedNamespaceFrontRange(SM, LangOpts);
138 Fronts.push_back(SR.value());
139 Backs.push_back(ND.getNamespaceBackRange(SM, LangOpts));
141 if (Fronts.empty() || Backs.empty())
146 SourceRange LastRBrace = Backs.pop_back_val();
149 for (
const NS &NS : Namespaces) {
150 NS.appendName(ConcatNameSpace);
151 if (&NS != &Namespaces.back())
152 ConcatNameSpace.append(
"::");
155 for (
const SourceRange &Front : Fronts)
156 DB << FixItHint::CreateRemoval(Front);
157 DB << FixItHint::CreateReplacement(
158 Namespaces.back().getReplacedNamespaceFrontRange(), ConcatNameSpace);
159 if (LastRBrace != Namespaces.back().getDefaultNamespaceBackRange())
160 DB << FixItHint::CreateReplacement(LastRBrace,
161 (
"} // " + ConcatNameSpace).str());
162 for (
const SourceRange &Back : llvm::reverse(Backs))
163 DB << FixItHint::CreateRemoval(Back);
167 const ast_matchers::MatchFinder::MatchResult &Result) {
168 const NamespaceDecl &ND = *Result.Nodes.getNodeAs<NamespaceDecl>(
"namespace");
169 const SourceManager &Sources = *Result.SourceManager;
178 Namespaces.push_back(
NS{});
179 if (!Namespaces.empty())
182 Namespaces.back().push_back(&ND);
187 if (Namespaces.size() > 1)
188 reportDiagnostic(Sources, getLangOpts());
bool singleNamedNamespaceChild(const NamespaceDecl &ND) const
bool unsupportedNamespace(const NamespaceDecl &ND, bool IsChild) const
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
SourceRange getDefaultNamespaceBackRange() const
void appendName(NamespaceName &Str) const
SourceRange getReplacedNamespaceFrontRange() const
std::optional< SourceRange > getCleanedNamespaceFrontRange(const SourceManager &SM, const LangOptions &LangOpts) const
SourceRange getNamespaceBackRange(const SourceManager &SM, const LangOptions &LangOpts) const
void appendCloseComment(NamespaceName &Str) const
llvm::SmallString< 40 > NamespaceName
static DeclarationName getName(const DependentScopeDeclRefExpr &D)
static StringRef getRawStringRef(const SourceRange &Range, const SourceManager &Sources, const LangOptions &LangOpts)
static bool locationsInSameFile(const SourceManager &Sources, SourceLocation Loc1, SourceLocation Loc2)
std::optional< Token > findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
std::optional< Token > findNextTokenIncludingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)