15f757f3fSDimitry Andric //===--- QualifierAlignmentFixer.cpp ----------------------------*- C++--*-===// 2349cc55cSDimitry Andric // 3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6349cc55cSDimitry Andric // 7349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 8349cc55cSDimitry Andric /// 9349cc55cSDimitry Andric /// \file 105f757f3fSDimitry Andric /// This file implements QualifierAlignmentFixer, a TokenAnalyzer that 11349cc55cSDimitry Andric /// enforces either left or right const depending on the style. 12349cc55cSDimitry Andric /// 13349cc55cSDimitry Andric //===----------------------------------------------------------------------===// 14349cc55cSDimitry Andric 15349cc55cSDimitry Andric #include "QualifierAlignmentFixer.h" 16349cc55cSDimitry Andric #include "FormatToken.h" 17349cc55cSDimitry Andric #include "llvm/Support/Debug.h" 18349cc55cSDimitry Andric #include "llvm/Support/Regex.h" 19349cc55cSDimitry Andric 20349cc55cSDimitry Andric #include <algorithm> 21bdd1243dSDimitry Andric #include <optional> 22349cc55cSDimitry Andric 23349cc55cSDimitry Andric #define DEBUG_TYPE "format-qualifier-alignment-fixer" 24349cc55cSDimitry Andric 25349cc55cSDimitry Andric namespace clang { 26349cc55cSDimitry Andric namespace format { 27349cc55cSDimitry Andric 2806c3fb27SDimitry Andric void addQualifierAlignmentFixerPasses(const FormatStyle &Style, 2906c3fb27SDimitry Andric SmallVectorImpl<AnalyzerPass> &Passes) { 30349cc55cSDimitry Andric std::vector<std::string> LeftOrder; 31349cc55cSDimitry Andric std::vector<std::string> RightOrder; 32349cc55cSDimitry Andric std::vector<tok::TokenKind> ConfiguredQualifierTokens; 3306c3fb27SDimitry Andric prepareLeftRightOrderingForQualifierAlignmentFixer( 3406c3fb27SDimitry Andric Style.QualifierOrder, LeftOrder, RightOrder, ConfiguredQualifierTokens); 35349cc55cSDimitry Andric 3681ad6265SDimitry Andric // Handle the left and right alignment separately. 37349cc55cSDimitry Andric for (const auto &Qualifier : LeftOrder) { 38349cc55cSDimitry Andric Passes.emplace_back( 39349cc55cSDimitry Andric [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { 40349cc55cSDimitry Andric return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, 41349cc55cSDimitry Andric ConfiguredQualifierTokens, 42349cc55cSDimitry Andric /*RightAlign=*/false) 43349cc55cSDimitry Andric .process(); 44349cc55cSDimitry Andric }); 45349cc55cSDimitry Andric } 46349cc55cSDimitry Andric for (const auto &Qualifier : RightOrder) { 47349cc55cSDimitry Andric Passes.emplace_back( 48349cc55cSDimitry Andric [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { 49349cc55cSDimitry Andric return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, 50349cc55cSDimitry Andric ConfiguredQualifierTokens, 51349cc55cSDimitry Andric /*RightAlign=*/true) 52349cc55cSDimitry Andric .process(); 53349cc55cSDimitry Andric }); 54349cc55cSDimitry Andric } 55349cc55cSDimitry Andric } 56349cc55cSDimitry Andric 57349cc55cSDimitry Andric static void replaceToken(const SourceManager &SourceMgr, 58349cc55cSDimitry Andric tooling::Replacements &Fixes, 59349cc55cSDimitry Andric const CharSourceRange &Range, std::string NewText) { 60349cc55cSDimitry Andric auto Replacement = tooling::Replacement(SourceMgr, Range, NewText); 61349cc55cSDimitry Andric auto Err = Fixes.add(Replacement); 62349cc55cSDimitry Andric 6381ad6265SDimitry Andric if (Err) { 64349cc55cSDimitry Andric llvm::errs() << "Error while rearranging Qualifier : " 65349cc55cSDimitry Andric << llvm::toString(std::move(Err)) << "\n"; 66349cc55cSDimitry Andric } 6781ad6265SDimitry Andric } 68349cc55cSDimitry Andric 69349cc55cSDimitry Andric static void removeToken(const SourceManager &SourceMgr, 70349cc55cSDimitry Andric tooling::Replacements &Fixes, 71349cc55cSDimitry Andric const FormatToken *First) { 72349cc55cSDimitry Andric auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), 73349cc55cSDimitry Andric First->Tok.getEndLoc()); 74349cc55cSDimitry Andric replaceToken(SourceMgr, Fixes, Range, ""); 75349cc55cSDimitry Andric } 76349cc55cSDimitry Andric 77349cc55cSDimitry Andric static void insertQualifierAfter(const SourceManager &SourceMgr, 78349cc55cSDimitry Andric tooling::Replacements &Fixes, 79349cc55cSDimitry Andric const FormatToken *First, 80349cc55cSDimitry Andric const std::string &Qualifier) { 8106c3fb27SDimitry Andric auto Range = CharSourceRange::getCharRange(First->Tok.getLocation(), 8206c3fb27SDimitry Andric First->Tok.getEndLoc()); 83349cc55cSDimitry Andric 8406c3fb27SDimitry Andric std::string NewText{}; 8506c3fb27SDimitry Andric NewText += First->TokenText; 8606c3fb27SDimitry Andric NewText += " " + Qualifier; 87349cc55cSDimitry Andric replaceToken(SourceMgr, Fixes, Range, NewText); 88349cc55cSDimitry Andric } 89349cc55cSDimitry Andric 90349cc55cSDimitry Andric static void insertQualifierBefore(const SourceManager &SourceMgr, 91349cc55cSDimitry Andric tooling::Replacements &Fixes, 92349cc55cSDimitry Andric const FormatToken *First, 93349cc55cSDimitry Andric const std::string &Qualifier) { 94349cc55cSDimitry Andric auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), 95349cc55cSDimitry Andric First->Tok.getEndLoc()); 96349cc55cSDimitry Andric 97349cc55cSDimitry Andric std::string NewText = " " + Qualifier + " "; 98349cc55cSDimitry Andric NewText += First->TokenText; 99349cc55cSDimitry Andric 100349cc55cSDimitry Andric replaceToken(SourceMgr, Fixes, Range, NewText); 101349cc55cSDimitry Andric } 102349cc55cSDimitry Andric 103349cc55cSDimitry Andric static bool endsWithSpace(const std::string &s) { 10481ad6265SDimitry Andric if (s.empty()) 105349cc55cSDimitry Andric return false; 106349cc55cSDimitry Andric return isspace(s.back()); 107349cc55cSDimitry Andric } 108349cc55cSDimitry Andric 109349cc55cSDimitry Andric static bool startsWithSpace(const std::string &s) { 11081ad6265SDimitry Andric if (s.empty()) 111349cc55cSDimitry Andric return false; 112349cc55cSDimitry Andric return isspace(s.front()); 113349cc55cSDimitry Andric } 114349cc55cSDimitry Andric 115349cc55cSDimitry Andric static void rotateTokens(const SourceManager &SourceMgr, 116349cc55cSDimitry Andric tooling::Replacements &Fixes, const FormatToken *First, 117349cc55cSDimitry Andric const FormatToken *Last, bool Left) { 118349cc55cSDimitry Andric auto *End = Last; 119349cc55cSDimitry Andric auto *Begin = First; 120349cc55cSDimitry Andric if (!Left) { 121349cc55cSDimitry Andric End = Last->Next; 122349cc55cSDimitry Andric Begin = First->Next; 123349cc55cSDimitry Andric } 124349cc55cSDimitry Andric 125349cc55cSDimitry Andric std::string NewText; 126349cc55cSDimitry Andric // If we are rotating to the left we move the Last token to the front. 127349cc55cSDimitry Andric if (Left) { 128349cc55cSDimitry Andric NewText += Last->TokenText; 129349cc55cSDimitry Andric NewText += " "; 130349cc55cSDimitry Andric } 131349cc55cSDimitry Andric 132349cc55cSDimitry Andric // Then move through the other tokens. 133349cc55cSDimitry Andric auto *Tok = Begin; 134349cc55cSDimitry Andric while (Tok != End) { 13581ad6265SDimitry Andric if (!NewText.empty() && !endsWithSpace(NewText)) 136349cc55cSDimitry Andric NewText += " "; 137349cc55cSDimitry Andric 138349cc55cSDimitry Andric NewText += Tok->TokenText; 139349cc55cSDimitry Andric Tok = Tok->Next; 140349cc55cSDimitry Andric } 141349cc55cSDimitry Andric 142349cc55cSDimitry Andric // If we are rotating to the right we move the first token to the back. 143349cc55cSDimitry Andric if (!Left) { 14481ad6265SDimitry Andric if (!NewText.empty() && !startsWithSpace(NewText)) 145349cc55cSDimitry Andric NewText += " "; 146349cc55cSDimitry Andric NewText += First->TokenText; 147349cc55cSDimitry Andric } 148349cc55cSDimitry Andric 149349cc55cSDimitry Andric auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), 150349cc55cSDimitry Andric Last->Tok.getEndLoc()); 151349cc55cSDimitry Andric 152349cc55cSDimitry Andric replaceToken(SourceMgr, Fixes, Range, NewText); 153349cc55cSDimitry Andric } 154349cc55cSDimitry Andric 15506c3fb27SDimitry Andric static bool 15606c3fb27SDimitry Andric isConfiguredQualifier(const FormatToken *const Tok, 15706c3fb27SDimitry Andric const std::vector<tok::TokenKind> &Qualifiers) { 15806c3fb27SDimitry Andric return Tok && llvm::is_contained(Qualifiers, Tok->Tok.getKind()); 15906c3fb27SDimitry Andric } 16006c3fb27SDimitry Andric 16106c3fb27SDimitry Andric static bool isQualifier(const FormatToken *const Tok) { 16206c3fb27SDimitry Andric if (!Tok) 16306c3fb27SDimitry Andric return false; 16406c3fb27SDimitry Andric 16506c3fb27SDimitry Andric switch (Tok->Tok.getKind()) { 16606c3fb27SDimitry Andric case tok::kw_const: 16706c3fb27SDimitry Andric case tok::kw_volatile: 16806c3fb27SDimitry Andric case tok::kw_static: 16906c3fb27SDimitry Andric case tok::kw_inline: 17006c3fb27SDimitry Andric case tok::kw_constexpr: 17106c3fb27SDimitry Andric case tok::kw_restrict: 17206c3fb27SDimitry Andric case tok::kw_friend: 17306c3fb27SDimitry Andric return true; 17406c3fb27SDimitry Andric default: 17506c3fb27SDimitry Andric return false; 17606c3fb27SDimitry Andric } 17706c3fb27SDimitry Andric } 17806c3fb27SDimitry Andric 17904eeddc0SDimitry Andric const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight( 180349cc55cSDimitry Andric const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, 18106c3fb27SDimitry Andric tooling::Replacements &Fixes, const FormatToken *const Tok, 182349cc55cSDimitry Andric const std::string &Qualifier, tok::TokenKind QualifierType) { 183349cc55cSDimitry Andric // We only need to think about streams that begin with a qualifier. 1845f757f3fSDimitry Andric if (Tok->isNot(QualifierType)) 185349cc55cSDimitry Andric return Tok; 186349cc55cSDimitry Andric // Don't concern yourself if nothing follows the qualifier. 187349cc55cSDimitry Andric if (!Tok->Next) 188349cc55cSDimitry Andric return Tok; 189349cc55cSDimitry Andric 19006c3fb27SDimitry Andric // Skip qualifiers to the left to find what preceeds the qualifiers. 19106c3fb27SDimitry Andric // Use isQualifier rather than isConfiguredQualifier to cover all qualifiers. 19206c3fb27SDimitry Andric const FormatToken *PreviousCheck = Tok->getPreviousNonComment(); 19306c3fb27SDimitry Andric while (isQualifier(PreviousCheck)) 19406c3fb27SDimitry Andric PreviousCheck = PreviousCheck->getPreviousNonComment(); 19581ad6265SDimitry Andric 19606c3fb27SDimitry Andric // Examples given in order of ['type', 'const', 'volatile'] 19706c3fb27SDimitry Andric const bool IsRightQualifier = PreviousCheck && [PreviousCheck]() { 19806c3fb27SDimitry Andric // The cases: 19906c3fb27SDimitry Andric // `Foo() const` -> `Foo() const` 20006c3fb27SDimitry Andric // `Foo() const final` -> `Foo() const final` 20106c3fb27SDimitry Andric // `Foo() const override` -> `Foo() const final` 20206c3fb27SDimitry Andric // `Foo() const volatile override` -> `Foo() const volatile override` 20306c3fb27SDimitry Andric // `Foo() volatile const final` -> `Foo() const volatile final` 20406c3fb27SDimitry Andric if (PreviousCheck->is(tok::r_paren)) 20506c3fb27SDimitry Andric return true; 20606c3fb27SDimitry Andric 20706c3fb27SDimitry Andric // The cases: 20806c3fb27SDimitry Andric // `struct {} volatile const a;` -> `struct {} const volatile a;` 20906c3fb27SDimitry Andric // `class {} volatile const a;` -> `class {} const volatile a;` 21006c3fb27SDimitry Andric if (PreviousCheck->is(tok::r_brace)) 21106c3fb27SDimitry Andric return true; 21206c3fb27SDimitry Andric 21306c3fb27SDimitry Andric // The case: 21406c3fb27SDimitry Andric // `template <class T> const Bar Foo()` -> 21506c3fb27SDimitry Andric // `template <class T> Bar const Foo()` 21606c3fb27SDimitry Andric // The cases: 21706c3fb27SDimitry Andric // `Foo<int> const foo` -> `Foo<int> const foo` 21806c3fb27SDimitry Andric // `Foo<int> volatile const` -> `Foo<int> const volatile` 21906c3fb27SDimitry Andric // The case: 22006c3fb27SDimitry Andric // ``` 22106c3fb27SDimitry Andric // template <class T> 22206c3fb27SDimitry Andric // requires Concept1<T> && requires Concept2<T> 22306c3fb27SDimitry Andric // const Foo f(); 22406c3fb27SDimitry Andric // ``` 22506c3fb27SDimitry Andric // -> 22606c3fb27SDimitry Andric // ``` 22706c3fb27SDimitry Andric // template <class T> 22806c3fb27SDimitry Andric // requires Concept1<T> && requires Concept2<T> 22906c3fb27SDimitry Andric // Foo const f(); 23006c3fb27SDimitry Andric // ``` 23106c3fb27SDimitry Andric if (PreviousCheck->is(TT_TemplateCloser)) { 23206c3fb27SDimitry Andric // If the token closes a template<> or requires clause, then it is a left 23306c3fb27SDimitry Andric // qualifier and should be moved to the right. 23406c3fb27SDimitry Andric return !(PreviousCheck->ClosesTemplateDeclaration || 23506c3fb27SDimitry Andric PreviousCheck->ClosesRequiresClause); 236349cc55cSDimitry Andric } 23706c3fb27SDimitry Andric 23806c3fb27SDimitry Andric // The case `Foo* const` -> `Foo* const` 23906c3fb27SDimitry Andric // The case `Foo* volatile const` -> `Foo* const volatile` 24006c3fb27SDimitry Andric // The case `int32_t const` -> `int32_t const` 24106c3fb27SDimitry Andric // The case `auto volatile const` -> `auto const volatile` 24206c3fb27SDimitry Andric if (PreviousCheck->isOneOf(TT_PointerOrReference, tok::identifier, 24306c3fb27SDimitry Andric tok::kw_auto)) { 24406c3fb27SDimitry Andric return true; 24506c3fb27SDimitry Andric } 24606c3fb27SDimitry Andric 24706c3fb27SDimitry Andric return false; 24806c3fb27SDimitry Andric }(); 24906c3fb27SDimitry Andric 25006c3fb27SDimitry Andric // Find the last qualifier to the right. 25106c3fb27SDimitry Andric const FormatToken *LastQual = Tok; 25206c3fb27SDimitry Andric while (isQualifier(LastQual->getNextNonComment())) 25306c3fb27SDimitry Andric LastQual = LastQual->getNextNonComment(); 25406c3fb27SDimitry Andric 25506c3fb27SDimitry Andric // If this qualifier is to the right of a type or pointer do a partial sort 25606c3fb27SDimitry Andric // and return. 25706c3fb27SDimitry Andric if (IsRightQualifier) { 25806c3fb27SDimitry Andric if (LastQual != Tok) 259349cc55cSDimitry Andric rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false); 260349cc55cSDimitry Andric return Tok; 26106c3fb27SDimitry Andric } 26206c3fb27SDimitry Andric 26306c3fb27SDimitry Andric const FormatToken *TypeToken = LastQual->getNextNonComment(); 26406c3fb27SDimitry Andric if (!TypeToken) 26506c3fb27SDimitry Andric return Tok; 26606c3fb27SDimitry Andric 26706c3fb27SDimitry Andric // Stay safe and don't move past macros, also don't bother with sorting. 26806c3fb27SDimitry Andric if (isPossibleMacro(TypeToken)) 26906c3fb27SDimitry Andric return Tok; 27006c3fb27SDimitry Andric 27106c3fb27SDimitry Andric // The case `const long long int volatile` -> `long long int const volatile` 27206c3fb27SDimitry Andric // The case `long const long int volatile` -> `long long int const volatile` 27306c3fb27SDimitry Andric // The case `long long volatile int const` -> `long long int const volatile` 27406c3fb27SDimitry Andric // The case `const long long volatile int` -> `long long int const volatile` 275*0fca6ea1SDimitry Andric if (TypeToken->isTypeName(LangOpts)) { 27606c3fb27SDimitry Andric // The case `const decltype(foo)` -> `const decltype(foo)` 27706c3fb27SDimitry Andric // The case `const typeof(foo)` -> `const typeof(foo)` 27806c3fb27SDimitry Andric // The case `const _Atomic(foo)` -> `const _Atomic(foo)` 27906c3fb27SDimitry Andric if (TypeToken->isOneOf(tok::kw_decltype, tok::kw_typeof, tok::kw__Atomic)) 28006c3fb27SDimitry Andric return Tok; 28106c3fb27SDimitry Andric 28206c3fb27SDimitry Andric const FormatToken *LastSimpleTypeSpecifier = TypeToken; 283*0fca6ea1SDimitry Andric while (isQualifierOrType(LastSimpleTypeSpecifier->getNextNonComment(), 284*0fca6ea1SDimitry Andric LangOpts)) { 28506c3fb27SDimitry Andric LastSimpleTypeSpecifier = LastSimpleTypeSpecifier->getNextNonComment(); 286*0fca6ea1SDimitry Andric } 28706c3fb27SDimitry Andric 28806c3fb27SDimitry Andric rotateTokens(SourceMgr, Fixes, Tok, LastSimpleTypeSpecifier, 28906c3fb27SDimitry Andric /*Left=*/false); 29006c3fb27SDimitry Andric return LastSimpleTypeSpecifier; 29106c3fb27SDimitry Andric } 29206c3fb27SDimitry Andric 29306c3fb27SDimitry Andric // The case `unsigned short const` -> `unsigned short const` 29406c3fb27SDimitry Andric // The case: 29506c3fb27SDimitry Andric // `unsigned short volatile const` -> `unsigned short const volatile` 296*0fca6ea1SDimitry Andric if (PreviousCheck && PreviousCheck->isTypeName(LangOpts)) { 29706c3fb27SDimitry Andric if (LastQual != Tok) 29806c3fb27SDimitry Andric rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false); 29906c3fb27SDimitry Andric return Tok; 30006c3fb27SDimitry Andric } 30106c3fb27SDimitry Andric 30206c3fb27SDimitry Andric // Skip the typename keyword. 30306c3fb27SDimitry Andric // The case `const typename C::type` -> `typename C::type const` 30406c3fb27SDimitry Andric if (TypeToken->is(tok::kw_typename)) 30506c3fb27SDimitry Andric TypeToken = TypeToken->getNextNonComment(); 30606c3fb27SDimitry Andric 30706c3fb27SDimitry Andric // Skip the initial :: of a global-namespace type. 30806c3fb27SDimitry Andric // The case `const ::...` -> `::... const` 30906c3fb27SDimitry Andric if (TypeToken->is(tok::coloncolon)) { 31006c3fb27SDimitry Andric // The case `const ::template Foo...` -> `::template Foo... const` 31106c3fb27SDimitry Andric TypeToken = TypeToken->getNextNonComment(); 31206c3fb27SDimitry Andric if (TypeToken && TypeToken->is(tok::kw_template)) 31306c3fb27SDimitry Andric TypeToken = TypeToken->getNextNonComment(); 31406c3fb27SDimitry Andric } 31506c3fb27SDimitry Andric 31606c3fb27SDimitry Andric // Don't change declarations such as 31706c3fb27SDimitry Andric // `foo(const struct Foo a);` -> `foo(const struct Foo a);` 31806c3fb27SDimitry Andric // as they would currently change code such as 31906c3fb27SDimitry Andric // `const struct my_struct_t {} my_struct;` -> `struct my_struct_t const {} 32006c3fb27SDimitry Andric // my_struct;` 32106c3fb27SDimitry Andric if (TypeToken->isOneOf(tok::kw_struct, tok::kw_class)) 32206c3fb27SDimitry Andric return Tok; 32306c3fb27SDimitry Andric 32406c3fb27SDimitry Andric if (TypeToken->isOneOf(tok::kw_auto, tok::identifier)) { 32506c3fb27SDimitry Andric // The case `const auto` -> `auto const` 326349cc55cSDimitry Andric // The case `const Foo` -> `Foo const` 32781ad6265SDimitry Andric // The case `const ::Foo` -> `::Foo const` 328349cc55cSDimitry Andric // The case `const Foo *` -> `Foo const *` 329349cc55cSDimitry Andric // The case `const Foo &` -> `Foo const &` 330349cc55cSDimitry Andric // The case `const Foo &&` -> `Foo const &&` 331349cc55cSDimitry Andric // The case `const std::Foo &&` -> `std::Foo const &&` 332349cc55cSDimitry Andric // The case `const std::Foo<T> &&` -> `std::Foo<T> const &&` 33306c3fb27SDimitry Andric // The case `const ::template Foo` -> `::template Foo const` 33406c3fb27SDimitry Andric // The case `const T::template Foo` -> `T::template Foo const` 33506c3fb27SDimitry Andric const FormatToken *Next = nullptr; 33606c3fb27SDimitry Andric while ((Next = TypeToken->getNextNonComment()) && 33706c3fb27SDimitry Andric (Next->is(TT_TemplateOpener) || 33806c3fb27SDimitry Andric Next->startsSequence(tok::coloncolon, tok::identifier) || 33906c3fb27SDimitry Andric Next->startsSequence(tok::coloncolon, tok::kw_template, 34006c3fb27SDimitry Andric tok::identifier))) { 34106c3fb27SDimitry Andric if (Next->is(TT_TemplateOpener)) { 34206c3fb27SDimitry Andric assert(Next->MatchingParen && "Missing template closer"); 34306c3fb27SDimitry Andric TypeToken = Next->MatchingParen; 34406c3fb27SDimitry Andric } else if (Next->startsSequence(tok::coloncolon, tok::identifier)) { 34506c3fb27SDimitry Andric TypeToken = Next->getNextNonComment(); 34606c3fb27SDimitry Andric } else { 34706c3fb27SDimitry Andric TypeToken = Next->getNextNonComment()->getNextNonComment(); 3481ac55f4cSDimitry Andric } 34981ad6265SDimitry Andric } 35006c3fb27SDimitry Andric 3515f757f3fSDimitry Andric if (Next->is(tok::kw_auto)) 3525f757f3fSDimitry Andric TypeToken = Next; 3535f757f3fSDimitry Andric 35406c3fb27SDimitry Andric // Place the Qualifier at the end of the list of qualifiers. 35506c3fb27SDimitry Andric while (isQualifier(TypeToken->getNextNonComment())) { 35606c3fb27SDimitry Andric // The case `volatile Foo::iter const` -> `Foo::iter const volatile` 35706c3fb27SDimitry Andric TypeToken = TypeToken->getNextNonComment(); 358349cc55cSDimitry Andric } 35906c3fb27SDimitry Andric 36006c3fb27SDimitry Andric insertQualifierAfter(SourceMgr, Fixes, TypeToken, Qualifier); 36106c3fb27SDimitry Andric // Remove token and following whitespace. 36206c3fb27SDimitry Andric auto Range = CharSourceRange::getCharRange( 36306c3fb27SDimitry Andric Tok->getStartOfNonWhitespace(), Tok->Next->getStartOfNonWhitespace()); 36406c3fb27SDimitry Andric replaceToken(SourceMgr, Fixes, Range, ""); 365349cc55cSDimitry Andric } 366349cc55cSDimitry Andric 367349cc55cSDimitry Andric return Tok; 368349cc55cSDimitry Andric } 369349cc55cSDimitry Andric 37004eeddc0SDimitry Andric const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft( 371349cc55cSDimitry Andric const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, 37206c3fb27SDimitry Andric tooling::Replacements &Fixes, const FormatToken *const Tok, 373349cc55cSDimitry Andric const std::string &Qualifier, tok::TokenKind QualifierType) { 37406c3fb27SDimitry Andric // We only need to think about streams that begin with a qualifier. 3755f757f3fSDimitry Andric if (Tok->isNot(QualifierType)) 37606c3fb27SDimitry Andric return Tok; 37706c3fb27SDimitry Andric // Don't concern yourself if nothing preceeds the qualifier. 37806c3fb27SDimitry Andric if (!Tok->getPreviousNonComment()) 379349cc55cSDimitry Andric return Tok; 380349cc55cSDimitry Andric 38106c3fb27SDimitry Andric // Skip qualifiers to the left to find what preceeds the qualifiers. 38206c3fb27SDimitry Andric const FormatToken *TypeToken = Tok->getPreviousNonComment(); 38306c3fb27SDimitry Andric while (isQualifier(TypeToken)) 38406c3fb27SDimitry Andric TypeToken = TypeToken->getPreviousNonComment(); 38506c3fb27SDimitry Andric 38606c3fb27SDimitry Andric // For left qualifiers preceeded by nothing, a template declaration, or *,&,&& 38706c3fb27SDimitry Andric // we only perform sorting. 3885f757f3fSDimitry Andric if (!TypeToken || TypeToken->isPointerOrReference() || 38906c3fb27SDimitry Andric TypeToken->ClosesRequiresClause || TypeToken->ClosesTemplateDeclaration) { 39006c3fb27SDimitry Andric 39106c3fb27SDimitry Andric // Don't sort past a non-configured qualifier token. 39206c3fb27SDimitry Andric const FormatToken *FirstQual = Tok; 39306c3fb27SDimitry Andric while (isConfiguredQualifier(FirstQual->getPreviousNonComment(), 39406c3fb27SDimitry Andric ConfiguredQualifierTokens)) { 39506c3fb27SDimitry Andric FirstQual = FirstQual->getPreviousNonComment(); 396349cc55cSDimitry Andric } 397349cc55cSDimitry Andric 39806c3fb27SDimitry Andric if (FirstQual != Tok) 39906c3fb27SDimitry Andric rotateTokens(SourceMgr, Fixes, FirstQual, Tok, /*Left=*/true); 400349cc55cSDimitry Andric return Tok; 401349cc55cSDimitry Andric } 40281ad6265SDimitry Andric 40306c3fb27SDimitry Andric // Stay safe and don't move past macros, also don't bother with sorting. 40406c3fb27SDimitry Andric if (isPossibleMacro(TypeToken)) 40506c3fb27SDimitry Andric return Tok; 406349cc55cSDimitry Andric 40706c3fb27SDimitry Andric // Examples given in order of ['const', 'volatile', 'type'] 40806c3fb27SDimitry Andric 40906c3fb27SDimitry Andric // The case `volatile long long int const` -> `const volatile long long int` 41006c3fb27SDimitry Andric // The case `volatile long long const int` -> `const volatile long long int` 41106c3fb27SDimitry Andric // The case `const long long volatile int` -> `const volatile long long int` 41206c3fb27SDimitry Andric // The case `long volatile long int const` -> `const volatile long long int` 413*0fca6ea1SDimitry Andric if (TypeToken->isTypeName(LangOpts)) { 41406c3fb27SDimitry Andric const FormatToken *LastSimpleTypeSpecifier = TypeToken; 41506c3fb27SDimitry Andric while (isConfiguredQualifierOrType( 41606c3fb27SDimitry Andric LastSimpleTypeSpecifier->getPreviousNonComment(), 417*0fca6ea1SDimitry Andric ConfiguredQualifierTokens, LangOpts)) { 41806c3fb27SDimitry Andric LastSimpleTypeSpecifier = 41906c3fb27SDimitry Andric LastSimpleTypeSpecifier->getPreviousNonComment(); 420349cc55cSDimitry Andric } 42106c3fb27SDimitry Andric 42206c3fb27SDimitry Andric rotateTokens(SourceMgr, Fixes, LastSimpleTypeSpecifier, Tok, 42306c3fb27SDimitry Andric /*Left=*/true); 42406c3fb27SDimitry Andric return Tok; 425349cc55cSDimitry Andric } 42606c3fb27SDimitry Andric 42706c3fb27SDimitry Andric if (TypeToken->isOneOf(tok::kw_auto, tok::identifier, TT_TemplateCloser)) { 42806c3fb27SDimitry Andric const auto IsStartOfType = [](const FormatToken *const Tok) -> bool { 42906c3fb27SDimitry Andric if (!Tok) 43006c3fb27SDimitry Andric return true; 43106c3fb27SDimitry Andric 43206c3fb27SDimitry Andric // A template closer is not the start of a type. 43306c3fb27SDimitry Andric // The case `?<> const` -> `const ?<>` 43406c3fb27SDimitry Andric if (Tok->is(TT_TemplateCloser)) 43506c3fb27SDimitry Andric return false; 43606c3fb27SDimitry Andric 43706c3fb27SDimitry Andric const FormatToken *const Previous = Tok->getPreviousNonComment(); 43806c3fb27SDimitry Andric if (!Previous) 43906c3fb27SDimitry Andric return true; 44006c3fb27SDimitry Andric 44106c3fb27SDimitry Andric // An identifier preceeded by :: is not the start of a type. 44206c3fb27SDimitry Andric // The case `?::Foo const` -> `const ?::Foo` 44306c3fb27SDimitry Andric if (Tok->is(tok::identifier) && Previous->is(tok::coloncolon)) 44406c3fb27SDimitry Andric return false; 44506c3fb27SDimitry Andric 44606c3fb27SDimitry Andric const FormatToken *const PrePrevious = Previous->getPreviousNonComment(); 44706c3fb27SDimitry Andric // An identifier preceeded by ::template is not the start of a type. 44806c3fb27SDimitry Andric // The case `?::template Foo const` -> `const ?::template Foo` 44906c3fb27SDimitry Andric if (Tok->is(tok::identifier) && Previous->is(tok::kw_template) && 45006c3fb27SDimitry Andric PrePrevious && PrePrevious->is(tok::coloncolon)) { 45106c3fb27SDimitry Andric return false; 45206c3fb27SDimitry Andric } 45306c3fb27SDimitry Andric 4545f757f3fSDimitry Andric if (Tok->endsSequence(tok::kw_auto, tok::identifier)) 4555f757f3fSDimitry Andric return false; 4565f757f3fSDimitry Andric 45706c3fb27SDimitry Andric return true; 45806c3fb27SDimitry Andric }; 45906c3fb27SDimitry Andric 46006c3fb27SDimitry Andric while (!IsStartOfType(TypeToken)) { 46106c3fb27SDimitry Andric // The case `?<>` 46206c3fb27SDimitry Andric if (TypeToken->is(TT_TemplateCloser)) { 46306c3fb27SDimitry Andric assert(TypeToken->MatchingParen && "Missing template opener"); 46406c3fb27SDimitry Andric TypeToken = TypeToken->MatchingParen->getPreviousNonComment(); 46506c3fb27SDimitry Andric } else { 46606c3fb27SDimitry Andric // The cases 46706c3fb27SDimitry Andric // `::Foo` 46806c3fb27SDimitry Andric // `?>::Foo` 46906c3fb27SDimitry Andric // `?Bar::Foo` 47006c3fb27SDimitry Andric // `::template Foo` 47106c3fb27SDimitry Andric // `?>::template Foo` 47206c3fb27SDimitry Andric // `?Bar::template Foo` 47306c3fb27SDimitry Andric if (TypeToken->getPreviousNonComment()->is(tok::kw_template)) 47406c3fb27SDimitry Andric TypeToken = TypeToken->getPreviousNonComment(); 47506c3fb27SDimitry Andric 47606c3fb27SDimitry Andric const FormatToken *const ColonColon = 47706c3fb27SDimitry Andric TypeToken->getPreviousNonComment(); 47806c3fb27SDimitry Andric const FormatToken *const PreColonColon = 47906c3fb27SDimitry Andric ColonColon->getPreviousNonComment(); 48006c3fb27SDimitry Andric if (PreColonColon && 48106c3fb27SDimitry Andric PreColonColon->isOneOf(TT_TemplateCloser, tok::identifier)) { 48206c3fb27SDimitry Andric TypeToken = PreColonColon; 48306c3fb27SDimitry Andric } else { 48406c3fb27SDimitry Andric TypeToken = ColonColon; 485349cc55cSDimitry Andric } 486349cc55cSDimitry Andric } 487349cc55cSDimitry Andric } 48806c3fb27SDimitry Andric 48906c3fb27SDimitry Andric assert(TypeToken && "Should be auto or identifier"); 49006c3fb27SDimitry Andric 49106c3fb27SDimitry Andric // Place the Qualifier at the start of the list of qualifiers. 49206c3fb27SDimitry Andric const FormatToken *Previous = nullptr; 49306c3fb27SDimitry Andric while ((Previous = TypeToken->getPreviousNonComment()) && 49406c3fb27SDimitry Andric (isConfiguredQualifier(Previous, ConfiguredQualifierTokens) || 49506c3fb27SDimitry Andric Previous->is(tok::kw_typename))) { 49606c3fb27SDimitry Andric // The case `volatile Foo::iter const` -> `const volatile Foo::iter` 49706c3fb27SDimitry Andric // The case `typename C::type const` -> `const typename C::type` 49806c3fb27SDimitry Andric TypeToken = Previous; 49906c3fb27SDimitry Andric } 50006c3fb27SDimitry Andric 50106c3fb27SDimitry Andric // Don't change declarations such as 50206c3fb27SDimitry Andric // `foo(struct Foo const a);` -> `foo(struct Foo const a);` 50306c3fb27SDimitry Andric if (!Previous || !Previous->isOneOf(tok::kw_struct, tok::kw_class)) { 50406c3fb27SDimitry Andric insertQualifierBefore(SourceMgr, Fixes, TypeToken, Qualifier); 50506c3fb27SDimitry Andric removeToken(SourceMgr, Fixes, Tok); 50606c3fb27SDimitry Andric } 50706c3fb27SDimitry Andric } 50806c3fb27SDimitry Andric 509349cc55cSDimitry Andric return Tok; 510349cc55cSDimitry Andric } 511349cc55cSDimitry Andric 512349cc55cSDimitry Andric tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier( 513349cc55cSDimitry Andric const std::string &Qualifier) { 514349cc55cSDimitry Andric // Don't let 'type' be an identifier, but steal typeof token. 515349cc55cSDimitry Andric return llvm::StringSwitch<tok::TokenKind>(Qualifier) 516349cc55cSDimitry Andric .Case("type", tok::kw_typeof) 517349cc55cSDimitry Andric .Case("const", tok::kw_const) 518349cc55cSDimitry Andric .Case("volatile", tok::kw_volatile) 519349cc55cSDimitry Andric .Case("static", tok::kw_static) 520349cc55cSDimitry Andric .Case("inline", tok::kw_inline) 521349cc55cSDimitry Andric .Case("constexpr", tok::kw_constexpr) 522349cc55cSDimitry Andric .Case("restrict", tok::kw_restrict) 523bdd1243dSDimitry Andric .Case("friend", tok::kw_friend) 524349cc55cSDimitry Andric .Default(tok::identifier); 525349cc55cSDimitry Andric } 526349cc55cSDimitry Andric 527349cc55cSDimitry Andric LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer( 528349cc55cSDimitry Andric const Environment &Env, const FormatStyle &Style, 529349cc55cSDimitry Andric const std::string &Qualifier, 530349cc55cSDimitry Andric const std::vector<tok::TokenKind> &QualifierTokens, bool RightAlign) 531349cc55cSDimitry Andric : TokenAnalyzer(Env, Style), Qualifier(Qualifier), RightAlign(RightAlign), 532349cc55cSDimitry Andric ConfiguredQualifierTokens(QualifierTokens) {} 533349cc55cSDimitry Andric 534349cc55cSDimitry Andric std::pair<tooling::Replacements, unsigned> 535349cc55cSDimitry Andric LeftRightQualifierAlignmentFixer::analyze( 53681ad6265SDimitry Andric TokenAnnotator & /*Annotator*/, 53781ad6265SDimitry Andric SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, 538349cc55cSDimitry Andric FormatTokenLexer &Tokens) { 539349cc55cSDimitry Andric tooling::Replacements Fixes; 5405f757f3fSDimitry Andric AffectedRangeMgr.computeAffectedLines(AnnotatedLines); 5415f757f3fSDimitry Andric fixQualifierAlignment(AnnotatedLines, Tokens, Fixes); 5425f757f3fSDimitry Andric return {Fixes, 0}; 5435f757f3fSDimitry Andric } 5445f757f3fSDimitry Andric 5455f757f3fSDimitry Andric void LeftRightQualifierAlignmentFixer::fixQualifierAlignment( 5465f757f3fSDimitry Andric SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, FormatTokenLexer &Tokens, 5475f757f3fSDimitry Andric tooling::Replacements &Fixes) { 548349cc55cSDimitry Andric const AdditionalKeywords &Keywords = Tokens.getKeywords(); 549349cc55cSDimitry Andric const SourceManager &SourceMgr = Env.getSourceManager(); 550349cc55cSDimitry Andric tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier); 551349cc55cSDimitry Andric assert(QualifierToken != tok::identifier && "Unrecognised Qualifier"); 552349cc55cSDimitry Andric 55304eeddc0SDimitry Andric for (AnnotatedLine *Line : AnnotatedLines) { 5545f757f3fSDimitry Andric fixQualifierAlignment(Line->Children, Tokens, Fixes); 55506c3fb27SDimitry Andric if (!Line->Affected || Line->InPPDirective) 55681ad6265SDimitry Andric continue; 55704eeddc0SDimitry Andric FormatToken *First = Line->First; 558d56accc7SDimitry Andric assert(First); 559d56accc7SDimitry Andric if (First->Finalized) 560d56accc7SDimitry Andric continue; 561d56accc7SDimitry Andric 56204eeddc0SDimitry Andric const auto *Last = Line->Last; 563349cc55cSDimitry Andric 56404eeddc0SDimitry Andric for (const auto *Tok = First; Tok && Tok != Last && Tok->Next; 56504eeddc0SDimitry Andric Tok = Tok->Next) { 566*0fca6ea1SDimitry Andric if (Tok->MustBreakBefore) 567*0fca6ea1SDimitry Andric break; 568349cc55cSDimitry Andric if (Tok->is(tok::comment)) 569349cc55cSDimitry Andric continue; 57081ad6265SDimitry Andric if (RightAlign) { 571349cc55cSDimitry Andric Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier, 572349cc55cSDimitry Andric QualifierToken); 57381ad6265SDimitry Andric } else { 574349cc55cSDimitry Andric Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier, 575349cc55cSDimitry Andric QualifierToken); 576349cc55cSDimitry Andric } 577349cc55cSDimitry Andric } 57881ad6265SDimitry Andric } 579349cc55cSDimitry Andric } 580349cc55cSDimitry Andric 58106c3fb27SDimitry Andric void prepareLeftRightOrderingForQualifierAlignmentFixer( 582349cc55cSDimitry Andric const std::vector<std::string> &Order, std::vector<std::string> &LeftOrder, 583349cc55cSDimitry Andric std::vector<std::string> &RightOrder, 584349cc55cSDimitry Andric std::vector<tok::TokenKind> &Qualifiers) { 585349cc55cSDimitry Andric 586349cc55cSDimitry Andric // Depending on the position of type in the order you need 587349cc55cSDimitry Andric // To iterate forward or backward through the order list as qualifier 588349cc55cSDimitry Andric // can push through each other. 589349cc55cSDimitry Andric // The Order list must define the position of "type" to signify 590349cc55cSDimitry Andric assert(llvm::is_contained(Order, "type") && 591349cc55cSDimitry Andric "QualifierOrder must contain type"); 592349cc55cSDimitry Andric // Split the Order list by type and reverse the left side. 593349cc55cSDimitry Andric 594349cc55cSDimitry Andric bool left = true; 595349cc55cSDimitry Andric for (const auto &s : Order) { 596349cc55cSDimitry Andric if (s == "type") { 597349cc55cSDimitry Andric left = false; 598349cc55cSDimitry Andric continue; 599349cc55cSDimitry Andric } 600349cc55cSDimitry Andric 601349cc55cSDimitry Andric tok::TokenKind QualifierToken = 602349cc55cSDimitry Andric LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s); 60381ad6265SDimitry Andric if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier) 604349cc55cSDimitry Andric Qualifiers.push_back(QualifierToken); 605349cc55cSDimitry Andric 60681ad6265SDimitry Andric if (left) { 607349cc55cSDimitry Andric // Reverse the order for left aligned items. 608349cc55cSDimitry Andric LeftOrder.insert(LeftOrder.begin(), s); 60981ad6265SDimitry Andric } else { 610349cc55cSDimitry Andric RightOrder.push_back(s); 611349cc55cSDimitry Andric } 612349cc55cSDimitry Andric } 61381ad6265SDimitry Andric } 614349cc55cSDimitry Andric 615*0fca6ea1SDimitry Andric bool isQualifierOrType(const FormatToken *Tok, const LangOptions &LangOpts) { 616*0fca6ea1SDimitry Andric return Tok && (Tok->isTypeName(LangOpts) || Tok->is(tok::kw_auto) || 61706c3fb27SDimitry Andric isQualifier(Tok)); 61806c3fb27SDimitry Andric } 61906c3fb27SDimitry Andric 620*0fca6ea1SDimitry Andric bool isConfiguredQualifierOrType(const FormatToken *Tok, 621*0fca6ea1SDimitry Andric const std::vector<tok::TokenKind> &Qualifiers, 622*0fca6ea1SDimitry Andric const LangOptions &LangOpts) { 623*0fca6ea1SDimitry Andric return Tok && (Tok->isTypeName(LangOpts) || Tok->is(tok::kw_auto) || 62406c3fb27SDimitry Andric isConfiguredQualifier(Tok, Qualifiers)); 625349cc55cSDimitry Andric } 626349cc55cSDimitry Andric 627349cc55cSDimitry Andric // If a token is an identifier and it's upper case, it could 628349cc55cSDimitry Andric // be a macro and hence we need to be able to ignore it. 629*0fca6ea1SDimitry Andric bool isPossibleMacro(const FormatToken *Tok) { 630349cc55cSDimitry Andric if (!Tok) 631349cc55cSDimitry Andric return false; 6325f757f3fSDimitry Andric if (Tok->isNot(tok::identifier)) 633349cc55cSDimitry Andric return false; 63481ad6265SDimitry Andric if (Tok->TokenText.upper() == Tok->TokenText.str()) { 63581ad6265SDimitry Andric // T,K,U,V likely could be template arguments 63606c3fb27SDimitry Andric return Tok->TokenText.size() != 1; 63781ad6265SDimitry Andric } 638349cc55cSDimitry Andric return false; 639349cc55cSDimitry Andric } 640349cc55cSDimitry Andric 641349cc55cSDimitry Andric } // namespace format 642349cc55cSDimitry Andric } // namespace clang 643