12c9372e7SMyDeveloperDay //===--- QualifierAlignmentFixer.cpp ----------------------------*- C++--*-===// 2a44ab170Smydeveloperday // 3a44ab170Smydeveloperday // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a44ab170Smydeveloperday // See https://llvm.org/LICENSE.txt for license information. 5a44ab170Smydeveloperday // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a44ab170Smydeveloperday // 7a44ab170Smydeveloperday //===----------------------------------------------------------------------===// 8a44ab170Smydeveloperday /// 9a44ab170Smydeveloperday /// \file 102c9372e7SMyDeveloperDay /// This file implements QualifierAlignmentFixer, a TokenAnalyzer that 11a44ab170Smydeveloperday /// enforces either left or right const depending on the style. 12a44ab170Smydeveloperday /// 13a44ab170Smydeveloperday //===----------------------------------------------------------------------===// 14a44ab170Smydeveloperday 15a44ab170Smydeveloperday #include "QualifierAlignmentFixer.h" 16b2082a98SOwen Pan #include "FormatToken.h" 17b2082a98SOwen Pan #include "llvm/Support/Debug.h" 18b2082a98SOwen Pan #include "llvm/Support/Regex.h" 19b2082a98SOwen Pan 20b2082a98SOwen Pan #include <algorithm> 21b2082a98SOwen Pan #include <optional> 22a44ab170Smydeveloperday 23a44ab170Smydeveloperday #define DEBUG_TYPE "format-qualifier-alignment-fixer" 24a44ab170Smydeveloperday 25a44ab170Smydeveloperday namespace clang { 26a44ab170Smydeveloperday namespace format { 27a44ab170Smydeveloperday 28899c8677SSedenion void addQualifierAlignmentFixerPasses(const FormatStyle &Style, 29899c8677SSedenion SmallVectorImpl<AnalyzerPass> &Passes) { 30a44ab170Smydeveloperday std::vector<std::string> LeftOrder; 31a44ab170Smydeveloperday std::vector<std::string> RightOrder; 32a44ab170Smydeveloperday std::vector<tok::TokenKind> ConfiguredQualifierTokens; 33899c8677SSedenion prepareLeftRightOrderingForQualifierAlignmentFixer( 34899c8677SSedenion Style.QualifierOrder, LeftOrder, RightOrder, ConfiguredQualifierTokens); 35a44ab170Smydeveloperday 367d639734SMarek Kurdej // Handle the left and right alignment separately. 37a44ab170Smydeveloperday for (const auto &Qualifier : LeftOrder) { 38a44ab170Smydeveloperday Passes.emplace_back( 39a44ab170Smydeveloperday [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { 40a44ab170Smydeveloperday return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, 41a44ab170Smydeveloperday ConfiguredQualifierTokens, 42a44ab170Smydeveloperday /*RightAlign=*/false) 43a44ab170Smydeveloperday .process(); 44a44ab170Smydeveloperday }); 45a44ab170Smydeveloperday } 46a44ab170Smydeveloperday for (const auto &Qualifier : RightOrder) { 47a44ab170Smydeveloperday Passes.emplace_back( 48a44ab170Smydeveloperday [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { 49a44ab170Smydeveloperday return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, 50a44ab170Smydeveloperday ConfiguredQualifierTokens, 51a44ab170Smydeveloperday /*RightAlign=*/true) 52a44ab170Smydeveloperday .process(); 53a44ab170Smydeveloperday }); 54a44ab170Smydeveloperday } 55a44ab170Smydeveloperday } 56a44ab170Smydeveloperday 57a44ab170Smydeveloperday static void replaceToken(const SourceManager &SourceMgr, 58a44ab170Smydeveloperday tooling::Replacements &Fixes, 59a44ab170Smydeveloperday const CharSourceRange &Range, std::string NewText) { 60a44ab170Smydeveloperday auto Replacement = tooling::Replacement(SourceMgr, Range, NewText); 61a44ab170Smydeveloperday auto Err = Fixes.add(Replacement); 62a44ab170Smydeveloperday 63bebf7bdfSowenca if (Err) { 64a44ab170Smydeveloperday llvm::errs() << "Error while rearranging Qualifier : " 65a44ab170Smydeveloperday << llvm::toString(std::move(Err)) << "\n"; 66a44ab170Smydeveloperday } 67bebf7bdfSowenca } 68a44ab170Smydeveloperday 69a44ab170Smydeveloperday static void removeToken(const SourceManager &SourceMgr, 70a44ab170Smydeveloperday tooling::Replacements &Fixes, 71a44ab170Smydeveloperday const FormatToken *First) { 72a44ab170Smydeveloperday auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), 73a44ab170Smydeveloperday First->Tok.getEndLoc()); 74a44ab170Smydeveloperday replaceToken(SourceMgr, Fixes, Range, ""); 75a44ab170Smydeveloperday } 76a44ab170Smydeveloperday 77a44ab170Smydeveloperday static void insertQualifierAfter(const SourceManager &SourceMgr, 78a44ab170Smydeveloperday tooling::Replacements &Fixes, 79a44ab170Smydeveloperday const FormatToken *First, 80a44ab170Smydeveloperday const std::string &Qualifier) { 81cd7ab4b5SAlexander Hederstaf auto Range = CharSourceRange::getCharRange(First->Tok.getLocation(), 82cd7ab4b5SAlexander Hederstaf First->Tok.getEndLoc()); 83a44ab170Smydeveloperday 84cd7ab4b5SAlexander Hederstaf std::string NewText{}; 85cd7ab4b5SAlexander Hederstaf NewText += First->TokenText; 86cd7ab4b5SAlexander Hederstaf NewText += " " + Qualifier; 87a44ab170Smydeveloperday replaceToken(SourceMgr, Fixes, Range, NewText); 88a44ab170Smydeveloperday } 89a44ab170Smydeveloperday 90a44ab170Smydeveloperday static void insertQualifierBefore(const SourceManager &SourceMgr, 91a44ab170Smydeveloperday tooling::Replacements &Fixes, 92a44ab170Smydeveloperday const FormatToken *First, 93a44ab170Smydeveloperday const std::string &Qualifier) { 94a44ab170Smydeveloperday auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), 95a44ab170Smydeveloperday First->Tok.getEndLoc()); 96a44ab170Smydeveloperday 97a44ab170Smydeveloperday std::string NewText = " " + Qualifier + " "; 98a44ab170Smydeveloperday NewText += First->TokenText; 99a44ab170Smydeveloperday 100a44ab170Smydeveloperday replaceToken(SourceMgr, Fixes, Range, NewText); 101a44ab170Smydeveloperday } 102a44ab170Smydeveloperday 103a44ab170Smydeveloperday static bool endsWithSpace(const std::string &s) { 104d079995dSMarek Kurdej if (s.empty()) 105a44ab170Smydeveloperday return false; 106a44ab170Smydeveloperday return isspace(s.back()); 107a44ab170Smydeveloperday } 108a44ab170Smydeveloperday 109a44ab170Smydeveloperday static bool startsWithSpace(const std::string &s) { 110d079995dSMarek Kurdej if (s.empty()) 111a44ab170Smydeveloperday return false; 112a44ab170Smydeveloperday return isspace(s.front()); 113a44ab170Smydeveloperday } 114a44ab170Smydeveloperday 115a44ab170Smydeveloperday static void rotateTokens(const SourceManager &SourceMgr, 116a44ab170Smydeveloperday tooling::Replacements &Fixes, const FormatToken *First, 117a44ab170Smydeveloperday const FormatToken *Last, bool Left) { 118a44ab170Smydeveloperday auto *End = Last; 119a44ab170Smydeveloperday auto *Begin = First; 120a44ab170Smydeveloperday if (!Left) { 121a44ab170Smydeveloperday End = Last->Next; 122a44ab170Smydeveloperday Begin = First->Next; 123a44ab170Smydeveloperday } 124a44ab170Smydeveloperday 125a44ab170Smydeveloperday std::string NewText; 126a44ab170Smydeveloperday // If we are rotating to the left we move the Last token to the front. 127a44ab170Smydeveloperday if (Left) { 128a44ab170Smydeveloperday NewText += Last->TokenText; 129a44ab170Smydeveloperday NewText += " "; 130a44ab170Smydeveloperday } 131a44ab170Smydeveloperday 132a44ab170Smydeveloperday // Then move through the other tokens. 133a44ab170Smydeveloperday auto *Tok = Begin; 134a44ab170Smydeveloperday while (Tok != End) { 135d079995dSMarek Kurdej if (!NewText.empty() && !endsWithSpace(NewText)) 136a44ab170Smydeveloperday NewText += " "; 137a44ab170Smydeveloperday 138a44ab170Smydeveloperday NewText += Tok->TokenText; 139a44ab170Smydeveloperday Tok = Tok->Next; 140a44ab170Smydeveloperday } 141a44ab170Smydeveloperday 142a44ab170Smydeveloperday // If we are rotating to the right we move the first token to the back. 143a44ab170Smydeveloperday if (!Left) { 144d079995dSMarek Kurdej if (!NewText.empty() && !startsWithSpace(NewText)) 145a44ab170Smydeveloperday NewText += " "; 146a44ab170Smydeveloperday NewText += First->TokenText; 147a44ab170Smydeveloperday } 148a44ab170Smydeveloperday 149a44ab170Smydeveloperday auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), 150a44ab170Smydeveloperday Last->Tok.getEndLoc()); 151a44ab170Smydeveloperday 152a44ab170Smydeveloperday replaceToken(SourceMgr, Fixes, Range, NewText); 153a44ab170Smydeveloperday } 154a44ab170Smydeveloperday 155cd7ab4b5SAlexander Hederstaf static bool 156cd7ab4b5SAlexander Hederstaf isConfiguredQualifier(const FormatToken *const Tok, 157cd7ab4b5SAlexander Hederstaf const std::vector<tok::TokenKind> &Qualifiers) { 158cd7ab4b5SAlexander Hederstaf return Tok && llvm::is_contained(Qualifiers, Tok->Tok.getKind()); 159cd7ab4b5SAlexander Hederstaf } 160cd7ab4b5SAlexander Hederstaf 161cd7ab4b5SAlexander Hederstaf static bool isQualifier(const FormatToken *const Tok) { 162cd7ab4b5SAlexander Hederstaf if (!Tok) 163cd7ab4b5SAlexander Hederstaf return false; 164cd7ab4b5SAlexander Hederstaf 165cd7ab4b5SAlexander Hederstaf switch (Tok->Tok.getKind()) { 166cd7ab4b5SAlexander Hederstaf case tok::kw_const: 167cd7ab4b5SAlexander Hederstaf case tok::kw_volatile: 168cd7ab4b5SAlexander Hederstaf case tok::kw_static: 169cd7ab4b5SAlexander Hederstaf case tok::kw_inline: 170cd7ab4b5SAlexander Hederstaf case tok::kw_constexpr: 171cd7ab4b5SAlexander Hederstaf case tok::kw_restrict: 172cd7ab4b5SAlexander Hederstaf case tok::kw_friend: 173cd7ab4b5SAlexander Hederstaf return true; 174cd7ab4b5SAlexander Hederstaf default: 175cd7ab4b5SAlexander Hederstaf return false; 176cd7ab4b5SAlexander Hederstaf } 177cd7ab4b5SAlexander Hederstaf } 178cd7ab4b5SAlexander Hederstaf 179031d3eceSmydeveloperday const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight( 180a44ab170Smydeveloperday const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, 181cd7ab4b5SAlexander Hederstaf tooling::Replacements &Fixes, const FormatToken *const Tok, 182a44ab170Smydeveloperday const std::string &Qualifier, tok::TokenKind QualifierType) { 183a44ab170Smydeveloperday // We only need to think about streams that begin with a qualifier. 18491c4db00SOwen Pan if (Tok->isNot(QualifierType)) 185a44ab170Smydeveloperday return Tok; 186a44ab170Smydeveloperday // Don't concern yourself if nothing follows the qualifier. 187a44ab170Smydeveloperday if (!Tok->Next) 188a44ab170Smydeveloperday return Tok; 189a44ab170Smydeveloperday 190cd7ab4b5SAlexander Hederstaf // Skip qualifiers to the left to find what preceeds the qualifiers. 191cd7ab4b5SAlexander Hederstaf // Use isQualifier rather than isConfiguredQualifier to cover all qualifiers. 192cd7ab4b5SAlexander Hederstaf const FormatToken *PreviousCheck = Tok->getPreviousNonComment(); 193cd7ab4b5SAlexander Hederstaf while (isQualifier(PreviousCheck)) 194cd7ab4b5SAlexander Hederstaf PreviousCheck = PreviousCheck->getPreviousNonComment(); 195d4d28f2aSMarek Kurdej 196cd7ab4b5SAlexander Hederstaf // Examples given in order of ['type', 'const', 'volatile'] 197cd7ab4b5SAlexander Hederstaf const bool IsRightQualifier = PreviousCheck && [PreviousCheck]() { 198cd7ab4b5SAlexander Hederstaf // The cases: 199cd7ab4b5SAlexander Hederstaf // `Foo() const` -> `Foo() const` 200cd7ab4b5SAlexander Hederstaf // `Foo() const final` -> `Foo() const final` 201cd7ab4b5SAlexander Hederstaf // `Foo() const override` -> `Foo() const final` 202cd7ab4b5SAlexander Hederstaf // `Foo() const volatile override` -> `Foo() const volatile override` 203cd7ab4b5SAlexander Hederstaf // `Foo() volatile const final` -> `Foo() const volatile final` 204cd7ab4b5SAlexander Hederstaf if (PreviousCheck->is(tok::r_paren)) 205cd7ab4b5SAlexander Hederstaf return true; 206cd7ab4b5SAlexander Hederstaf 207cd7ab4b5SAlexander Hederstaf // The cases: 208cd7ab4b5SAlexander Hederstaf // `struct {} volatile const a;` -> `struct {} const volatile a;` 209cd7ab4b5SAlexander Hederstaf // `class {} volatile const a;` -> `class {} const volatile a;` 210cd7ab4b5SAlexander Hederstaf if (PreviousCheck->is(tok::r_brace)) 211cd7ab4b5SAlexander Hederstaf return true; 212cd7ab4b5SAlexander Hederstaf 213cd7ab4b5SAlexander Hederstaf // The case: 214cd7ab4b5SAlexander Hederstaf // `template <class T> const Bar Foo()` -> 215cd7ab4b5SAlexander Hederstaf // `template <class T> Bar const Foo()` 216cd7ab4b5SAlexander Hederstaf // The cases: 217cd7ab4b5SAlexander Hederstaf // `Foo<int> const foo` -> `Foo<int> const foo` 218cd7ab4b5SAlexander Hederstaf // `Foo<int> volatile const` -> `Foo<int> const volatile` 219cd7ab4b5SAlexander Hederstaf // The case: 220cd7ab4b5SAlexander Hederstaf // ``` 221cd7ab4b5SAlexander Hederstaf // template <class T> 222cd7ab4b5SAlexander Hederstaf // requires Concept1<T> && requires Concept2<T> 223cd7ab4b5SAlexander Hederstaf // const Foo f(); 224cd7ab4b5SAlexander Hederstaf // ``` 225cd7ab4b5SAlexander Hederstaf // -> 226cd7ab4b5SAlexander Hederstaf // ``` 227cd7ab4b5SAlexander Hederstaf // template <class T> 228cd7ab4b5SAlexander Hederstaf // requires Concept1<T> && requires Concept2<T> 229cd7ab4b5SAlexander Hederstaf // Foo const f(); 230cd7ab4b5SAlexander Hederstaf // ``` 231cd7ab4b5SAlexander Hederstaf if (PreviousCheck->is(TT_TemplateCloser)) { 232cd7ab4b5SAlexander Hederstaf // If the token closes a template<> or requires clause, then it is a left 233cd7ab4b5SAlexander Hederstaf // qualifier and should be moved to the right. 234cd7ab4b5SAlexander Hederstaf return !(PreviousCheck->ClosesTemplateDeclaration || 235cd7ab4b5SAlexander Hederstaf PreviousCheck->ClosesRequiresClause); 236a44ab170Smydeveloperday } 237cd7ab4b5SAlexander Hederstaf 238cd7ab4b5SAlexander Hederstaf // The case `Foo* const` -> `Foo* const` 239cd7ab4b5SAlexander Hederstaf // The case `Foo* volatile const` -> `Foo* const volatile` 240cd7ab4b5SAlexander Hederstaf // The case `int32_t const` -> `int32_t const` 241cd7ab4b5SAlexander Hederstaf // The case `auto volatile const` -> `auto const volatile` 242cd7ab4b5SAlexander Hederstaf if (PreviousCheck->isOneOf(TT_PointerOrReference, tok::identifier, 243cd7ab4b5SAlexander Hederstaf tok::kw_auto)) { 244cd7ab4b5SAlexander Hederstaf return true; 245cd7ab4b5SAlexander Hederstaf } 246cd7ab4b5SAlexander Hederstaf 247cd7ab4b5SAlexander Hederstaf return false; 248cd7ab4b5SAlexander Hederstaf }(); 249cd7ab4b5SAlexander Hederstaf 250cd7ab4b5SAlexander Hederstaf // Find the last qualifier to the right. 251cd7ab4b5SAlexander Hederstaf const FormatToken *LastQual = Tok; 252cd7ab4b5SAlexander Hederstaf while (isQualifier(LastQual->getNextNonComment())) 253cd7ab4b5SAlexander Hederstaf LastQual = LastQual->getNextNonComment(); 254cd7ab4b5SAlexander Hederstaf 255cd7ab4b5SAlexander Hederstaf // If this qualifier is to the right of a type or pointer do a partial sort 256cd7ab4b5SAlexander Hederstaf // and return. 257cd7ab4b5SAlexander Hederstaf if (IsRightQualifier) { 258cd7ab4b5SAlexander Hederstaf if (LastQual != Tok) 259a44ab170Smydeveloperday rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false); 26028bb040dSmydeveloperday return Tok; 261cd7ab4b5SAlexander Hederstaf } 262cd7ab4b5SAlexander Hederstaf 263cd7ab4b5SAlexander Hederstaf const FormatToken *TypeToken = LastQual->getNextNonComment(); 264cd7ab4b5SAlexander Hederstaf if (!TypeToken) 265cd7ab4b5SAlexander Hederstaf return Tok; 266cd7ab4b5SAlexander Hederstaf 267cd7ab4b5SAlexander Hederstaf // Stay safe and don't move past macros, also don't bother with sorting. 268cd7ab4b5SAlexander Hederstaf if (isPossibleMacro(TypeToken)) 269cd7ab4b5SAlexander Hederstaf return Tok; 270cd7ab4b5SAlexander Hederstaf 271cd7ab4b5SAlexander Hederstaf // The case `const long long int volatile` -> `long long int const volatile` 272cd7ab4b5SAlexander Hederstaf // The case `long const long int volatile` -> `long long int const volatile` 273cd7ab4b5SAlexander Hederstaf // The case `long long volatile int const` -> `long long int const volatile` 274cd7ab4b5SAlexander Hederstaf // The case `const long long volatile int` -> `long long int const volatile` 275364f988dSOwen Pan if (TypeToken->isTypeName(LangOpts)) { 276cd7ab4b5SAlexander Hederstaf // The case `const decltype(foo)` -> `const decltype(foo)` 277cd7ab4b5SAlexander Hederstaf // The case `const typeof(foo)` -> `const typeof(foo)` 278cd7ab4b5SAlexander Hederstaf // The case `const _Atomic(foo)` -> `const _Atomic(foo)` 279cd7ab4b5SAlexander Hederstaf if (TypeToken->isOneOf(tok::kw_decltype, tok::kw_typeof, tok::kw__Atomic)) 280cd7ab4b5SAlexander Hederstaf return Tok; 281cd7ab4b5SAlexander Hederstaf 282cd7ab4b5SAlexander Hederstaf const FormatToken *LastSimpleTypeSpecifier = TypeToken; 2836f31cf51SOwen Pan while (isQualifierOrType(LastSimpleTypeSpecifier->getNextNonComment(), 284364f988dSOwen Pan LangOpts)) { 285cd7ab4b5SAlexander Hederstaf LastSimpleTypeSpecifier = LastSimpleTypeSpecifier->getNextNonComment(); 2866f31cf51SOwen Pan } 287cd7ab4b5SAlexander Hederstaf 288cd7ab4b5SAlexander Hederstaf rotateTokens(SourceMgr, Fixes, Tok, LastSimpleTypeSpecifier, 289cd7ab4b5SAlexander Hederstaf /*Left=*/false); 290cd7ab4b5SAlexander Hederstaf return LastSimpleTypeSpecifier; 291cd7ab4b5SAlexander Hederstaf } 292cd7ab4b5SAlexander Hederstaf 293cd7ab4b5SAlexander Hederstaf // The case `unsigned short const` -> `unsigned short const` 294cd7ab4b5SAlexander Hederstaf // The case: 295cd7ab4b5SAlexander Hederstaf // `unsigned short volatile const` -> `unsigned short const volatile` 296364f988dSOwen Pan if (PreviousCheck && PreviousCheck->isTypeName(LangOpts)) { 297cd7ab4b5SAlexander Hederstaf if (LastQual != Tok) 298cd7ab4b5SAlexander Hederstaf rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false); 299cd7ab4b5SAlexander Hederstaf return Tok; 300cd7ab4b5SAlexander Hederstaf } 301cd7ab4b5SAlexander Hederstaf 302cd7ab4b5SAlexander Hederstaf // Skip the typename keyword. 303cd7ab4b5SAlexander Hederstaf // The case `const typename C::type` -> `typename C::type const` 304cd7ab4b5SAlexander Hederstaf if (TypeToken->is(tok::kw_typename)) 305cd7ab4b5SAlexander Hederstaf TypeToken = TypeToken->getNextNonComment(); 306cd7ab4b5SAlexander Hederstaf 307cd7ab4b5SAlexander Hederstaf // Skip the initial :: of a global-namespace type. 308cd7ab4b5SAlexander Hederstaf // The case `const ::...` -> `::... const` 309cd7ab4b5SAlexander Hederstaf if (TypeToken->is(tok::coloncolon)) { 310cd7ab4b5SAlexander Hederstaf // The case `const ::template Foo...` -> `::template Foo... const` 311cd7ab4b5SAlexander Hederstaf TypeToken = TypeToken->getNextNonComment(); 312cd7ab4b5SAlexander Hederstaf if (TypeToken && TypeToken->is(tok::kw_template)) 313cd7ab4b5SAlexander Hederstaf TypeToken = TypeToken->getNextNonComment(); 314cd7ab4b5SAlexander Hederstaf } 315cd7ab4b5SAlexander Hederstaf 316cd7ab4b5SAlexander Hederstaf // Don't change declarations such as 317cd7ab4b5SAlexander Hederstaf // `foo(const struct Foo a);` -> `foo(const struct Foo a);` 318cd7ab4b5SAlexander Hederstaf // as they would currently change code such as 319cd7ab4b5SAlexander Hederstaf // `const struct my_struct_t {} my_struct;` -> `struct my_struct_t const {} 320cd7ab4b5SAlexander Hederstaf // my_struct;` 321cd7ab4b5SAlexander Hederstaf if (TypeToken->isOneOf(tok::kw_struct, tok::kw_class)) 322cd7ab4b5SAlexander Hederstaf return Tok; 323cd7ab4b5SAlexander Hederstaf 324cd7ab4b5SAlexander Hederstaf if (TypeToken->isOneOf(tok::kw_auto, tok::identifier)) { 325cd7ab4b5SAlexander Hederstaf // The case `const auto` -> `auto const` 326a44ab170Smydeveloperday // The case `const Foo` -> `Foo const` 327d4d28f2aSMarek Kurdej // The case `const ::Foo` -> `::Foo const` 328a44ab170Smydeveloperday // The case `const Foo *` -> `Foo const *` 329a44ab170Smydeveloperday // The case `const Foo &` -> `Foo const &` 330a44ab170Smydeveloperday // The case `const Foo &&` -> `Foo const &&` 331a44ab170Smydeveloperday // The case `const std::Foo &&` -> `std::Foo const &&` 332a44ab170Smydeveloperday // The case `const std::Foo<T> &&` -> `std::Foo<T> const &&` 333cd7ab4b5SAlexander Hederstaf // The case `const ::template Foo` -> `::template Foo const` 334cd7ab4b5SAlexander Hederstaf // The case `const T::template Foo` -> `T::template Foo const` 335cd7ab4b5SAlexander Hederstaf const FormatToken *Next = nullptr; 336cd7ab4b5SAlexander Hederstaf while ((Next = TypeToken->getNextNonComment()) && 337cd7ab4b5SAlexander Hederstaf (Next->is(TT_TemplateOpener) || 338cd7ab4b5SAlexander Hederstaf Next->startsSequence(tok::coloncolon, tok::identifier) || 339cd7ab4b5SAlexander Hederstaf Next->startsSequence(tok::coloncolon, tok::kw_template, 340cd7ab4b5SAlexander Hederstaf tok::identifier))) { 341cd7ab4b5SAlexander Hederstaf if (Next->is(TT_TemplateOpener)) { 342cd7ab4b5SAlexander Hederstaf assert(Next->MatchingParen && "Missing template closer"); 343cd7ab4b5SAlexander Hederstaf TypeToken = Next->MatchingParen; 344cd7ab4b5SAlexander Hederstaf } else if (Next->startsSequence(tok::coloncolon, tok::identifier)) { 345cd7ab4b5SAlexander Hederstaf TypeToken = Next->getNextNonComment(); 346cd7ab4b5SAlexander Hederstaf } else { 347cd7ab4b5SAlexander Hederstaf TypeToken = Next->getNextNonComment()->getNextNonComment(); 348393e197cSEmilia Dreamer } 349bebf7bdfSowenca } 350cd7ab4b5SAlexander Hederstaf 35140acaa39SOwen Pan if (Next && Next->is(tok::kw_auto)) 352b04664beSOwen Pan TypeToken = Next; 353b04664beSOwen Pan 354cd7ab4b5SAlexander Hederstaf // Place the Qualifier at the end of the list of qualifiers. 355cd7ab4b5SAlexander Hederstaf while (isQualifier(TypeToken->getNextNonComment())) { 356cd7ab4b5SAlexander Hederstaf // The case `volatile Foo::iter const` -> `Foo::iter const volatile` 357cd7ab4b5SAlexander Hederstaf TypeToken = TypeToken->getNextNonComment(); 358a44ab170Smydeveloperday } 359cd7ab4b5SAlexander Hederstaf 360cd7ab4b5SAlexander Hederstaf insertQualifierAfter(SourceMgr, Fixes, TypeToken, Qualifier); 361cd7ab4b5SAlexander Hederstaf // Remove token and following whitespace. 362cd7ab4b5SAlexander Hederstaf auto Range = CharSourceRange::getCharRange( 363cd7ab4b5SAlexander Hederstaf Tok->getStartOfNonWhitespace(), Tok->Next->getStartOfNonWhitespace()); 364cd7ab4b5SAlexander Hederstaf replaceToken(SourceMgr, Fixes, Range, ""); 365a44ab170Smydeveloperday } 366a44ab170Smydeveloperday 367a44ab170Smydeveloperday return Tok; 368a44ab170Smydeveloperday } 369a44ab170Smydeveloperday 370031d3eceSmydeveloperday const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft( 371a44ab170Smydeveloperday const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, 372cd7ab4b5SAlexander Hederstaf tooling::Replacements &Fixes, const FormatToken *const Tok, 373a44ab170Smydeveloperday const std::string &Qualifier, tok::TokenKind QualifierType) { 374cd7ab4b5SAlexander Hederstaf // We only need to think about streams that begin with a qualifier. 37591c4db00SOwen Pan if (Tok->isNot(QualifierType)) 376cd7ab4b5SAlexander Hederstaf return Tok; 377cd7ab4b5SAlexander Hederstaf // Don't concern yourself if nothing preceeds the qualifier. 378cd7ab4b5SAlexander Hederstaf if (!Tok->getPreviousNonComment()) 379a44ab170Smydeveloperday return Tok; 380a44ab170Smydeveloperday 381cd7ab4b5SAlexander Hederstaf // Skip qualifiers to the left to find what preceeds the qualifiers. 382cd7ab4b5SAlexander Hederstaf const FormatToken *TypeToken = Tok->getPreviousNonComment(); 383cd7ab4b5SAlexander Hederstaf while (isQualifier(TypeToken)) 384cd7ab4b5SAlexander Hederstaf TypeToken = TypeToken->getPreviousNonComment(); 385cd7ab4b5SAlexander Hederstaf 386cd7ab4b5SAlexander Hederstaf // For left qualifiers preceeded by nothing, a template declaration, or *,&,&& 387cd7ab4b5SAlexander Hederstaf // we only perform sorting. 388cc75e520SOwen Pan if (!TypeToken || TypeToken->isPointerOrReference() || 389*3bd8b02aSOwen Pan TypeToken->ClosesRequiresClause || TypeToken->ClosesTemplateDeclaration || 390*3bd8b02aSOwen Pan TypeToken->is(tok::r_square)) { 391cd7ab4b5SAlexander Hederstaf 392cd7ab4b5SAlexander Hederstaf // Don't sort past a non-configured qualifier token. 393cd7ab4b5SAlexander Hederstaf const FormatToken *FirstQual = Tok; 394cd7ab4b5SAlexander Hederstaf while (isConfiguredQualifier(FirstQual->getPreviousNonComment(), 395cd7ab4b5SAlexander Hederstaf ConfiguredQualifierTokens)) { 396cd7ab4b5SAlexander Hederstaf FirstQual = FirstQual->getPreviousNonComment(); 397a44ab170Smydeveloperday } 398a44ab170Smydeveloperday 399cd7ab4b5SAlexander Hederstaf if (FirstQual != Tok) 400cd7ab4b5SAlexander Hederstaf rotateTokens(SourceMgr, Fixes, FirstQual, Tok, /*Left=*/true); 401a44ab170Smydeveloperday return Tok; 402bebf7bdfSowenca } 40328bb040dSmydeveloperday 404cd7ab4b5SAlexander Hederstaf // Stay safe and don't move past macros, also don't bother with sorting. 405cd7ab4b5SAlexander Hederstaf if (isPossibleMacro(TypeToken)) 406cd7ab4b5SAlexander Hederstaf return Tok; 407a44ab170Smydeveloperday 408cd7ab4b5SAlexander Hederstaf // Examples given in order of ['const', 'volatile', 'type'] 409cd7ab4b5SAlexander Hederstaf 410cd7ab4b5SAlexander Hederstaf // The case `volatile long long int const` -> `const volatile long long int` 411cd7ab4b5SAlexander Hederstaf // The case `volatile long long const int` -> `const volatile long long int` 412cd7ab4b5SAlexander Hederstaf // The case `const long long volatile int` -> `const volatile long long int` 413cd7ab4b5SAlexander Hederstaf // The case `long volatile long int const` -> `const volatile long long int` 414364f988dSOwen Pan if (TypeToken->isTypeName(LangOpts)) { 415cd7ab4b5SAlexander Hederstaf const FormatToken *LastSimpleTypeSpecifier = TypeToken; 416cd7ab4b5SAlexander Hederstaf while (isConfiguredQualifierOrType( 417cd7ab4b5SAlexander Hederstaf LastSimpleTypeSpecifier->getPreviousNonComment(), 418364f988dSOwen Pan ConfiguredQualifierTokens, LangOpts)) { 419cd7ab4b5SAlexander Hederstaf LastSimpleTypeSpecifier = 420cd7ab4b5SAlexander Hederstaf LastSimpleTypeSpecifier->getPreviousNonComment(); 421a44ab170Smydeveloperday } 422cd7ab4b5SAlexander Hederstaf 423cd7ab4b5SAlexander Hederstaf rotateTokens(SourceMgr, Fixes, LastSimpleTypeSpecifier, Tok, 424cd7ab4b5SAlexander Hederstaf /*Left=*/true); 425cd7ab4b5SAlexander Hederstaf return Tok; 426a44ab170Smydeveloperday } 427cd7ab4b5SAlexander Hederstaf 428cd7ab4b5SAlexander Hederstaf if (TypeToken->isOneOf(tok::kw_auto, tok::identifier, TT_TemplateCloser)) { 429cd7ab4b5SAlexander Hederstaf const auto IsStartOfType = [](const FormatToken *const Tok) -> bool { 430cd7ab4b5SAlexander Hederstaf if (!Tok) 431cd7ab4b5SAlexander Hederstaf return true; 432cd7ab4b5SAlexander Hederstaf 433cd7ab4b5SAlexander Hederstaf // A template closer is not the start of a type. 434cd7ab4b5SAlexander Hederstaf // The case `?<> const` -> `const ?<>` 435cd7ab4b5SAlexander Hederstaf if (Tok->is(TT_TemplateCloser)) 436cd7ab4b5SAlexander Hederstaf return false; 437cd7ab4b5SAlexander Hederstaf 438cd7ab4b5SAlexander Hederstaf const FormatToken *const Previous = Tok->getPreviousNonComment(); 439cd7ab4b5SAlexander Hederstaf if (!Previous) 440cd7ab4b5SAlexander Hederstaf return true; 441cd7ab4b5SAlexander Hederstaf 442cd7ab4b5SAlexander Hederstaf // An identifier preceeded by :: is not the start of a type. 443cd7ab4b5SAlexander Hederstaf // The case `?::Foo const` -> `const ?::Foo` 444cd7ab4b5SAlexander Hederstaf if (Tok->is(tok::identifier) && Previous->is(tok::coloncolon)) 445cd7ab4b5SAlexander Hederstaf return false; 446cd7ab4b5SAlexander Hederstaf 447cd7ab4b5SAlexander Hederstaf const FormatToken *const PrePrevious = Previous->getPreviousNonComment(); 448cd7ab4b5SAlexander Hederstaf // An identifier preceeded by ::template is not the start of a type. 449cd7ab4b5SAlexander Hederstaf // The case `?::template Foo const` -> `const ?::template Foo` 450cd7ab4b5SAlexander Hederstaf if (Tok->is(tok::identifier) && Previous->is(tok::kw_template) && 451cd7ab4b5SAlexander Hederstaf PrePrevious && PrePrevious->is(tok::coloncolon)) { 452cd7ab4b5SAlexander Hederstaf return false; 453cd7ab4b5SAlexander Hederstaf } 454cd7ab4b5SAlexander Hederstaf 455b04664beSOwen Pan if (Tok->endsSequence(tok::kw_auto, tok::identifier)) 456b04664beSOwen Pan return false; 457b04664beSOwen Pan 458cd7ab4b5SAlexander Hederstaf return true; 459cd7ab4b5SAlexander Hederstaf }; 460cd7ab4b5SAlexander Hederstaf 461cd7ab4b5SAlexander Hederstaf while (!IsStartOfType(TypeToken)) { 462cd7ab4b5SAlexander Hederstaf // The case `?<>` 463cd7ab4b5SAlexander Hederstaf if (TypeToken->is(TT_TemplateCloser)) { 464cd7ab4b5SAlexander Hederstaf assert(TypeToken->MatchingParen && "Missing template opener"); 465cd7ab4b5SAlexander Hederstaf TypeToken = TypeToken->MatchingParen->getPreviousNonComment(); 466cd7ab4b5SAlexander Hederstaf } else { 467cd7ab4b5SAlexander Hederstaf // The cases 468cd7ab4b5SAlexander Hederstaf // `::Foo` 469cd7ab4b5SAlexander Hederstaf // `?>::Foo` 470cd7ab4b5SAlexander Hederstaf // `?Bar::Foo` 471cd7ab4b5SAlexander Hederstaf // `::template Foo` 472cd7ab4b5SAlexander Hederstaf // `?>::template Foo` 473cd7ab4b5SAlexander Hederstaf // `?Bar::template Foo` 474cd7ab4b5SAlexander Hederstaf if (TypeToken->getPreviousNonComment()->is(tok::kw_template)) 475cd7ab4b5SAlexander Hederstaf TypeToken = TypeToken->getPreviousNonComment(); 476cd7ab4b5SAlexander Hederstaf 477cd7ab4b5SAlexander Hederstaf const FormatToken *const ColonColon = 478cd7ab4b5SAlexander Hederstaf TypeToken->getPreviousNonComment(); 479cd7ab4b5SAlexander Hederstaf const FormatToken *const PreColonColon = 480cd7ab4b5SAlexander Hederstaf ColonColon->getPreviousNonComment(); 481cd7ab4b5SAlexander Hederstaf if (PreColonColon && 482cd7ab4b5SAlexander Hederstaf PreColonColon->isOneOf(TT_TemplateCloser, tok::identifier)) { 483cd7ab4b5SAlexander Hederstaf TypeToken = PreColonColon; 484cd7ab4b5SAlexander Hederstaf } else { 485cd7ab4b5SAlexander Hederstaf TypeToken = ColonColon; 486a44ab170Smydeveloperday } 487a44ab170Smydeveloperday } 488a44ab170Smydeveloperday } 489cd7ab4b5SAlexander Hederstaf 490cd7ab4b5SAlexander Hederstaf assert(TypeToken && "Should be auto or identifier"); 491cd7ab4b5SAlexander Hederstaf 492cd7ab4b5SAlexander Hederstaf // Place the Qualifier at the start of the list of qualifiers. 493cd7ab4b5SAlexander Hederstaf const FormatToken *Previous = nullptr; 494cd7ab4b5SAlexander Hederstaf while ((Previous = TypeToken->getPreviousNonComment()) && 495cd7ab4b5SAlexander Hederstaf (isConfiguredQualifier(Previous, ConfiguredQualifierTokens) || 496cd7ab4b5SAlexander Hederstaf Previous->is(tok::kw_typename))) { 497cd7ab4b5SAlexander Hederstaf // The case `volatile Foo::iter const` -> `const volatile Foo::iter` 498cd7ab4b5SAlexander Hederstaf // The case `typename C::type const` -> `const typename C::type` 499cd7ab4b5SAlexander Hederstaf TypeToken = Previous; 500cd7ab4b5SAlexander Hederstaf } 501cd7ab4b5SAlexander Hederstaf 502cd7ab4b5SAlexander Hederstaf // Don't change declarations such as 503cd7ab4b5SAlexander Hederstaf // `foo(struct Foo const a);` -> `foo(struct Foo const a);` 504cd7ab4b5SAlexander Hederstaf if (!Previous || !Previous->isOneOf(tok::kw_struct, tok::kw_class)) { 505cd7ab4b5SAlexander Hederstaf insertQualifierBefore(SourceMgr, Fixes, TypeToken, Qualifier); 506cd7ab4b5SAlexander Hederstaf removeToken(SourceMgr, Fixes, Tok); 507cd7ab4b5SAlexander Hederstaf } 508cd7ab4b5SAlexander Hederstaf } 509cd7ab4b5SAlexander Hederstaf 510a44ab170Smydeveloperday return Tok; 511a44ab170Smydeveloperday } 512a44ab170Smydeveloperday 513a44ab170Smydeveloperday tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier( 514a44ab170Smydeveloperday const std::string &Qualifier) { 5152c60cfc0SMarek Kurdej // Don't let 'type' be an identifier, but steal typeof token. 516a44ab170Smydeveloperday return llvm::StringSwitch<tok::TokenKind>(Qualifier) 517a44ab170Smydeveloperday .Case("type", tok::kw_typeof) 518a44ab170Smydeveloperday .Case("const", tok::kw_const) 519a44ab170Smydeveloperday .Case("volatile", tok::kw_volatile) 520a44ab170Smydeveloperday .Case("static", tok::kw_static) 521a44ab170Smydeveloperday .Case("inline", tok::kw_inline) 522a44ab170Smydeveloperday .Case("constexpr", tok::kw_constexpr) 523a44ab170Smydeveloperday .Case("restrict", tok::kw_restrict) 5244cafc372SMicah Weston .Case("friend", tok::kw_friend) 525a44ab170Smydeveloperday .Default(tok::identifier); 526a44ab170Smydeveloperday } 527a44ab170Smydeveloperday 528a44ab170Smydeveloperday LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer( 529a44ab170Smydeveloperday const Environment &Env, const FormatStyle &Style, 530a44ab170Smydeveloperday const std::string &Qualifier, 531a44ab170Smydeveloperday const std::vector<tok::TokenKind> &QualifierTokens, bool RightAlign) 532a44ab170Smydeveloperday : TokenAnalyzer(Env, Style), Qualifier(Qualifier), RightAlign(RightAlign), 5336f31cf51SOwen Pan ConfiguredQualifierTokens(QualifierTokens) {} 534a44ab170Smydeveloperday 535a44ab170Smydeveloperday std::pair<tooling::Replacements, unsigned> 536a44ab170Smydeveloperday LeftRightQualifierAlignmentFixer::analyze( 53706e42590SMarek Kurdej TokenAnnotator & /*Annotator*/, 53806e42590SMarek Kurdej SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, 539a44ab170Smydeveloperday FormatTokenLexer &Tokens) { 540a44ab170Smydeveloperday tooling::Replacements Fixes; 541f0ad9ea3SOwen Pan AffectedRangeMgr.computeAffectedLines(AnnotatedLines); 542f0ad9ea3SOwen Pan fixQualifierAlignment(AnnotatedLines, Tokens, Fixes); 543f0ad9ea3SOwen Pan return {Fixes, 0}; 544f0ad9ea3SOwen Pan } 545f0ad9ea3SOwen Pan 546f0ad9ea3SOwen Pan void LeftRightQualifierAlignmentFixer::fixQualifierAlignment( 547f0ad9ea3SOwen Pan SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, FormatTokenLexer &Tokens, 548f0ad9ea3SOwen Pan tooling::Replacements &Fixes) { 549a44ab170Smydeveloperday const AdditionalKeywords &Keywords = Tokens.getKeywords(); 550a44ab170Smydeveloperday const SourceManager &SourceMgr = Env.getSourceManager(); 551a44ab170Smydeveloperday tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier); 552a44ab170Smydeveloperday assert(QualifierToken != tok::identifier && "Unrecognised Qualifier"); 553a44ab170Smydeveloperday 5544681ae93SMarek Kurdej for (AnnotatedLine *Line : AnnotatedLines) { 555f0ad9ea3SOwen Pan fixQualifierAlignment(Line->Children, Tokens, Fixes); 556f3dcd3adSColin Ogilvie if (!Line->Affected || Line->InPPDirective) 557eee536ddSowenca continue; 5584681ae93SMarek Kurdej FormatToken *First = Line->First; 559e329b586SMarek Kurdej assert(First); 560e329b586SMarek Kurdej if (First->Finalized) 561e329b586SMarek Kurdej continue; 562e329b586SMarek Kurdej 5634681ae93SMarek Kurdej const auto *Last = Line->Last; 564a44ab170Smydeveloperday 565031d3eceSmydeveloperday for (const auto *Tok = First; Tok && Tok != Last && Tok->Next; 566031d3eceSmydeveloperday Tok = Tok->Next) { 567ea16a3bbSOwen Pan if (Tok->MustBreakBefore) 568ea16a3bbSOwen Pan break; 569a44ab170Smydeveloperday if (Tok->is(tok::comment)) 570a44ab170Smydeveloperday continue; 571bebf7bdfSowenca if (RightAlign) { 572a44ab170Smydeveloperday Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier, 573a44ab170Smydeveloperday QualifierToken); 574bebf7bdfSowenca } else { 575a44ab170Smydeveloperday Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier, 576a44ab170Smydeveloperday QualifierToken); 577a44ab170Smydeveloperday } 578a44ab170Smydeveloperday } 579bebf7bdfSowenca } 580a44ab170Smydeveloperday } 581a44ab170Smydeveloperday 582899c8677SSedenion void prepareLeftRightOrderingForQualifierAlignmentFixer( 583a44ab170Smydeveloperday const std::vector<std::string> &Order, std::vector<std::string> &LeftOrder, 584a44ab170Smydeveloperday std::vector<std::string> &RightOrder, 585a44ab170Smydeveloperday std::vector<tok::TokenKind> &Qualifiers) { 586a44ab170Smydeveloperday 587a44ab170Smydeveloperday // Depending on the position of type in the order you need 588a44ab170Smydeveloperday // To iterate forward or backward through the order list as qualifier 589a44ab170Smydeveloperday // can push through each other. 590a44ab170Smydeveloperday // The Order list must define the position of "type" to signify 591e567f37dSKazu Hirata assert(llvm::is_contained(Order, "type") && 592efb284c0SDmitri Gribenko "QualifierOrder must contain type"); 593a44ab170Smydeveloperday // Split the Order list by type and reverse the left side. 594a44ab170Smydeveloperday 595a44ab170Smydeveloperday bool left = true; 596a44ab170Smydeveloperday for (const auto &s : Order) { 597a44ab170Smydeveloperday if (s == "type") { 598a44ab170Smydeveloperday left = false; 599a44ab170Smydeveloperday continue; 600a44ab170Smydeveloperday } 601a44ab170Smydeveloperday 602a44ab170Smydeveloperday tok::TokenKind QualifierToken = 603a44ab170Smydeveloperday LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s); 604d079995dSMarek Kurdej if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier) 605a44ab170Smydeveloperday Qualifiers.push_back(QualifierToken); 606a44ab170Smydeveloperday 607bebf7bdfSowenca if (left) { 608a44ab170Smydeveloperday // Reverse the order for left aligned items. 609a44ab170Smydeveloperday LeftOrder.insert(LeftOrder.begin(), s); 610bebf7bdfSowenca } else { 611a44ab170Smydeveloperday RightOrder.push_back(s); 612a44ab170Smydeveloperday } 613a44ab170Smydeveloperday } 614bebf7bdfSowenca } 615a44ab170Smydeveloperday 616364f988dSOwen Pan bool isQualifierOrType(const FormatToken *Tok, const LangOptions &LangOpts) { 617364f988dSOwen Pan return Tok && (Tok->isTypeName(LangOpts) || Tok->is(tok::kw_auto) || 618364f988dSOwen Pan isQualifier(Tok)); 619cd7ab4b5SAlexander Hederstaf } 620cd7ab4b5SAlexander Hederstaf 621c72e9438SOwen Pan bool isConfiguredQualifierOrType(const FormatToken *Tok, 622c72e9438SOwen Pan const std::vector<tok::TokenKind> &Qualifiers, 623364f988dSOwen Pan const LangOptions &LangOpts) { 624364f988dSOwen Pan return Tok && (Tok->isTypeName(LangOpts) || Tok->is(tok::kw_auto) || 625626025acSOwen Pan isConfiguredQualifier(Tok, Qualifiers)); 626a44ab170Smydeveloperday } 627a44ab170Smydeveloperday 628a44ab170Smydeveloperday // If a token is an identifier and it's upper case, it could 629a44ab170Smydeveloperday // be a macro and hence we need to be able to ignore it. 630c72e9438SOwen Pan bool isPossibleMacro(const FormatToken *Tok) { 631a44ab170Smydeveloperday if (!Tok) 632a44ab170Smydeveloperday return false; 63391c4db00SOwen Pan if (Tok->isNot(tok::identifier)) 634a44ab170Smydeveloperday return false; 635bebf7bdfSowenca if (Tok->TokenText.upper() == Tok->TokenText.str()) { 63628bb040dSmydeveloperday // T,K,U,V likely could be template arguments 637875b8811SOwen Pan return Tok->TokenText.size() != 1; 638bebf7bdfSowenca } 639a44ab170Smydeveloperday return false; 640a44ab170Smydeveloperday } 641a44ab170Smydeveloperday 642a44ab170Smydeveloperday } // namespace format 643a44ab170Smydeveloperday } // namespace clang 644