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