Path: blob/main/contrib/llvm-project/clang/lib/ARCMigrate/TransformActions.cpp
35236 views
//===-- TransformActions.cpp - Migration to ARC mode ----------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "Internals.h"9#include "clang/AST/ASTContext.h"10#include "clang/AST/Expr.h"11#include "clang/Basic/SourceManager.h"12#include "clang/Lex/Preprocessor.h"13#include "llvm/ADT/DenseSet.h"14#include <map>15using namespace clang;16using namespace arcmt;1718namespace {1920/// Collects transformations and merges them before applying them with21/// with applyRewrites(). E.g. if the same source range22/// is requested to be removed twice, only one rewriter remove will be invoked.23/// Rewrites happen in "transactions"; if one rewrite in the transaction cannot24/// be done (e.g. it resides in a macro) all rewrites in the transaction are25/// aborted.26/// FIXME: "Transactional" rewrites support should be baked in the Rewriter.27class TransformActionsImpl {28CapturedDiagList &CapturedDiags;29ASTContext &Ctx;30Preprocessor &PP;3132bool IsInTransaction;3334enum ActionKind {35Act_Insert, Act_InsertAfterToken,36Act_Remove, Act_RemoveStmt,37Act_Replace, Act_ReplaceText,38Act_IncreaseIndentation,39Act_ClearDiagnostic40};4142struct ActionData {43ActionKind Kind;44SourceLocation Loc;45SourceRange R1, R2;46StringRef Text1, Text2;47Stmt *S;48SmallVector<unsigned, 2> DiagIDs;49};5051std::vector<ActionData> CachedActions;5253enum RangeComparison {54Range_Before,55Range_After,56Range_Contains,57Range_Contained,58Range_ExtendsBegin,59Range_ExtendsEnd60};6162/// A range to remove. It is a character range.63struct CharRange {64FullSourceLoc Begin, End;6566CharRange(CharSourceRange range, SourceManager &srcMgr, Preprocessor &PP) {67SourceLocation beginLoc = range.getBegin(), endLoc = range.getEnd();68assert(beginLoc.isValid() && endLoc.isValid());69if (range.isTokenRange()) {70Begin = FullSourceLoc(srcMgr.getExpansionLoc(beginLoc), srcMgr);71End = FullSourceLoc(getLocForEndOfToken(endLoc, srcMgr, PP), srcMgr);72} else {73Begin = FullSourceLoc(srcMgr.getExpansionLoc(beginLoc), srcMgr);74End = FullSourceLoc(srcMgr.getExpansionLoc(endLoc), srcMgr);75}76assert(Begin.isValid() && End.isValid());77}7879RangeComparison compareWith(const CharRange &RHS) const {80if (End.isBeforeInTranslationUnitThan(RHS.Begin))81return Range_Before;82if (RHS.End.isBeforeInTranslationUnitThan(Begin))83return Range_After;84if (!Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&85!RHS.End.isBeforeInTranslationUnitThan(End))86return Range_Contained;87if (Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&88RHS.End.isBeforeInTranslationUnitThan(End))89return Range_Contains;90if (Begin.isBeforeInTranslationUnitThan(RHS.Begin))91return Range_ExtendsBegin;92else93return Range_ExtendsEnd;94}9596static RangeComparison compare(SourceRange LHS, SourceRange RHS,97SourceManager &SrcMgr, Preprocessor &PP) {98return CharRange(CharSourceRange::getTokenRange(LHS), SrcMgr, PP)99.compareWith(CharRange(CharSourceRange::getTokenRange(RHS),100SrcMgr, PP));101}102};103104typedef SmallVector<StringRef, 2> TextsVec;105typedef std::map<FullSourceLoc, TextsVec, FullSourceLoc::BeforeThanCompare>106InsertsMap;107InsertsMap Inserts;108/// A list of ranges to remove. They are always sorted and they never109/// intersect with each other.110std::list<CharRange> Removals;111112llvm::DenseSet<Stmt *> StmtRemovals;113114std::vector<std::pair<CharRange, SourceLocation> > IndentationRanges;115116/// Keeps text passed to transformation methods.117llvm::StringMap<bool> UniqueText;118119public:120TransformActionsImpl(CapturedDiagList &capturedDiags,121ASTContext &ctx, Preprocessor &PP)122: CapturedDiags(capturedDiags), Ctx(ctx), PP(PP), IsInTransaction(false) { }123124ASTContext &getASTContext() { return Ctx; }125126void startTransaction();127bool commitTransaction();128void abortTransaction();129130bool isInTransaction() const { return IsInTransaction; }131132void insert(SourceLocation loc, StringRef text);133void insertAfterToken(SourceLocation loc, StringRef text);134void remove(SourceRange range);135void removeStmt(Stmt *S);136void replace(SourceRange range, StringRef text);137void replace(SourceRange range, SourceRange replacementRange);138void replaceStmt(Stmt *S, StringRef text);139void replaceText(SourceLocation loc, StringRef text,140StringRef replacementText);141void increaseIndentation(SourceRange range,142SourceLocation parentIndent);143144bool clearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);145146void applyRewrites(TransformActions::RewriteReceiver &receiver);147148private:149bool canInsert(SourceLocation loc);150bool canInsertAfterToken(SourceLocation loc);151bool canRemoveRange(SourceRange range);152bool canReplaceRange(SourceRange range, SourceRange replacementRange);153bool canReplaceText(SourceLocation loc, StringRef text);154155void commitInsert(SourceLocation loc, StringRef text);156void commitInsertAfterToken(SourceLocation loc, StringRef text);157void commitRemove(SourceRange range);158void commitRemoveStmt(Stmt *S);159void commitReplace(SourceRange range, SourceRange replacementRange);160void commitReplaceText(SourceLocation loc, StringRef text,161StringRef replacementText);162void commitIncreaseIndentation(SourceRange range,SourceLocation parentIndent);163void commitClearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);164165void addRemoval(CharSourceRange range);166void addInsertion(SourceLocation loc, StringRef text);167168/// Stores text passed to the transformation methods to keep the string169/// "alive". Since the vast majority of text will be the same, we also unique170/// the strings using a StringMap.171StringRef getUniqueText(StringRef text);172173/// Computes the source location just past the end of the token at174/// the given source location. If the location points at a macro, the whole175/// macro expansion is skipped.176static SourceLocation getLocForEndOfToken(SourceLocation loc,177SourceManager &SM,Preprocessor &PP);178};179180} // anonymous namespace181182void TransformActionsImpl::startTransaction() {183assert(!IsInTransaction &&184"Cannot start a transaction in the middle of another one");185IsInTransaction = true;186}187188bool TransformActionsImpl::commitTransaction() {189assert(IsInTransaction && "No transaction started");190191if (CachedActions.empty()) {192IsInTransaction = false;193return false;194}195196// Verify that all actions are possible otherwise abort the whole transaction.197bool AllActionsPossible = true;198for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) {199ActionData &act = CachedActions[i];200switch (act.Kind) {201case Act_Insert:202if (!canInsert(act.Loc))203AllActionsPossible = false;204break;205case Act_InsertAfterToken:206if (!canInsertAfterToken(act.Loc))207AllActionsPossible = false;208break;209case Act_Remove:210if (!canRemoveRange(act.R1))211AllActionsPossible = false;212break;213case Act_RemoveStmt:214assert(act.S);215if (!canRemoveRange(act.S->getSourceRange()))216AllActionsPossible = false;217break;218case Act_Replace:219if (!canReplaceRange(act.R1, act.R2))220AllActionsPossible = false;221break;222case Act_ReplaceText:223if (!canReplaceText(act.Loc, act.Text1))224AllActionsPossible = false;225break;226case Act_IncreaseIndentation:227// This is not important, we don't care if it will fail.228break;229case Act_ClearDiagnostic:230// We are just checking source rewrites.231break;232}233if (!AllActionsPossible)234break;235}236237if (!AllActionsPossible) {238abortTransaction();239return true;240}241242for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) {243ActionData &act = CachedActions[i];244switch (act.Kind) {245case Act_Insert:246commitInsert(act.Loc, act.Text1);247break;248case Act_InsertAfterToken:249commitInsertAfterToken(act.Loc, act.Text1);250break;251case Act_Remove:252commitRemove(act.R1);253break;254case Act_RemoveStmt:255commitRemoveStmt(act.S);256break;257case Act_Replace:258commitReplace(act.R1, act.R2);259break;260case Act_ReplaceText:261commitReplaceText(act.Loc, act.Text1, act.Text2);262break;263case Act_IncreaseIndentation:264commitIncreaseIndentation(act.R1, act.Loc);265break;266case Act_ClearDiagnostic:267commitClearDiagnostic(act.DiagIDs, act.R1);268break;269}270}271272CachedActions.clear();273IsInTransaction = false;274return false;275}276277void TransformActionsImpl::abortTransaction() {278assert(IsInTransaction && "No transaction started");279CachedActions.clear();280IsInTransaction = false;281}282283void TransformActionsImpl::insert(SourceLocation loc, StringRef text) {284assert(IsInTransaction && "Actions only allowed during a transaction");285text = getUniqueText(text);286ActionData data;287data.Kind = Act_Insert;288data.Loc = loc;289data.Text1 = text;290CachedActions.push_back(data);291}292293void TransformActionsImpl::insertAfterToken(SourceLocation loc, StringRef text) {294assert(IsInTransaction && "Actions only allowed during a transaction");295text = getUniqueText(text);296ActionData data;297data.Kind = Act_InsertAfterToken;298data.Loc = loc;299data.Text1 = text;300CachedActions.push_back(data);301}302303void TransformActionsImpl::remove(SourceRange range) {304assert(IsInTransaction && "Actions only allowed during a transaction");305ActionData data;306data.Kind = Act_Remove;307data.R1 = range;308CachedActions.push_back(data);309}310311void TransformActionsImpl::removeStmt(Stmt *S) {312assert(IsInTransaction && "Actions only allowed during a transaction");313ActionData data;314data.Kind = Act_RemoveStmt;315if (auto *E = dyn_cast<Expr>(S))316S = E->IgnoreImplicit(); // important for uniquing317data.S = S;318CachedActions.push_back(data);319}320321void TransformActionsImpl::replace(SourceRange range, StringRef text) {322assert(IsInTransaction && "Actions only allowed during a transaction");323text = getUniqueText(text);324remove(range);325insert(range.getBegin(), text);326}327328void TransformActionsImpl::replace(SourceRange range,329SourceRange replacementRange) {330assert(IsInTransaction && "Actions only allowed during a transaction");331ActionData data;332data.Kind = Act_Replace;333data.R1 = range;334data.R2 = replacementRange;335CachedActions.push_back(data);336}337338void TransformActionsImpl::replaceText(SourceLocation loc, StringRef text,339StringRef replacementText) {340text = getUniqueText(text);341replacementText = getUniqueText(replacementText);342ActionData data;343data.Kind = Act_ReplaceText;344data.Loc = loc;345data.Text1 = text;346data.Text2 = replacementText;347CachedActions.push_back(data);348}349350void TransformActionsImpl::replaceStmt(Stmt *S, StringRef text) {351assert(IsInTransaction && "Actions only allowed during a transaction");352text = getUniqueText(text);353insert(S->getBeginLoc(), text);354removeStmt(S);355}356357void TransformActionsImpl::increaseIndentation(SourceRange range,358SourceLocation parentIndent) {359if (range.isInvalid()) return;360assert(IsInTransaction && "Actions only allowed during a transaction");361ActionData data;362data.Kind = Act_IncreaseIndentation;363data.R1 = range;364data.Loc = parentIndent;365CachedActions.push_back(data);366}367368bool TransformActionsImpl::clearDiagnostic(ArrayRef<unsigned> IDs,369SourceRange range) {370assert(IsInTransaction && "Actions only allowed during a transaction");371if (!CapturedDiags.hasDiagnostic(IDs, range))372return false;373374ActionData data;375data.Kind = Act_ClearDiagnostic;376data.R1 = range;377data.DiagIDs.append(IDs.begin(), IDs.end());378CachedActions.push_back(data);379return true;380}381382bool TransformActionsImpl::canInsert(SourceLocation loc) {383if (loc.isInvalid())384return false;385386SourceManager &SM = Ctx.getSourceManager();387if (SM.isInSystemHeader(SM.getExpansionLoc(loc)))388return false;389390if (loc.isFileID())391return true;392return PP.isAtStartOfMacroExpansion(loc);393}394395bool TransformActionsImpl::canInsertAfterToken(SourceLocation loc) {396if (loc.isInvalid())397return false;398399SourceManager &SM = Ctx.getSourceManager();400if (SM.isInSystemHeader(SM.getExpansionLoc(loc)))401return false;402403if (loc.isFileID())404return true;405return PP.isAtEndOfMacroExpansion(loc);406}407408bool TransformActionsImpl::canRemoveRange(SourceRange range) {409return canInsert(range.getBegin()) && canInsertAfterToken(range.getEnd());410}411412bool TransformActionsImpl::canReplaceRange(SourceRange range,413SourceRange replacementRange) {414return canRemoveRange(range) && canRemoveRange(replacementRange);415}416417bool TransformActionsImpl::canReplaceText(SourceLocation loc, StringRef text) {418if (!canInsert(loc))419return false;420421SourceManager &SM = Ctx.getSourceManager();422loc = SM.getExpansionLoc(loc);423424// Break down the source location.425std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);426427// Try to load the file buffer.428bool invalidTemp = false;429StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);430if (invalidTemp)431return false;432433return file.substr(locInfo.second).starts_with(text);434}435436void TransformActionsImpl::commitInsert(SourceLocation loc, StringRef text) {437addInsertion(loc, text);438}439440void TransformActionsImpl::commitInsertAfterToken(SourceLocation loc,441StringRef text) {442addInsertion(getLocForEndOfToken(loc, Ctx.getSourceManager(), PP), text);443}444445void TransformActionsImpl::commitRemove(SourceRange range) {446addRemoval(CharSourceRange::getTokenRange(range));447}448449void TransformActionsImpl::commitRemoveStmt(Stmt *S) {450assert(S);451if (StmtRemovals.count(S))452return; // already removed.453454if (Expr *E = dyn_cast<Expr>(S)) {455commitRemove(E->getSourceRange());456commitInsert(E->getSourceRange().getBegin(), getARCMTMacroName());457} else458commitRemove(S->getSourceRange());459460StmtRemovals.insert(S);461}462463void TransformActionsImpl::commitReplace(SourceRange range,464SourceRange replacementRange) {465RangeComparison comp = CharRange::compare(replacementRange, range,466Ctx.getSourceManager(), PP);467assert(comp == Range_Contained);468if (comp != Range_Contained)469return; // Although we asserted, be extra safe for release build.470if (range.getBegin() != replacementRange.getBegin())471addRemoval(CharSourceRange::getCharRange(range.getBegin(),472replacementRange.getBegin()));473if (replacementRange.getEnd() != range.getEnd())474addRemoval(CharSourceRange::getTokenRange(475getLocForEndOfToken(replacementRange.getEnd(),476Ctx.getSourceManager(), PP),477range.getEnd()));478}479void TransformActionsImpl::commitReplaceText(SourceLocation loc,480StringRef text,481StringRef replacementText) {482SourceManager &SM = Ctx.getSourceManager();483loc = SM.getExpansionLoc(loc);484// canReplaceText already checked if loc points at text.485SourceLocation afterText = loc.getLocWithOffset(text.size());486487addRemoval(CharSourceRange::getCharRange(loc, afterText));488commitInsert(loc, replacementText);489}490491void TransformActionsImpl::commitIncreaseIndentation(SourceRange range,492SourceLocation parentIndent) {493SourceManager &SM = Ctx.getSourceManager();494IndentationRanges.push_back(495std::make_pair(CharRange(CharSourceRange::getTokenRange(range),496SM, PP),497SM.getExpansionLoc(parentIndent)));498}499500void TransformActionsImpl::commitClearDiagnostic(ArrayRef<unsigned> IDs,501SourceRange range) {502CapturedDiags.clearDiagnostic(IDs, range);503}504505void TransformActionsImpl::addInsertion(SourceLocation loc, StringRef text) {506SourceManager &SM = Ctx.getSourceManager();507loc = SM.getExpansionLoc(loc);508for (const CharRange &I : llvm::reverse(Removals)) {509if (!SM.isBeforeInTranslationUnit(loc, I.End))510break;511if (I.Begin.isBeforeInTranslationUnitThan(loc))512return;513}514515Inserts[FullSourceLoc(loc, SM)].push_back(text);516}517518void TransformActionsImpl::addRemoval(CharSourceRange range) {519CharRange newRange(range, Ctx.getSourceManager(), PP);520if (newRange.Begin == newRange.End)521return;522523Inserts.erase(Inserts.upper_bound(newRange.Begin),524Inserts.lower_bound(newRange.End));525526std::list<CharRange>::iterator I = Removals.end();527while (I != Removals.begin()) {528std::list<CharRange>::iterator RI = I;529--RI;530RangeComparison comp = newRange.compareWith(*RI);531switch (comp) {532case Range_Before:533--I;534break;535case Range_After:536Removals.insert(I, newRange);537return;538case Range_Contained:539return;540case Range_Contains:541RI->End = newRange.End;542[[fallthrough]];543case Range_ExtendsBegin:544newRange.End = RI->End;545Removals.erase(RI);546break;547case Range_ExtendsEnd:548RI->End = newRange.End;549return;550}551}552553Removals.insert(Removals.begin(), newRange);554}555556void TransformActionsImpl::applyRewrites(557TransformActions::RewriteReceiver &receiver) {558for (InsertsMap::iterator I = Inserts.begin(), E = Inserts.end(); I!=E; ++I) {559SourceLocation loc = I->first;560for (TextsVec::iterator561TI = I->second.begin(), TE = I->second.end(); TI != TE; ++TI) {562receiver.insert(loc, *TI);563}564}565566for (std::vector<std::pair<CharRange, SourceLocation> >::iterator567I = IndentationRanges.begin(), E = IndentationRanges.end(); I!=E; ++I) {568CharSourceRange range = CharSourceRange::getCharRange(I->first.Begin,569I->first.End);570receiver.increaseIndentation(range, I->second);571}572573for (std::list<CharRange>::iterator574I = Removals.begin(), E = Removals.end(); I != E; ++I) {575CharSourceRange range = CharSourceRange::getCharRange(I->Begin, I->End);576receiver.remove(range);577}578}579580/// Stores text passed to the transformation methods to keep the string581/// "alive". Since the vast majority of text will be the same, we also unique582/// the strings using a StringMap.583StringRef TransformActionsImpl::getUniqueText(StringRef text) {584return UniqueText.insert(std::make_pair(text, false)).first->first();585}586587/// Computes the source location just past the end of the token at588/// the given source location. If the location points at a macro, the whole589/// macro expansion is skipped.590SourceLocation TransformActionsImpl::getLocForEndOfToken(SourceLocation loc,591SourceManager &SM,592Preprocessor &PP) {593if (loc.isMacroID()) {594CharSourceRange Exp = SM.getExpansionRange(loc);595if (Exp.isCharRange())596return Exp.getEnd();597loc = Exp.getEnd();598}599return PP.getLocForEndOfToken(loc);600}601602TransformActions::RewriteReceiver::~RewriteReceiver() { }603604TransformActions::TransformActions(DiagnosticsEngine &diag,605CapturedDiagList &capturedDiags,606ASTContext &ctx, Preprocessor &PP)607: Diags(diag), CapturedDiags(capturedDiags) {608Impl = new TransformActionsImpl(capturedDiags, ctx, PP);609}610611TransformActions::~TransformActions() {612delete static_cast<TransformActionsImpl*>(Impl);613}614615void TransformActions::startTransaction() {616static_cast<TransformActionsImpl*>(Impl)->startTransaction();617}618619bool TransformActions::commitTransaction() {620return static_cast<TransformActionsImpl*>(Impl)->commitTransaction();621}622623void TransformActions::abortTransaction() {624static_cast<TransformActionsImpl*>(Impl)->abortTransaction();625}626627628void TransformActions::insert(SourceLocation loc, StringRef text) {629static_cast<TransformActionsImpl*>(Impl)->insert(loc, text);630}631632void TransformActions::insertAfterToken(SourceLocation loc,633StringRef text) {634static_cast<TransformActionsImpl*>(Impl)->insertAfterToken(loc, text);635}636637void TransformActions::remove(SourceRange range) {638static_cast<TransformActionsImpl*>(Impl)->remove(range);639}640641void TransformActions::removeStmt(Stmt *S) {642static_cast<TransformActionsImpl*>(Impl)->removeStmt(S);643}644645void TransformActions::replace(SourceRange range, StringRef text) {646static_cast<TransformActionsImpl*>(Impl)->replace(range, text);647}648649void TransformActions::replace(SourceRange range,650SourceRange replacementRange) {651static_cast<TransformActionsImpl*>(Impl)->replace(range, replacementRange);652}653654void TransformActions::replaceStmt(Stmt *S, StringRef text) {655static_cast<TransformActionsImpl*>(Impl)->replaceStmt(S, text);656}657658void TransformActions::replaceText(SourceLocation loc, StringRef text,659StringRef replacementText) {660static_cast<TransformActionsImpl*>(Impl)->replaceText(loc, text,661replacementText);662}663664void TransformActions::increaseIndentation(SourceRange range,665SourceLocation parentIndent) {666static_cast<TransformActionsImpl*>(Impl)->increaseIndentation(range,667parentIndent);668}669670bool TransformActions::clearDiagnostic(ArrayRef<unsigned> IDs,671SourceRange range) {672return static_cast<TransformActionsImpl*>(Impl)->clearDiagnostic(IDs, range);673}674675void TransformActions::applyRewrites(RewriteReceiver &receiver) {676static_cast<TransformActionsImpl*>(Impl)->applyRewrites(receiver);677}678679DiagnosticBuilder TransformActions::report(SourceLocation loc, unsigned diagId,680SourceRange range) {681assert(!static_cast<TransformActionsImpl *>(Impl)->isInTransaction() &&682"Errors should be emitted out of a transaction");683return Diags.Report(loc, diagId) << range;684}685686void TransformActions::reportError(StringRef message, SourceLocation loc,687SourceRange range) {688report(loc, diag::err_mt_message, range) << message;689}690691void TransformActions::reportWarning(StringRef message, SourceLocation loc,692SourceRange range) {693report(loc, diag::warn_mt_message, range) << message;694}695696void TransformActions::reportNote(StringRef message, SourceLocation loc,697SourceRange range) {698report(loc, diag::note_mt_message, range) << message;699}700701702