1 //===--- QualifierAlignmentFixer.cpp ----------------------------*- C++--*-===//
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
7 //===----------------------------------------------------------------------===//
10 /// This file implements QualifierAlignmentFixer, a TokenAnalyzer that
11 /// enforces either left or right const depending on the style.
13 //===----------------------------------------------------------------------===//
15 #include "QualifierAlignmentFixer.h"
16 #include "FormatToken.h"
17 #include "llvm/Support/Debug.h"
18 #include "llvm/Support/Regex.h"
23 #define DEBUG_TYPE "format-qualifier-alignment-fixer"
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 Style
.QualifierOrder
, LeftOrder
, RightOrder
, ConfiguredQualifierTokens
);
36 // Handle the left and right alignment separately.
37 for (const auto &Qualifier
: LeftOrder
) {
39 [&, Qualifier
, ConfiguredQualifierTokens
](const Environment
&Env
) {
40 return LeftRightQualifierAlignmentFixer(Env
, Style
, Qualifier
,
41 ConfiguredQualifierTokens
,
46 for (const auto &Qualifier
: RightOrder
) {
48 [&, Qualifier
, ConfiguredQualifierTokens
](const Environment
&Env
) {
49 return LeftRightQualifierAlignmentFixer(Env
, Style
, Qualifier
,
50 ConfiguredQualifierTokens
,
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(Replacement
);
64 llvm::errs() << "Error while rearranging Qualifier : "
65 << llvm::toString(std::move(Err
)) << "\n";
69 static void removeToken(const SourceManager
&SourceMgr
,
70 tooling::Replacements
&Fixes
,
71 const FormatToken
*First
) {
72 auto Range
= CharSourceRange::getCharRange(First
->getStartOfNonWhitespace(),
73 First
->Tok
.getEndLoc());
74 replaceToken(SourceMgr
, Fixes
, Range
, "");
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(First
->Tok
.getLocation(),
82 First
->Tok
.getEndLoc());
84 std::string NewText
{};
85 NewText
+= First
->TokenText
;
86 NewText
+= " " + Qualifier
;
87 replaceToken(SourceMgr
, Fixes
, Range
, NewText
);
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(First
->getStartOfNonWhitespace(),
95 First
->Tok
.getEndLoc());
97 std::string NewText
= " " + Qualifier
+ " ";
98 NewText
+= First
->TokenText
;
100 replaceToken(SourceMgr
, Fixes
, Range
, NewText
);
103 static bool endsWithSpace(const std::string
&s
) {
106 return isspace(s
.back());
109 static bool startsWithSpace(const std::string
&s
) {
112 return isspace(s
.front());
115 static void rotateTokens(const SourceManager
&SourceMgr
,
116 tooling::Replacements
&Fixes
, const FormatToken
*First
,
117 const FormatToken
*Last
, bool Left
) {
126 // If we are rotating to the left we move the Last token to the front.
128 NewText
+= Last
->TokenText
;
132 // Then move through the other tokens.
135 if (!NewText
.empty() && !endsWithSpace(NewText
))
138 NewText
+= Tok
->TokenText
;
142 // If we are rotating to the right we move the first token to the back.
144 if (!NewText
.empty() && !startsWithSpace(NewText
))
146 NewText
+= First
->TokenText
;
149 auto Range
= CharSourceRange::getCharRange(First
->getStartOfNonWhitespace(),
150 Last
->Tok
.getEndLoc());
152 replaceToken(SourceMgr
, Fixes
, Range
, NewText
);
156 isConfiguredQualifier(const FormatToken
*const Tok
,
157 const std::vector
<tok::TokenKind
> &Qualifiers
) {
158 return Tok
&& llvm::is_contained(Qualifiers
, Tok
->Tok
.getKind());
161 static bool isQualifier(const FormatToken
*const Tok
) {
165 switch (Tok
->Tok
.getKind()) {
167 case tok::kw_volatile
:
170 case tok::kw_constexpr
:
171 case tok::kw_restrict
:
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(QualifierType
))
186 // Don't concern yourself if nothing follows the qualifier.
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(PreviousCheck
))
194 PreviousCheck
= PreviousCheck
->getPreviousNonComment();
196 // Examples given in order of ['type', 'const', 'volatile']
197 const bool IsRightQualifier
= PreviousCheck
&& [PreviousCheck
]() {
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(tok::r_paren
))
208 // `struct {} volatile const a;` -> `struct {} const volatile a;`
209 // `class {} volatile const a;` -> `class {} const volatile a;`
210 if (PreviousCheck
->is(tok::r_brace
))
214 // `template <class T> const Bar Foo()` ->
215 // `template <class T> Bar const Foo()`
217 // `Foo<int> const foo` -> `Foo<int> const foo`
218 // `Foo<int> volatile const` -> `Foo<int> const volatile`
221 // template <class T>
222 // requires Concept1<T> && requires Concept2<T>
227 // template <class T>
228 // requires Concept1<T> && requires Concept2<T>
231 if (PreviousCheck
->is(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
);
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(TT_PointerOrReference
, tok::identifier
,
250 // Find the last qualifier to the right.
251 const FormatToken
*LastQual
= Tok
;
252 while (isQualifier(LastQual
->getNextNonComment()))
253 LastQual
= LastQual
->getNextNonComment();
255 // If this qualifier is to the right of a type or pointer do a partial sort
257 if (IsRightQualifier
) {
259 rotateTokens(SourceMgr
, Fixes
, Tok
, LastQual
, /*Left=*/false);
263 const FormatToken
*TypeToken
= LastQual
->getNextNonComment();
267 // Stay safe and don't move past macros, also don't bother with sorting.
268 if (isPossibleMacro(TypeToken
))
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
->isSimpleTypeSpecifier()) {
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(tok::kw_decltype
, tok::kw_typeof
, tok::kw__Atomic
))
282 const FormatToken
*LastSimpleTypeSpecifier
= TypeToken
;
283 while (isQualifierOrType(LastSimpleTypeSpecifier
->getNextNonComment()))
284 LastSimpleTypeSpecifier
= LastSimpleTypeSpecifier
->getNextNonComment();
286 rotateTokens(SourceMgr
, Fixes
, Tok
, LastSimpleTypeSpecifier
,
288 return LastSimpleTypeSpecifier
;
291 // The case `unsigned short const` -> `unsigned short const`
293 // `unsigned short volatile const` -> `unsigned short const volatile`
294 if (PreviousCheck
&& PreviousCheck
->isSimpleTypeSpecifier()) {
296 rotateTokens(SourceMgr
, Fixes
, Tok
, LastQual
, /*Left=*/false);
300 // Skip the typename keyword.
301 // The case `const typename C::type` -> `typename C::type const`
302 if (TypeToken
->is(tok::kw_typename
))
303 TypeToken
= TypeToken
->getNextNonComment();
305 // Skip the initial :: of a global-namespace type.
306 // The case `const ::...` -> `::... const`
307 if (TypeToken
->is(tok::coloncolon
)) {
308 // The case `const ::template Foo...` -> `::template Foo... const`
309 TypeToken
= TypeToken
->getNextNonComment();
310 if (TypeToken
&& TypeToken
->is(tok::kw_template
))
311 TypeToken
= TypeToken
->getNextNonComment();
314 // Don't change declarations such as
315 // `foo(const struct Foo a);` -> `foo(const struct Foo a);`
316 // as they would currently change code such as
317 // `const struct my_struct_t {} my_struct;` -> `struct my_struct_t const {}
319 if (TypeToken
->isOneOf(tok::kw_struct
, tok::kw_class
))
322 if (TypeToken
->isOneOf(tok::kw_auto
, tok::identifier
)) {
323 // The case `const auto` -> `auto const`
324 // The case `const Foo` -> `Foo 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 std::Foo &&` -> `std::Foo const &&`
330 // The case `const std::Foo<T> &&` -> `std::Foo<T> const &&`
331 // The case `const ::template Foo` -> `::template Foo const`
332 // The case `const T::template Foo` -> `T::template Foo const`
333 const FormatToken
*Next
= nullptr;
334 while ((Next
= TypeToken
->getNextNonComment()) &&
335 (Next
->is(TT_TemplateOpener
) ||
336 Next
->startsSequence(tok::coloncolon
, tok::identifier
) ||
337 Next
->startsSequence(tok::coloncolon
, tok::kw_template
,
339 if (Next
->is(TT_TemplateOpener
)) {
340 assert(Next
->MatchingParen
&& "Missing template closer");
341 TypeToken
= Next
->MatchingParen
;
342 } else if (Next
->startsSequence(tok::coloncolon
, tok::identifier
)) {
343 TypeToken
= Next
->getNextNonComment();
345 TypeToken
= Next
->getNextNonComment()->getNextNonComment();
349 if (Next
->is(tok::kw_auto
))
352 // Place the Qualifier at the end of the list of qualifiers.
353 while (isQualifier(TypeToken
->getNextNonComment())) {
354 // The case `volatile Foo::iter const` -> `Foo::iter const volatile`
355 TypeToken
= TypeToken
->getNextNonComment();
358 insertQualifierAfter(SourceMgr
, Fixes
, TypeToken
, Qualifier
);
359 // Remove token and following whitespace.
360 auto Range
= CharSourceRange::getCharRange(
361 Tok
->getStartOfNonWhitespace(), Tok
->Next
->getStartOfNonWhitespace());
362 replaceToken(SourceMgr
, Fixes
, Range
, "");
368 const FormatToken
*LeftRightQualifierAlignmentFixer::analyzeLeft(
369 const SourceManager
&SourceMgr
, const AdditionalKeywords
&Keywords
,
370 tooling::Replacements
&Fixes
, const FormatToken
*const Tok
,
371 const std::string
&Qualifier
, tok::TokenKind QualifierType
) {
372 // We only need to think about streams that begin with a qualifier.
373 if (Tok
->isNot(QualifierType
))
375 // Don't concern yourself if nothing preceeds the qualifier.
376 if (!Tok
->getPreviousNonComment())
379 // Skip qualifiers to the left to find what preceeds the qualifiers.
380 const FormatToken
*TypeToken
= Tok
->getPreviousNonComment();
381 while (isQualifier(TypeToken
))
382 TypeToken
= TypeToken
->getPreviousNonComment();
384 // For left qualifiers preceeded by nothing, a template declaration, or *,&,&&
385 // we only perform sorting.
386 if (!TypeToken
|| TypeToken
->isPointerOrReference() ||
387 TypeToken
->ClosesRequiresClause
|| TypeToken
->ClosesTemplateDeclaration
) {
389 // Don't sort past a non-configured qualifier token.
390 const FormatToken
*FirstQual
= Tok
;
391 while (isConfiguredQualifier(FirstQual
->getPreviousNonComment(),
392 ConfiguredQualifierTokens
)) {
393 FirstQual
= FirstQual
->getPreviousNonComment();
396 if (FirstQual
!= Tok
)
397 rotateTokens(SourceMgr
, Fixes
, FirstQual
, Tok
, /*Left=*/true);
401 // Stay safe and don't move past macros, also don't bother with sorting.
402 if (isPossibleMacro(TypeToken
))
405 // Examples given in order of ['const', 'volatile', 'type']
407 // The case `volatile long long int const` -> `const volatile long long int`
408 // The case `volatile long long const int` -> `const volatile long long int`
409 // The case `const long long volatile int` -> `const volatile long long int`
410 // The case `long volatile long int const` -> `const volatile long long int`
411 if (TypeToken
->isSimpleTypeSpecifier()) {
412 const FormatToken
*LastSimpleTypeSpecifier
= TypeToken
;
413 while (isConfiguredQualifierOrType(
414 LastSimpleTypeSpecifier
->getPreviousNonComment(),
415 ConfiguredQualifierTokens
)) {
416 LastSimpleTypeSpecifier
=
417 LastSimpleTypeSpecifier
->getPreviousNonComment();
420 rotateTokens(SourceMgr
, Fixes
, LastSimpleTypeSpecifier
, Tok
,
425 if (TypeToken
->isOneOf(tok::kw_auto
, tok::identifier
, TT_TemplateCloser
)) {
426 const auto IsStartOfType
= [](const FormatToken
*const Tok
) -> bool {
430 // A template closer is not the start of a type.
431 // The case `?<> const` -> `const ?<>`
432 if (Tok
->is(TT_TemplateCloser
))
435 const FormatToken
*const Previous
= Tok
->getPreviousNonComment();
439 // An identifier preceeded by :: is not the start of a type.
440 // The case `?::Foo const` -> `const ?::Foo`
441 if (Tok
->is(tok::identifier
) && Previous
->is(tok::coloncolon
))
444 const FormatToken
*const PrePrevious
= Previous
->getPreviousNonComment();
445 // An identifier preceeded by ::template is not the start of a type.
446 // The case `?::template Foo const` -> `const ?::template Foo`
447 if (Tok
->is(tok::identifier
) && Previous
->is(tok::kw_template
) &&
448 PrePrevious
&& PrePrevious
->is(tok::coloncolon
)) {
452 if (Tok
->endsSequence(tok::kw_auto
, tok::identifier
))
458 while (!IsStartOfType(TypeToken
)) {
460 if (TypeToken
->is(TT_TemplateCloser
)) {
461 assert(TypeToken
->MatchingParen
&& "Missing template opener");
462 TypeToken
= TypeToken
->MatchingParen
->getPreviousNonComment();
469 // `?>::template Foo`
470 // `?Bar::template Foo`
471 if (TypeToken
->getPreviousNonComment()->is(tok::kw_template
))
472 TypeToken
= TypeToken
->getPreviousNonComment();
474 const FormatToken
*const ColonColon
=
475 TypeToken
->getPreviousNonComment();
476 const FormatToken
*const PreColonColon
=
477 ColonColon
->getPreviousNonComment();
479 PreColonColon
->isOneOf(TT_TemplateCloser
, tok::identifier
)) {
480 TypeToken
= PreColonColon
;
482 TypeToken
= ColonColon
;
487 assert(TypeToken
&& "Should be auto or identifier");
489 // Place the Qualifier at the start of the list of qualifiers.
490 const FormatToken
*Previous
= nullptr;
491 while ((Previous
= TypeToken
->getPreviousNonComment()) &&
492 (isConfiguredQualifier(Previous
, ConfiguredQualifierTokens
) ||
493 Previous
->is(tok::kw_typename
))) {
494 // The case `volatile Foo::iter const` -> `const volatile Foo::iter`
495 // The case `typename C::type const` -> `const typename C::type`
496 TypeToken
= Previous
;
499 // Don't change declarations such as
500 // `foo(struct Foo const a);` -> `foo(struct Foo const a);`
501 if (!Previous
|| !Previous
->isOneOf(tok::kw_struct
, tok::kw_class
)) {
502 insertQualifierBefore(SourceMgr
, Fixes
, TypeToken
, Qualifier
);
503 removeToken(SourceMgr
, Fixes
, Tok
);
510 tok::TokenKind
LeftRightQualifierAlignmentFixer::getTokenFromQualifier(
511 const std::string
&Qualifier
) {
512 // Don't let 'type' be an identifier, but steal typeof token.
513 return llvm::StringSwitch
<tok::TokenKind
>(Qualifier
)
514 .Case("type", tok::kw_typeof
)
515 .Case("const", tok::kw_const
)
516 .Case("volatile", tok::kw_volatile
)
517 .Case("static", tok::kw_static
)
518 .Case("inline", tok::kw_inline
)
519 .Case("constexpr", tok::kw_constexpr
)
520 .Case("restrict", tok::kw_restrict
)
521 .Case("friend", tok::kw_friend
)
522 .Default(tok::identifier
);
525 LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer(
526 const Environment
&Env
, const FormatStyle
&Style
,
527 const std::string
&Qualifier
,
528 const std::vector
<tok::TokenKind
> &QualifierTokens
, bool RightAlign
)
529 : TokenAnalyzer(Env
, Style
), Qualifier(Qualifier
), RightAlign(RightAlign
),
530 ConfiguredQualifierTokens(QualifierTokens
) {}
532 std::pair
<tooling::Replacements
, unsigned>
533 LeftRightQualifierAlignmentFixer::analyze(
534 TokenAnnotator
& /*Annotator*/,
535 SmallVectorImpl
<AnnotatedLine
*> &AnnotatedLines
,
536 FormatTokenLexer
&Tokens
) {
537 tooling::Replacements Fixes
;
538 AffectedRangeMgr
.computeAffectedLines(AnnotatedLines
);
539 fixQualifierAlignment(AnnotatedLines
, Tokens
, Fixes
);
543 void LeftRightQualifierAlignmentFixer::fixQualifierAlignment(
544 SmallVectorImpl
<AnnotatedLine
*> &AnnotatedLines
, FormatTokenLexer
&Tokens
,
545 tooling::Replacements
&Fixes
) {
546 const AdditionalKeywords
&Keywords
= Tokens
.getKeywords();
547 const SourceManager
&SourceMgr
= Env
.getSourceManager();
548 tok::TokenKind QualifierToken
= getTokenFromQualifier(Qualifier
);
549 assert(QualifierToken
!= tok::identifier
&& "Unrecognised Qualifier");
551 for (AnnotatedLine
*Line
: AnnotatedLines
) {
552 fixQualifierAlignment(Line
->Children
, Tokens
, Fixes
);
553 if (!Line
->Affected
|| Line
->InPPDirective
)
555 FormatToken
*First
= Line
->First
;
557 if (First
->Finalized
)
560 const auto *Last
= Line
->Last
;
562 for (const auto *Tok
= First
; Tok
&& Tok
!= Last
&& Tok
->Next
;
564 if (Tok
->is(tok::comment
))
567 Tok
= analyzeRight(SourceMgr
, Keywords
, Fixes
, Tok
, Qualifier
,
570 Tok
= analyzeLeft(SourceMgr
, Keywords
, Fixes
, Tok
, Qualifier
,
577 void prepareLeftRightOrderingForQualifierAlignmentFixer(
578 const std::vector
<std::string
> &Order
, std::vector
<std::string
> &LeftOrder
,
579 std::vector
<std::string
> &RightOrder
,
580 std::vector
<tok::TokenKind
> &Qualifiers
) {
582 // Depending on the position of type in the order you need
583 // To iterate forward or backward through the order list as qualifier
584 // can push through each other.
585 // The Order list must define the position of "type" to signify
586 assert(llvm::is_contained(Order
, "type") &&
587 "QualifierOrder must contain type");
588 // Split the Order list by type and reverse the left side.
591 for (const auto &s
: Order
) {
597 tok::TokenKind QualifierToken
=
598 LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s
);
599 if (QualifierToken
!= tok::kw_typeof
&& QualifierToken
!= tok::identifier
)
600 Qualifiers
.push_back(QualifierToken
);
603 // Reverse the order for left aligned items.
604 LeftOrder
.insert(LeftOrder
.begin(), s
);
606 RightOrder
.push_back(s
);
611 bool LeftRightQualifierAlignmentFixer::isQualifierOrType(
612 const FormatToken
*const Tok
) {
613 return Tok
&& (Tok
->isSimpleTypeSpecifier() || Tok
->is(tok::kw_auto
) ||
617 bool LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
618 const FormatToken
*const Tok
,
619 const std::vector
<tok::TokenKind
> &Qualifiers
) {
620 return Tok
&& (Tok
->isSimpleTypeSpecifier() || Tok
->is(tok::kw_auto
) ||
621 isConfiguredQualifier(Tok
, Qualifiers
));
624 // If a token is an identifier and it's upper case, it could
625 // be a macro and hence we need to be able to ignore it.
626 bool LeftRightQualifierAlignmentFixer::isPossibleMacro(const FormatToken
*Tok
) {
629 if (Tok
->isNot(tok::identifier
))
631 if (Tok
->TokenText
.upper() == Tok
->TokenText
.str()) {
632 // T,K,U,V likely could be template arguments
633 return Tok
->TokenText
.size() != 1;
638 } // namespace format