1 //===-- TransformActions.cpp - Migration to ARC mode ----------------------===//
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-exception
7 //===----------------------------------------------------------------------===//
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Expr.h"
12 #include "clang/Basic/SourceManager.h"
13 #include "clang/Lex/Preprocessor.h"
14 #include "llvm/ADT/DenseSet.h"
16 using namespace clang
;
17 using namespace arcmt
;
21 /// Collects transformations and merges them before applying them with
22 /// with applyRewrites(). E.g. if the same source range
23 /// is requested to be removed twice, only one rewriter remove will be invoked.
24 /// Rewrites happen in "transactions"; if one rewrite in the transaction cannot
25 /// be done (e.g. it resides in a macro) all rewrites in the transaction are
27 /// FIXME: "Transactional" rewrites support should be baked in the Rewriter.
28 class TransformActionsImpl
{
29 CapturedDiagList
&CapturedDiags
;
36 Act_Insert
, Act_InsertAfterToken
,
37 Act_Remove
, Act_RemoveStmt
,
38 Act_Replace
, Act_ReplaceText
,
39 Act_IncreaseIndentation
,
47 StringRef Text1
, Text2
;
49 SmallVector
<unsigned, 2> DiagIDs
;
52 std::vector
<ActionData
> CachedActions
;
54 enum RangeComparison
{
63 /// A range to remove. It is a character range.
65 FullSourceLoc Begin
, End
;
67 CharRange(CharSourceRange range
, SourceManager
&srcMgr
, Preprocessor
&PP
) {
68 SourceLocation beginLoc
= range
.getBegin(), endLoc
= range
.getEnd();
69 assert(beginLoc
.isValid() && endLoc
.isValid());
70 if (range
.isTokenRange()) {
71 Begin
= FullSourceLoc(srcMgr
.getExpansionLoc(beginLoc
), srcMgr
);
72 End
= FullSourceLoc(getLocForEndOfToken(endLoc
, srcMgr
, PP
), srcMgr
);
74 Begin
= FullSourceLoc(srcMgr
.getExpansionLoc(beginLoc
), srcMgr
);
75 End
= FullSourceLoc(srcMgr
.getExpansionLoc(endLoc
), srcMgr
);
77 assert(Begin
.isValid() && End
.isValid());
80 RangeComparison
compareWith(const CharRange
&RHS
) const {
81 if (End
.isBeforeInTranslationUnitThan(RHS
.Begin
))
83 if (RHS
.End
.isBeforeInTranslationUnitThan(Begin
))
85 if (!Begin
.isBeforeInTranslationUnitThan(RHS
.Begin
) &&
86 !RHS
.End
.isBeforeInTranslationUnitThan(End
))
87 return Range_Contained
;
88 if (Begin
.isBeforeInTranslationUnitThan(RHS
.Begin
) &&
89 RHS
.End
.isBeforeInTranslationUnitThan(End
))
90 return Range_Contains
;
91 if (Begin
.isBeforeInTranslationUnitThan(RHS
.Begin
))
92 return Range_ExtendsBegin
;
94 return Range_ExtendsEnd
;
97 static RangeComparison
compare(SourceRange LHS
, SourceRange RHS
,
98 SourceManager
&SrcMgr
, Preprocessor
&PP
) {
99 return CharRange(CharSourceRange::getTokenRange(LHS
), SrcMgr
, PP
)
100 .compareWith(CharRange(CharSourceRange::getTokenRange(RHS
),
105 typedef SmallVector
<StringRef
, 2> TextsVec
;
106 typedef std::map
<FullSourceLoc
, TextsVec
, FullSourceLoc::BeforeThanCompare
>
109 /// A list of ranges to remove. They are always sorted and they never
110 /// intersect with each other.
111 std::list
<CharRange
> Removals
;
113 llvm::DenseSet
<Stmt
*> StmtRemovals
;
115 std::vector
<std::pair
<CharRange
, SourceLocation
> > IndentationRanges
;
117 /// Keeps text passed to transformation methods.
118 llvm::StringMap
<bool> UniqueText
;
121 TransformActionsImpl(CapturedDiagList
&capturedDiags
,
122 ASTContext
&ctx
, Preprocessor
&PP
)
123 : CapturedDiags(capturedDiags
), Ctx(ctx
), PP(PP
), IsInTransaction(false) { }
125 ASTContext
&getASTContext() { return Ctx
; }
127 void startTransaction();
128 bool commitTransaction();
129 void abortTransaction();
131 bool isInTransaction() const { return IsInTransaction
; }
133 void insert(SourceLocation loc
, StringRef text
);
134 void insertAfterToken(SourceLocation loc
, StringRef text
);
135 void remove(SourceRange range
);
136 void removeStmt(Stmt
*S
);
137 void replace(SourceRange range
, StringRef text
);
138 void replace(SourceRange range
, SourceRange replacementRange
);
139 void replaceStmt(Stmt
*S
, StringRef text
);
140 void replaceText(SourceLocation loc
, StringRef text
,
141 StringRef replacementText
);
142 void increaseIndentation(SourceRange range
,
143 SourceLocation parentIndent
);
145 bool clearDiagnostic(ArrayRef
<unsigned> IDs
, SourceRange range
);
147 void applyRewrites(TransformActions::RewriteReceiver
&receiver
);
150 bool canInsert(SourceLocation loc
);
151 bool canInsertAfterToken(SourceLocation loc
);
152 bool canRemoveRange(SourceRange range
);
153 bool canReplaceRange(SourceRange range
, SourceRange replacementRange
);
154 bool canReplaceText(SourceLocation loc
, StringRef text
);
156 void commitInsert(SourceLocation loc
, StringRef text
);
157 void commitInsertAfterToken(SourceLocation loc
, StringRef text
);
158 void commitRemove(SourceRange range
);
159 void commitRemoveStmt(Stmt
*S
);
160 void commitReplace(SourceRange range
, SourceRange replacementRange
);
161 void commitReplaceText(SourceLocation loc
, StringRef text
,
162 StringRef replacementText
);
163 void commitIncreaseIndentation(SourceRange range
,SourceLocation parentIndent
);
164 void commitClearDiagnostic(ArrayRef
<unsigned> IDs
, SourceRange range
);
166 void addRemoval(CharSourceRange range
);
167 void addInsertion(SourceLocation loc
, StringRef text
);
169 /// Stores text passed to the transformation methods to keep the string
170 /// "alive". Since the vast majority of text will be the same, we also unique
171 /// the strings using a StringMap.
172 StringRef
getUniqueText(StringRef text
);
174 /// Computes the source location just past the end of the token at
175 /// the given source location. If the location points at a macro, the whole
176 /// macro expansion is skipped.
177 static SourceLocation
getLocForEndOfToken(SourceLocation loc
,
178 SourceManager
&SM
,Preprocessor
&PP
);
181 } // anonymous namespace
183 void TransformActionsImpl::startTransaction() {
184 assert(!IsInTransaction
&&
185 "Cannot start a transaction in the middle of another one");
186 IsInTransaction
= true;
189 bool TransformActionsImpl::commitTransaction() {
190 assert(IsInTransaction
&& "No transaction started");
192 if (CachedActions
.empty()) {
193 IsInTransaction
= false;
197 // Verify that all actions are possible otherwise abort the whole transaction.
198 bool AllActionsPossible
= true;
199 for (unsigned i
= 0, e
= CachedActions
.size(); i
!= e
; ++i
) {
200 ActionData
&act
= CachedActions
[i
];
203 if (!canInsert(act
.Loc
))
204 AllActionsPossible
= false;
206 case Act_InsertAfterToken
:
207 if (!canInsertAfterToken(act
.Loc
))
208 AllActionsPossible
= false;
211 if (!canRemoveRange(act
.R1
))
212 AllActionsPossible
= false;
216 if (!canRemoveRange(act
.S
->getSourceRange()))
217 AllActionsPossible
= false;
220 if (!canReplaceRange(act
.R1
, act
.R2
))
221 AllActionsPossible
= false;
223 case Act_ReplaceText
:
224 if (!canReplaceText(act
.Loc
, act
.Text1
))
225 AllActionsPossible
= false;
227 case Act_IncreaseIndentation
:
228 // This is not important, we don't care if it will fail.
230 case Act_ClearDiagnostic
:
231 // We are just checking source rewrites.
234 if (!AllActionsPossible
)
238 if (!AllActionsPossible
) {
243 for (unsigned i
= 0, e
= CachedActions
.size(); i
!= e
; ++i
) {
244 ActionData
&act
= CachedActions
[i
];
247 commitInsert(act
.Loc
, act
.Text1
);
249 case Act_InsertAfterToken
:
250 commitInsertAfterToken(act
.Loc
, act
.Text1
);
253 commitRemove(act
.R1
);
256 commitRemoveStmt(act
.S
);
259 commitReplace(act
.R1
, act
.R2
);
261 case Act_ReplaceText
:
262 commitReplaceText(act
.Loc
, act
.Text1
, act
.Text2
);
264 case Act_IncreaseIndentation
:
265 commitIncreaseIndentation(act
.R1
, act
.Loc
);
267 case Act_ClearDiagnostic
:
268 commitClearDiagnostic(act
.DiagIDs
, act
.R1
);
273 CachedActions
.clear();
274 IsInTransaction
= false;
278 void TransformActionsImpl::abortTransaction() {
279 assert(IsInTransaction
&& "No transaction started");
280 CachedActions
.clear();
281 IsInTransaction
= false;
284 void TransformActionsImpl::insert(SourceLocation loc
, StringRef text
) {
285 assert(IsInTransaction
&& "Actions only allowed during a transaction");
286 text
= getUniqueText(text
);
288 data
.Kind
= Act_Insert
;
291 CachedActions
.push_back(data
);
294 void TransformActionsImpl::insertAfterToken(SourceLocation loc
, StringRef text
) {
295 assert(IsInTransaction
&& "Actions only allowed during a transaction");
296 text
= getUniqueText(text
);
298 data
.Kind
= Act_InsertAfterToken
;
301 CachedActions
.push_back(data
);
304 void TransformActionsImpl::remove(SourceRange range
) {
305 assert(IsInTransaction
&& "Actions only allowed during a transaction");
307 data
.Kind
= Act_Remove
;
309 CachedActions
.push_back(data
);
312 void TransformActionsImpl::removeStmt(Stmt
*S
) {
313 assert(IsInTransaction
&& "Actions only allowed during a transaction");
315 data
.Kind
= Act_RemoveStmt
;
316 if (auto *E
= dyn_cast
<Expr
>(S
))
317 S
= E
->IgnoreImplicit(); // important for uniquing
319 CachedActions
.push_back(data
);
322 void TransformActionsImpl::replace(SourceRange range
, StringRef text
) {
323 assert(IsInTransaction
&& "Actions only allowed during a transaction");
324 text
= getUniqueText(text
);
326 insert(range
.getBegin(), text
);
329 void TransformActionsImpl::replace(SourceRange range
,
330 SourceRange replacementRange
) {
331 assert(IsInTransaction
&& "Actions only allowed during a transaction");
333 data
.Kind
= Act_Replace
;
335 data
.R2
= replacementRange
;
336 CachedActions
.push_back(data
);
339 void TransformActionsImpl::replaceText(SourceLocation loc
, StringRef text
,
340 StringRef replacementText
) {
341 text
= getUniqueText(text
);
342 replacementText
= getUniqueText(replacementText
);
344 data
.Kind
= Act_ReplaceText
;
347 data
.Text2
= replacementText
;
348 CachedActions
.push_back(data
);
351 void TransformActionsImpl::replaceStmt(Stmt
*S
, StringRef text
) {
352 assert(IsInTransaction
&& "Actions only allowed during a transaction");
353 text
= getUniqueText(text
);
354 insert(S
->getBeginLoc(), text
);
358 void TransformActionsImpl::increaseIndentation(SourceRange range
,
359 SourceLocation parentIndent
) {
360 if (range
.isInvalid()) return;
361 assert(IsInTransaction
&& "Actions only allowed during a transaction");
363 data
.Kind
= Act_IncreaseIndentation
;
365 data
.Loc
= parentIndent
;
366 CachedActions
.push_back(data
);
369 bool TransformActionsImpl::clearDiagnostic(ArrayRef
<unsigned> IDs
,
371 assert(IsInTransaction
&& "Actions only allowed during a transaction");
372 if (!CapturedDiags
.hasDiagnostic(IDs
, range
))
376 data
.Kind
= Act_ClearDiagnostic
;
378 data
.DiagIDs
.append(IDs
.begin(), IDs
.end());
379 CachedActions
.push_back(data
);
383 bool TransformActionsImpl::canInsert(SourceLocation loc
) {
387 SourceManager
&SM
= Ctx
.getSourceManager();
388 if (SM
.isInSystemHeader(SM
.getExpansionLoc(loc
)))
393 return PP
.isAtStartOfMacroExpansion(loc
);
396 bool TransformActionsImpl::canInsertAfterToken(SourceLocation loc
) {
400 SourceManager
&SM
= Ctx
.getSourceManager();
401 if (SM
.isInSystemHeader(SM
.getExpansionLoc(loc
)))
406 return PP
.isAtEndOfMacroExpansion(loc
);
409 bool TransformActionsImpl::canRemoveRange(SourceRange range
) {
410 return canInsert(range
.getBegin()) && canInsertAfterToken(range
.getEnd());
413 bool TransformActionsImpl::canReplaceRange(SourceRange range
,
414 SourceRange replacementRange
) {
415 return canRemoveRange(range
) && canRemoveRange(replacementRange
);
418 bool TransformActionsImpl::canReplaceText(SourceLocation loc
, StringRef text
) {
422 SourceManager
&SM
= Ctx
.getSourceManager();
423 loc
= SM
.getExpansionLoc(loc
);
425 // Break down the source location.
426 std::pair
<FileID
, unsigned> locInfo
= SM
.getDecomposedLoc(loc
);
428 // Try to load the file buffer.
429 bool invalidTemp
= false;
430 StringRef file
= SM
.getBufferData(locInfo
.first
, &invalidTemp
);
434 return file
.substr(locInfo
.second
).starts_with(text
);
437 void TransformActionsImpl::commitInsert(SourceLocation loc
, StringRef text
) {
438 addInsertion(loc
, text
);
441 void TransformActionsImpl::commitInsertAfterToken(SourceLocation loc
,
443 addInsertion(getLocForEndOfToken(loc
, Ctx
.getSourceManager(), PP
), text
);
446 void TransformActionsImpl::commitRemove(SourceRange range
) {
447 addRemoval(CharSourceRange::getTokenRange(range
));
450 void TransformActionsImpl::commitRemoveStmt(Stmt
*S
) {
452 if (StmtRemovals
.count(S
))
453 return; // already removed.
455 if (Expr
*E
= dyn_cast
<Expr
>(S
)) {
456 commitRemove(E
->getSourceRange());
457 commitInsert(E
->getSourceRange().getBegin(), getARCMTMacroName());
459 commitRemove(S
->getSourceRange());
461 StmtRemovals
.insert(S
);
464 void TransformActionsImpl::commitReplace(SourceRange range
,
465 SourceRange replacementRange
) {
466 RangeComparison comp
= CharRange::compare(replacementRange
, range
,
467 Ctx
.getSourceManager(), PP
);
468 assert(comp
== Range_Contained
);
469 if (comp
!= Range_Contained
)
470 return; // Although we asserted, be extra safe for release build.
471 if (range
.getBegin() != replacementRange
.getBegin())
472 addRemoval(CharSourceRange::getCharRange(range
.getBegin(),
473 replacementRange
.getBegin()));
474 if (replacementRange
.getEnd() != range
.getEnd())
475 addRemoval(CharSourceRange::getTokenRange(
476 getLocForEndOfToken(replacementRange
.getEnd(),
477 Ctx
.getSourceManager(), PP
),
480 void TransformActionsImpl::commitReplaceText(SourceLocation loc
,
482 StringRef replacementText
) {
483 SourceManager
&SM
= Ctx
.getSourceManager();
484 loc
= SM
.getExpansionLoc(loc
);
485 // canReplaceText already checked if loc points at text.
486 SourceLocation afterText
= loc
.getLocWithOffset(text
.size());
488 addRemoval(CharSourceRange::getCharRange(loc
, afterText
));
489 commitInsert(loc
, replacementText
);
492 void TransformActionsImpl::commitIncreaseIndentation(SourceRange range
,
493 SourceLocation parentIndent
) {
494 SourceManager
&SM
= Ctx
.getSourceManager();
495 IndentationRanges
.push_back(
496 std::make_pair(CharRange(CharSourceRange::getTokenRange(range
),
498 SM
.getExpansionLoc(parentIndent
)));
501 void TransformActionsImpl::commitClearDiagnostic(ArrayRef
<unsigned> IDs
,
503 CapturedDiags
.clearDiagnostic(IDs
, range
);
506 void TransformActionsImpl::addInsertion(SourceLocation loc
, StringRef text
) {
507 SourceManager
&SM
= Ctx
.getSourceManager();
508 loc
= SM
.getExpansionLoc(loc
);
509 for (const CharRange
&I
: llvm::reverse(Removals
)) {
510 if (!SM
.isBeforeInTranslationUnit(loc
, I
.End
))
512 if (I
.Begin
.isBeforeInTranslationUnitThan(loc
))
516 Inserts
[FullSourceLoc(loc
, SM
)].push_back(text
);
519 void TransformActionsImpl::addRemoval(CharSourceRange range
) {
520 CharRange
newRange(range
, Ctx
.getSourceManager(), PP
);
521 if (newRange
.Begin
== newRange
.End
)
524 Inserts
.erase(Inserts
.upper_bound(newRange
.Begin
),
525 Inserts
.lower_bound(newRange
.End
));
527 std::list
<CharRange
>::iterator I
= Removals
.end();
528 while (I
!= Removals
.begin()) {
529 std::list
<CharRange
>::iterator RI
= I
;
531 RangeComparison comp
= newRange
.compareWith(*RI
);
537 Removals
.insert(I
, newRange
);
539 case Range_Contained
:
542 RI
->End
= newRange
.End
;
544 case Range_ExtendsBegin
:
545 newRange
.End
= RI
->End
;
548 case Range_ExtendsEnd
:
549 RI
->End
= newRange
.End
;
554 Removals
.insert(Removals
.begin(), newRange
);
557 void TransformActionsImpl::applyRewrites(
558 TransformActions::RewriteReceiver
&receiver
) {
559 for (InsertsMap::iterator I
= Inserts
.begin(), E
= Inserts
.end(); I
!=E
; ++I
) {
560 SourceLocation loc
= I
->first
;
561 for (TextsVec::iterator
562 TI
= I
->second
.begin(), TE
= I
->second
.end(); TI
!= TE
; ++TI
) {
563 receiver
.insert(loc
, *TI
);
567 for (std::vector
<std::pair
<CharRange
, SourceLocation
> >::iterator
568 I
= IndentationRanges
.begin(), E
= IndentationRanges
.end(); I
!=E
; ++I
) {
569 CharSourceRange range
= CharSourceRange::getCharRange(I
->first
.Begin
,
571 receiver
.increaseIndentation(range
, I
->second
);
574 for (std::list
<CharRange
>::iterator
575 I
= Removals
.begin(), E
= Removals
.end(); I
!= E
; ++I
) {
576 CharSourceRange range
= CharSourceRange::getCharRange(I
->Begin
, I
->End
);
577 receiver
.remove(range
);
581 /// Stores text passed to the transformation methods to keep the string
582 /// "alive". Since the vast majority of text will be the same, we also unique
583 /// the strings using a StringMap.
584 StringRef
TransformActionsImpl::getUniqueText(StringRef text
) {
585 return UniqueText
.insert(std::make_pair(text
, false)).first
->first();
588 /// Computes the source location just past the end of the token at
589 /// the given source location. If the location points at a macro, the whole
590 /// macro expansion is skipped.
591 SourceLocation
TransformActionsImpl::getLocForEndOfToken(SourceLocation loc
,
594 if (loc
.isMacroID()) {
595 CharSourceRange Exp
= SM
.getExpansionRange(loc
);
596 if (Exp
.isCharRange())
600 return PP
.getLocForEndOfToken(loc
);
603 TransformActions::RewriteReceiver::~RewriteReceiver() { }
605 TransformActions::TransformActions(DiagnosticsEngine
&diag
,
606 CapturedDiagList
&capturedDiags
,
607 ASTContext
&ctx
, Preprocessor
&PP
)
608 : Diags(diag
), CapturedDiags(capturedDiags
) {
609 Impl
= new TransformActionsImpl(capturedDiags
, ctx
, PP
);
612 TransformActions::~TransformActions() {
613 delete static_cast<TransformActionsImpl
*>(Impl
);
616 void TransformActions::startTransaction() {
617 static_cast<TransformActionsImpl
*>(Impl
)->startTransaction();
620 bool TransformActions::commitTransaction() {
621 return static_cast<TransformActionsImpl
*>(Impl
)->commitTransaction();
624 void TransformActions::abortTransaction() {
625 static_cast<TransformActionsImpl
*>(Impl
)->abortTransaction();
629 void TransformActions::insert(SourceLocation loc
, StringRef text
) {
630 static_cast<TransformActionsImpl
*>(Impl
)->insert(loc
, text
);
633 void TransformActions::insertAfterToken(SourceLocation loc
,
635 static_cast<TransformActionsImpl
*>(Impl
)->insertAfterToken(loc
, text
);
638 void TransformActions::remove(SourceRange range
) {
639 static_cast<TransformActionsImpl
*>(Impl
)->remove(range
);
642 void TransformActions::removeStmt(Stmt
*S
) {
643 static_cast<TransformActionsImpl
*>(Impl
)->removeStmt(S
);
646 void TransformActions::replace(SourceRange range
, StringRef text
) {
647 static_cast<TransformActionsImpl
*>(Impl
)->replace(range
, text
);
650 void TransformActions::replace(SourceRange range
,
651 SourceRange replacementRange
) {
652 static_cast<TransformActionsImpl
*>(Impl
)->replace(range
, replacementRange
);
655 void TransformActions::replaceStmt(Stmt
*S
, StringRef text
) {
656 static_cast<TransformActionsImpl
*>(Impl
)->replaceStmt(S
, text
);
659 void TransformActions::replaceText(SourceLocation loc
, StringRef text
,
660 StringRef replacementText
) {
661 static_cast<TransformActionsImpl
*>(Impl
)->replaceText(loc
, text
,
665 void TransformActions::increaseIndentation(SourceRange range
,
666 SourceLocation parentIndent
) {
667 static_cast<TransformActionsImpl
*>(Impl
)->increaseIndentation(range
,
671 bool TransformActions::clearDiagnostic(ArrayRef
<unsigned> IDs
,
673 return static_cast<TransformActionsImpl
*>(Impl
)->clearDiagnostic(IDs
, range
);
676 void TransformActions::applyRewrites(RewriteReceiver
&receiver
) {
677 static_cast<TransformActionsImpl
*>(Impl
)->applyRewrites(receiver
);
680 DiagnosticBuilder
TransformActions::report(SourceLocation loc
, unsigned diagId
,
682 assert(!static_cast<TransformActionsImpl
*>(Impl
)->isInTransaction() &&
683 "Errors should be emitted out of a transaction");
684 return Diags
.Report(loc
, diagId
) << range
;
687 void TransformActions::reportError(StringRef message
, SourceLocation loc
,
689 report(loc
, diag::err_mt_message
, range
) << message
;
692 void TransformActions::reportWarning(StringRef message
, SourceLocation loc
,
694 report(loc
, diag::warn_mt_message
, range
) << message
;
697 void TransformActions::reportNote(StringRef message
, SourceLocation loc
,
699 report(loc
, diag::note_mt_message
, range
) << message
;