| //===--- QualifierAlignmentFixer.cpp ----------------------------*- C++--*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// This file implements QualifierAlignmentFixer, a TokenAnalyzer that |
| /// enforces either left or right const depending on the style. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "QualifierAlignmentFixer.h" |
| #include "FormatToken.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/Regex.h" |
| |
| #include <algorithm> |
| #include <optional> |
| |
| #define DEBUG_TYPE "format-qualifier-alignment-fixer" |
| |
| namespace clang { |
| namespace format { |
| |
| void addQualifierAlignmentFixerPasses(const FormatStyle &Style, |
| SmallVectorImpl<AnalyzerPass> &Passes) { |
| std::vector<std::string> LeftOrder; |
| std::vector<std::string> RightOrder; |
| std::vector<tok::TokenKind> ConfiguredQualifierTokens; |
| prepareLeftRightOrderingForQualifierAlignmentFixer( |
| Style.QualifierOrder, LeftOrder, RightOrder, ConfiguredQualifierTokens); |
| |
| // Handle the left and right alignment separately. |
| for (const auto &Qualifier : LeftOrder) { |
| Passes.emplace_back( |
| [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { |
| return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, |
| ConfiguredQualifierTokens, |
| /*RightAlign=*/false) |
| .process(); |
| }); |
| } |
| for (const auto &Qualifier : RightOrder) { |
| Passes.emplace_back( |
| [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { |
| return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, |
| ConfiguredQualifierTokens, |
| /*RightAlign=*/true) |
| .process(); |
| }); |
| } |
| } |
| |
| static void replaceToken(const SourceManager &SourceMgr, |
| tooling::Replacements &Fixes, |
| const CharSourceRange &Range, std::string NewText) { |
| auto Replacement = tooling::Replacement(SourceMgr, Range, NewText); |
| auto Err = Fixes.add(Replacement); |
| |
| if (Err) { |
| llvm::errs() << "Error while rearranging Qualifier : " |
| << llvm::toString(std::move(Err)) << "\n"; |
| } |
| } |
| |
| static void removeToken(const SourceManager &SourceMgr, |
| tooling::Replacements &Fixes, |
| const FormatToken *First) { |
| auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), |
| First->Tok.getEndLoc()); |
| replaceToken(SourceMgr, Fixes, Range, ""); |
| } |
| |
| static void insertQualifierAfter(const SourceManager &SourceMgr, |
| tooling::Replacements &Fixes, |
| const FormatToken *First, |
| const std::string &Qualifier) { |
| auto Range = CharSourceRange::getCharRange(First->Tok.getLocation(), |
| First->Tok.getEndLoc()); |
| |
| std::string NewText{}; |
| NewText += First->TokenText; |
| NewText += " " + Qualifier; |
| replaceToken(SourceMgr, Fixes, Range, NewText); |
| } |
| |
| static void insertQualifierBefore(const SourceManager &SourceMgr, |
| tooling::Replacements &Fixes, |
| const FormatToken *First, |
| const std::string &Qualifier) { |
| auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), |
| First->Tok.getEndLoc()); |
| |
| std::string NewText = " " + Qualifier + " "; |
| NewText += First->TokenText; |
| |
| replaceToken(SourceMgr, Fixes, Range, NewText); |
| } |
| |
| static bool endsWithSpace(const std::string &s) { |
| if (s.empty()) |
| return false; |
| return isspace(s.back()); |
| } |
| |
| static bool startsWithSpace(const std::string &s) { |
| if (s.empty()) |
| return false; |
| return isspace(s.front()); |
| } |
| |
| static void rotateTokens(const SourceManager &SourceMgr, |
| tooling::Replacements &Fixes, const FormatToken *First, |
| const FormatToken *Last, bool Left) { |
| auto *End = Last; |
| auto *Begin = First; |
| if (!Left) { |
| End = Last->Next; |
| Begin = First->Next; |
| } |
| |
| std::string NewText; |
| // If we are rotating to the left we move the Last token to the front. |
| if (Left) { |
| NewText += Last->TokenText; |
| NewText += " "; |
| } |
| |
| // Then move through the other tokens. |
| auto *Tok = Begin; |
| while (Tok != End) { |
| if (!NewText.empty() && !endsWithSpace(NewText)) |
| NewText += " "; |
| |
| NewText += Tok->TokenText; |
| Tok = Tok->Next; |
| } |
| |
| // If we are rotating to the right we move the first token to the back. |
| if (!Left) { |
| if (!NewText.empty() && !startsWithSpace(NewText)) |
| NewText += " "; |
| NewText += First->TokenText; |
| } |
| |
| auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), |
| Last->Tok.getEndLoc()); |
| |
| replaceToken(SourceMgr, Fixes, Range, NewText); |
| } |
| |
| static bool |
| isConfiguredQualifier(const FormatToken *const Tok, |
| const std::vector<tok::TokenKind> &Qualifiers) { |
| return Tok && llvm::is_contained(Qualifiers, Tok->Tok.getKind()); |
| } |
| |
| static bool isQualifier(const FormatToken *const Tok) { |
| if (!Tok) |
| return false; |
| |
| switch (Tok->Tok.getKind()) { |
| case tok::kw_const: |
| case tok::kw_volatile: |
| case tok::kw_static: |
| case tok::kw_inline: |
| case tok::kw_constexpr: |
| case tok::kw_restrict: |
| case tok::kw_friend: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight( |
| const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, |
| tooling::Replacements &Fixes, const FormatToken *const Tok, |
| const std::string &Qualifier, tok::TokenKind QualifierType) { |
| // We only need to think about streams that begin with a qualifier. |
| if (Tok->isNot(QualifierType)) |
| return Tok; |
| // Don't concern yourself if nothing follows the qualifier. |
| if (!Tok->Next) |
| return Tok; |
| |
| // Skip qualifiers to the left to find what preceeds the qualifiers. |
| // Use isQualifier rather than isConfiguredQualifier to cover all qualifiers. |
| const FormatToken *PreviousCheck = Tok->getPreviousNonComment(); |
| while (isQualifier(PreviousCheck)) |
| PreviousCheck = PreviousCheck->getPreviousNonComment(); |
| |
| // Examples given in order of ['type', 'const', 'volatile'] |
| const bool IsRightQualifier = PreviousCheck && [PreviousCheck]() { |
| // The cases: |
| // `Foo() const` -> `Foo() const` |
| // `Foo() const final` -> `Foo() const final` |
| // `Foo() const override` -> `Foo() const final` |
| // `Foo() const volatile override` -> `Foo() const volatile override` |
| // `Foo() volatile const final` -> `Foo() const volatile final` |
| if (PreviousCheck->is(tok::r_paren)) |
| return true; |
| |
| // The cases: |
| // `struct {} volatile const a;` -> `struct {} const volatile a;` |
| // `class {} volatile const a;` -> `class {} const volatile a;` |
| if (PreviousCheck->is(tok::r_brace)) |
| return true; |
| |
| // The case: |
| // `template <class T> const Bar Foo()` -> |
| // `template <class T> Bar const Foo()` |
| // The cases: |
| // `Foo<int> const foo` -> `Foo<int> const foo` |
| // `Foo<int> volatile const` -> `Foo<int> const volatile` |
| // The case: |
| // ``` |
| // template <class T> |
| // requires Concept1<T> && requires Concept2<T> |
| // const Foo f(); |
| // ``` |
| // -> |
| // ``` |
| // template <class T> |
| // requires Concept1<T> && requires Concept2<T> |
| // Foo const f(); |
| // ``` |
| if (PreviousCheck->is(TT_TemplateCloser)) { |
| // If the token closes a template<> or requires clause, then it is a left |
| // qualifier and should be moved to the right. |
| return !(PreviousCheck->ClosesTemplateDeclaration || |
| PreviousCheck->ClosesRequiresClause); |
| } |
| |
| // The case `Foo* const` -> `Foo* const` |
| // The case `Foo* volatile const` -> `Foo* const volatile` |
| // The case `int32_t const` -> `int32_t const` |
| // The case `auto volatile const` -> `auto const volatile` |
| if (PreviousCheck->isOneOf(TT_PointerOrReference, tok::identifier, |
| tok::kw_auto)) { |
| return true; |
| } |
| |
| return false; |
| }(); |
| |
| // Find the last qualifier to the right. |
| const FormatToken *LastQual = Tok; |
| while (isQualifier(LastQual->getNextNonComment())) |
| LastQual = LastQual->getNextNonComment(); |
| |
| // If this qualifier is to the right of a type or pointer do a partial sort |
| // and return. |
| if (IsRightQualifier) { |
| if (LastQual != Tok) |
| rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false); |
| return Tok; |
| } |
| |
| const FormatToken *TypeToken = LastQual->getNextNonComment(); |
| if (!TypeToken) |
| return Tok; |
| |
| // Stay safe and don't move past macros, also don't bother with sorting. |
| if (isPossibleMacro(TypeToken)) |
| return Tok; |
| |
| // The case `const long long int volatile` -> `long long int const volatile` |
| // The case `long const long int volatile` -> `long long int const volatile` |
| // The case `long long volatile int const` -> `long long int const volatile` |
| // The case `const long long volatile int` -> `long long int const volatile` |
| if (TypeToken->isTypeName(LangOpts)) { |
| // The case `const decltype(foo)` -> `const decltype(foo)` |
| // The case `const typeof(foo)` -> `const typeof(foo)` |
| // The case `const _Atomic(foo)` -> `const _Atomic(foo)` |
| if (TypeToken->isOneOf(tok::kw_decltype, tok::kw_typeof, tok::kw__Atomic)) |
| return Tok; |
| |
| const FormatToken *LastSimpleTypeSpecifier = TypeToken; |
| while (isQualifierOrType(LastSimpleTypeSpecifier->getNextNonComment(), |
| LangOpts)) { |
| LastSimpleTypeSpecifier = LastSimpleTypeSpecifier->getNextNonComment(); |
| } |
| |
| rotateTokens(SourceMgr, Fixes, Tok, LastSimpleTypeSpecifier, |
| /*Left=*/false); |
| return LastSimpleTypeSpecifier; |
| } |
| |
| // The case `unsigned short const` -> `unsigned short const` |
| // The case: |
| // `unsigned short volatile const` -> `unsigned short const volatile` |
| if (PreviousCheck && PreviousCheck->isTypeName(LangOpts)) { |
| if (LastQual != Tok) |
| rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false); |
| return Tok; |
| } |
| |
| // Skip the typename keyword. |
| // The case `const typename C::type` -> `typename C::type const` |
| if (TypeToken->is(tok::kw_typename)) |
| TypeToken = TypeToken->getNextNonComment(); |
| |
| // Skip the initial :: of a global-namespace type. |
| // The case `const ::...` -> `::... const` |
| if (TypeToken->is(tok::coloncolon)) { |
| // The case `const ::template Foo...` -> `::template Foo... const` |
| TypeToken = TypeToken->getNextNonComment(); |
| if (TypeToken && TypeToken->is(tok::kw_template)) |
| TypeToken = TypeToken->getNextNonComment(); |
| } |
| |
| // Don't change declarations such as |
| // `foo(const struct Foo a);` -> `foo(const struct Foo a);` |
| // as they would currently change code such as |
| // `const struct my_struct_t {} my_struct;` -> `struct my_struct_t const {} |
| // my_struct;` |
| if (TypeToken->isOneOf(tok::kw_struct, tok::kw_class)) |
| return Tok; |
| |
| if (TypeToken->isOneOf(tok::kw_auto, tok::identifier)) { |
| // The case `const auto` -> `auto const` |
| // The case `const Foo` -> `Foo const` |
| // The case `const ::Foo` -> `::Foo const` |
| // The case `const Foo *` -> `Foo const *` |
| // The case `const Foo &` -> `Foo const &` |
| // The case `const Foo &&` -> `Foo const &&` |
| // The case `const std::Foo &&` -> `std::Foo const &&` |
| // The case `const std::Foo<T> &&` -> `std::Foo<T> const &&` |
| // The case `const ::template Foo` -> `::template Foo const` |
| // The case `const T::template Foo` -> `T::template Foo const` |
| const FormatToken *Next = nullptr; |
| while ((Next = TypeToken->getNextNonComment()) && |
| (Next->is(TT_TemplateOpener) || |
| Next->startsSequence(tok::coloncolon, tok::identifier) || |
| Next->startsSequence(tok::coloncolon, tok::kw_template, |
| tok::identifier))) { |
| if (Next->is(TT_TemplateOpener)) { |
| assert(Next->MatchingParen && "Missing template closer"); |
| TypeToken = Next->MatchingParen; |
| } else if (Next->startsSequence(tok::coloncolon, tok::identifier)) { |
| TypeToken = Next->getNextNonComment(); |
| } else { |
| TypeToken = Next->getNextNonComment()->getNextNonComment(); |
| } |
| } |
| |
| if (Next && Next->is(tok::kw_auto)) |
| TypeToken = Next; |
| |
| // Place the Qualifier at the end of the list of qualifiers. |
| while (isQualifier(TypeToken->getNextNonComment())) { |
| // The case `volatile Foo::iter const` -> `Foo::iter const volatile` |
| TypeToken = TypeToken->getNextNonComment(); |
| } |
| |
| insertQualifierAfter(SourceMgr, Fixes, TypeToken, Qualifier); |
| // Remove token and following whitespace. |
| auto Range = CharSourceRange::getCharRange( |
| Tok->getStartOfNonWhitespace(), Tok->Next->getStartOfNonWhitespace()); |
| replaceToken(SourceMgr, Fixes, Range, ""); |
| } |
| |
| return Tok; |
| } |
| |
| const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft( |
| const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, |
| tooling::Replacements &Fixes, const FormatToken *const Tok, |
| const std::string &Qualifier, tok::TokenKind QualifierType) { |
| // We only need to think about streams that begin with a qualifier. |
| if (Tok->isNot(QualifierType)) |
| return Tok; |
| // Don't concern yourself if nothing preceeds the qualifier. |
| if (!Tok->getPreviousNonComment()) |
| return Tok; |
| |
| // Skip qualifiers to the left to find what preceeds the qualifiers. |
| const FormatToken *TypeToken = Tok->getPreviousNonComment(); |
| while (isQualifier(TypeToken)) |
| TypeToken = TypeToken->getPreviousNonComment(); |
| |
| // For left qualifiers preceeded by nothing, a template declaration, or *,&,&& |
| // we only perform sorting. |
| if (!TypeToken || TypeToken->isPointerOrReference() || |
| TypeToken->ClosesRequiresClause || TypeToken->ClosesTemplateDeclaration || |
| TypeToken->is(tok::r_square)) { |
| |
| // Don't sort past a non-configured qualifier token. |
| const FormatToken *FirstQual = Tok; |
| while (isConfiguredQualifier(FirstQual->getPreviousNonComment(), |
| ConfiguredQualifierTokens)) { |
| FirstQual = FirstQual->getPreviousNonComment(); |
| } |
| |
| if (FirstQual != Tok) |
| rotateTokens(SourceMgr, Fixes, FirstQual, Tok, /*Left=*/true); |
| return Tok; |
| } |
| |
| // Stay safe and don't move past macros, also don't bother with sorting. |
| if (isPossibleMacro(TypeToken)) |
| return Tok; |
| |
| // Examples given in order of ['const', 'volatile', 'type'] |
| |
| // The case `volatile long long int const` -> `const volatile long long int` |
| // The case `volatile long long const int` -> `const volatile long long int` |
| // The case `const long long volatile int` -> `const volatile long long int` |
| // The case `long volatile long int const` -> `const volatile long long int` |
| if (TypeToken->isTypeName(LangOpts)) { |
| const FormatToken *LastSimpleTypeSpecifier = TypeToken; |
| while (isConfiguredQualifierOrType( |
| LastSimpleTypeSpecifier->getPreviousNonComment(), |
| ConfiguredQualifierTokens, LangOpts)) { |
| LastSimpleTypeSpecifier = |
| LastSimpleTypeSpecifier->getPreviousNonComment(); |
| } |
| |
| rotateTokens(SourceMgr, Fixes, LastSimpleTypeSpecifier, Tok, |
| /*Left=*/true); |
| return Tok; |
| } |
| |
| if (TypeToken->isOneOf(tok::kw_auto, tok::identifier, TT_TemplateCloser)) { |
| const auto IsStartOfType = [](const FormatToken *const Tok) -> bool { |
| if (!Tok) |
| return true; |
| |
| // A template closer is not the start of a type. |
| // The case `?<> const` -> `const ?<>` |
| if (Tok->is(TT_TemplateCloser)) |
| return false; |
| |
| const FormatToken *const Previous = Tok->getPreviousNonComment(); |
| if (!Previous) |
| return true; |
| |
| // An identifier preceeded by :: is not the start of a type. |
| // The case `?::Foo const` -> `const ?::Foo` |
| if (Tok->is(tok::identifier) && Previous->is(tok::coloncolon)) |
| return false; |
| |
| const FormatToken *const PrePrevious = Previous->getPreviousNonComment(); |
| // An identifier preceeded by ::template is not the start of a type. |
| // The case `?::template Foo const` -> `const ?::template Foo` |
| if (Tok->is(tok::identifier) && Previous->is(tok::kw_template) && |
| PrePrevious && PrePrevious->is(tok::coloncolon)) { |
| return false; |
| } |
| |
| if (Tok->endsSequence(tok::kw_auto, tok::identifier)) |
| return false; |
| |
| return true; |
| }; |
| |
| while (!IsStartOfType(TypeToken)) { |
| // The case `?<>` |
| if (TypeToken->is(TT_TemplateCloser)) { |
| assert(TypeToken->MatchingParen && "Missing template opener"); |
| TypeToken = TypeToken->MatchingParen->getPreviousNonComment(); |
| } else { |
| // The cases |
| // `::Foo` |
| // `?>::Foo` |
| // `?Bar::Foo` |
| // `::template Foo` |
| // `?>::template Foo` |
| // `?Bar::template Foo` |
| if (TypeToken->getPreviousNonComment()->is(tok::kw_template)) |
| TypeToken = TypeToken->getPreviousNonComment(); |
| |
| const FormatToken *const ColonColon = |
| TypeToken->getPreviousNonComment(); |
| const FormatToken *const PreColonColon = |
| ColonColon->getPreviousNonComment(); |
| if (PreColonColon && |
| PreColonColon->isOneOf(TT_TemplateCloser, tok::identifier)) { |
| TypeToken = PreColonColon; |
| } else { |
| TypeToken = ColonColon; |
| } |
| } |
| } |
| |
| assert(TypeToken && "Should be auto or identifier"); |
| |
| // Place the Qualifier at the start of the list of qualifiers. |
| const FormatToken *Previous = nullptr; |
| while ((Previous = TypeToken->getPreviousNonComment()) && |
| (isConfiguredQualifier(Previous, ConfiguredQualifierTokens) || |
| Previous->is(tok::kw_typename))) { |
| // The case `volatile Foo::iter const` -> `const volatile Foo::iter` |
| // The case `typename C::type const` -> `const typename C::type` |
| TypeToken = Previous; |
| } |
| |
| // Don't change declarations such as |
| // `foo(struct Foo const a);` -> `foo(struct Foo const a);` |
| if (!Previous || !Previous->isOneOf(tok::kw_struct, tok::kw_class)) { |
| insertQualifierBefore(SourceMgr, Fixes, TypeToken, Qualifier); |
| removeToken(SourceMgr, Fixes, Tok); |
| } |
| } |
| |
| return Tok; |
| } |
| |
| tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier( |
| const std::string &Qualifier) { |
| // Don't let 'type' be an identifier, but steal typeof token. |
| return llvm::StringSwitch<tok::TokenKind>(Qualifier) |
| .Case("type", tok::kw_typeof) |
| .Case("const", tok::kw_const) |
| .Case("volatile", tok::kw_volatile) |
| .Case("static", tok::kw_static) |
| .Case("inline", tok::kw_inline) |
| .Case("constexpr", tok::kw_constexpr) |
| .Case("restrict", tok::kw_restrict) |
| .Case("friend", tok::kw_friend) |
| .Default(tok::identifier); |
| } |
| |
| LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer( |
| const Environment &Env, const FormatStyle &Style, |
| const std::string &Qualifier, |
| const std::vector<tok::TokenKind> &QualifierTokens, bool RightAlign) |
| : TokenAnalyzer(Env, Style), Qualifier(Qualifier), RightAlign(RightAlign), |
| ConfiguredQualifierTokens(QualifierTokens) {} |
| |
| std::pair<tooling::Replacements, unsigned> |
| LeftRightQualifierAlignmentFixer::analyze( |
| TokenAnnotator & /*Annotator*/, |
| SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, |
| FormatTokenLexer &Tokens) { |
| tooling::Replacements Fixes; |
| AffectedRangeMgr.computeAffectedLines(AnnotatedLines); |
| fixQualifierAlignment(AnnotatedLines, Tokens, Fixes); |
| return {Fixes, 0}; |
| } |
| |
| void LeftRightQualifierAlignmentFixer::fixQualifierAlignment( |
| SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, FormatTokenLexer &Tokens, |
| tooling::Replacements &Fixes) { |
| const AdditionalKeywords &Keywords = Tokens.getKeywords(); |
| const SourceManager &SourceMgr = Env.getSourceManager(); |
| tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier); |
| assert(QualifierToken != tok::identifier && "Unrecognised Qualifier"); |
| |
| for (AnnotatedLine *Line : AnnotatedLines) { |
| fixQualifierAlignment(Line->Children, Tokens, Fixes); |
| if (!Line->Affected || Line->InPPDirective) |
| continue; |
| FormatToken *First = Line->First; |
| assert(First); |
| if (First->Finalized) |
| continue; |
| |
| const auto *Last = Line->Last; |
| |
| for (const auto *Tok = First; Tok && Tok != Last && Tok->Next; |
| Tok = Tok->Next) { |
| if (Tok->MustBreakBefore) |
| break; |
| if (Tok->is(tok::comment)) |
| continue; |
| if (RightAlign) { |
| Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier, |
| QualifierToken); |
| } else { |
| Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier, |
| QualifierToken); |
| } |
| } |
| } |
| } |
| |
| void prepareLeftRightOrderingForQualifierAlignmentFixer( |
| const std::vector<std::string> &Order, std::vector<std::string> &LeftOrder, |
| std::vector<std::string> &RightOrder, |
| std::vector<tok::TokenKind> &Qualifiers) { |
| |
| // Depending on the position of type in the order you need |
| // To iterate forward or backward through the order list as qualifier |
| // can push through each other. |
| // The Order list must define the position of "type" to signify |
| assert(llvm::is_contained(Order, "type") && |
| "QualifierOrder must contain type"); |
| // Split the Order list by type and reverse the left side. |
| |
| bool left = true; |
| for (const auto &s : Order) { |
| if (s == "type") { |
| left = false; |
| continue; |
| } |
| |
| tok::TokenKind QualifierToken = |
| LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s); |
| if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier) |
| Qualifiers.push_back(QualifierToken); |
| |
| if (left) { |
| // Reverse the order for left aligned items. |
| LeftOrder.insert(LeftOrder.begin(), s); |
| } else { |
| RightOrder.push_back(s); |
| } |
| } |
| } |
| |
| bool isQualifierOrType(const FormatToken *Tok, const LangOptions &LangOpts) { |
| return Tok && (Tok->isTypeName(LangOpts) || Tok->is(tok::kw_auto) || |
| isQualifier(Tok)); |
| } |
| |
| bool isConfiguredQualifierOrType(const FormatToken *Tok, |
| const std::vector<tok::TokenKind> &Qualifiers, |
| const LangOptions &LangOpts) { |
| return Tok && (Tok->isTypeName(LangOpts) || Tok->is(tok::kw_auto) || |
| isConfiguredQualifier(Tok, Qualifiers)); |
| } |
| |
| // If a token is an identifier and it's upper case, it could |
| // be a macro and hence we need to be able to ignore it. |
| bool isPossibleMacro(const FormatToken *Tok) { |
| if (!Tok) |
| return false; |
| if (Tok->isNot(tok::identifier)) |
| return false; |
| if (Tok->TokenText.upper() == Tok->TokenText.str()) { |
| // T,K,U,V likely could be template arguments |
| return Tok->TokenText.size() != 1; |
| } |
| return false; |
| } |
| |
| } // namespace format |
| } // namespace clang |