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