123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- //===--- LeftRightQualifierAlignmentFixer.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 LeftRightQualifierAlignmentFixer, 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 {
- QualifierAlignmentFixer::QualifierAlignmentFixer(
- const Environment &Env, const FormatStyle &Style, StringRef &Code,
- ArrayRef<tooling::Range> Ranges, unsigned FirstStartColumn,
- unsigned NextStartColumn, unsigned LastStartColumn, StringRef FileName)
- : TokenAnalyzer(Env, Style), Code(Code), Ranges(Ranges),
- FirstStartColumn(FirstStartColumn), NextStartColumn(NextStartColumn),
- LastStartColumn(LastStartColumn), FileName(FileName) {
- std::vector<std::string> LeftOrder;
- std::vector<std::string> RightOrder;
- std::vector<tok::TokenKind> ConfiguredQualifierTokens;
- PrepareLeftRightOrdering(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();
- });
- }
- }
- std::pair<tooling::Replacements, unsigned> QualifierAlignmentFixer::analyze(
- TokenAnnotator & /*Annotator*/,
- SmallVectorImpl<AnnotatedLine *> & /*AnnotatedLines*/,
- FormatTokenLexer & /*Tokens*/) {
- auto Env = Environment::make(Code, FileName, Ranges, FirstStartColumn,
- NextStartColumn, LastStartColumn);
- if (!Env)
- return {};
- std::optional<std::string> CurrentCode;
- tooling::Replacements Fixes;
- for (size_t I = 0, E = Passes.size(); I < E; ++I) {
- std::pair<tooling::Replacements, unsigned> PassFixes = Passes[I](*Env);
- auto NewCode = applyAllReplacements(
- CurrentCode ? StringRef(*CurrentCode) : Code, PassFixes.first);
- if (NewCode) {
- Fixes = Fixes.merge(PassFixes.first);
- if (I + 1 < E) {
- CurrentCode = std::move(*NewCode);
- Env = Environment::make(
- *CurrentCode, FileName,
- tooling::calculateRangesAfterReplacements(Fixes, Ranges),
- FirstStartColumn, NextStartColumn, LastStartColumn);
- if (!Env)
- return {};
- }
- }
- }
- // Don't make replacements that replace nothing.
- tooling::Replacements NonNoOpFixes;
- for (const tooling::Replacement &Fix : Fixes) {
- StringRef OriginalCode = Code.substr(Fix.getOffset(), Fix.getLength());
- if (!OriginalCode.equals(Fix.getReplacementText())) {
- auto Err = NonNoOpFixes.add(Fix);
- if (Err) {
- llvm::errs() << "Error adding replacements : "
- << llvm::toString(std::move(Err)) << "\n";
- }
- }
- }
- return {NonNoOpFixes, 0};
- }
- 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) {
- FormatToken *Next = First->Next;
- if (!Next)
- return;
- auto Range = CharSourceRange::getCharRange(Next->getStartOfNonWhitespace(),
- Next->Tok.getEndLoc());
- std::string NewText = " " + Qualifier + " ";
- NewText += Next->TokenText;
- 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);
- }
- const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(
- const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
- tooling::Replacements &Fixes, const FormatToken *Tok,
- const std::string &Qualifier, tok::TokenKind QualifierType) {
- // We only need to think about streams that begin with a qualifier.
- if (!Tok->is(QualifierType))
- return Tok;
- // Don't concern yourself if nothing follows the qualifier.
- if (!Tok->Next)
- return Tok;
- if (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok->Next))
- return Tok;
- auto AnalyzeTemplate =
- [&](const FormatToken *Tok,
- const FormatToken *StartTemplate) -> const FormatToken * {
- // Read from the TemplateOpener to TemplateCloser.
- FormatToken *EndTemplate = StartTemplate->MatchingParen;
- if (EndTemplate) {
- // Move to the end of any template class members e.g.
- // `Foo<int>::iterator`.
- if (EndTemplate->startsSequence(TT_TemplateCloser, tok::coloncolon,
- tok::identifier)) {
- EndTemplate = EndTemplate->Next->Next;
- }
- }
- if (EndTemplate && EndTemplate->Next &&
- !EndTemplate->Next->isOneOf(tok::equal, tok::l_paren)) {
- insertQualifierAfter(SourceMgr, Fixes, EndTemplate, Qualifier);
- // Remove the qualifier.
- removeToken(SourceMgr, Fixes, Tok);
- return Tok;
- }
- return nullptr;
- };
- FormatToken *Qual = Tok->Next;
- FormatToken *LastQual = Qual;
- while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) {
- LastQual = Qual;
- Qual = Qual->Next;
- }
- if (LastQual && Qual != LastQual) {
- rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false);
- Tok = LastQual;
- } else if (Tok->startsSequence(QualifierType, tok::identifier,
- TT_TemplateCloser)) {
- FormatToken *Closer = Tok->Next->Next;
- rotateTokens(SourceMgr, Fixes, Tok, Tok->Next, /*Left=*/false);
- Tok = Closer;
- return Tok;
- } else if (Tok->startsSequence(QualifierType, tok::identifier,
- TT_TemplateOpener)) {
- // `const ArrayRef<int> a;`
- // `const ArrayRef<int> &a;`
- const FormatToken *NewTok = AnalyzeTemplate(Tok, Tok->Next->Next);
- if (NewTok)
- return NewTok;
- } else if (Tok->startsSequence(QualifierType, tok::coloncolon,
- tok::identifier, TT_TemplateOpener)) {
- // `const ::ArrayRef<int> a;`
- // `const ::ArrayRef<int> &a;`
- const FormatToken *NewTok = AnalyzeTemplate(Tok, Tok->Next->Next->Next);
- if (NewTok)
- return NewTok;
- } else if (Tok->startsSequence(QualifierType, tok::identifier) ||
- Tok->startsSequence(QualifierType, tok::coloncolon,
- tok::identifier)) {
- FormatToken *Next = Tok->Next;
- // 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 &&`
- // However, `const Bar::*` remains the same.
- while (Next && Next->isOneOf(tok::identifier, tok::coloncolon) &&
- !Next->startsSequence(tok::coloncolon, tok::star)) {
- Next = Next->Next;
- }
- if (Next && Next->is(TT_TemplateOpener)) {
- Next = Next->MatchingParen;
- // Move to the end of any template class members e.g.
- // `Foo<int>::iterator`.
- if (Next && Next->startsSequence(TT_TemplateCloser, tok::coloncolon,
- tok::identifier)) {
- return Tok;
- }
- assert(Next && "Missing template opener");
- Next = Next->Next;
- }
- if (Next && Next->isOneOf(tok::star, tok::amp, tok::ampamp) &&
- !Tok->Next->isOneOf(Keywords.kw_override, Keywords.kw_final)) {
- if (Next->Previous && !Next->Previous->is(QualifierType)) {
- insertQualifierAfter(SourceMgr, Fixes, Next->Previous, Qualifier);
- removeToken(SourceMgr, Fixes, Tok);
- }
- return Next;
- }
- }
- return Tok;
- }
- const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft(
- const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
- tooling::Replacements &Fixes, const FormatToken *Tok,
- const std::string &Qualifier, tok::TokenKind QualifierType) {
- // if Tok is an identifier and possibly a macro then don't convert.
- if (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok))
- return Tok;
- const FormatToken *Qual = Tok;
- const FormatToken *LastQual = Qual;
- while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) {
- LastQual = Qual;
- Qual = Qual->Next;
- if (Qual && Qual->is(QualifierType))
- break;
- }
- if (!Qual)
- return Tok;
- if (LastQual && Qual != LastQual && Qual->is(QualifierType)) {
- rotateTokens(SourceMgr, Fixes, Tok, Qual, /*Left=*/true);
- if (!Qual->Next)
- return Tok;
- Tok = Qual->Next;
- } else if (Tok->startsSequence(tok::identifier, QualifierType)) {
- if (Tok->Next->Next && Tok->Next->Next->isOneOf(tok::identifier, tok::star,
- tok::amp, tok::ampamp)) {
- // Don't swap `::iterator const` to `::const iterator`.
- if (!Tok->Previous ||
- (Tok->Previous && !Tok->Previous->is(tok::coloncolon))) {
- rotateTokens(SourceMgr, Fixes, Tok, Tok->Next, /*Left=*/true);
- Tok = Tok->Next;
- }
- } else if (Tok->startsSequence(tok::identifier, QualifierType,
- TT_TemplateCloser)) {
- FormatToken *Closer = Tok->Next->Next;
- rotateTokens(SourceMgr, Fixes, Tok, Tok->Next, /*Left=*/true);
- Tok = Closer;
- }
- }
- if (Tok->is(TT_TemplateOpener) && Tok->Next &&
- (Tok->Next->is(tok::identifier) || Tok->Next->isSimpleTypeSpecifier()) &&
- Tok->Next->Next && Tok->Next->Next->is(QualifierType)) {
- rotateTokens(SourceMgr, Fixes, Tok->Next, Tok->Next->Next, /*Left=*/true);
- }
- if ((Tok->startsSequence(tok::coloncolon, tok::identifier) ||
- Tok->is(tok::identifier)) &&
- Tok->Next) {
- if (Tok->Previous &&
- Tok->Previous->isOneOf(tok::star, tok::ampamp, tok::amp)) {
- return Tok;
- }
- const FormatToken *Next = Tok->Next;
- // The case `std::Foo<T> const` -> `const std::Foo<T> &&`
- while (Next && Next->isOneOf(tok::identifier, tok::coloncolon))
- Next = Next->Next;
- if (Next && Next->Previous &&
- Next->Previous->startsSequence(tok::identifier, TT_TemplateOpener)) {
- // Read from to the end of the TemplateOpener to
- // TemplateCloser const ArrayRef<int> a; const ArrayRef<int> &a;
- if (Next->is(tok::comment) && Next->getNextNonComment())
- Next = Next->getNextNonComment();
- assert(Next->MatchingParen && "Missing template closer");
- Next = Next->MatchingParen;
- // If the template closer is closing the requires clause,
- // then stop and go back to the TemplateOpener and do whatever is
- // inside the <>.
- if (Next->ClosesRequiresClause)
- return Next->MatchingParen;
- Next = Next->Next;
- // Move to the end of any template class members e.g.
- // `Foo<int>::iterator`.
- if (Next && Next->startsSequence(tok::coloncolon, tok::identifier))
- Next = Next->Next->Next;
- if (Next && Next->is(QualifierType)) {
- // Move the qualifier.
- insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier);
- removeToken(SourceMgr, Fixes, Next);
- return Next;
- }
- }
- if (Next && Next->Next &&
- Next->Next->isOneOf(tok::amp, tok::ampamp, tok::star)) {
- if (Next->is(QualifierType)) {
- // Move the qualifier.
- insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier);
- removeToken(SourceMgr, Fixes, Next);
- return Next;
- }
- }
- }
- 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;
- const AdditionalKeywords &Keywords = Tokens.getKeywords();
- const SourceManager &SourceMgr = Env.getSourceManager();
- AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
- tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier);
- assert(QualifierToken != tok::identifier && "Unrecognised Qualifier");
- for (AnnotatedLine *Line : AnnotatedLines) {
- if (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->is(tok::comment))
- continue;
- if (RightAlign) {
- Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier,
- QualifierToken);
- } else {
- Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier,
- QualifierToken);
- }
- }
- }
- return {Fixes, 0};
- }
- void QualifierAlignmentFixer::PrepareLeftRightOrdering(
- 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 LeftRightQualifierAlignmentFixer::isQualifierOrType(
- const FormatToken *Tok, const std::vector<tok::TokenKind> &specifiedTypes) {
- return Tok && (Tok->isSimpleTypeSpecifier() || Tok->is(tok::kw_auto) ||
- llvm::is_contained(specifiedTypes, Tok->Tok.getKind()));
- }
- // 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 LeftRightQualifierAlignmentFixer::isPossibleMacro(const FormatToken *Tok) {
- if (!Tok)
- return false;
- if (!Tok->is(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
|