clang-tools 17.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
15namespace clang::tidy::modernize {
16
17static bool locationsInSameFile(const SourceManager &Sources,
18 SourceLocation Loc1, SourceLocation Loc2) {
19 return Loc1.isFileID() && Loc2.isFileID() &&
20 Sources.getFileID(Loc1) == Sources.getFileID(Loc2);
21}
22
23static bool anonymousOrInlineNamespace(const NamespaceDecl &ND) {
24 return ND.isAnonymousNamespace() || ND.isInlineNamespace();
25}
26
27static bool singleNamedNamespaceChild(const NamespaceDecl &ND) {
28 NamespaceDecl::decl_range Decls = ND.decls();
29 if (std::distance(Decls.begin(), Decls.end()) != 1)
30 return false;
31
32 const auto *ChildNamespace = dyn_cast<const NamespaceDecl>(*Decls.begin());
33 return ChildNamespace && !anonymousOrInlineNamespace(*ChildNamespace);
34}
35
36static bool alreadyConcatenated(std::size_t NumCandidates,
37 const SourceRange &ReplacementRange,
38 const SourceManager &Sources,
39 const LangOptions &LangOpts) {
40 // FIXME: This logic breaks when there is a comment with ':'s in the middle.
41 CharSourceRange TextRange =
42 Lexer::getAsCharRange(ReplacementRange, Sources, LangOpts);
43 StringRef CurrentNamespacesText =
44 Lexer::getSourceText(TextRange, Sources, LangOpts);
45 return CurrentNamespacesText.count(':') == (NumCandidates - 1) * 2;
46}
47
48ConcatNestedNamespacesCheck::NamespaceString
49ConcatNestedNamespacesCheck::concatNamespaces() {
50 NamespaceString Result("namespace ");
51 Result.append(Namespaces.front()->getName());
52
53 std::for_each(std::next(Namespaces.begin()), Namespaces.end(),
54 [&Result](const NamespaceDecl *ND) {
55 Result.append("::");
56 Result.append(ND->getName());
57 });
58
59 return Result;
60}
61
63 ast_matchers::MatchFinder *Finder) {
64 Finder->addMatcher(ast_matchers::namespaceDecl().bind("namespace"), this);
65}
66
67void ConcatNestedNamespacesCheck::reportDiagnostic(
68 const SourceRange &FrontReplacement, const SourceRange &BackReplacement) {
69 diag(Namespaces.front()->getBeginLoc(),
70 "nested namespaces can be concatenated", DiagnosticIDs::Warning)
71 << FixItHint::CreateReplacement(FrontReplacement, concatNamespaces())
72 << FixItHint::CreateReplacement(BackReplacement, "}");
73}
74
76 const ast_matchers::MatchFinder::MatchResult &Result) {
77 const NamespaceDecl &ND = *Result.Nodes.getNodeAs<NamespaceDecl>("namespace");
78 const SourceManager &Sources = *Result.SourceManager;
79
80 if (!locationsInSameFile(Sources, ND.getBeginLoc(), ND.getRBraceLoc()))
81 return;
82
84 return;
85
86 Namespaces.push_back(&ND);
87
89 return;
90
91 SourceRange FrontReplacement(Namespaces.front()->getBeginLoc(),
92 Namespaces.back()->getLocation());
93 SourceRange BackReplacement(Namespaces.back()->getRBraceLoc(),
94 Namespaces.front()->getRBraceLoc());
95
96 if (!alreadyConcatenated(Namespaces.size(), FrontReplacement, Sources,
97 getLangOpts()))
98 reportDiagnostic(FrontReplacement, BackReplacement);
99
100 Namespaces.clear();
101}
102
103} // namespace clang::tidy::modernize
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static bool alreadyConcatenated(std::size_t NumCandidates, const SourceRange &ReplacementRange, const SourceManager &Sources, const LangOptions &LangOpts)
static bool singleNamedNamespaceChild(const NamespaceDecl &ND)
static bool anonymousOrInlineNamespace(const NamespaceDecl &ND)
static bool locationsInSameFile(const SourceManager &Sources, SourceLocation Loc1, SourceLocation Loc2)