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