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