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 | // 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 | if (TypeToken->isTypeName(LangOpts)) { |
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 | if (TypeToken->isOneOf(K1: tok::kw_decltype, K2: tok::kw_typeof, Ks: tok::kw__Atomic)) |
280 | return Tok; |
281 | |
282 | const FormatToken *LastSimpleTypeSpecifier = TypeToken; |
283 | while (isQualifierOrType(Tok: LastSimpleTypeSpecifier->getNextNonComment(), |
284 | LangOpts)) { |
285 | LastSimpleTypeSpecifier = LastSimpleTypeSpecifier->getNextNonComment(); |
286 | } |
287 | |
288 | rotateTokens(SourceMgr, Fixes, First: Tok, Last: LastSimpleTypeSpecifier, |
289 | /*Left=*/false); |
290 | return LastSimpleTypeSpecifier; |
291 | } |
292 | |
293 | // The case `unsigned short const` -> `unsigned short const` |
294 | // The case: |
295 | // `unsigned short volatile const` -> `unsigned short const volatile` |
296 | if (PreviousCheck && PreviousCheck->isTypeName(LangOpts)) { |
297 | if (LastQual != Tok) |
298 | rotateTokens(SourceMgr, Fixes, First: Tok, Last: LastQual, /*Left=*/false); |
299 | return Tok; |
300 | } |
301 | |
302 | // Skip the typename keyword. |
303 | // The case `const typename C::type` -> `typename C::type const` |
304 | if (TypeToken->is(Kind: tok::kw_typename)) |
305 | TypeToken = TypeToken->getNextNonComment(); |
306 | |
307 | // Skip the initial :: of a global-namespace type. |
308 | // The case `const ::...` -> `::... const` |
309 | if (TypeToken->is(Kind: tok::coloncolon)) { |
310 | // The case `const ::template Foo...` -> `::template Foo... const` |
311 | TypeToken = TypeToken->getNextNonComment(); |
312 | if (TypeToken && TypeToken->is(Kind: tok::kw_template)) |
313 | TypeToken = TypeToken->getNextNonComment(); |
314 | } |
315 | |
316 | // Don't change declarations such as |
317 | // `foo(const struct Foo a);` -> `foo(const struct Foo a);` |
318 | // as they would currently change code such as |
319 | // `const struct my_struct_t {} my_struct;` -> `struct my_struct_t const {} |
320 | // my_struct;` |
321 | if (TypeToken->isOneOf(K1: tok::kw_struct, K2: tok::kw_class)) |
322 | return Tok; |
323 | |
324 | if (TypeToken->isOneOf(K1: tok::kw_auto, K2: tok::identifier)) { |
325 | // The case `const auto` -> `auto 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 Foo &&` -> `Foo const &&` |
331 | // The case `const std::Foo &&` -> `std::Foo const &&` |
332 | // The case `const std::Foo<T> &&` -> `std::Foo<T> const &&` |
333 | // The case `const ::template Foo` -> `::template Foo const` |
334 | // The case `const T::template Foo` -> `T::template Foo const` |
335 | const FormatToken *Next = nullptr; |
336 | while ((Next = TypeToken->getNextNonComment()) && |
337 | (Next->is(TT: TT_TemplateOpener) || |
338 | Next->startsSequence(K1: tok::coloncolon, Tokens: tok::identifier) || |
339 | Next->startsSequence(K1: tok::coloncolon, Tokens: tok::kw_template, |
340 | Tokens: tok::identifier))) { |
341 | if (Next->is(TT: TT_TemplateOpener)) { |
342 | assert(Next->MatchingParen && "Missing template closer" ); |
343 | TypeToken = Next->MatchingParen; |
344 | } else if (Next->startsSequence(K1: tok::coloncolon, Tokens: tok::identifier)) { |
345 | TypeToken = Next->getNextNonComment(); |
346 | } else { |
347 | TypeToken = Next->getNextNonComment()->getNextNonComment(); |
348 | } |
349 | } |
350 | |
351 | if (Next->is(Kind: tok::kw_auto)) |
352 | TypeToken = Next; |
353 | |
354 | // Place the Qualifier at the end of the list of qualifiers. |
355 | while (isQualifier(Tok: TypeToken->getNextNonComment())) { |
356 | // The case `volatile Foo::iter const` -> `Foo::iter const volatile` |
357 | TypeToken = TypeToken->getNextNonComment(); |
358 | } |
359 | |
360 | insertQualifierAfter(SourceMgr, Fixes, First: TypeToken, Qualifier); |
361 | // Remove token and following whitespace. |
362 | auto Range = CharSourceRange::getCharRange( |
363 | B: Tok->getStartOfNonWhitespace(), E: Tok->Next->getStartOfNonWhitespace()); |
364 | replaceToken(SourceMgr, Fixes, Range, NewText: "" ); |
365 | } |
366 | |
367 | return Tok; |
368 | } |
369 | |
370 | const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft( |
371 | const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, |
372 | tooling::Replacements &Fixes, const FormatToken *const Tok, |
373 | const std::string &Qualifier, tok::TokenKind QualifierType) { |
374 | // We only need to think about streams that begin with a qualifier. |
375 | if (Tok->isNot(Kind: QualifierType)) |
376 | return Tok; |
377 | // Don't concern yourself if nothing preceeds the qualifier. |
378 | if (!Tok->getPreviousNonComment()) |
379 | return Tok; |
380 | |
381 | // Skip qualifiers to the left to find what preceeds the qualifiers. |
382 | const FormatToken *TypeToken = Tok->getPreviousNonComment(); |
383 | while (isQualifier(Tok: TypeToken)) |
384 | TypeToken = TypeToken->getPreviousNonComment(); |
385 | |
386 | // For left qualifiers preceeded by nothing, a template declaration, or *,&,&& |
387 | // we only perform sorting. |
388 | if (!TypeToken || TypeToken->isPointerOrReference() || |
389 | TypeToken->ClosesRequiresClause || TypeToken->ClosesTemplateDeclaration) { |
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 | const FormatToken *LastSimpleTypeSpecifier = TypeToken; |
415 | while (isConfiguredQualifierOrType( |
416 | Tok: LastSimpleTypeSpecifier->getPreviousNonComment(), |
417 | Qualifiers: ConfiguredQualifierTokens, LangOpts)) { |
418 | LastSimpleTypeSpecifier = |
419 | LastSimpleTypeSpecifier->getPreviousNonComment(); |
420 | } |
421 | |
422 | rotateTokens(SourceMgr, Fixes, First: LastSimpleTypeSpecifier, Last: Tok, |
423 | /*Left=*/true); |
424 | return Tok; |
425 | } |
426 | |
427 | if (TypeToken->isOneOf(K1: tok::kw_auto, K2: tok::identifier, Ks: TT_TemplateCloser)) { |
428 | const auto IsStartOfType = [](const FormatToken *const Tok) -> bool { |
429 | if (!Tok) |
430 | return true; |
431 | |
432 | // A template closer is not the start of a type. |
433 | // The case `?<> const` -> `const ?<>` |
434 | if (Tok->is(TT: TT_TemplateCloser)) |
435 | return false; |
436 | |
437 | const FormatToken *const Previous = Tok->getPreviousNonComment(); |
438 | if (!Previous) |
439 | return true; |
440 | |
441 | // An identifier preceeded by :: is not the start of a type. |
442 | // The case `?::Foo const` -> `const ?::Foo` |
443 | if (Tok->is(Kind: tok::identifier) && Previous->is(Kind: tok::coloncolon)) |
444 | return false; |
445 | |
446 | const FormatToken *const PrePrevious = Previous->getPreviousNonComment(); |
447 | // An identifier preceeded by ::template is not the start of a type. |
448 | // The case `?::template Foo const` -> `const ?::template Foo` |
449 | if (Tok->is(Kind: tok::identifier) && Previous->is(Kind: tok::kw_template) && |
450 | PrePrevious && PrePrevious->is(Kind: tok::coloncolon)) { |
451 | return false; |
452 | } |
453 | |
454 | if (Tok->endsSequence(K1: tok::kw_auto, Tokens: tok::identifier)) |
455 | return false; |
456 | |
457 | return true; |
458 | }; |
459 | |
460 | while (!IsStartOfType(TypeToken)) { |
461 | // The case `?<>` |
462 | if (TypeToken->is(TT: TT_TemplateCloser)) { |
463 | assert(TypeToken->MatchingParen && "Missing template opener" ); |
464 | TypeToken = TypeToken->MatchingParen->getPreviousNonComment(); |
465 | } else { |
466 | // The cases |
467 | // `::Foo` |
468 | // `?>::Foo` |
469 | // `?Bar::Foo` |
470 | // `::template Foo` |
471 | // `?>::template Foo` |
472 | // `?Bar::template Foo` |
473 | if (TypeToken->getPreviousNonComment()->is(Kind: tok::kw_template)) |
474 | TypeToken = TypeToken->getPreviousNonComment(); |
475 | |
476 | const FormatToken *const ColonColon = |
477 | TypeToken->getPreviousNonComment(); |
478 | const FormatToken *const PreColonColon = |
479 | ColonColon->getPreviousNonComment(); |
480 | if (PreColonColon && |
481 | PreColonColon->isOneOf(K1: TT_TemplateCloser, K2: tok::identifier)) { |
482 | TypeToken = PreColonColon; |
483 | } else { |
484 | TypeToken = ColonColon; |
485 | } |
486 | } |
487 | } |
488 | |
489 | assert(TypeToken && "Should be auto or identifier" ); |
490 | |
491 | // Place the Qualifier at the start of the list of qualifiers. |
492 | const FormatToken *Previous = nullptr; |
493 | while ((Previous = TypeToken->getPreviousNonComment()) && |
494 | (isConfiguredQualifier(Tok: Previous, Qualifiers: ConfiguredQualifierTokens) || |
495 | Previous->is(Kind: tok::kw_typename))) { |
496 | // The case `volatile Foo::iter const` -> `const volatile Foo::iter` |
497 | // The case `typename C::type const` -> `const typename C::type` |
498 | TypeToken = Previous; |
499 | } |
500 | |
501 | // Don't change declarations such as |
502 | // `foo(struct Foo const a);` -> `foo(struct Foo const a);` |
503 | if (!Previous || !Previous->isOneOf(K1: tok::kw_struct, K2: tok::kw_class)) { |
504 | insertQualifierBefore(SourceMgr, Fixes, First: TypeToken, Qualifier); |
505 | removeToken(SourceMgr, Fixes, First: Tok); |
506 | } |
507 | } |
508 | |
509 | return Tok; |
510 | } |
511 | |
512 | tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier( |
513 | const std::string &Qualifier) { |
514 | // Don't let 'type' be an identifier, but steal typeof token. |
515 | return llvm::StringSwitch<tok::TokenKind>(Qualifier) |
516 | .Case(S: "type" , Value: tok::kw_typeof) |
517 | .Case(S: "const" , Value: tok::kw_const) |
518 | .Case(S: "volatile" , Value: tok::kw_volatile) |
519 | .Case(S: "static" , Value: tok::kw_static) |
520 | .Case(S: "inline" , Value: tok::kw_inline) |
521 | .Case(S: "constexpr" , Value: tok::kw_constexpr) |
522 | .Case(S: "restrict" , Value: tok::kw_restrict) |
523 | .Case(S: "friend" , Value: tok::kw_friend) |
524 | .Default(Value: tok::identifier); |
525 | } |
526 | |
527 | LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer( |
528 | const Environment &Env, const FormatStyle &Style, |
529 | const std::string &Qualifier, |
530 | const std::vector<tok::TokenKind> &QualifierTokens, bool RightAlign) |
531 | : TokenAnalyzer(Env, Style), Qualifier(Qualifier), RightAlign(RightAlign), |
532 | ConfiguredQualifierTokens(QualifierTokens) {} |
533 | |
534 | std::pair<tooling::Replacements, unsigned> |
535 | LeftRightQualifierAlignmentFixer::analyze( |
536 | TokenAnnotator & /*Annotator*/, |
537 | SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, |
538 | FormatTokenLexer &Tokens) { |
539 | tooling::Replacements Fixes; |
540 | AffectedRangeMgr.computeAffectedLines(Lines&: AnnotatedLines); |
541 | fixQualifierAlignment(AnnotatedLines, Tokens, Fixes); |
542 | return {Fixes, 0}; |
543 | } |
544 | |
545 | void LeftRightQualifierAlignmentFixer::fixQualifierAlignment( |
546 | SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, FormatTokenLexer &Tokens, |
547 | tooling::Replacements &Fixes) { |
548 | const AdditionalKeywords &Keywords = Tokens.getKeywords(); |
549 | const SourceManager &SourceMgr = Env.getSourceManager(); |
550 | tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier); |
551 | assert(QualifierToken != tok::identifier && "Unrecognised Qualifier" ); |
552 | |
553 | for (AnnotatedLine *Line : AnnotatedLines) { |
554 | fixQualifierAlignment(AnnotatedLines&: Line->Children, Tokens, Fixes); |
555 | if (!Line->Affected || Line->InPPDirective) |
556 | continue; |
557 | FormatToken *First = Line->First; |
558 | assert(First); |
559 | if (First->Finalized) |
560 | continue; |
561 | |
562 | const auto *Last = Line->Last; |
563 | |
564 | for (const auto *Tok = First; Tok && Tok != Last && Tok->Next; |
565 | Tok = Tok->Next) { |
566 | if (Tok->MustBreakBefore) |
567 | break; |
568 | if (Tok->is(Kind: tok::comment)) |
569 | continue; |
570 | if (RightAlign) { |
571 | Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier, |
572 | QualifierType: QualifierToken); |
573 | } else { |
574 | Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier, |
575 | QualifierType: QualifierToken); |
576 | } |
577 | } |
578 | } |
579 | } |
580 | |
581 | void prepareLeftRightOrderingForQualifierAlignmentFixer( |
582 | const std::vector<std::string> &Order, std::vector<std::string> &LeftOrder, |
583 | std::vector<std::string> &RightOrder, |
584 | std::vector<tok::TokenKind> &Qualifiers) { |
585 | |
586 | // Depending on the position of type in the order you need |
587 | // To iterate forward or backward through the order list as qualifier |
588 | // can push through each other. |
589 | // The Order list must define the position of "type" to signify |
590 | assert(llvm::is_contained(Order, "type" ) && |
591 | "QualifierOrder must contain type" ); |
592 | // Split the Order list by type and reverse the left side. |
593 | |
594 | bool left = true; |
595 | for (const auto &s : Order) { |
596 | if (s == "type" ) { |
597 | left = false; |
598 | continue; |
599 | } |
600 | |
601 | tok::TokenKind QualifierToken = |
602 | LeftRightQualifierAlignmentFixer::getTokenFromQualifier(Qualifier: s); |
603 | if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier) |
604 | Qualifiers.push_back(x: QualifierToken); |
605 | |
606 | if (left) { |
607 | // Reverse the order for left aligned items. |
608 | LeftOrder.insert(position: LeftOrder.begin(), x: s); |
609 | } else { |
610 | RightOrder.push_back(x: s); |
611 | } |
612 | } |
613 | } |
614 | |
615 | bool isQualifierOrType(const FormatToken *Tok, const LangOptions &LangOpts) { |
616 | return Tok && (Tok->isTypeName(LangOpts) || Tok->is(Kind: tok::kw_auto) || |
617 | isQualifier(Tok)); |
618 | } |
619 | |
620 | bool isConfiguredQualifierOrType(const FormatToken *Tok, |
621 | const std::vector<tok::TokenKind> &Qualifiers, |
622 | const LangOptions &LangOpts) { |
623 | return Tok && (Tok->isTypeName(LangOpts) || Tok->is(Kind: tok::kw_auto) || |
624 | isConfiguredQualifier(Tok, Qualifiers)); |
625 | } |
626 | |
627 | // If a token is an identifier and it's upper case, it could |
628 | // be a macro and hence we need to be able to ignore it. |
629 | bool isPossibleMacro(const FormatToken *Tok) { |
630 | if (!Tok) |
631 | return false; |
632 | if (Tok->isNot(Kind: tok::identifier)) |
633 | return false; |
634 | if (Tok->TokenText.upper() == Tok->TokenText.str()) { |
635 | // T,K,U,V likely could be template arguments |
636 | return Tok->TokenText.size() != 1; |
637 | } |
638 | return false; |
639 | } |
640 | |
641 | } // namespace format |
642 | } // namespace clang |
643 | |