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 // Place the Qualifier at the end of the list of qualifiers.
350 while (isQualifier(TypeToken
->getNextNonComment())) {
351 // The case `volatile Foo::iter const` -> `Foo::iter const volatile`
352 TypeToken
= TypeToken
->getNextNonComment();
355 insertQualifierAfter(SourceMgr
, Fixes
, TypeToken
, Qualifier
);
356 // Remove token and following whitespace.
357 auto Range
= CharSourceRange::getCharRange(
358 Tok
->getStartOfNonWhitespace(), Tok
->Next
->getStartOfNonWhitespace());
359 replaceToken(SourceMgr
, Fixes
, Range
, "");
365 const FormatToken
*LeftRightQualifierAlignmentFixer::analyzeLeft(
366 const SourceManager
&SourceMgr
, const AdditionalKeywords
&Keywords
,
367 tooling::Replacements
&Fixes
, const FormatToken
*const Tok
,
368 const std::string
&Qualifier
, tok::TokenKind QualifierType
) {
369 // We only need to think about streams that begin with a qualifier.
370 if (Tok
->isNot(QualifierType
))
372 // Don't concern yourself if nothing preceeds the qualifier.
373 if (!Tok
->getPreviousNonComment())
376 // Skip qualifiers to the left to find what preceeds the qualifiers.
377 const FormatToken
*TypeToken
= Tok
->getPreviousNonComment();
378 while (isQualifier(TypeToken
))
379 TypeToken
= TypeToken
->getPreviousNonComment();
381 // For left qualifiers preceeded by nothing, a template declaration, or *,&,&&
382 // we only perform sorting.
383 if (!TypeToken
|| TypeToken
->isOneOf(tok::star
, tok::amp
, tok::ampamp
) ||
384 TypeToken
->ClosesRequiresClause
|| TypeToken
->ClosesTemplateDeclaration
) {
386 // Don't sort past a non-configured qualifier token.
387 const FormatToken
*FirstQual
= Tok
;
388 while (isConfiguredQualifier(FirstQual
->getPreviousNonComment(),
389 ConfiguredQualifierTokens
)) {
390 FirstQual
= FirstQual
->getPreviousNonComment();
393 if (FirstQual
!= Tok
)
394 rotateTokens(SourceMgr
, Fixes
, FirstQual
, Tok
, /*Left=*/true);
398 // Stay safe and don't move past macros, also don't bother with sorting.
399 if (isPossibleMacro(TypeToken
))
402 // Examples given in order of ['const', 'volatile', 'type']
404 // The case `volatile long long int const` -> `const volatile long long int`
405 // The case `volatile long long const int` -> `const volatile long long int`
406 // The case `const long long volatile int` -> `const volatile long long int`
407 // The case `long volatile long int const` -> `const volatile long long int`
408 if (TypeToken
->isSimpleTypeSpecifier()) {
409 const FormatToken
*LastSimpleTypeSpecifier
= TypeToken
;
410 while (isConfiguredQualifierOrType(
411 LastSimpleTypeSpecifier
->getPreviousNonComment(),
412 ConfiguredQualifierTokens
)) {
413 LastSimpleTypeSpecifier
=
414 LastSimpleTypeSpecifier
->getPreviousNonComment();
417 rotateTokens(SourceMgr
, Fixes
, LastSimpleTypeSpecifier
, Tok
,
422 if (TypeToken
->isOneOf(tok::kw_auto
, tok::identifier
, TT_TemplateCloser
)) {
423 const auto IsStartOfType
= [](const FormatToken
*const Tok
) -> bool {
427 // A template closer is not the start of a type.
428 // The case `?<> const` -> `const ?<>`
429 if (Tok
->is(TT_TemplateCloser
))
432 const FormatToken
*const Previous
= Tok
->getPreviousNonComment();
436 // An identifier preceeded by :: is not the start of a type.
437 // The case `?::Foo const` -> `const ?::Foo`
438 if (Tok
->is(tok::identifier
) && Previous
->is(tok::coloncolon
))
441 const FormatToken
*const PrePrevious
= Previous
->getPreviousNonComment();
442 // An identifier preceeded by ::template is not the start of a type.
443 // The case `?::template Foo const` -> `const ?::template Foo`
444 if (Tok
->is(tok::identifier
) && Previous
->is(tok::kw_template
) &&
445 PrePrevious
&& PrePrevious
->is(tok::coloncolon
)) {
452 while (!IsStartOfType(TypeToken
)) {
454 if (TypeToken
->is(TT_TemplateCloser
)) {
455 assert(TypeToken
->MatchingParen
&& "Missing template opener");
456 TypeToken
= TypeToken
->MatchingParen
->getPreviousNonComment();
463 // `?>::template Foo`
464 // `?Bar::template Foo`
465 if (TypeToken
->getPreviousNonComment()->is(tok::kw_template
))
466 TypeToken
= TypeToken
->getPreviousNonComment();
468 const FormatToken
*const ColonColon
=
469 TypeToken
->getPreviousNonComment();
470 const FormatToken
*const PreColonColon
=
471 ColonColon
->getPreviousNonComment();
473 PreColonColon
->isOneOf(TT_TemplateCloser
, tok::identifier
)) {
474 TypeToken
= PreColonColon
;
476 TypeToken
= ColonColon
;
481 assert(TypeToken
&& "Should be auto or identifier");
483 // Place the Qualifier at the start of the list of qualifiers.
484 const FormatToken
*Previous
= nullptr;
485 while ((Previous
= TypeToken
->getPreviousNonComment()) &&
486 (isConfiguredQualifier(Previous
, ConfiguredQualifierTokens
) ||
487 Previous
->is(tok::kw_typename
))) {
488 // The case `volatile Foo::iter const` -> `const volatile Foo::iter`
489 // The case `typename C::type const` -> `const typename C::type`
490 TypeToken
= Previous
;
493 // Don't change declarations such as
494 // `foo(struct Foo const a);` -> `foo(struct Foo const a);`
495 if (!Previous
|| !Previous
->isOneOf(tok::kw_struct
, tok::kw_class
)) {
496 insertQualifierBefore(SourceMgr
, Fixes
, TypeToken
, Qualifier
);
497 removeToken(SourceMgr
, Fixes
, Tok
);
504 tok::TokenKind
LeftRightQualifierAlignmentFixer::getTokenFromQualifier(
505 const std::string
&Qualifier
) {
506 // Don't let 'type' be an identifier, but steal typeof token.
507 return llvm::StringSwitch
<tok::TokenKind
>(Qualifier
)
508 .Case("type", tok::kw_typeof
)
509 .Case("const", tok::kw_const
)
510 .Case("volatile", tok::kw_volatile
)
511 .Case("static", tok::kw_static
)
512 .Case("inline", tok::kw_inline
)
513 .Case("constexpr", tok::kw_constexpr
)
514 .Case("restrict", tok::kw_restrict
)
515 .Case("friend", tok::kw_friend
)
516 .Default(tok::identifier
);
519 LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer(
520 const Environment
&Env
, const FormatStyle
&Style
,
521 const std::string
&Qualifier
,
522 const std::vector
<tok::TokenKind
> &QualifierTokens
, bool RightAlign
)
523 : TokenAnalyzer(Env
, Style
), Qualifier(Qualifier
), RightAlign(RightAlign
),
524 ConfiguredQualifierTokens(QualifierTokens
) {}
526 std::pair
<tooling::Replacements
, unsigned>
527 LeftRightQualifierAlignmentFixer::analyze(
528 TokenAnnotator
& /*Annotator*/,
529 SmallVectorImpl
<AnnotatedLine
*> &AnnotatedLines
,
530 FormatTokenLexer
&Tokens
) {
531 tooling::Replacements Fixes
;
532 const AdditionalKeywords
&Keywords
= Tokens
.getKeywords();
533 const SourceManager
&SourceMgr
= Env
.getSourceManager();
534 AffectedRangeMgr
.computeAffectedLines(AnnotatedLines
);
536 tok::TokenKind QualifierToken
= getTokenFromQualifier(Qualifier
);
537 assert(QualifierToken
!= tok::identifier
&& "Unrecognised Qualifier");
539 for (AnnotatedLine
*Line
: AnnotatedLines
) {
540 if (!Line
->Affected
|| Line
->InPPDirective
)
542 FormatToken
*First
= Line
->First
;
544 if (First
->Finalized
)
547 const auto *Last
= Line
->Last
;
549 for (const auto *Tok
= First
; Tok
&& Tok
!= Last
&& Tok
->Next
;
551 if (Tok
->is(tok::comment
))
554 Tok
= analyzeRight(SourceMgr
, Keywords
, Fixes
, Tok
, Qualifier
,
557 Tok
= analyzeLeft(SourceMgr
, Keywords
, Fixes
, Tok
, Qualifier
,
565 void prepareLeftRightOrderingForQualifierAlignmentFixer(
566 const std::vector
<std::string
> &Order
, std::vector
<std::string
> &LeftOrder
,
567 std::vector
<std::string
> &RightOrder
,
568 std::vector
<tok::TokenKind
> &Qualifiers
) {
570 // Depending on the position of type in the order you need
571 // To iterate forward or backward through the order list as qualifier
572 // can push through each other.
573 // The Order list must define the position of "type" to signify
574 assert(llvm::is_contained(Order
, "type") &&
575 "QualifierOrder must contain type");
576 // Split the Order list by type and reverse the left side.
579 for (const auto &s
: Order
) {
585 tok::TokenKind QualifierToken
=
586 LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s
);
587 if (QualifierToken
!= tok::kw_typeof
&& QualifierToken
!= tok::identifier
)
588 Qualifiers
.push_back(QualifierToken
);
591 // Reverse the order for left aligned items.
592 LeftOrder
.insert(LeftOrder
.begin(), s
);
594 RightOrder
.push_back(s
);
599 bool LeftRightQualifierAlignmentFixer::isQualifierOrType(
600 const FormatToken
*const Tok
) {
601 return Tok
&& (Tok
->isSimpleTypeSpecifier() || Tok
->is(tok::kw_auto
) ||
605 bool LeftRightQualifierAlignmentFixer::isConfiguredQualifierOrType(
606 const FormatToken
*const Tok
,
607 const std::vector
<tok::TokenKind
> &Qualifiers
) {
608 return Tok
&& (Tok
->isSimpleTypeSpecifier() || Tok
->is(tok::kw_auto
) ||
609 isConfiguredQualifier(Tok
, Qualifiers
));
612 // If a token is an identifier and it's upper case, it could
613 // be a macro and hence we need to be able to ignore it.
614 bool LeftRightQualifierAlignmentFixer::isPossibleMacro(const FormatToken
*Tok
) {
617 if (Tok
->isNot(tok::identifier
))
619 if (Tok
->TokenText
.upper() == Tok
->TokenText
.str()) {
620 // T,K,U,V likely could be template arguments
621 return Tok
->TokenText
.size() != 1;
626 } // namespace format