150
|
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
|
|
9 #include "ConcatNestedNamespacesCheck.h"
|
|
10 #include "clang/AST/ASTContext.h"
|
|
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
12 #include <algorithm>
|
|
13 #include <iterator>
|
|
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
|
|
64 void ConcatNestedNamespacesCheck::registerMatchers(
|
|
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
|
|
77 void ConcatNestedNamespacesCheck::check(
|
|
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
|
|
85 if (!Sources.isInMainFile(ND.getBeginLoc()))
|
|
86 return;
|
|
87
|
|
88 if (anonymousOrInlineNamespace(ND))
|
|
89 return;
|
|
90
|
|
91 Namespaces.push_back(&ND);
|
|
92
|
|
93 if (singleNamedNamespaceChild(ND))
|
|
94 return;
|
|
95
|
|
96 SourceRange FrontReplacement(Namespaces.front()->getBeginLoc(),
|
|
97 Namespaces.back()->getLocation());
|
|
98 SourceRange BackReplacement(Namespaces.back()->getRBraceLoc(),
|
|
99 Namespaces.front()->getRBraceLoc());
|
|
100
|
|
101 if (!alreadyConcatenated(Namespaces.size(), FrontReplacement, Sources,
|
|
102 getLangOpts()))
|
|
103 reportDiagnostic(FrontReplacement, BackReplacement);
|
|
104
|
|
105 Namespaces.clear();
|
|
106 }
|
|
107
|
|
108 } // namespace modernize
|
|
109 } // namespace tidy
|
|
110 } // namespace clang
|