1b1c73532SDimitry Andric //===--- QualifierAlignmentFixer.cpp ----------------------------*- C++--*-===//
2c0981da4SDimitry Andric //
3c0981da4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c0981da4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5c0981da4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c0981da4SDimitry Andric //
7c0981da4SDimitry Andric //===----------------------------------------------------------------------===//
8c0981da4SDimitry Andric ///
9c0981da4SDimitry Andric /// \file
10b1c73532SDimitry Andric /// This file implements QualifierAlignmentFixer, a TokenAnalyzer that
11c0981da4SDimitry Andric /// enforces either left or right const depending on the style.
12c0981da4SDimitry Andric ///
13c0981da4SDimitry Andric //===----------------------------------------------------------------------===//
14c0981da4SDimitry Andric
15c0981da4SDimitry Andric #include "QualifierAlignmentFixer.h"
16c0981da4SDimitry Andric #include "FormatToken.h"
17c0981da4SDimitry Andric #include "llvm/Support/Debug.h"
18c0981da4SDimitry Andric #include "llvm/Support/Regex.h"
19c0981da4SDimitry Andric
20c0981da4SDimitry Andric #include <algorithm>
21e3b55780SDimitry Andric #include <optional>
22c0981da4SDimitry Andric
23c0981da4SDimitry Andric #define DEBUG_TYPE "format-qualifier-alignment-fixer"
24c0981da4SDimitry Andric
25c0981da4SDimitry Andric namespace clang {
26c0981da4SDimitry Andric namespace format {
27c0981da4SDimitry Andric
addQualifierAlignmentFixerPasses(const FormatStyle & Style,SmallVectorImpl<AnalyzerPass> & Passes)287fa27ce4SDimitry Andric void addQualifierAlignmentFixerPasses(const FormatStyle &Style,
297fa27ce4SDimitry Andric SmallVectorImpl<AnalyzerPass> &Passes) {
30c0981da4SDimitry Andric std::vector<std::string> LeftOrder;
31c0981da4SDimitry Andric std::vector<std::string> RightOrder;
32c0981da4SDimitry Andric std::vector<tok::TokenKind> ConfiguredQualifierTokens;
337fa27ce4SDimitry Andric prepareLeftRightOrderingForQualifierAlignmentFixer(
347fa27ce4SDimitry Andric Style.QualifierOrder, LeftOrder, RightOrder, ConfiguredQualifierTokens);
35c0981da4SDimitry Andric
36145449b1SDimitry Andric // Handle the left and right alignment separately.
37c0981da4SDimitry Andric for (const auto &Qualifier : LeftOrder) {
38c0981da4SDimitry Andric Passes.emplace_back(
39c0981da4SDimitry Andric [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) {
40c0981da4SDimitry Andric return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier,
41c0981da4SDimitry Andric ConfiguredQualifierTokens,
42c0981da4SDimitry Andric /*RightAlign=*/false)
43c0981da4SDimitry Andric .process();
44c0981da4SDimitry Andric });
45c0981da4SDimitry Andric }
46c0981da4SDimitry Andric for (const auto &Qualifier : RightOrder) {
47c0981da4SDimitry Andric Passes.emplace_back(
48c0981da4SDimitry Andric [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) {
49c0981da4SDimitry Andric return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier,
50c0981da4SDimitry Andric ConfiguredQualifierTokens,
51c0981da4SDimitry Andric /*RightAlign=*/true)
52c0981da4SDimitry Andric .process();
53c0981da4SDimitry Andric });
54c0981da4SDimitry Andric }
55c0981da4SDimitry Andric }
56c0981da4SDimitry Andric
replaceToken(const SourceManager & SourceMgr,tooling::Replacements & Fixes,const CharSourceRange & Range,std::string NewText)57c0981da4SDimitry Andric static void replaceToken(const SourceManager &SourceMgr,
58c0981da4SDimitry Andric tooling::Replacements &Fixes,
59c0981da4SDimitry Andric const CharSourceRange &Range, std::string NewText) {
60c0981da4SDimitry Andric auto Replacement = tooling::Replacement(SourceMgr, Range, NewText);
61c0981da4SDimitry Andric auto Err = Fixes.add(Replacement);
62c0981da4SDimitry Andric
63145449b1SDimitry Andric if (Err) {
64c0981da4SDimitry Andric llvm::errs() << "Error while rearranging Qualifier : "
65c0981da4SDimitry Andric << llvm::toString(std::move(Err)) << "\n";
66c0981da4SDimitry Andric }
67145449b1SDimitry Andric }
68c0981da4SDimitry Andric
removeToken(const SourceManager & SourceMgr,tooling::Replacements & Fixes,const FormatToken * First)69c0981da4SDimitry Andric static void removeToken(const SourceManager &SourceMgr,
70c0981da4SDimitry Andric tooling::Replacements &Fixes,
71c0981da4SDimitry Andric const FormatToken *First) {
72c0981da4SDimitry Andric auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
73c0981da4SDimitry Andric First->Tok.getEndLoc());
74c0981da4SDimitry Andric replaceToken(SourceMgr, Fixes, Range, "");
75c0981da4SDimitry Andric }
76c0981da4SDimitry Andric
insertQualifierAfter(const SourceManager & SourceMgr,tooling::Replacements & Fixes,const FormatToken * First,const std::string & Qualifier)77c0981da4SDimitry Andric static void insertQualifierAfter(const SourceManager &SourceMgr,
78c0981da4SDimitry Andric tooling::Replacements &Fixes,
79c0981da4SDimitry Andric const FormatToken *First,
80c0981da4SDimitry Andric const std::string &Qualifier) {
817fa27ce4SDimitry Andric auto Range = CharSourceRange::getCharRange(First->Tok.getLocation(),
827fa27ce4SDimitry Andric First->Tok.getEndLoc());
83c0981da4SDimitry Andric
847fa27ce4SDimitry Andric std::string NewText{};
857fa27ce4SDimitry Andric NewText += First->TokenText;
867fa27ce4SDimitry Andric NewText += " " + Qualifier;
87c0981da4SDimitry Andric replaceToken(SourceMgr, Fixes, Range, NewText);
88c0981da4SDimitry Andric }
89c0981da4SDimitry Andric
insertQualifierBefore(const SourceManager & SourceMgr,tooling::Replacements & Fixes,const FormatToken * First,const std::string & Qualifier)90c0981da4SDimitry Andric static void insertQualifierBefore(const SourceManager &SourceMgr,
91c0981da4SDimitry Andric tooling::Replacements &Fixes,
92c0981da4SDimitry Andric const FormatToken *First,
93c0981da4SDimitry Andric const std::string &Qualifier) {
94c0981da4SDimitry Andric auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
95c0981da4SDimitry Andric First->Tok.getEndLoc());
96c0981da4SDimitry Andric
97c0981da4SDimitry Andric std::string NewText = " " + Qualifier + " ";
98c0981da4SDimitry Andric NewText += First->TokenText;
99c0981da4SDimitry Andric
100c0981da4SDimitry Andric replaceToken(SourceMgr, Fixes, Range, NewText);
101c0981da4SDimitry Andric }
102c0981da4SDimitry Andric
endsWithSpace(const std::string & s)103c0981da4SDimitry Andric static bool endsWithSpace(const std::string &s) {
104145449b1SDimitry Andric if (s.empty())
105c0981da4SDimitry Andric return false;
106c0981da4SDimitry Andric return isspace(s.back());
107c0981da4SDimitry Andric }
108c0981da4SDimitry Andric
startsWithSpace(const std::string & s)109c0981da4SDimitry Andric static bool startsWithSpace(const std::string &s) {
110145449b1SDimitry Andric if (s.empty())
111c0981da4SDimitry Andric return false;
112c0981da4SDimitry Andric return isspace(s.front());
113c0981da4SDimitry Andric }
114c0981da4SDimitry Andric
rotateTokens(const SourceManager & SourceMgr,tooling::Replacements & Fixes,const FormatToken * First,const FormatToken * Last,bool Left)115c0981da4SDimitry Andric static void rotateTokens(const SourceManager &SourceMgr,
116c0981da4SDimitry Andric tooling::Replacements &Fixes, const FormatToken *First,
117c0981da4SDimitry Andric const FormatToken *Last, bool Left) {
118c0981da4SDimitry Andric auto *End = Last;
119c0981da4SDimitry Andric auto *Begin = First;
120c0981da4SDimitry Andric if (!Left) {
121c0981da4SDimitry Andric End = Last->Next;
122c0981da4SDimitry Andric Begin = First->Next;
123c0981da4SDimitry Andric }
124c0981da4SDimitry Andric
125c0981da4SDimitry Andric std::string NewText;
126c0981da4SDimitry Andric // If we are rotating to the left we move the Last token to the front.
127c0981da4SDimitry Andric if (Left) {
128c0981da4SDimitry Andric NewText += Last->TokenText;
129c0981da4SDimitry Andric NewText += " ";
130c0981da4SDimitry Andric }
131c0981da4SDimitry Andric
132c0981da4SDimitry Andric // Then move through the other tokens.
133c0981da4SDimitry Andric auto *Tok = Begin;
134c0981da4SDimitry Andric while (Tok != End) {
135145449b1SDimitry Andric if (!NewText.empty() && !endsWithSpace(NewText))
136c0981da4SDimitry Andric NewText += " ";
137c0981da4SDimitry Andric
138c0981da4SDimitry Andric NewText += Tok->TokenText;
139c0981da4SDimitry Andric Tok = Tok->Next;
140c0981da4SDimitry Andric }
141c0981da4SDimitry Andric
142c0981da4SDimitry Andric // If we are rotating to the right we move the first token to the back.
143c0981da4SDimitry Andric if (!Left) {
144145449b1SDimitry Andric if (!NewText.empty() && !startsWithSpace(NewText))
145c0981da4SDimitry Andric NewText += " ";
146c0981da4SDimitry Andric NewText += First->TokenText;
147c0981da4SDimitry Andric }
148c0981da4SDimitry Andric
149c0981da4SDimitry Andric auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
150c0981da4SDimitry Andric Last->Tok.getEndLoc());
151c0981da4SDimitry Andric
152c0981da4SDimitry Andric replaceToken(SourceMgr, Fixes, Range, NewText);
153c0981da4SDimitry Andric }
154c0981da4SDimitry Andric
1557fa27ce4SDimitry Andric static bool
isConfiguredQualifier(const FormatToken * const Tok,const std::vector<tok::TokenKind> & Qualifiers)1567fa27ce4SDimitry Andric isConfiguredQualifier(const FormatToken *const Tok,
1577fa27ce4SDimitry Andric const std::vector<tok::TokenKind> &Qualifiers) {
1587fa27ce4SDimitry Andric return Tok && llvm::is_contained(Qualifiers, Tok->Tok.getKind());
1597fa27ce4SDimitry Andric }
1607fa27ce4SDimitry Andric
isQualifier(const FormatToken * const Tok)1617fa27ce4SDimitry Andric static bool isQualifier(const FormatToken *const Tok) {
1627fa27ce4SDimitry Andric if (!Tok)
1637fa27ce4SDimitry Andric return false;
1647fa27ce4SDimitry Andric
1657fa27ce4SDimitry Andric switch (Tok->Tok.getKind()) {
1667fa27ce4SDimitry Andric case tok::kw_const:
1677fa27ce4SDimitry Andric case tok::kw_volatile:
1687fa27ce4SDimitry Andric case tok::kw_static:
1697fa27ce4SDimitry Andric case tok::kw_inline:
1707fa27ce4SDimitry Andric case tok::kw_constexpr:
1717fa27ce4SDimitry Andric case tok::kw_restrict:
1727fa27ce4SDimitry Andric case tok::kw_friend:
1737fa27ce4SDimitry Andric return true;
1747fa27ce4SDimitry Andric default:
1757fa27ce4SDimitry Andric return false;
1767fa27ce4SDimitry Andric }
1777fa27ce4SDimitry Andric }
1787fa27ce4SDimitry Andric
analyzeRight(const SourceManager & SourceMgr,const AdditionalKeywords & Keywords,tooling::Replacements & Fixes,const FormatToken * const Tok,const std::string & Qualifier,tok::TokenKind QualifierType)1796f8fc217SDimitry Andric const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(
180c0981da4SDimitry Andric const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
1817fa27ce4SDimitry Andric tooling::Replacements &Fixes, const FormatToken *const Tok,
182c0981da4SDimitry Andric const std::string &Qualifier, tok::TokenKind QualifierType) {
183c0981da4SDimitry Andric // We only need to think about streams that begin with a qualifier.
184b1c73532SDimitry Andric if (Tok->isNot(QualifierType))
185c0981da4SDimitry Andric return Tok;
186c0981da4SDimitry Andric // Don't concern yourself if nothing follows the qualifier.
187c0981da4SDimitry Andric if (!Tok->Next)
188c0981da4SDimitry Andric return Tok;
189c0981da4SDimitry Andric
1907fa27ce4SDimitry Andric // Skip qualifiers to the left to find what preceeds the qualifiers.
1917fa27ce4SDimitry Andric // Use isQualifier rather than isConfiguredQualifier to cover all qualifiers.
1927fa27ce4SDimitry Andric const FormatToken *PreviousCheck = Tok->getPreviousNonComment();
1937fa27ce4SDimitry Andric while (isQualifier(PreviousCheck))
1947fa27ce4SDimitry Andric PreviousCheck = PreviousCheck->getPreviousNonComment();
195145449b1SDimitry Andric
1967fa27ce4SDimitry Andric // Examples given in order of ['type', 'const', 'volatile']
1977fa27ce4SDimitry Andric const bool IsRightQualifier = PreviousCheck && [PreviousCheck]() {
1987fa27ce4SDimitry Andric // The cases:
1997fa27ce4SDimitry Andric // `Foo() const` -> `Foo() const`
2007fa27ce4SDimitry Andric // `Foo() const final` -> `Foo() const final`
2017fa27ce4SDimitry Andric // `Foo() const override` -> `Foo() const final`
2027fa27ce4SDimitry Andric // `Foo() const volatile override` -> `Foo() const volatile override`
2037fa27ce4SDimitry Andric // `Foo() volatile const final` -> `Foo() const volatile final`
2047fa27ce4SDimitry Andric if (PreviousCheck->is(tok::r_paren))
2057fa27ce4SDimitry Andric return true;
2067fa27ce4SDimitry Andric
2077fa27ce4SDimitry Andric // The cases:
2087fa27ce4SDimitry Andric // `struct {} volatile const a;` -> `struct {} const volatile a;`
2097fa27ce4SDimitry Andric // `class {} volatile const a;` -> `class {} const volatile a;`
2107fa27ce4SDimitry Andric if (PreviousCheck->is(tok::r_brace))
2117fa27ce4SDimitry Andric return true;
2127fa27ce4SDimitry Andric
2137fa27ce4SDimitry Andric // The case:
2147fa27ce4SDimitry Andric // `template <class T> const Bar Foo()` ->
2157fa27ce4SDimitry Andric // `template <class T> Bar const Foo()`
2167fa27ce4SDimitry Andric // The cases:
2177fa27ce4SDimitry Andric // `Foo<int> const foo` -> `Foo<int> const foo`
2187fa27ce4SDimitry Andric // `Foo<int> volatile const` -> `Foo<int> const volatile`
2197fa27ce4SDimitry Andric // The case:
2207fa27ce4SDimitry Andric // ```
2217fa27ce4SDimitry Andric // template <class T>
2227fa27ce4SDimitry Andric // requires Concept1<T> && requires Concept2<T>
2237fa27ce4SDimitry Andric // const Foo f();
2247fa27ce4SDimitry Andric // ```
2257fa27ce4SDimitry Andric // ->
2267fa27ce4SDimitry Andric // ```
2277fa27ce4SDimitry Andric // template <class T>
2287fa27ce4SDimitry Andric // requires Concept1<T> && requires Concept2<T>
2297fa27ce4SDimitry Andric // Foo const f();
2307fa27ce4SDimitry Andric // ```
2317fa27ce4SDimitry Andric if (PreviousCheck->is(TT_TemplateCloser)) {
2327fa27ce4SDimitry Andric // If the token closes a template<> or requires clause, then it is a left
2337fa27ce4SDimitry Andric // qualifier and should be moved to the right.
2347fa27ce4SDimitry Andric return !(PreviousCheck->ClosesTemplateDeclaration ||
2357fa27ce4SDimitry Andric PreviousCheck->ClosesRequiresClause);
236c0981da4SDimitry Andric }
2377fa27ce4SDimitry Andric
2387fa27ce4SDimitry Andric // The case `Foo* const` -> `Foo* const`
2397fa27ce4SDimitry Andric // The case `Foo* volatile const` -> `Foo* const volatile`
2407fa27ce4SDimitry Andric // The case `int32_t const` -> `int32_t const`
2417fa27ce4SDimitry Andric // The case `auto volatile const` -> `auto const volatile`
2427fa27ce4SDimitry Andric if (PreviousCheck->isOneOf(TT_PointerOrReference, tok::identifier,
2437fa27ce4SDimitry Andric tok::kw_auto)) {
2447fa27ce4SDimitry Andric return true;
2457fa27ce4SDimitry Andric }
2467fa27ce4SDimitry Andric
2477fa27ce4SDimitry Andric return false;
2487fa27ce4SDimitry Andric }();
2497fa27ce4SDimitry Andric
2507fa27ce4SDimitry Andric // Find the last qualifier to the right.
2517fa27ce4SDimitry Andric const FormatToken *LastQual = Tok;
2527fa27ce4SDimitry Andric while (isQualifier(LastQual->getNextNonComment()))
2537fa27ce4SDimitry Andric LastQual = LastQual->getNextNonComment();
2547fa27ce4SDimitry Andric
2557fa27ce4SDimitry Andric // If this qualifier is to the right of a type or pointer do a partial sort
2567fa27ce4SDimitry Andric // and return.
2577fa27ce4SDimitry Andric if (IsRightQualifier) {
2587fa27ce4SDimitry Andric if (LastQual != Tok)
259c0981da4SDimitry Andric rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false);
260c0981da4SDimitry Andric return Tok;
2617fa27ce4SDimitry Andric }
2627fa27ce4SDimitry Andric
2637fa27ce4SDimitry Andric const FormatToken *TypeToken = LastQual->getNextNonComment();
2647fa27ce4SDimitry Andric if (!TypeToken)
2657fa27ce4SDimitry Andric return Tok;
2667fa27ce4SDimitry Andric
2677fa27ce4SDimitry Andric // Stay safe and don't move past macros, also don't bother with sorting.
2687fa27ce4SDimitry Andric if (isPossibleMacro(TypeToken))
2697fa27ce4SDimitry Andric return Tok;
2707fa27ce4SDimitry Andric
2717fa27ce4SDimitry Andric // The case `const long long int volatile` -> `long long int const volatile`
2727fa27ce4SDimitry Andric // The case `long const long int volatile` -> `long long int const volatile`
2737fa27ce4SDimitry Andric // The case `long long volatile int const` -> `long long int const volatile`
2747fa27ce4SDimitry Andric // The case `const long long volatile int` -> `long long int const volatile`
275ac9a064cSDimitry Andric if (TypeToken->isTypeName(LangOpts)) {
2767fa27ce4SDimitry Andric // The case `const decltype(foo)` -> `const decltype(foo)`
2777fa27ce4SDimitry Andric // The case `const typeof(foo)` -> `const typeof(foo)`
2787fa27ce4SDimitry Andric // The case `const _Atomic(foo)` -> `const _Atomic(foo)`
2797fa27ce4SDimitry Andric if (TypeToken->isOneOf(tok::kw_decltype, tok::kw_typeof, tok::kw__Atomic))
2807fa27ce4SDimitry Andric return Tok;
2817fa27ce4SDimitry Andric
2827fa27ce4SDimitry Andric const FormatToken *LastSimpleTypeSpecifier = TypeToken;
283ac9a064cSDimitry Andric while (isQualifierOrType(LastSimpleTypeSpecifier->getNextNonComment(),
284ac9a064cSDimitry Andric LangOpts)) {
2857fa27ce4SDimitry Andric LastSimpleTypeSpecifier = LastSimpleTypeSpecifier->getNextNonComment();
286ac9a064cSDimitry Andric }
2877fa27ce4SDimitry Andric
2887fa27ce4SDimitry Andric rotateTokens(SourceMgr, Fixes, Tok, LastSimpleTypeSpecifier,
2897fa27ce4SDimitry Andric /*Left=*/false);
2907fa27ce4SDimitry Andric return LastSimpleTypeSpecifier;
2917fa27ce4SDimitry Andric }
2927fa27ce4SDimitry Andric
2937fa27ce4SDimitry Andric // The case `unsigned short const` -> `unsigned short const`
2947fa27ce4SDimitry Andric // The case:
2957fa27ce4SDimitry Andric // `unsigned short volatile const` -> `unsigned short const volatile`
296ac9a064cSDimitry Andric if (PreviousCheck && PreviousCheck->isTypeName(LangOpts)) {
2977fa27ce4SDimitry Andric if (LastQual != Tok)
2987fa27ce4SDimitry Andric rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false);
2997fa27ce4SDimitry Andric return Tok;
3007fa27ce4SDimitry Andric }
3017fa27ce4SDimitry Andric
3027fa27ce4SDimitry Andric // Skip the typename keyword.
3037fa27ce4SDimitry Andric // The case `const typename C::type` -> `typename C::type const`
3047fa27ce4SDimitry Andric if (TypeToken->is(tok::kw_typename))
3057fa27ce4SDimitry Andric TypeToken = TypeToken->getNextNonComment();
3067fa27ce4SDimitry Andric
3077fa27ce4SDimitry Andric // Skip the initial :: of a global-namespace type.
3087fa27ce4SDimitry Andric // The case `const ::...` -> `::... const`
3097fa27ce4SDimitry Andric if (TypeToken->is(tok::coloncolon)) {
3107fa27ce4SDimitry Andric // The case `const ::template Foo...` -> `::template Foo... const`
3117fa27ce4SDimitry Andric TypeToken = TypeToken->getNextNonComment();
3127fa27ce4SDimitry Andric if (TypeToken && TypeToken->is(tok::kw_template))
3137fa27ce4SDimitry Andric TypeToken = TypeToken->getNextNonComment();
3147fa27ce4SDimitry Andric }
3157fa27ce4SDimitry Andric
3167fa27ce4SDimitry Andric // Don't change declarations such as
3177fa27ce4SDimitry Andric // `foo(const struct Foo a);` -> `foo(const struct Foo a);`
3187fa27ce4SDimitry Andric // as they would currently change code such as
3197fa27ce4SDimitry Andric // `const struct my_struct_t {} my_struct;` -> `struct my_struct_t const {}
3207fa27ce4SDimitry Andric // my_struct;`
3217fa27ce4SDimitry Andric if (TypeToken->isOneOf(tok::kw_struct, tok::kw_class))
3227fa27ce4SDimitry Andric return Tok;
3237fa27ce4SDimitry Andric
3247fa27ce4SDimitry Andric if (TypeToken->isOneOf(tok::kw_auto, tok::identifier)) {
3257fa27ce4SDimitry Andric // The case `const auto` -> `auto const`
326c0981da4SDimitry Andric // The case `const Foo` -> `Foo const`
327145449b1SDimitry Andric // The case `const ::Foo` -> `::Foo const`
328c0981da4SDimitry Andric // The case `const Foo *` -> `Foo const *`
329c0981da4SDimitry Andric // The case `const Foo &` -> `Foo const &`
330c0981da4SDimitry Andric // The case `const Foo &&` -> `Foo const &&`
331c0981da4SDimitry Andric // The case `const std::Foo &&` -> `std::Foo const &&`
332c0981da4SDimitry Andric // The case `const std::Foo<T> &&` -> `std::Foo<T> const &&`
3337fa27ce4SDimitry Andric // The case `const ::template Foo` -> `::template Foo const`
3347fa27ce4SDimitry Andric // The case `const T::template Foo` -> `T::template Foo const`
3357fa27ce4SDimitry Andric const FormatToken *Next = nullptr;
3367fa27ce4SDimitry Andric while ((Next = TypeToken->getNextNonComment()) &&
3377fa27ce4SDimitry Andric (Next->is(TT_TemplateOpener) ||
3387fa27ce4SDimitry Andric Next->startsSequence(tok::coloncolon, tok::identifier) ||
3397fa27ce4SDimitry Andric Next->startsSequence(tok::coloncolon, tok::kw_template,
3407fa27ce4SDimitry Andric tok::identifier))) {
3417fa27ce4SDimitry Andric if (Next->is(TT_TemplateOpener)) {
3427fa27ce4SDimitry Andric assert(Next->MatchingParen && "Missing template closer");
3437fa27ce4SDimitry Andric TypeToken = Next->MatchingParen;
3447fa27ce4SDimitry Andric } else if (Next->startsSequence(tok::coloncolon, tok::identifier)) {
3457fa27ce4SDimitry Andric TypeToken = Next->getNextNonComment();
3467fa27ce4SDimitry Andric } else {
3477fa27ce4SDimitry Andric TypeToken = Next->getNextNonComment()->getNextNonComment();
348145449b1SDimitry Andric }
349c0981da4SDimitry Andric }
3507fa27ce4SDimitry Andric
351b1c73532SDimitry Andric if (Next->is(tok::kw_auto))
352b1c73532SDimitry Andric TypeToken = Next;
353b1c73532SDimitry Andric
3547fa27ce4SDimitry Andric // Place the Qualifier at the end of the list of qualifiers.
3557fa27ce4SDimitry Andric while (isQualifier(TypeToken->getNextNonComment())) {
3567fa27ce4SDimitry Andric // The case `volatile Foo::iter const` -> `Foo::iter const volatile`
3577fa27ce4SDimitry Andric TypeToken = TypeToken->getNextNonComment();
358c0981da4SDimitry Andric }
3597fa27ce4SDimitry Andric
3607fa27ce4SDimitry Andric insertQualifierAfter(SourceMgr, Fixes, TypeToken, Qualifier);
3617fa27ce4SDimitry Andric // Remove token and following whitespace.
3627fa27ce4SDimitry Andric auto Range = CharSourceRange::getCharRange(
3637fa27ce4SDimitry Andric Tok->getStartOfNonWhitespace(), Tok->Next->getStartOfNonWhitespace());
3647fa27ce4SDimitry Andric replaceToken(SourceMgr, Fixes, Range, "");
365c0981da4SDimitry Andric }
366c0981da4SDimitry Andric
367c0981da4SDimitry Andric return Tok;
368c0981da4SDimitry Andric }
369c0981da4SDimitry Andric
analyzeLeft(const SourceManager & SourceMgr,const AdditionalKeywords & Keywords,tooling::Replacements & Fixes,const FormatToken * const Tok,const std::string & Qualifier,tok::TokenKind QualifierType)3706f8fc217SDimitry Andric const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft(
371c0981da4SDimitry Andric const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
3727fa27ce4SDimitry Andric tooling::Replacements &Fixes, const FormatToken *const Tok,
373c0981da4SDimitry Andric const std::string &Qualifier, tok::TokenKind QualifierType) {
3747fa27ce4SDimitry Andric // We only need to think about streams that begin with a qualifier.
375b1c73532SDimitry Andric if (Tok->isNot(QualifierType))
3767fa27ce4SDimitry Andric return Tok;
3777fa27ce4SDimitry Andric // Don't concern yourself if nothing preceeds the qualifier.
3787fa27ce4SDimitry Andric if (!Tok->getPreviousNonComment())
379c0981da4SDimitry Andric return Tok;
380c0981da4SDimitry Andric
3817fa27ce4SDimitry Andric // Skip qualifiers to the left to find what preceeds the qualifiers.
3827fa27ce4SDimitry Andric const FormatToken *TypeToken = Tok->getPreviousNonComment();
3837fa27ce4SDimitry Andric while (isQualifier(TypeToken))
3847fa27ce4SDimitry Andric TypeToken = TypeToken->getPreviousNonComment();
3857fa27ce4SDimitry Andric
3867fa27ce4SDimitry Andric // For left qualifiers preceeded by nothing, a template declaration, or *,&,&&
3877fa27ce4SDimitry Andric // we only perform sorting.
388b1c73532SDimitry Andric if (!TypeToken || TypeToken->isPointerOrReference() ||
3897fa27ce4SDimitry Andric TypeToken->ClosesRequiresClause || TypeToken->ClosesTemplateDeclaration) {
3907fa27ce4SDimitry Andric
3917fa27ce4SDimitry Andric // Don't sort past a non-configured qualifier token.
3927fa27ce4SDimitry Andric const FormatToken *FirstQual = Tok;
3937fa27ce4SDimitry Andric while (isConfiguredQualifier(FirstQual->getPreviousNonComment(),
3947fa27ce4SDimitry Andric ConfiguredQualifierTokens)) {
3957fa27ce4SDimitry Andric FirstQual = FirstQual->getPreviousNonComment();
396c0981da4SDimitry Andric }
397c0981da4SDimitry Andric
3987fa27ce4SDimitry Andric if (FirstQual != Tok)
3997fa27ce4SDimitry Andric rotateTokens(SourceMgr, Fixes, FirstQual, Tok, /*Left=*/true);
400c0981da4SDimitry Andric return Tok;
401c0981da4SDimitry Andric }
402145449b1SDimitry Andric
4037fa27ce4SDimitry Andric // Stay safe and don't move past macros, also don't bother with sorting.
4047fa27ce4SDimitry Andric if (isPossibleMacro(TypeToken))
4057fa27ce4SDimitry Andric return Tok;
406c0981da4SDimitry Andric
4077fa27ce4SDimitry Andric // Examples given in order of ['const', 'volatile', 'type']
4087fa27ce4SDimitry Andric
4097fa27ce4SDimitry Andric // The case `volatile long long int const` -> `const volatile long long int`
4107fa27ce4SDimitry Andric // The case `volatile long long const int` -> `const volatile long long int`
4117fa27ce4SDimitry Andric // The case `const long long volatile int` -> `const volatile long long int`
4127fa27ce4SDimitry Andric // The case `long volatile long int const` -> `const volatile long long int`
413ac9a064cSDimitry Andric if (TypeToken->isTypeName(LangOpts)) {
4147fa27ce4SDimitry Andric const FormatToken *LastSimpleTypeSpecifier = TypeToken;
4157fa27ce4SDimitry Andric while (isConfiguredQualifierOrType(
4167fa27ce4SDimitry Andric LastSimpleTypeSpecifier->getPreviousNonComment(),
417ac9a064cSDimitry Andric ConfiguredQualifierTokens, LangOpts)) {
4187fa27ce4SDimitry Andric LastSimpleTypeSpecifier =
4197fa27ce4SDimitry Andric LastSimpleTypeSpecifier->getPreviousNonComment();
420c0981da4SDimitry Andric }
4217fa27ce4SDimitry Andric
4227fa27ce4SDimitry Andric rotateTokens(SourceMgr, Fixes, LastSimpleTypeSpecifier, Tok,
4237fa27ce4SDimitry Andric /*Left=*/true);
4247fa27ce4SDimitry Andric return Tok;
425c0981da4SDimitry Andric }
4267fa27ce4SDimitry Andric
4277fa27ce4SDimitry Andric if (TypeToken->isOneOf(tok::kw_auto, tok::identifier, TT_TemplateCloser)) {
4287fa27ce4SDimitry Andric const auto IsStartOfType = [](const FormatToken *const Tok) -> bool {
4297fa27ce4SDimitry Andric if (!Tok)
4307fa27ce4SDimitry Andric return true;
4317fa27ce4SDimitry Andric
4327fa27ce4SDimitry Andric // A template closer is not the start of a type.
4337fa27ce4SDimitry Andric // The case `?<> const` -> `const ?<>`
4347fa27ce4SDimitry Andric if (Tok->is(TT_TemplateCloser))
4357fa27ce4SDimitry Andric return false;
4367fa27ce4SDimitry Andric
4377fa27ce4SDimitry Andric const FormatToken *const Previous = Tok->getPreviousNonComment();
4387fa27ce4SDimitry Andric if (!Previous)
4397fa27ce4SDimitry Andric return true;
4407fa27ce4SDimitry Andric
4417fa27ce4SDimitry Andric // An identifier preceeded by :: is not the start of a type.
4427fa27ce4SDimitry Andric // The case `?::Foo const` -> `const ?::Foo`
4437fa27ce4SDimitry Andric if (Tok->is(tok::identifier) && Previous->is(tok::coloncolon))
4447fa27ce4SDimitry Andric return false;
4457fa27ce4SDimitry Andric
4467fa27ce4SDimitry Andric const FormatToken *const PrePrevious = Previous->getPreviousNonComment();
4477fa27ce4SDimitry Andric // An identifier preceeded by ::template is not the start of a type.
4487fa27ce4SDimitry Andric // The case `?::template Foo const` -> `const ?::template Foo`
4497fa27ce4SDimitry Andric if (Tok->is(tok::identifier) && Previous->is(tok::kw_template) &&
4507fa27ce4SDimitry Andric PrePrevious && PrePrevious->is(tok::coloncolon)) {
4517fa27ce4SDimitry Andric return false;
4527fa27ce4SDimitry Andric }
4537fa27ce4SDimitry Andric
454b1c73532SDimitry Andric if (Tok->endsSequence(tok::kw_auto, tok::identifier))
455b1c73532SDimitry Andric return false;
456b1c73532SDimitry Andric
4577fa27ce4SDimitry Andric return true;
4587fa27ce4SDimitry Andric };
4597fa27ce4SDimitry Andric
4607fa27ce4SDimitry Andric while (!IsStartOfType(TypeToken)) {
4617fa27ce4SDimitry Andric // The case `?<>`
4627fa27ce4SDimitry Andric if (TypeToken->is(TT_TemplateCloser)) {
4637fa27ce4SDimitry Andric assert(TypeToken->MatchingParen && "Missing template opener");
4647fa27ce4SDimitry Andric TypeToken = TypeToken->MatchingParen->getPreviousNonComment();
4657fa27ce4SDimitry Andric } else {
4667fa27ce4SDimitry Andric // The cases
4677fa27ce4SDimitry Andric // `::Foo`
4687fa27ce4SDimitry Andric // `?>::Foo`
4697fa27ce4SDimitry Andric // `?Bar::Foo`
4707fa27ce4SDimitry Andric // `::template Foo`
4717fa27ce4SDimitry Andric // `?>::template Foo`
4727fa27ce4SDimitry Andric // `?Bar::template Foo`
4737fa27ce4SDimitry Andric if (TypeToken->getPreviousNonComment()->is(tok::kw_template))
4747fa27ce4SDimitry Andric TypeToken = TypeToken->getPreviousNonComment();
4757fa27ce4SDimitry Andric
4767fa27ce4SDimitry Andric const FormatToken *const ColonColon =
4777fa27ce4SDimitry Andric TypeToken->getPreviousNonComment();
4787fa27ce4SDimitry Andric const FormatToken *const PreColonColon =
4797fa27ce4SDimitry Andric ColonColon->getPreviousNonComment();
4807fa27ce4SDimitry Andric if (PreColonColon &&
4817fa27ce4SDimitry Andric PreColonColon->isOneOf(TT_TemplateCloser, tok::identifier)) {
4827fa27ce4SDimitry Andric TypeToken = PreColonColon;
4837fa27ce4SDimitry Andric } else {
4847fa27ce4SDimitry Andric TypeToken = ColonColon;
485c0981da4SDimitry Andric }
486c0981da4SDimitry Andric }
487c0981da4SDimitry Andric }
4887fa27ce4SDimitry Andric
4897fa27ce4SDimitry Andric assert(TypeToken && "Should be auto or identifier");
4907fa27ce4SDimitry Andric
4917fa27ce4SDimitry Andric // Place the Qualifier at the start of the list of qualifiers.
4927fa27ce4SDimitry Andric const FormatToken *Previous = nullptr;
4937fa27ce4SDimitry Andric while ((Previous = TypeToken->getPreviousNonComment()) &&
4947fa27ce4SDimitry Andric (isConfiguredQualifier(Previous, ConfiguredQualifierTokens) ||
4957fa27ce4SDimitry Andric Previous->is(tok::kw_typename))) {
4967fa27ce4SDimitry Andric // The case `volatile Foo::iter const` -> `const volatile Foo::iter`
4977fa27ce4SDimitry Andric // The case `typename C::type const` -> `const typename C::type`
4987fa27ce4SDimitry Andric TypeToken = Previous;
4997fa27ce4SDimitry Andric }
5007fa27ce4SDimitry Andric
5017fa27ce4SDimitry Andric // Don't change declarations such as
5027fa27ce4SDimitry Andric // `foo(struct Foo const a);` -> `foo(struct Foo const a);`
5037fa27ce4SDimitry Andric if (!Previous || !Previous->isOneOf(tok::kw_struct, tok::kw_class)) {
5047fa27ce4SDimitry Andric insertQualifierBefore(SourceMgr, Fixes, TypeToken, Qualifier);
5057fa27ce4SDimitry Andric removeToken(SourceMgr, Fixes, Tok);
5067fa27ce4SDimitry Andric }
5077fa27ce4SDimitry Andric }
5087fa27ce4SDimitry Andric
509c0981da4SDimitry Andric return Tok;
510c0981da4SDimitry Andric }
511c0981da4SDimitry Andric
getTokenFromQualifier(const std::string & Qualifier)512c0981da4SDimitry Andric tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier(
513c0981da4SDimitry Andric const std::string &Qualifier) {
514c0981da4SDimitry Andric // Don't let 'type' be an identifier, but steal typeof token.
515c0981da4SDimitry Andric return llvm::StringSwitch<tok::TokenKind>(Qualifier)
516c0981da4SDimitry Andric .Case("type", tok::kw_typeof)
517c0981da4SDimitry Andric .Case("const", tok::kw_const)
518c0981da4SDimitry Andric .Case("volatile", tok::kw_volatile)
519c0981da4SDimitry Andric .Case("static", tok::kw_static)
520c0981da4SDimitry Andric .Case("inline", tok::kw_inline)
521c0981da4SDimitry Andric .Case("constexpr", tok::kw_constexpr)
522c0981da4SDimitry Andric .Case("restrict", tok::kw_restrict)
523e3b55780SDimitry Andric .Case("friend", tok::kw_friend)
524c0981da4SDimitry Andric .Default(tok::identifier);
525c0981da4SDimitry Andric }
526c0981da4SDimitry Andric
LeftRightQualifierAlignmentFixer(const Environment & Env,const FormatStyle & Style,const std::string & Qualifier,const std::vector<tok::TokenKind> & QualifierTokens,bool RightAlign)527c0981da4SDimitry Andric LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer(
528c0981da4SDimitry Andric const Environment &Env, const FormatStyle &Style,
529c0981da4SDimitry Andric const std::string &Qualifier,
530c0981da4SDimitry Andric const std::vector<tok::TokenKind> &QualifierTokens, bool RightAlign)
531c0981da4SDimitry Andric : TokenAnalyzer(Env, Style), Qualifier(Qualifier), RightAlign(RightAlign),
532c0981da4SDimitry Andric ConfiguredQualifierTokens(QualifierTokens) {}
533c0981da4SDimitry Andric
534c0981da4SDimitry Andric std::pair<tooling::Replacements, unsigned>
analyze(TokenAnnotator &,SmallVectorImpl<AnnotatedLine * > & AnnotatedLines,FormatTokenLexer & Tokens)535c0981da4SDimitry Andric LeftRightQualifierAlignmentFixer::analyze(
536145449b1SDimitry Andric TokenAnnotator & /*Annotator*/,
537145449b1SDimitry Andric SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
538c0981da4SDimitry Andric FormatTokenLexer &Tokens) {
539c0981da4SDimitry Andric tooling::Replacements Fixes;
540b1c73532SDimitry Andric AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
541b1c73532SDimitry Andric fixQualifierAlignment(AnnotatedLines, Tokens, Fixes);
542b1c73532SDimitry Andric return {Fixes, 0};
543b1c73532SDimitry Andric }
544b1c73532SDimitry Andric
fixQualifierAlignment(SmallVectorImpl<AnnotatedLine * > & AnnotatedLines,FormatTokenLexer & Tokens,tooling::Replacements & Fixes)545b1c73532SDimitry Andric void LeftRightQualifierAlignmentFixer::fixQualifierAlignment(
546b1c73532SDimitry Andric SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, FormatTokenLexer &Tokens,
547b1c73532SDimitry Andric tooling::Replacements &Fixes) {
548c0981da4SDimitry Andric const AdditionalKeywords &Keywords = Tokens.getKeywords();
549c0981da4SDimitry Andric const SourceManager &SourceMgr = Env.getSourceManager();
550c0981da4SDimitry Andric tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier);
551c0981da4SDimitry Andric assert(QualifierToken != tok::identifier && "Unrecognised Qualifier");
552c0981da4SDimitry Andric
5536f8fc217SDimitry Andric for (AnnotatedLine *Line : AnnotatedLines) {
554b1c73532SDimitry Andric fixQualifierAlignment(Line->Children, Tokens, Fixes);
5557fa27ce4SDimitry Andric if (!Line->Affected || Line->InPPDirective)
556145449b1SDimitry Andric continue;
5576f8fc217SDimitry Andric FormatToken *First = Line->First;
558145449b1SDimitry Andric assert(First);
559145449b1SDimitry Andric if (First->Finalized)
560145449b1SDimitry Andric continue;
561145449b1SDimitry Andric
5626f8fc217SDimitry Andric const auto *Last = Line->Last;
563c0981da4SDimitry Andric
5646f8fc217SDimitry Andric for (const auto *Tok = First; Tok && Tok != Last && Tok->Next;
5656f8fc217SDimitry Andric Tok = Tok->Next) {
566ac9a064cSDimitry Andric if (Tok->MustBreakBefore)
567ac9a064cSDimitry Andric break;
568c0981da4SDimitry Andric if (Tok->is(tok::comment))
569c0981da4SDimitry Andric continue;
570145449b1SDimitry Andric if (RightAlign) {
571c0981da4SDimitry Andric Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier,
572c0981da4SDimitry Andric QualifierToken);
573145449b1SDimitry Andric } else {
574c0981da4SDimitry Andric Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier,
575c0981da4SDimitry Andric QualifierToken);
576c0981da4SDimitry Andric }
577c0981da4SDimitry Andric }
578145449b1SDimitry Andric }
579c0981da4SDimitry Andric }
580c0981da4SDimitry Andric
prepareLeftRightOrderingForQualifierAlignmentFixer(const std::vector<std::string> & Order,std::vector<std::string> & LeftOrder,std::vector<std::string> & RightOrder,std::vector<tok::TokenKind> & Qualifiers)5817fa27ce4SDimitry Andric void prepareLeftRightOrderingForQualifierAlignmentFixer(
582c0981da4SDimitry Andric const std::vector<std::string> &Order, std::vector<std::string> &LeftOrder,
583c0981da4SDimitry Andric std::vector<std::string> &RightOrder,
584c0981da4SDimitry Andric std::vector<tok::TokenKind> &Qualifiers) {
585c0981da4SDimitry Andric
586c0981da4SDimitry Andric // Depending on the position of type in the order you need
587c0981da4SDimitry Andric // To iterate forward or backward through the order list as qualifier
588c0981da4SDimitry Andric // can push through each other.
589c0981da4SDimitry Andric // The Order list must define the position of "type" to signify
590c0981da4SDimitry Andric assert(llvm::is_contained(Order, "type") &&
591c0981da4SDimitry Andric "QualifierOrder must contain type");
592c0981da4SDimitry Andric // Split the Order list by type and reverse the left side.
593c0981da4SDimitry Andric
594c0981da4SDimitry Andric bool left = true;
595c0981da4SDimitry Andric for (const auto &s : Order) {
596c0981da4SDimitry Andric if (s == "type") {
597c0981da4SDimitry Andric left = false;
598c0981da4SDimitry Andric continue;
599c0981da4SDimitry Andric }
600c0981da4SDimitry Andric
601c0981da4SDimitry Andric tok::TokenKind QualifierToken =
602c0981da4SDimitry Andric LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s);
603145449b1SDimitry Andric if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier)
604c0981da4SDimitry Andric Qualifiers.push_back(QualifierToken);
605c0981da4SDimitry Andric
606145449b1SDimitry Andric if (left) {
607c0981da4SDimitry Andric // Reverse the order for left aligned items.
608c0981da4SDimitry Andric LeftOrder.insert(LeftOrder.begin(), s);
609145449b1SDimitry Andric } else {
610c0981da4SDimitry Andric RightOrder.push_back(s);
611c0981da4SDimitry Andric }
612c0981da4SDimitry Andric }
613145449b1SDimitry Andric }
614c0981da4SDimitry Andric
isQualifierOrType(const FormatToken * Tok,const LangOptions & LangOpts)615ac9a064cSDimitry Andric bool isQualifierOrType(const FormatToken *Tok, const LangOptions &LangOpts) {
616ac9a064cSDimitry Andric return Tok && (Tok->isTypeName(LangOpts) || Tok->is(tok::kw_auto) ||
6177fa27ce4SDimitry Andric isQualifier(Tok));
6187fa27ce4SDimitry Andric }
6197fa27ce4SDimitry Andric
isConfiguredQualifierOrType(const FormatToken * Tok,const std::vector<tok::TokenKind> & Qualifiers,const LangOptions & LangOpts)620ac9a064cSDimitry Andric bool isConfiguredQualifierOrType(const FormatToken *Tok,
621ac9a064cSDimitry Andric const std::vector<tok::TokenKind> &Qualifiers,
622ac9a064cSDimitry Andric const LangOptions &LangOpts) {
623ac9a064cSDimitry Andric return Tok && (Tok->isTypeName(LangOpts) || Tok->is(tok::kw_auto) ||
6247fa27ce4SDimitry Andric isConfiguredQualifier(Tok, Qualifiers));
625c0981da4SDimitry Andric }
626c0981da4SDimitry Andric
627c0981da4SDimitry Andric // If a token is an identifier and it's upper case, it could
628c0981da4SDimitry Andric // be a macro and hence we need to be able to ignore it.
isPossibleMacro(const FormatToken * Tok)629ac9a064cSDimitry Andric bool isPossibleMacro(const FormatToken *Tok) {
630c0981da4SDimitry Andric if (!Tok)
631c0981da4SDimitry Andric return false;
632b1c73532SDimitry Andric if (Tok->isNot(tok::identifier))
633c0981da4SDimitry Andric return false;
634145449b1SDimitry Andric if (Tok->TokenText.upper() == Tok->TokenText.str()) {
635145449b1SDimitry Andric // T,K,U,V likely could be template arguments
6367fa27ce4SDimitry Andric return Tok->TokenText.size() != 1;
637145449b1SDimitry Andric }
638c0981da4SDimitry Andric return false;
639c0981da4SDimitry Andric }
640c0981da4SDimitry Andric
641c0981da4SDimitry Andric } // namespace format
642c0981da4SDimitry Andric } // namespace clang
643