/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Format/UsingDeclarationsSorter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- UsingDeclarationsSorter.cpp ----------------------------*- C++ -*-===// |
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 | | /// \file |
10 | | /// This file implements UsingDeclarationsSorter, a TokenAnalyzer that |
11 | | /// sorts consecutive using declarations. |
12 | | /// |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #include "UsingDeclarationsSorter.h" |
16 | | #include "llvm/Support/Debug.h" |
17 | | #include "llvm/Support/Regex.h" |
18 | | |
19 | | #include <algorithm> |
20 | | |
21 | | #define DEBUG_TYPE "using-declarations-sorter" |
22 | | |
23 | | namespace clang { |
24 | | namespace format { |
25 | | |
26 | | namespace { |
27 | | |
28 | | // The order of using declaration is defined as follows: |
29 | | // Split the strings by "::" and discard any initial empty strings. The last |
30 | | // element of each list is a non-namespace name; all others are namespace |
31 | | // names. Sort the lists of names lexicographically, where the sort order of |
32 | | // individual names is that all non-namespace names come before all namespace |
33 | | // names, and within those groups, names are in case-insensitive lexicographic |
34 | | // order. |
35 | 107 | int compareLabels(StringRef A, StringRef B) { |
36 | 107 | SmallVector<StringRef, 2> NamesA; |
37 | 107 | A.split(NamesA, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false); |
38 | 107 | SmallVector<StringRef, 2> NamesB; |
39 | 107 | B.split(NamesB, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false); |
40 | 107 | size_t SizeA = NamesA.size(); |
41 | 107 | size_t SizeB = NamesB.size(); |
42 | 125 | for (size_t I = 0, E = std::min(SizeA, SizeB); I < E; ++I18 ) { |
43 | 125 | if (I + 1 == SizeA) { |
44 | | // I is the last index of NamesA and NamesA[I] is a non-namespace name. |
45 | | |
46 | | // Non-namespace names come before all namespace names. |
47 | 101 | if (SizeB > SizeA) |
48 | 1 | return -1; |
49 | | |
50 | | // Two names within a group compare case-insensitively. |
51 | 100 | return NamesA[I].compare_insensitive(NamesB[I]); |
52 | 101 | } |
53 | | |
54 | | // I is the last index of NamesB and NamesB[I] is a non-namespace name. |
55 | | // Non-namespace names come before all namespace names. |
56 | 24 | if (I + 1 == SizeB) |
57 | 4 | return 1; |
58 | | |
59 | | // Two namespaces names within a group compare case-insensitively. |
60 | 20 | int C = NamesA[I].compare_insensitive(NamesB[I]); |
61 | 20 | if (C != 0) |
62 | 2 | return C; |
63 | 20 | } |
64 | 0 | return 0; |
65 | 107 | } |
66 | | |
67 | | struct UsingDeclaration { |
68 | | const AnnotatedLine *Line; |
69 | | std::string Label; |
70 | | |
71 | | UsingDeclaration(const AnnotatedLine *Line, const std::string &Label) |
72 | 161 | : Line(Line), Label(Label) {} |
73 | | |
74 | 107 | bool operator<(const UsingDeclaration &Other) const { |
75 | 107 | return compareLabels(Label, Other.Label) < 0; |
76 | 107 | } |
77 | | }; |
78 | | |
79 | | /// Computes the label of a using declaration starting at tthe using token |
80 | | /// \p UsingTok. |
81 | | /// If \p UsingTok doesn't begin a using declaration, returns the empty string. |
82 | | /// Note that this detects specifically using declarations, as in: |
83 | | /// using A::B::C; |
84 | | /// and not type aliases, as in: |
85 | | /// using A = B::C; |
86 | | /// Type aliases are in general not safe to permute. |
87 | 360 | std::string computeUsingDeclarationLabel(const FormatToken *UsingTok) { |
88 | 360 | assert(UsingTok && UsingTok->is(tok::kw_using) && "Expecting a using token"); |
89 | 0 | std::string Label; |
90 | 360 | const FormatToken *Tok = UsingTok->Next; |
91 | 360 | if (Tok && Tok->is(tok::kw_typename)) { |
92 | 2 | Label.append("typename "); |
93 | 2 | Tok = Tok->Next; |
94 | 2 | } |
95 | 360 | if (Tok && Tok->is(tok::coloncolon)) { |
96 | 16 | Label.append("::"); |
97 | 16 | Tok = Tok->Next; |
98 | 16 | } |
99 | 360 | bool HasIdentifier = false; |
100 | 450 | while (Tok && Tok->is(tok::identifier)) { |
101 | 440 | HasIdentifier = true; |
102 | 440 | Label.append(Tok->TokenText.str()); |
103 | 440 | Tok = Tok->Next; |
104 | 440 | if (!Tok || Tok->isNot(tok::coloncolon)439 ) |
105 | 350 | break; |
106 | 90 | Label.append("::"); |
107 | 90 | Tok = Tok->Next; |
108 | 90 | } |
109 | 360 | if (HasIdentifier && Tok356 && Tok->isOneOf(tok::semi, tok::comma)355 ) |
110 | 161 | return Label; |
111 | 199 | return ""; |
112 | 360 | } |
113 | | |
114 | | void endUsingDeclarationBlock( |
115 | | SmallVectorImpl<UsingDeclaration> *UsingDeclarations, |
116 | 93.8k | const SourceManager &SourceMgr, tooling::Replacements *Fixes) { |
117 | 93.8k | bool BlockAffected = false; |
118 | 93.8k | for (const UsingDeclaration &Declaration : *UsingDeclarations) { |
119 | 99 | if (Declaration.Line->Affected) { |
120 | 92 | BlockAffected = true; |
121 | 92 | break; |
122 | 92 | } |
123 | 99 | } |
124 | 93.8k | if (!BlockAffected) { |
125 | 93.7k | UsingDeclarations->clear(); |
126 | 93.7k | return; |
127 | 93.7k | } |
128 | 92 | SmallVector<UsingDeclaration, 4> SortedUsingDeclarations( |
129 | 92 | UsingDeclarations->begin(), UsingDeclarations->end()); |
130 | 92 | llvm::stable_sort(SortedUsingDeclarations); |
131 | 92 | SortedUsingDeclarations.erase( |
132 | 92 | std::unique(SortedUsingDeclarations.begin(), |
133 | 92 | SortedUsingDeclarations.end(), |
134 | 92 | [](const UsingDeclaration &a, const UsingDeclaration &b) { |
135 | 64 | return a.Label == b.Label; |
136 | 64 | }), |
137 | 92 | SortedUsingDeclarations.end()); |
138 | 248 | for (size_t I = 0, E = UsingDeclarations->size(); I < E; ++I156 ) { |
139 | 156 | if (I >= SortedUsingDeclarations.size()) { |
140 | | // This using declaration has been deduplicated, delete it. |
141 | 7 | auto Begin = |
142 | 7 | (*UsingDeclarations)[I].Line->First->WhitespaceRange.getBegin(); |
143 | 7 | auto End = (*UsingDeclarations)[I].Line->Last->Tok.getEndLoc(); |
144 | 7 | auto Range = CharSourceRange::getCharRange(Begin, End); |
145 | 7 | auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, "")); |
146 | 7 | if (Err) { |
147 | 0 | llvm::errs() << "Error while sorting using declarations: " |
148 | 0 | << llvm::toString(std::move(Err)) << "\n"; |
149 | 0 | } |
150 | 7 | continue; |
151 | 7 | } |
152 | 149 | if ((*UsingDeclarations)[I].Line == SortedUsingDeclarations[I].Line) |
153 | 76 | continue; |
154 | 73 | auto Begin = (*UsingDeclarations)[I].Line->First->Tok.getLocation(); |
155 | 73 | auto End = (*UsingDeclarations)[I].Line->Last->Tok.getEndLoc(); |
156 | 73 | auto SortedBegin = |
157 | 73 | SortedUsingDeclarations[I].Line->First->Tok.getLocation(); |
158 | 73 | auto SortedEnd = SortedUsingDeclarations[I].Line->Last->Tok.getEndLoc(); |
159 | 73 | StringRef Text(SourceMgr.getCharacterData(SortedBegin), |
160 | 73 | SourceMgr.getCharacterData(SortedEnd) - |
161 | 73 | SourceMgr.getCharacterData(SortedBegin)); |
162 | 73 | LLVM_DEBUG({ |
163 | 73 | StringRef OldText(SourceMgr.getCharacterData(Begin), |
164 | 73 | SourceMgr.getCharacterData(End) - |
165 | 73 | SourceMgr.getCharacterData(Begin)); |
166 | 73 | llvm::dbgs() << "Replacing '" << OldText << "' with '" << Text << "'\n"; |
167 | 73 | }); |
168 | 73 | auto Range = CharSourceRange::getCharRange(Begin, End); |
169 | 73 | auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, Text)); |
170 | 73 | if (Err) { |
171 | 0 | llvm::errs() << "Error while sorting using declarations: " |
172 | 0 | << llvm::toString(std::move(Err)) << "\n"; |
173 | 0 | } |
174 | 73 | } |
175 | 92 | UsingDeclarations->clear(); |
176 | 92 | } |
177 | | |
178 | | } // namespace |
179 | | |
180 | | UsingDeclarationsSorter::UsingDeclarationsSorter(const Environment &Env, |
181 | | const FormatStyle &Style) |
182 | 19.1k | : TokenAnalyzer(Env, Style) {} |
183 | | |
184 | | std::pair<tooling::Replacements, unsigned> UsingDeclarationsSorter::analyze( |
185 | | TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, |
186 | 19.3k | FormatTokenLexer &Tokens) { |
187 | 19.3k | const SourceManager &SourceMgr = Env.getSourceManager(); |
188 | 19.3k | AffectedRangeMgr.computeAffectedLines(AnnotatedLines); |
189 | 19.3k | tooling::Replacements Fixes; |
190 | 19.3k | SmallVector<UsingDeclaration, 4> UsingDeclarations; |
191 | 74.6k | for (const AnnotatedLine *Line : AnnotatedLines) { |
192 | 74.6k | const auto *FirstTok = Line->First; |
193 | 74.6k | if (Line->InPPDirective || !Line->startsWith(tok::kw_using)69.3k || |
194 | 74.6k | FirstTok->Finalized362 ) { |
195 | 74.2k | endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes); |
196 | 74.2k | continue; |
197 | 74.2k | } |
198 | 360 | if (FirstTok->NewlinesBefore > 1) |
199 | 7 | endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes); |
200 | 360 | const auto *UsingTok = |
201 | 360 | FirstTok->is(tok::comment) ? FirstTok->getNextNonComment()2 : FirstTok358 ; |
202 | 360 | std::string Label = computeUsingDeclarationLabel(UsingTok); |
203 | 360 | if (Label.empty()) { |
204 | 199 | endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes); |
205 | 199 | continue; |
206 | 199 | } |
207 | 161 | UsingDeclarations.push_back(UsingDeclaration(Line, Label)); |
208 | 161 | } |
209 | 19.3k | endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes); |
210 | 19.3k | return {Fixes, 0}; |
211 | 19.3k | } |
212 | | |
213 | | } // namespace format |
214 | | } // namespace clang |