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 // 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
54static 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
66static 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
74static 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
87static 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
100static bool endsWithSpace(const std::string &s) {
101 if (s.empty())
102 return false;
103 return isspace(s.back());
104}
105
106static bool startsWithSpace(const std::string &s) {
107 if (s.empty())
108 return false;
109 return isspace(s.front());
110}
111
112static 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
154static bool
155isConfiguredQualifier(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
160static 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
178const 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
369const 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
520tok::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
535LeftRightQualifierAlignmentFixer::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
542std::pair<tooling::Replacements, unsigned>
543LeftRightQualifierAlignmentFixer::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
553void 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
589void 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
623bool isQualifierOrType(const FormatToken *Tok, const LangOptions &LangOpts) {
624 return Tok && (Tok->isTypeName(LangOpts) || Tok->is(Kind: tok::kw_auto) ||
625 isQualifier(Tok));
626}
627
628bool 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.
637bool 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