/src/llvm-project/clang/lib/Format/QualifierAlignmentFixer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- QualifierAlignmentFixer.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 QualifierAlignmentFixer, a TokenAnalyzer that |
11 | | /// enforces either left or right const depending on the style. |
12 | | /// |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #include "QualifierAlignmentFixer.h" |
16 | | #include "FormatToken.h" |
17 | | #include "llvm/Support/Debug.h" |
18 | | #include "llvm/Support/Regex.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <optional> |
22 | | |
23 | | #define DEBUG_TYPE "format-qualifier-alignment-fixer" |
24 | | |
25 | | namespace clang { |
26 | | namespace format { |
27 | | |
28 | | void addQualifierAlignmentFixerPasses(const FormatStyle &Style, |
29 | 0 | SmallVectorImpl<AnalyzerPass> &Passes) { |
30 | 0 | std::vector<std::string> LeftOrder; |
31 | 0 | std::vector<std::string> RightOrder; |
32 | 0 | std::vector<tok::TokenKind> ConfiguredQualifierTokens; |
33 | 0 | prepareLeftRightOrderingForQualifierAlignmentFixer( |
34 | 0 | Style.QualifierOrder, LeftOrder, RightOrder, ConfiguredQualifierTokens); |
35 | | |
36 | | // Handle the left and right alignment separately. |
37 | 0 | for (const auto &Qualifier : LeftOrder) { |
38 | 0 | Passes.emplace_back( |
39 | 0 | [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { |
40 | 0 | return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, |
41 | 0 | ConfiguredQualifierTokens, |
42 | 0 | /*RightAlign=*/false) |
43 | 0 | .process(); |
44 | 0 | }); |
45 | 0 | } |
46 | 0 | for (const auto &Qualifier : RightOrder) { |
47 | 0 | Passes.emplace_back( |
48 | 0 | [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { |
49 | 0 | return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, |
50 | 0 | ConfiguredQualifierTokens, |
51 | 0 | /*RightAlign=*/true) |
52 | 0 | .process(); |
53 | 0 | }); |
54 | 0 | } |
55 | 0 | } |
56 | | |
57 | | static void replaceToken(const SourceManager &SourceMgr, |
58 | | tooling::Replacements &Fixes, |
59 | 0 | const CharSourceRange &Range, std::string NewText) { |
60 | 0 | auto Replacement = tooling::Replacement(SourceMgr, Range, NewText); |
61 | 0 | auto Err = Fixes.add(Replacement); |
62 | |
|
63 | 0 | if (Err) { |
64 | 0 | llvm::errs() << "Error while rearranging Qualifier : " |
65 | 0 | << llvm::toString(std::move(Err)) << "\n"; |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | | static void removeToken(const SourceManager &SourceMgr, |
70 | | tooling::Replacements &Fixes, |
71 | 0 | const FormatToken *First) { |
72 | 0 | auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), |
73 | 0 | First->Tok.getEndLoc()); |
74 | 0 | replaceToken(SourceMgr, Fixes, Range, ""); |
75 | 0 | } |
76 | | |
77 | | static void insertQualifierAfter(const SourceManager &SourceMgr, |
78 | | tooling::Replacements &Fixes, |
79 | | const FormatToken *First, |
80 | 0 | const std::string &Qualifier) { |
81 | 0 | auto Range = CharSourceRange::getCharRange(First->Tok.getLocation(), |
82 | 0 | First->Tok.getEndLoc()); |
83 | |
|
84 | 0 | std::string NewText{}; |
85 | 0 | NewText += First->TokenText; |
86 | 0 | NewText += " " + Qualifier; |
87 | 0 | replaceToken(SourceMgr, Fixes, Range, NewText); |
88 | 0 | } |
89 | | |
90 | | static void insertQualifierBefore(const SourceManager &SourceMgr, |
91 | | tooling::Replacements &Fixes, |
92 | | const FormatToken *First, |
93 | 0 | const std::string &Qualifier) { |
94 | 0 | auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), |
95 | 0 | First->Tok.getEndLoc()); |
96 | |
|
97 | 0 | std::string NewText = " " + Qualifier + " "; |
98 | 0 | NewText += First->TokenText; |
99 | |
|
100 | 0 | replaceToken(SourceMgr, Fixes, Range, NewText); |
101 | 0 | } |
102 | | |
103 | 0 | static bool endsWithSpace(const std::string &s) { |
104 | 0 | if (s.empty()) |
105 | 0 | return false; |
106 | 0 | return isspace(s.back()); |
107 | 0 | } |
108 | | |
109 | 0 | static bool startsWithSpace(const std::string &s) { |
110 | 0 | if (s.empty()) |
111 | 0 | return false; |
112 | 0 | return isspace(s.front()); |
113 | 0 | } |
114 | | |
115 | | static void rotateTokens(const SourceManager &SourceMgr, |
116 | | tooling::Replacements &Fixes, const FormatToken *First, |
117 | 0 | const FormatToken *Last, bool Left) { |
118 | 0 | auto *End = Last; |
119 | 0 | auto *Begin = First; |
120 | 0 | if (!Left) { |
121 | 0 | End = Last->Next; |
122 | 0 | Begin = First->Next; |
123 | 0 | } |
124 | |
|
125 | 0 | std::string NewText; |
126 | | // If we are rotating to the left we move the Last token to the front. |
127 | 0 | if (Left) { |
128 | 0 | NewText += Last->TokenText; |
129 | 0 | NewText += " "; |
130 | 0 | } |
131 | | |
132 | | // Then move through the other tokens. |
133 | 0 | auto *Tok = Begin; |
134 | 0 | while (Tok != End) { |
135 | 0 | if (!NewText.empty() && !endsWithSpace(NewText)) |
136 | 0 | NewText += " "; |
137 | |
|
138 | 0 | NewText += Tok->TokenText; |
139 | 0 | Tok = Tok->Next; |
140 | 0 | } |
141 | | |
142 | | // If we are rotating to the right we move the first token to the back. |
143 | 0 | if (!Left) { |
144 | 0 | if (!NewText.empty() && !startsWithSpace(NewText)) |
145 | 0 | NewText += " "; |
146 | 0 | NewText += First->TokenText; |
147 | 0 | } |
148 | |
|
149 | 0 | auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), |
150 | 0 | Last->Tok.getEndLoc()); |
151 | |
|
152 | 0 | replaceToken(SourceMgr, Fixes, Range, NewText); |
153 | 0 | } |
154 | | |
155 | | static bool |
156 | | isConfiguredQualifier(const FormatToken *const Tok, |
157 | 0 | const std::vector<tok::TokenKind> &Qualifiers) { |
158 | 0 | return Tok && llvm::is_contained(Qualifiers, Tok->Tok.getKind()); |
159 | 0 | } |
160 | | |
161 | 0 | static bool isQualifier(const FormatToken *const Tok) { |
162 | 0 | if (!Tok) |
163 | 0 | return false; |
164 | | |
165 | 0 | switch (Tok->Tok.getKind()) { |
166 | 0 | case tok::kw_const: |
167 | 0 | case tok::kw_volatile: |
168 | 0 | case tok::kw_static: |
169 | 0 | case tok::kw_inline: |
170 | 0 | case tok::kw_constexpr: |
171 | 0 | case tok::kw_restrict: |
172 | 0 | case tok::kw_friend: |
173 | 0 | return true; |
174 | 0 | default: |
175 | 0 | return false; |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | | const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight( |
180 | | const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, |
181 | | tooling::Replacements &Fixes, const FormatToken *const Tok, |
182 | 0 | const std::string &Qualifier, tok::TokenKind QualifierType) { |
183 | | // We only need to think about streams that begin with a qualifier. |
184 | 0 | if (Tok->isNot(QualifierType)) |
185 | 0 | return Tok; |
186 | | // Don't concern yourself if nothing follows the qualifier. |
187 | 0 | if (!Tok->Next) |
188 | 0 | return Tok; |
189 | | |
190 | | // Skip qualifiers to the left to find what preceeds the qualifiers. |
191 | | // Use isQualifier rather than isConfiguredQualifier to cover all qualifiers. |
192 | 0 | const FormatToken *PreviousCheck = Tok->getPreviousNonComment(); |
193 | 0 | while (isQualifier(PreviousCheck)) |
194 | 0 | PreviousCheck = PreviousCheck->getPreviousNonComment(); |
195 | | |
196 | | // Examples given in order of ['type', 'const', 'volatile'] |
197 | 0 | const bool IsRightQualifier = PreviousCheck && [PreviousCheck]() { |
198 | | // The cases: |
199 | | // `Foo() const` -> `Foo() const` |
200 | | // `Foo() const final` -> `Foo() const final` |
201 | | // `Foo() const override` -> `Foo() const final` |
202 | | // `Foo() const volatile override` -> `Foo() const volatile override` |
203 | | // `Foo() volatile const final` -> `Foo() const volatile final` |
204 | 0 | if (PreviousCheck->is(tok::r_paren)) |
205 | 0 | return true; |
206 | | |
207 | | // The cases: |
208 | | // `struct {} volatile const a;` -> `struct {} const volatile a;` |
209 | | // `class {} volatile const a;` -> `class {} const volatile a;` |
210 | 0 | if (PreviousCheck->is(tok::r_brace)) |
211 | 0 | return true; |
212 | | |
213 | | // The case: |
214 | | // `template <class T> const Bar Foo()` -> |
215 | | // `template <class T> Bar const Foo()` |
216 | | // The cases: |
217 | | // `Foo<int> const foo` -> `Foo<int> const foo` |
218 | | // `Foo<int> volatile const` -> `Foo<int> const volatile` |
219 | | // The case: |
220 | | // ``` |
221 | | // template <class T> |
222 | | // requires Concept1<T> && requires Concept2<T> |
223 | | // const Foo f(); |
224 | | // ``` |
225 | | // -> |
226 | | // ``` |
227 | | // template <class T> |
228 | | // requires Concept1<T> && requires Concept2<T> |
229 | | // Foo const f(); |
230 | | // ``` |
231 | 0 | if (PreviousCheck->is(TT_TemplateCloser)) { |
232 | | // If the token closes a template<> or requires clause, then it is a left |
233 | | // qualifier and should be moved to the right. |
234 | 0 | return !(PreviousCheck->ClosesTemplateDeclaration || |
235 | 0 | PreviousCheck->ClosesRequiresClause); |
236 | 0 | } |
237 | | |
238 | | // The case `Foo* const` -> `Foo* const` |
239 | | // The case `Foo* volatile const` -> `Foo* const volatile` |
240 | | // The case `int32_t const` -> `int32_t const` |
241 | | // The case `auto volatile const` -> `auto const volatile` |
242 | 0 | if (PreviousCheck->isOneOf(TT_PointerOrReference, tok::identifier, |
243 | 0 | tok::kw_auto)) { |
244 | 0 | return true; |
245 | 0 | } |
246 | | |
247 | 0 | return false; |
248 | 0 | }(); |
249 | | |
250 | | // Find the last qualifier to the right. |
251 | 0 | const FormatToken *LastQual = Tok; |
252 | 0 | while (isQualifier(LastQual->getNextNonComment())) |
253 | 0 | LastQual = LastQual->getNextNonComment(); |
254 | | |
255 | | // If this qualifier is to the right of a type or pointer do a partial sort |
256 | | // and return. |
257 | 0 | if (IsRightQualifier) { |
258 | 0 | if (LastQual != Tok) |
259 | 0 | rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false); |
260 | 0 | return Tok; |
261 | 0 | } |
262 | | |
263 | 0 | const FormatToken *TypeToken = LastQual->getNextNonComment(); |
264 | 0 | if (!TypeToken) |
265 | 0 | return Tok; |
266 | | |
267 | | // Stay safe and don't move past macros, also don't bother with sorting. |
268 | 0 | if (isPossibleMacro(TypeToken)) |
269 | 0 | return Tok; |
270 | | |
271 | | // The case `const long long int volatile` -> `long long int const volatile` |
272 | | // The case `long const long int volatile` -> `long long int const volatile` |
273 | | // The case `long long volatile int const` -> `long long int const volatile` |
274 | | // The case `const long long volatile int` -> `long long int const volatile` |
275 | 0 | if (TypeToken->isSimpleTypeSpecifier()) { |
276 | | // The case `const decltype(foo)` -> `const decltype(foo)` |
277 | | // The case `const typeof(foo)` -> `const typeof(foo)` |
278 | | // The case `const _Atomic(foo)` -> `const _Atomic(foo)` |
279 | 0 | if (TypeToken->isOneOf(tok::kw_decltype, tok::kw_typeof, tok::kw__Atomic)) |
280 | 0 | return Tok; |
281 | | |
282 | 0 | const FormatToken *LastSimpleTypeSpecifier = TypeToken; |
283 | 0 | while (isQualifierOrType(LastSimpleTypeSpecifier->getNextNonComment())) |
284 | 0 | LastSimpleTypeSpecifier = LastSimpleTypeSpecifier->getNextNonComment(); |
285 | |
|
286 | 0 | rotateTokens(SourceMgr, Fixes, Tok, LastSimpleTypeSpecifier, |
287 | 0 | /*Left=*/false); |
288 | 0 | return LastSimpleTypeSpecifier; |
289 | 0 | } |
290 | | |
291 | | // The case `unsigned short const` -> `unsigned short const` |
292 | | // The case: |
293 | | // `unsigned short volatile const` -> `unsigned short const volatile` |
294 | 0 | if (PreviousCheck && PreviousCheck->isSimpleTypeSpecifier()) { |
295 | 0 | if (LastQual != Tok) |
296 | 0 | rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false); |
297 | 0 | return Tok; |
298 | 0 | } |
299 | | |
300 | | // Skip the typename keyword. |
301 | | // The case `const typename C::type` -> `typename C::type const` |
302 | 0 | if (TypeToken->is(tok::kw_typename)) |
303 | 0 | TypeToken = TypeToken->getNextNonComment(); |
304 | | |
305 | | // Skip the initial :: of a global-namespace type. |
306 | | // The case `const ::...` -> `::... const` |
307 | 0 | if (TypeToken->is(tok::coloncolon)) { |
308 | | // The case `const ::template Foo...` -> `::template Foo... const` |
309 | 0 | TypeToken = TypeToken->getNextNonComment(); |
310 | 0 | if (TypeToken && TypeToken->is(tok::kw_template)) |
311 | 0 | TypeToken = TypeToken->getNextNonComment(); |
312 | 0 | } |
313 | | |
314 | | // Don't change declarations such as |
315 | | // `foo(const struct Foo a);` -> `foo(const struct Foo a);` |
316 | | // as they would currently change code such as |
317 | | // `const struct my_struct_t {} my_struct;` -> `struct my_struct_t const {} |
318 | | // my_struct;` |
319 | 0 | if (TypeToken->isOneOf(tok::kw_struct, tok::kw_class)) |
320 | 0 | return Tok; |
321 | | |
322 | 0 | if (TypeToken->isOneOf(tok::kw_auto, tok::identifier)) { |
323 | | // The case `const auto` -> `auto const` |
324 | | // The case `const Foo` -> `Foo const` |
325 | | // The case `const ::Foo` -> `::Foo const` |
326 | | // The case `const Foo *` -> `Foo const *` |
327 | | // The case `const Foo &` -> `Foo const &` |
328 | | // The case `const Foo &&` -> `Foo const &&` |
329 | | // The case `const std::Foo &&` -> `std::Foo const &&` |
330 | | // The case `const std::Foo<T> &&` -> `std::Foo<T> const &&` |
331 | | // The case `const ::template Foo` -> `::template Foo const` |
332 | | // The case `const T::template Foo` -> `T::template Foo const` |
333 | 0 | const FormatToken *Next = nullptr; |
334 | 0 | while ((Next = TypeToken->getNextNonComment()) && |
335 | 0 | (Next->is(TT_TemplateOpener) || |
336 | 0 | Next->startsSequence(tok::coloncolon, tok::identifier) || |
337 | 0 | Next->startsSequence(tok::coloncolon, tok::kw_template, |
338 | 0 | tok::identifier))) { |
339 | 0 | if (Next->is(TT_TemplateOpener)) { |
340 | 0 | assert(Next->MatchingParen && "Missing template closer"); |
341 | 0 | TypeToken = Next->MatchingParen; |
342 | 0 | } else if (Next->startsSequence(tok::coloncolon, tok::identifier)) { |
343 | 0 | TypeToken = Next->getNextNonComment(); |
344 | 0 | } else { |
345 | 0 | TypeToken = Next->getNextNonComment()->getNextNonComment(); |
346 | 0 | } |
347 | 0 | } |
348 | |
|
349 | 0 | if (Next->is(tok::kw_auto)) |
350 | 0 | TypeToken = Next; |
351 | | |
352 | | // Place the Qualifier at the end of the list of qualifiers. |
353 | 0 | while (isQualifier(TypeToken->getNextNonComment())) { |
354 | | // The case `volatile Foo::iter const` -> `Foo::iter const volatile` |
355 | 0 | TypeToken = TypeToken->getNextNonComment(); |
356 | 0 | } |
357 | |
|
358 | 0 | insertQualifierAfter(SourceMgr, Fixes, TypeToken, Qualifier); |
359 | | // Remove token and following whitespace. |
360 | 0 | auto Range = CharSourceRange::getCharRange( |
361 | 0 | Tok->getStartOfNonWhitespace(), Tok->Next->getStartOfNonWhitespace()); |
362 | 0 | replaceToken(SourceMgr, Fixes, Range, ""); |
363 | 0 | } |
364 | |
|
365 | 0 | return Tok; |
366 | 0 | } |
367 | | |
368 | | const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft( |
369 | | const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, |
370 | | tooling::Replacements &Fixes, const FormatToken *const Tok, |
371 | 0 | const std::string &Qualifier, tok::TokenKind QualifierType) { |
372 | | // We only need to think about streams that begin with a qualifier. |
373 | 0 | if (Tok->isNot(QualifierType)) |
374 | 0 | return Tok; |
375 | | // Don't concern yourself if nothing preceeds the qualifier. |
376 | 0 | if (!Tok->getPreviousNonComment()) |
377 | 0 | return Tok; |
378 | | |
379 | | // Skip qualifiers to the left to find what preceeds the qualifiers. |
380 | 0 | const FormatToken *TypeToken = Tok->getPreviousNonComment(); |
381 | 0 | while (isQualifier(TypeToken)) |
382 | 0 | TypeToken = TypeToken->getPreviousNonComment(); |
383 | | |
384 | | // For left qualifiers preceeded by nothing, a template declaration, or *,&,&& |
385 | | // we only perform sorting. |
386 | 0 | if (!TypeToken || TypeToken->isPointerOrReference() || |
387 | 0 | TypeToken->ClosesRequiresClause || TypeToken->ClosesTemplateDeclaration) { |
388 | | |
389 | | // Don't sort past a non-configured qualifier token. |
390 | 0 | const FormatToken *FirstQual = Tok; |
391 | 0 | while (isConfiguredQualifier(FirstQual->getPreviousNonComment(), |
392 | 0 | ConfiguredQualifierTokens)) { |
393 | 0 | FirstQual = FirstQual->getPreviousNonComment(); |
394 | 0 | } |
395 | |
|
396 | 0 | if (FirstQual != Tok) |
397 | 0 | rotateTokens(SourceMgr, Fixes, FirstQual, Tok, /*Left=*/true); |
398 | 0 | return Tok; |
399 | 0 | } |
400 | | |
401 | | // Stay safe and don't move past macros, also don't bother with sorting. |
402 | 0 | if (isPossibleMacro(TypeToken)) |
403 | 0 | return Tok; |
404 | | |
405 | | // Examples given in order of ['const', 'volatile', 'type'] |
406 | | |
407 | | // The case `volatile long long int const` -> `const volatile long long int` |
408 | | // The case `volatile long long const int` -> `const volatile long long int` |
409 | | // The case `const long long volatile int` -> `const volatile long long int` |
410 | | // The case `long volatile long int const` -> `const volatile long long int` |
411 | 0 | if (TypeToken->isSimpleTypeSpecifier()) { |
412 | 0 | const FormatToken *LastSimpleTypeSpecifier = TypeToken; |
413 | 0 | while (isConfiguredQualifierOrType( |
414 | 0 | LastSimpleTypeSpecifier->getPreviousNonComment(), |
415 | 0 | ConfiguredQualifierTokens)) { |
416 | 0 | LastSimpleTypeSpecifier = |
417 | 0 | LastSimpleTypeSpecifier->getPreviousNonComment(); |
418 | 0 | } |
419 | |
|
420 | 0 | rotateTokens(SourceMgr, Fixes, LastSimpleTypeSpecifier, Tok, |
421 | 0 | /*Left=*/true); |
422 | 0 | return Tok; |
423 | 0 | } |
424 | | |
425 | 0 | if (TypeToken->isOneOf(tok::kw_auto, tok::identifier, TT_TemplateCloser)) { |
426 | 0 | const auto IsStartOfType = [](const FormatToken *const Tok) -> bool { |
427 | 0 | if (!Tok) |
428 | 0 | return true; |
429 | | |
430 | | // A template closer is not the start of a type. |
431 | | // The case `?<> const` -> `const ?<>` |
432 | 0 | if (Tok->is(TT_TemplateCloser)) |
433 | 0 | return false; |
434 | | |
435 | 0 | const FormatToken *const Previous = Tok->getPreviousNonComment(); |
436 | 0 | if (!Previous) |
437 | 0 | return true; |
438 | | |
439 | | // An identifier preceeded by :: is not the start of a type. |
440 | | // The case `?::Foo const` -> `const ?::Foo` |
441 | 0 | if (Tok->is(tok::identifier) && Previous->is(tok::coloncolon)) |
442 | 0 | return false; |
443 | | |
444 | 0 | const FormatToken *const PrePrevious = Previous->getPreviousNonComment(); |
445 | | // An identifier preceeded by ::template is not the start of a type. |
446 | | // The case `?::template Foo const` -> `const ?::template Foo` |
447 | 0 | if (Tok->is(tok::identifier) && Previous->is(tok::kw_template) && |
448 | 0 | PrePrevious && PrePrevious->is(tok::coloncolon)) { |
449 | 0 | return false; |
450 | 0 | } |
451 | | |
452 | 0 | if (Tok->endsSequence(tok::kw_auto, tok::identifier)) |
453 | 0 | return false; |
454 | | |
455 | 0 | return true; |
456 | 0 | }; |
457 | |
|
458 | 0 | while (!IsStartOfType(TypeToken)) { |
459 | | // The case `?<>` |
460 | 0 | if (TypeToken->is(TT_TemplateCloser)) { |
461 | 0 | assert(TypeToken->MatchingParen && "Missing template opener"); |
462 | 0 | TypeToken = TypeToken->MatchingParen->getPreviousNonComment(); |
463 | 0 | } else { |
464 | | // The cases |
465 | | // `::Foo` |
466 | | // `?>::Foo` |
467 | | // `?Bar::Foo` |
468 | | // `::template Foo` |
469 | | // `?>::template Foo` |
470 | | // `?Bar::template Foo` |
471 | 0 | if (TypeToken->getPreviousNonComment()->is(tok::kw_template)) |
472 | 0 | TypeToken = TypeToken->getPreviousNonComment(); |
473 | |
|
474 | 0 | const FormatToken *const ColonColon = |
475 | 0 | TypeToken->getPreviousNonComment(); |
476 | 0 | const FormatToken *const PreColonColon = |
477 | 0 | ColonColon->getPreviousNonComment(); |
478 | 0 | if (PreColonColon && |
479 | 0 | PreColonColon->isOneOf(TT_TemplateCloser, tok::identifier)) { |
480 | 0 | TypeToken = PreColonColon; |
481 | 0 | } else { |
482 | 0 | TypeToken = ColonColon; |
483 | 0 | } |
484 | 0 | } |
485 | 0 | } |
486 | |
|
487 | 0 | assert(TypeToken && "Should be auto or identifier"); |
488 | | |
489 | | // Place the Qualifier at the start of the list of qualifiers. |
490 | 0 | const FormatToken *Previous = nullptr; |
491 | 0 | while ((Previous = TypeToken->getPreviousNonComment()) && |
492 | 0 | (isConfiguredQualifier(Previous, ConfiguredQualifierTokens) || |
493 | 0 | Previous->is(tok::kw_typename))) { |
494 | | // The case `volatile Foo::iter const` -> `const volatile Foo::iter` |
495 | | // The case `typename C::type const` -> `const typename C::type` |
496 | 0 | TypeToken = Previous; |
497 | 0 | } |
498 | | |
499 | | // Don't change declarations such as |
500 | | // `foo(struct Foo const a);` -> `foo(struct Foo const a);` |
501 | 0 | if (!Previous || !Previous->isOneOf(tok::kw_struct, tok::kw_class)) { |
502 | 0 | insertQualifierBefore(SourceMgr, Fixes, TypeToken, Qualifier); |
503 | 0 | removeToken(SourceMgr, Fixes, Tok); |
504 | 0 | } |
505 | 0 | } |
506 | | |
507 | 0 | return Tok; |
508 | 0 | } |
509 | | |
510 | | tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier( |
511 | 0 | const std::string &Qualifier) { |
512 | | // Don't let 'type' be an identifier, but steal typeof token. |
513 | 0 | return llvm::StringSwitch<tok::TokenKind>(Qualifier) |
514 | 0 | .Case("type", tok::kw_typeof) |
515 | 0 | .Case("const", tok::kw_const) |
516 | 0 | .Case("volatile", tok::kw_volatile) |
517 | 0 | .Case("static", tok::kw_static) |
518 | 0 | .Case("inline", tok::kw_inline) |
519 | 0 | .Case("constexpr", tok::kw_constexpr) |
520 | 0 | .Case("restrict", tok::kw_restrict) |
521 | 0 | .Case("friend", tok::kw_friend) |
522 | 0 | .Default(tok::identifier); |
523 | 0 | } |
524 | | |
525 | | LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer( |
526 | | const Environment &Env, const FormatStyle &Style, |
527 | | const std::string &Qualifier, |
528 | | const std::vector<tok::TokenKind> &QualifierTokens, bool RightAlign) |
529 | | : TokenAnalyzer(Env, Style), Qualifier(Qualifier), RightAlign(RightAlign), |
530 | 0 | ConfiguredQualifierTokens(QualifierTokens) {} |
531 | | |
532 | | std::pair<tooling::Replacements, unsigned> |
533 | | LeftRightQualifierAlignmentFixer::analyze( |
534 | | TokenAnnotator & /*Annotator*/, |
535 | | SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, |
536 | 0 | FormatTokenLexer &Tokens) { |
537 | 0 | tooling::Replacements Fixes; |
538 | 0 | AffectedRangeMgr.computeAffectedLines(AnnotatedLines); |
539 | 0 | fixQualifierAlignment(AnnotatedLines, Tokens, Fixes); |
540 | 0 | return {Fixes, 0}; |
541 | 0 | } |
542 | | |
543 | | void LeftRightQualifierAlignmentFixer::fixQualifierAlignment( |
544 | | SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, FormatTokenLexer &Tokens, |
545 | 0 | tooling::Replacements &Fixes) { |
546 | 0 | const AdditionalKeywords &Keywords = Tokens.getKeywords(); |
547 | 0 | const SourceManager &SourceMgr = Env.getSourceManager(); |
548 | 0 | tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier); |
549 | 0 | assert(QualifierToken != tok::identifier && "Unrecognised Qualifier"); |
550 | | |
551 | 0 | for (AnnotatedLine *Line : AnnotatedLines) { |
552 | 0 | fixQualifierAlignment(Line->Children, Tokens, Fixes); |
553 | 0 | if (!Line->Affected || Line->InPPDirective) |
554 | 0 | continue; |
555 | 0 | FormatToken *First = Line->First; |
556 | 0 | assert(First); |
557 | 0 | if (First->Finalized) |
558 | 0 | continue; |
559 | | |
560 | 0 | const auto *Last = Line->Last; |
561 | |
|
562 | 0 | for (const auto *Tok = First; Tok && Tok != Last && Tok->Next; |
563 | 0 | Tok = Tok->Next) { |
564 | 0 | if (Tok->is(tok::comment)) |
565 | 0 | continue; |
566 | 0 | if (RightAlign) { |
567 | 0 | Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier, |
568 | 0 | QualifierToken); |
569 | 0 | } else { |
570 | 0 | Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier, |
571 | 0 | QualifierToken); |
572 | 0 | } |
573 | 0 | } |
574 | 0 | } |
575 | 0 | } |
576 | | |
577 | | void prepareLeftRightOrderingForQualifierAlignmentFixer( |
578 | | const std::vector<std::string> &Order, std::vector<std::string> &LeftOrder, |
579 | | std::vector<std::string> &RightOrder, |
580 | 0 | std::vector<tok::TokenKind> &Qualifiers) { |
581 | | |
582 | | // Depending on the position of type in the order you need |
583 | | // To iterate forward or backward through the order list as qualifier |
584 | | // can push through each other. |
585 | | // The Order list must define the position of "type" to signify |
586 | 0 | assert(llvm::is_contained(Order, "type") && |
587 | 0 | "QualifierOrder must contain type"); |
588 | | // Split the Order list by type and reverse the left side. |
589 | | |
590 | 0 | bool left = true; |
591 | 0 | for (const auto &s : Order) { |
592 | 0 | if (s == "type") { |
593 | 0 | left = false; |
594 | 0 | continue; |
595 | 0 | } |
596 | | |
597 | 0 | tok::TokenKind QualifierToken = |
598 | 0 | LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s); |
599 | 0 | if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier) |
600 | 0 | Qualifiers.push_back(QualifierToken); |
601 | |
|
602 | 0 | if (left) { |
603 | | // Reverse the order for left aligned items. |
604 | 0 | LeftOrder.insert(LeftOrder.begin(), s); |
605 | 0 | } else { |
606 | 0 | RightOrder.push_back(s); |
607 | 0 | } |
608 | 0 | } |
609 | 0 | } |
610 | | |
611 | | bool LeftRightQualifierAlignmentFixer::isQualifierOrType( |
612 | 0 | const FormatToken *const Tok) { |
613 | 0 | return Tok && (Tok->isSimpleTypeSpecifier() || Tok->is(tok::kw_auto) || |
614 | 0 | isQualifier(Tok)); |
615 | 0 | } |
616 | | |
617 | | bool LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType( |
618 | | const FormatToken *const Tok, |
619 | 0 | const std::vector<tok::TokenKind> &Qualifiers) { |
620 | 0 | return Tok && (Tok->isSimpleTypeSpecifier() || Tok->is(tok::kw_auto) || |
621 | 0 | isConfiguredQualifier(Tok, Qualifiers)); |
622 | 0 | } |
623 | | |
624 | | // If a token is an identifier and it's upper case, it could |
625 | | // be a macro and hence we need to be able to ignore it. |
626 | 0 | bool LeftRightQualifierAlignmentFixer::isPossibleMacro(const FormatToken *Tok) { |
627 | 0 | if (!Tok) |
628 | 0 | return false; |
629 | 0 | if (Tok->isNot(tok::identifier)) |
630 | 0 | return false; |
631 | 0 | if (Tok->TokenText.upper() == Tok->TokenText.str()) { |
632 | | // T,K,U,V likely could be template arguments |
633 | 0 | return Tok->TokenText.size() != 1; |
634 | 0 | } |
635 | 0 | return false; |
636 | 0 | } |
637 | | |
638 | | } // namespace format |
639 | | } // namespace clang |