1e5dd7070Spatrick //===-- TransformActions.cpp - Migration to ARC mode ----------------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick
9e5dd7070Spatrick #include "Internals.h"
10e5dd7070Spatrick #include "clang/AST/ASTContext.h"
11e5dd7070Spatrick #include "clang/AST/Expr.h"
12e5dd7070Spatrick #include "clang/Basic/SourceManager.h"
13e5dd7070Spatrick #include "clang/Lex/Preprocessor.h"
14e5dd7070Spatrick #include "llvm/ADT/DenseSet.h"
15e5dd7070Spatrick #include <map>
16e5dd7070Spatrick using namespace clang;
17e5dd7070Spatrick using namespace arcmt;
18e5dd7070Spatrick
19e5dd7070Spatrick namespace {
20e5dd7070Spatrick
21e5dd7070Spatrick /// Collects transformations and merges them before applying them with
22e5dd7070Spatrick /// with applyRewrites(). E.g. if the same source range
23e5dd7070Spatrick /// is requested to be removed twice, only one rewriter remove will be invoked.
24e5dd7070Spatrick /// Rewrites happen in "transactions"; if one rewrite in the transaction cannot
25e5dd7070Spatrick /// be done (e.g. it resides in a macro) all rewrites in the transaction are
26e5dd7070Spatrick /// aborted.
27e5dd7070Spatrick /// FIXME: "Transactional" rewrites support should be baked in the Rewriter.
28e5dd7070Spatrick class TransformActionsImpl {
29e5dd7070Spatrick CapturedDiagList &CapturedDiags;
30e5dd7070Spatrick ASTContext &Ctx;
31e5dd7070Spatrick Preprocessor &PP;
32e5dd7070Spatrick
33e5dd7070Spatrick bool IsInTransaction;
34e5dd7070Spatrick
35e5dd7070Spatrick enum ActionKind {
36e5dd7070Spatrick Act_Insert, Act_InsertAfterToken,
37e5dd7070Spatrick Act_Remove, Act_RemoveStmt,
38e5dd7070Spatrick Act_Replace, Act_ReplaceText,
39e5dd7070Spatrick Act_IncreaseIndentation,
40e5dd7070Spatrick Act_ClearDiagnostic
41e5dd7070Spatrick };
42e5dd7070Spatrick
43e5dd7070Spatrick struct ActionData {
44e5dd7070Spatrick ActionKind Kind;
45e5dd7070Spatrick SourceLocation Loc;
46e5dd7070Spatrick SourceRange R1, R2;
47e5dd7070Spatrick StringRef Text1, Text2;
48e5dd7070Spatrick Stmt *S;
49e5dd7070Spatrick SmallVector<unsigned, 2> DiagIDs;
50e5dd7070Spatrick };
51e5dd7070Spatrick
52e5dd7070Spatrick std::vector<ActionData> CachedActions;
53e5dd7070Spatrick
54e5dd7070Spatrick enum RangeComparison {
55e5dd7070Spatrick Range_Before,
56e5dd7070Spatrick Range_After,
57e5dd7070Spatrick Range_Contains,
58e5dd7070Spatrick Range_Contained,
59e5dd7070Spatrick Range_ExtendsBegin,
60e5dd7070Spatrick Range_ExtendsEnd
61e5dd7070Spatrick };
62e5dd7070Spatrick
63e5dd7070Spatrick /// A range to remove. It is a character range.
64e5dd7070Spatrick struct CharRange {
65e5dd7070Spatrick FullSourceLoc Begin, End;
66e5dd7070Spatrick
CharRange__anona90279ec0111::TransformActionsImpl::CharRange67e5dd7070Spatrick CharRange(CharSourceRange range, SourceManager &srcMgr, Preprocessor &PP) {
68e5dd7070Spatrick SourceLocation beginLoc = range.getBegin(), endLoc = range.getEnd();
69e5dd7070Spatrick assert(beginLoc.isValid() && endLoc.isValid());
70e5dd7070Spatrick if (range.isTokenRange()) {
71e5dd7070Spatrick Begin = FullSourceLoc(srcMgr.getExpansionLoc(beginLoc), srcMgr);
72e5dd7070Spatrick End = FullSourceLoc(getLocForEndOfToken(endLoc, srcMgr, PP), srcMgr);
73e5dd7070Spatrick } else {
74e5dd7070Spatrick Begin = FullSourceLoc(srcMgr.getExpansionLoc(beginLoc), srcMgr);
75e5dd7070Spatrick End = FullSourceLoc(srcMgr.getExpansionLoc(endLoc), srcMgr);
76e5dd7070Spatrick }
77e5dd7070Spatrick assert(Begin.isValid() && End.isValid());
78e5dd7070Spatrick }
79e5dd7070Spatrick
compareWith__anona90279ec0111::TransformActionsImpl::CharRange80e5dd7070Spatrick RangeComparison compareWith(const CharRange &RHS) const {
81e5dd7070Spatrick if (End.isBeforeInTranslationUnitThan(RHS.Begin))
82e5dd7070Spatrick return Range_Before;
83e5dd7070Spatrick if (RHS.End.isBeforeInTranslationUnitThan(Begin))
84e5dd7070Spatrick return Range_After;
85e5dd7070Spatrick if (!Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&
86e5dd7070Spatrick !RHS.End.isBeforeInTranslationUnitThan(End))
87e5dd7070Spatrick return Range_Contained;
88e5dd7070Spatrick if (Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&
89e5dd7070Spatrick RHS.End.isBeforeInTranslationUnitThan(End))
90e5dd7070Spatrick return Range_Contains;
91e5dd7070Spatrick if (Begin.isBeforeInTranslationUnitThan(RHS.Begin))
92e5dd7070Spatrick return Range_ExtendsBegin;
93e5dd7070Spatrick else
94e5dd7070Spatrick return Range_ExtendsEnd;
95e5dd7070Spatrick }
96e5dd7070Spatrick
compare__anona90279ec0111::TransformActionsImpl::CharRange97e5dd7070Spatrick static RangeComparison compare(SourceRange LHS, SourceRange RHS,
98e5dd7070Spatrick SourceManager &SrcMgr, Preprocessor &PP) {
99e5dd7070Spatrick return CharRange(CharSourceRange::getTokenRange(LHS), SrcMgr, PP)
100e5dd7070Spatrick .compareWith(CharRange(CharSourceRange::getTokenRange(RHS),
101e5dd7070Spatrick SrcMgr, PP));
102e5dd7070Spatrick }
103e5dd7070Spatrick };
104e5dd7070Spatrick
105e5dd7070Spatrick typedef SmallVector<StringRef, 2> TextsVec;
106e5dd7070Spatrick typedef std::map<FullSourceLoc, TextsVec, FullSourceLoc::BeforeThanCompare>
107e5dd7070Spatrick InsertsMap;
108e5dd7070Spatrick InsertsMap Inserts;
109e5dd7070Spatrick /// A list of ranges to remove. They are always sorted and they never
110e5dd7070Spatrick /// intersect with each other.
111e5dd7070Spatrick std::list<CharRange> Removals;
112e5dd7070Spatrick
113e5dd7070Spatrick llvm::DenseSet<Stmt *> StmtRemovals;
114e5dd7070Spatrick
115e5dd7070Spatrick std::vector<std::pair<CharRange, SourceLocation> > IndentationRanges;
116e5dd7070Spatrick
117e5dd7070Spatrick /// Keeps text passed to transformation methods.
118e5dd7070Spatrick llvm::StringMap<bool> UniqueText;
119e5dd7070Spatrick
120e5dd7070Spatrick public:
TransformActionsImpl(CapturedDiagList & capturedDiags,ASTContext & ctx,Preprocessor & PP)121e5dd7070Spatrick TransformActionsImpl(CapturedDiagList &capturedDiags,
122e5dd7070Spatrick ASTContext &ctx, Preprocessor &PP)
123e5dd7070Spatrick : CapturedDiags(capturedDiags), Ctx(ctx), PP(PP), IsInTransaction(false) { }
124e5dd7070Spatrick
getASTContext()125e5dd7070Spatrick ASTContext &getASTContext() { return Ctx; }
126e5dd7070Spatrick
127e5dd7070Spatrick void startTransaction();
128e5dd7070Spatrick bool commitTransaction();
129e5dd7070Spatrick void abortTransaction();
130e5dd7070Spatrick
isInTransaction() const131e5dd7070Spatrick bool isInTransaction() const { return IsInTransaction; }
132e5dd7070Spatrick
133e5dd7070Spatrick void insert(SourceLocation loc, StringRef text);
134e5dd7070Spatrick void insertAfterToken(SourceLocation loc, StringRef text);
135e5dd7070Spatrick void remove(SourceRange range);
136e5dd7070Spatrick void removeStmt(Stmt *S);
137e5dd7070Spatrick void replace(SourceRange range, StringRef text);
138e5dd7070Spatrick void replace(SourceRange range, SourceRange replacementRange);
139e5dd7070Spatrick void replaceStmt(Stmt *S, StringRef text);
140e5dd7070Spatrick void replaceText(SourceLocation loc, StringRef text,
141e5dd7070Spatrick StringRef replacementText);
142e5dd7070Spatrick void increaseIndentation(SourceRange range,
143e5dd7070Spatrick SourceLocation parentIndent);
144e5dd7070Spatrick
145e5dd7070Spatrick bool clearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);
146e5dd7070Spatrick
147e5dd7070Spatrick void applyRewrites(TransformActions::RewriteReceiver &receiver);
148e5dd7070Spatrick
149e5dd7070Spatrick private:
150e5dd7070Spatrick bool canInsert(SourceLocation loc);
151e5dd7070Spatrick bool canInsertAfterToken(SourceLocation loc);
152e5dd7070Spatrick bool canRemoveRange(SourceRange range);
153e5dd7070Spatrick bool canReplaceRange(SourceRange range, SourceRange replacementRange);
154e5dd7070Spatrick bool canReplaceText(SourceLocation loc, StringRef text);
155e5dd7070Spatrick
156e5dd7070Spatrick void commitInsert(SourceLocation loc, StringRef text);
157e5dd7070Spatrick void commitInsertAfterToken(SourceLocation loc, StringRef text);
158e5dd7070Spatrick void commitRemove(SourceRange range);
159e5dd7070Spatrick void commitRemoveStmt(Stmt *S);
160e5dd7070Spatrick void commitReplace(SourceRange range, SourceRange replacementRange);
161e5dd7070Spatrick void commitReplaceText(SourceLocation loc, StringRef text,
162e5dd7070Spatrick StringRef replacementText);
163e5dd7070Spatrick void commitIncreaseIndentation(SourceRange range,SourceLocation parentIndent);
164e5dd7070Spatrick void commitClearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);
165e5dd7070Spatrick
166e5dd7070Spatrick void addRemoval(CharSourceRange range);
167e5dd7070Spatrick void addInsertion(SourceLocation loc, StringRef text);
168e5dd7070Spatrick
169e5dd7070Spatrick /// Stores text passed to the transformation methods to keep the string
170e5dd7070Spatrick /// "alive". Since the vast majority of text will be the same, we also unique
171e5dd7070Spatrick /// the strings using a StringMap.
172e5dd7070Spatrick StringRef getUniqueText(StringRef text);
173e5dd7070Spatrick
174e5dd7070Spatrick /// Computes the source location just past the end of the token at
175e5dd7070Spatrick /// the given source location. If the location points at a macro, the whole
176e5dd7070Spatrick /// macro expansion is skipped.
177e5dd7070Spatrick static SourceLocation getLocForEndOfToken(SourceLocation loc,
178e5dd7070Spatrick SourceManager &SM,Preprocessor &PP);
179e5dd7070Spatrick };
180e5dd7070Spatrick
181e5dd7070Spatrick } // anonymous namespace
182e5dd7070Spatrick
startTransaction()183e5dd7070Spatrick void TransformActionsImpl::startTransaction() {
184e5dd7070Spatrick assert(!IsInTransaction &&
185e5dd7070Spatrick "Cannot start a transaction in the middle of another one");
186e5dd7070Spatrick IsInTransaction = true;
187e5dd7070Spatrick }
188e5dd7070Spatrick
commitTransaction()189e5dd7070Spatrick bool TransformActionsImpl::commitTransaction() {
190e5dd7070Spatrick assert(IsInTransaction && "No transaction started");
191e5dd7070Spatrick
192e5dd7070Spatrick if (CachedActions.empty()) {
193e5dd7070Spatrick IsInTransaction = false;
194e5dd7070Spatrick return false;
195e5dd7070Spatrick }
196e5dd7070Spatrick
197e5dd7070Spatrick // Verify that all actions are possible otherwise abort the whole transaction.
198e5dd7070Spatrick bool AllActionsPossible = true;
199e5dd7070Spatrick for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) {
200e5dd7070Spatrick ActionData &act = CachedActions[i];
201e5dd7070Spatrick switch (act.Kind) {
202e5dd7070Spatrick case Act_Insert:
203e5dd7070Spatrick if (!canInsert(act.Loc))
204e5dd7070Spatrick AllActionsPossible = false;
205e5dd7070Spatrick break;
206e5dd7070Spatrick case Act_InsertAfterToken:
207e5dd7070Spatrick if (!canInsertAfterToken(act.Loc))
208e5dd7070Spatrick AllActionsPossible = false;
209e5dd7070Spatrick break;
210e5dd7070Spatrick case Act_Remove:
211e5dd7070Spatrick if (!canRemoveRange(act.R1))
212e5dd7070Spatrick AllActionsPossible = false;
213e5dd7070Spatrick break;
214e5dd7070Spatrick case Act_RemoveStmt:
215e5dd7070Spatrick assert(act.S);
216e5dd7070Spatrick if (!canRemoveRange(act.S->getSourceRange()))
217e5dd7070Spatrick AllActionsPossible = false;
218e5dd7070Spatrick break;
219e5dd7070Spatrick case Act_Replace:
220e5dd7070Spatrick if (!canReplaceRange(act.R1, act.R2))
221e5dd7070Spatrick AllActionsPossible = false;
222e5dd7070Spatrick break;
223e5dd7070Spatrick case Act_ReplaceText:
224e5dd7070Spatrick if (!canReplaceText(act.Loc, act.Text1))
225e5dd7070Spatrick AllActionsPossible = false;
226e5dd7070Spatrick break;
227e5dd7070Spatrick case Act_IncreaseIndentation:
228e5dd7070Spatrick // This is not important, we don't care if it will fail.
229e5dd7070Spatrick break;
230e5dd7070Spatrick case Act_ClearDiagnostic:
231e5dd7070Spatrick // We are just checking source rewrites.
232e5dd7070Spatrick break;
233e5dd7070Spatrick }
234e5dd7070Spatrick if (!AllActionsPossible)
235e5dd7070Spatrick break;
236e5dd7070Spatrick }
237e5dd7070Spatrick
238e5dd7070Spatrick if (!AllActionsPossible) {
239e5dd7070Spatrick abortTransaction();
240e5dd7070Spatrick return true;
241e5dd7070Spatrick }
242e5dd7070Spatrick
243e5dd7070Spatrick for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) {
244e5dd7070Spatrick ActionData &act = CachedActions[i];
245e5dd7070Spatrick switch (act.Kind) {
246e5dd7070Spatrick case Act_Insert:
247e5dd7070Spatrick commitInsert(act.Loc, act.Text1);
248e5dd7070Spatrick break;
249e5dd7070Spatrick case Act_InsertAfterToken:
250e5dd7070Spatrick commitInsertAfterToken(act.Loc, act.Text1);
251e5dd7070Spatrick break;
252e5dd7070Spatrick case Act_Remove:
253e5dd7070Spatrick commitRemove(act.R1);
254e5dd7070Spatrick break;
255e5dd7070Spatrick case Act_RemoveStmt:
256e5dd7070Spatrick commitRemoveStmt(act.S);
257e5dd7070Spatrick break;
258e5dd7070Spatrick case Act_Replace:
259e5dd7070Spatrick commitReplace(act.R1, act.R2);
260e5dd7070Spatrick break;
261e5dd7070Spatrick case Act_ReplaceText:
262e5dd7070Spatrick commitReplaceText(act.Loc, act.Text1, act.Text2);
263e5dd7070Spatrick break;
264e5dd7070Spatrick case Act_IncreaseIndentation:
265e5dd7070Spatrick commitIncreaseIndentation(act.R1, act.Loc);
266e5dd7070Spatrick break;
267e5dd7070Spatrick case Act_ClearDiagnostic:
268e5dd7070Spatrick commitClearDiagnostic(act.DiagIDs, act.R1);
269e5dd7070Spatrick break;
270e5dd7070Spatrick }
271e5dd7070Spatrick }
272e5dd7070Spatrick
273e5dd7070Spatrick CachedActions.clear();
274e5dd7070Spatrick IsInTransaction = false;
275e5dd7070Spatrick return false;
276e5dd7070Spatrick }
277e5dd7070Spatrick
abortTransaction()278e5dd7070Spatrick void TransformActionsImpl::abortTransaction() {
279e5dd7070Spatrick assert(IsInTransaction && "No transaction started");
280e5dd7070Spatrick CachedActions.clear();
281e5dd7070Spatrick IsInTransaction = false;
282e5dd7070Spatrick }
283e5dd7070Spatrick
insert(SourceLocation loc,StringRef text)284e5dd7070Spatrick void TransformActionsImpl::insert(SourceLocation loc, StringRef text) {
285e5dd7070Spatrick assert(IsInTransaction && "Actions only allowed during a transaction");
286e5dd7070Spatrick text = getUniqueText(text);
287e5dd7070Spatrick ActionData data;
288e5dd7070Spatrick data.Kind = Act_Insert;
289e5dd7070Spatrick data.Loc = loc;
290e5dd7070Spatrick data.Text1 = text;
291e5dd7070Spatrick CachedActions.push_back(data);
292e5dd7070Spatrick }
293e5dd7070Spatrick
insertAfterToken(SourceLocation loc,StringRef text)294e5dd7070Spatrick void TransformActionsImpl::insertAfterToken(SourceLocation loc, StringRef text) {
295e5dd7070Spatrick assert(IsInTransaction && "Actions only allowed during a transaction");
296e5dd7070Spatrick text = getUniqueText(text);
297e5dd7070Spatrick ActionData data;
298e5dd7070Spatrick data.Kind = Act_InsertAfterToken;
299e5dd7070Spatrick data.Loc = loc;
300e5dd7070Spatrick data.Text1 = text;
301e5dd7070Spatrick CachedActions.push_back(data);
302e5dd7070Spatrick }
303e5dd7070Spatrick
remove(SourceRange range)304e5dd7070Spatrick void TransformActionsImpl::remove(SourceRange range) {
305e5dd7070Spatrick assert(IsInTransaction && "Actions only allowed during a transaction");
306e5dd7070Spatrick ActionData data;
307e5dd7070Spatrick data.Kind = Act_Remove;
308e5dd7070Spatrick data.R1 = range;
309e5dd7070Spatrick CachedActions.push_back(data);
310e5dd7070Spatrick }
311e5dd7070Spatrick
removeStmt(Stmt * S)312e5dd7070Spatrick void TransformActionsImpl::removeStmt(Stmt *S) {
313e5dd7070Spatrick assert(IsInTransaction && "Actions only allowed during a transaction");
314e5dd7070Spatrick ActionData data;
315e5dd7070Spatrick data.Kind = Act_RemoveStmt;
316e5dd7070Spatrick if (auto *E = dyn_cast<Expr>(S))
317e5dd7070Spatrick S = E->IgnoreImplicit(); // important for uniquing
318e5dd7070Spatrick data.S = S;
319e5dd7070Spatrick CachedActions.push_back(data);
320e5dd7070Spatrick }
321e5dd7070Spatrick
replace(SourceRange range,StringRef text)322e5dd7070Spatrick void TransformActionsImpl::replace(SourceRange range, StringRef text) {
323e5dd7070Spatrick assert(IsInTransaction && "Actions only allowed during a transaction");
324e5dd7070Spatrick text = getUniqueText(text);
325e5dd7070Spatrick remove(range);
326e5dd7070Spatrick insert(range.getBegin(), text);
327e5dd7070Spatrick }
328e5dd7070Spatrick
replace(SourceRange range,SourceRange replacementRange)329e5dd7070Spatrick void TransformActionsImpl::replace(SourceRange range,
330e5dd7070Spatrick SourceRange replacementRange) {
331e5dd7070Spatrick assert(IsInTransaction && "Actions only allowed during a transaction");
332e5dd7070Spatrick ActionData data;
333e5dd7070Spatrick data.Kind = Act_Replace;
334e5dd7070Spatrick data.R1 = range;
335e5dd7070Spatrick data.R2 = replacementRange;
336e5dd7070Spatrick CachedActions.push_back(data);
337e5dd7070Spatrick }
338e5dd7070Spatrick
replaceText(SourceLocation loc,StringRef text,StringRef replacementText)339e5dd7070Spatrick void TransformActionsImpl::replaceText(SourceLocation loc, StringRef text,
340e5dd7070Spatrick StringRef replacementText) {
341e5dd7070Spatrick text = getUniqueText(text);
342e5dd7070Spatrick replacementText = getUniqueText(replacementText);
343e5dd7070Spatrick ActionData data;
344e5dd7070Spatrick data.Kind = Act_ReplaceText;
345e5dd7070Spatrick data.Loc = loc;
346e5dd7070Spatrick data.Text1 = text;
347e5dd7070Spatrick data.Text2 = replacementText;
348e5dd7070Spatrick CachedActions.push_back(data);
349e5dd7070Spatrick }
350e5dd7070Spatrick
replaceStmt(Stmt * S,StringRef text)351e5dd7070Spatrick void TransformActionsImpl::replaceStmt(Stmt *S, StringRef text) {
352e5dd7070Spatrick assert(IsInTransaction && "Actions only allowed during a transaction");
353e5dd7070Spatrick text = getUniqueText(text);
354e5dd7070Spatrick insert(S->getBeginLoc(), text);
355e5dd7070Spatrick removeStmt(S);
356e5dd7070Spatrick }
357e5dd7070Spatrick
increaseIndentation(SourceRange range,SourceLocation parentIndent)358e5dd7070Spatrick void TransformActionsImpl::increaseIndentation(SourceRange range,
359e5dd7070Spatrick SourceLocation parentIndent) {
360e5dd7070Spatrick if (range.isInvalid()) return;
361e5dd7070Spatrick assert(IsInTransaction && "Actions only allowed during a transaction");
362e5dd7070Spatrick ActionData data;
363e5dd7070Spatrick data.Kind = Act_IncreaseIndentation;
364e5dd7070Spatrick data.R1 = range;
365e5dd7070Spatrick data.Loc = parentIndent;
366e5dd7070Spatrick CachedActions.push_back(data);
367e5dd7070Spatrick }
368e5dd7070Spatrick
clearDiagnostic(ArrayRef<unsigned> IDs,SourceRange range)369e5dd7070Spatrick bool TransformActionsImpl::clearDiagnostic(ArrayRef<unsigned> IDs,
370e5dd7070Spatrick SourceRange range) {
371e5dd7070Spatrick assert(IsInTransaction && "Actions only allowed during a transaction");
372e5dd7070Spatrick if (!CapturedDiags.hasDiagnostic(IDs, range))
373e5dd7070Spatrick return false;
374e5dd7070Spatrick
375e5dd7070Spatrick ActionData data;
376e5dd7070Spatrick data.Kind = Act_ClearDiagnostic;
377e5dd7070Spatrick data.R1 = range;
378e5dd7070Spatrick data.DiagIDs.append(IDs.begin(), IDs.end());
379e5dd7070Spatrick CachedActions.push_back(data);
380e5dd7070Spatrick return true;
381e5dd7070Spatrick }
382e5dd7070Spatrick
canInsert(SourceLocation loc)383e5dd7070Spatrick bool TransformActionsImpl::canInsert(SourceLocation loc) {
384e5dd7070Spatrick if (loc.isInvalid())
385e5dd7070Spatrick return false;
386e5dd7070Spatrick
387e5dd7070Spatrick SourceManager &SM = Ctx.getSourceManager();
388e5dd7070Spatrick if (SM.isInSystemHeader(SM.getExpansionLoc(loc)))
389e5dd7070Spatrick return false;
390e5dd7070Spatrick
391e5dd7070Spatrick if (loc.isFileID())
392e5dd7070Spatrick return true;
393e5dd7070Spatrick return PP.isAtStartOfMacroExpansion(loc);
394e5dd7070Spatrick }
395e5dd7070Spatrick
canInsertAfterToken(SourceLocation loc)396e5dd7070Spatrick bool TransformActionsImpl::canInsertAfterToken(SourceLocation loc) {
397e5dd7070Spatrick if (loc.isInvalid())
398e5dd7070Spatrick return false;
399e5dd7070Spatrick
400e5dd7070Spatrick SourceManager &SM = Ctx.getSourceManager();
401e5dd7070Spatrick if (SM.isInSystemHeader(SM.getExpansionLoc(loc)))
402e5dd7070Spatrick return false;
403e5dd7070Spatrick
404e5dd7070Spatrick if (loc.isFileID())
405e5dd7070Spatrick return true;
406e5dd7070Spatrick return PP.isAtEndOfMacroExpansion(loc);
407e5dd7070Spatrick }
408e5dd7070Spatrick
canRemoveRange(SourceRange range)409e5dd7070Spatrick bool TransformActionsImpl::canRemoveRange(SourceRange range) {
410e5dd7070Spatrick return canInsert(range.getBegin()) && canInsertAfterToken(range.getEnd());
411e5dd7070Spatrick }
412e5dd7070Spatrick
canReplaceRange(SourceRange range,SourceRange replacementRange)413e5dd7070Spatrick bool TransformActionsImpl::canReplaceRange(SourceRange range,
414e5dd7070Spatrick SourceRange replacementRange) {
415e5dd7070Spatrick return canRemoveRange(range) && canRemoveRange(replacementRange);
416e5dd7070Spatrick }
417e5dd7070Spatrick
canReplaceText(SourceLocation loc,StringRef text)418e5dd7070Spatrick bool TransformActionsImpl::canReplaceText(SourceLocation loc, StringRef text) {
419e5dd7070Spatrick if (!canInsert(loc))
420e5dd7070Spatrick return false;
421e5dd7070Spatrick
422e5dd7070Spatrick SourceManager &SM = Ctx.getSourceManager();
423e5dd7070Spatrick loc = SM.getExpansionLoc(loc);
424e5dd7070Spatrick
425e5dd7070Spatrick // Break down the source location.
426e5dd7070Spatrick std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
427e5dd7070Spatrick
428e5dd7070Spatrick // Try to load the file buffer.
429e5dd7070Spatrick bool invalidTemp = false;
430e5dd7070Spatrick StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
431e5dd7070Spatrick if (invalidTemp)
432e5dd7070Spatrick return false;
433e5dd7070Spatrick
434e5dd7070Spatrick return file.substr(locInfo.second).startswith(text);
435e5dd7070Spatrick }
436e5dd7070Spatrick
commitInsert(SourceLocation loc,StringRef text)437e5dd7070Spatrick void TransformActionsImpl::commitInsert(SourceLocation loc, StringRef text) {
438e5dd7070Spatrick addInsertion(loc, text);
439e5dd7070Spatrick }
440e5dd7070Spatrick
commitInsertAfterToken(SourceLocation loc,StringRef text)441e5dd7070Spatrick void TransformActionsImpl::commitInsertAfterToken(SourceLocation loc,
442e5dd7070Spatrick StringRef text) {
443e5dd7070Spatrick addInsertion(getLocForEndOfToken(loc, Ctx.getSourceManager(), PP), text);
444e5dd7070Spatrick }
445e5dd7070Spatrick
commitRemove(SourceRange range)446e5dd7070Spatrick void TransformActionsImpl::commitRemove(SourceRange range) {
447e5dd7070Spatrick addRemoval(CharSourceRange::getTokenRange(range));
448e5dd7070Spatrick }
449e5dd7070Spatrick
commitRemoveStmt(Stmt * S)450e5dd7070Spatrick void TransformActionsImpl::commitRemoveStmt(Stmt *S) {
451e5dd7070Spatrick assert(S);
452e5dd7070Spatrick if (StmtRemovals.count(S))
453e5dd7070Spatrick return; // already removed.
454e5dd7070Spatrick
455e5dd7070Spatrick if (Expr *E = dyn_cast<Expr>(S)) {
456e5dd7070Spatrick commitRemove(E->getSourceRange());
457e5dd7070Spatrick commitInsert(E->getSourceRange().getBegin(), getARCMTMacroName());
458e5dd7070Spatrick } else
459e5dd7070Spatrick commitRemove(S->getSourceRange());
460e5dd7070Spatrick
461e5dd7070Spatrick StmtRemovals.insert(S);
462e5dd7070Spatrick }
463e5dd7070Spatrick
commitReplace(SourceRange range,SourceRange replacementRange)464e5dd7070Spatrick void TransformActionsImpl::commitReplace(SourceRange range,
465e5dd7070Spatrick SourceRange replacementRange) {
466e5dd7070Spatrick RangeComparison comp = CharRange::compare(replacementRange, range,
467e5dd7070Spatrick Ctx.getSourceManager(), PP);
468e5dd7070Spatrick assert(comp == Range_Contained);
469e5dd7070Spatrick if (comp != Range_Contained)
470e5dd7070Spatrick return; // Although we asserted, be extra safe for release build.
471e5dd7070Spatrick if (range.getBegin() != replacementRange.getBegin())
472e5dd7070Spatrick addRemoval(CharSourceRange::getCharRange(range.getBegin(),
473e5dd7070Spatrick replacementRange.getBegin()));
474e5dd7070Spatrick if (replacementRange.getEnd() != range.getEnd())
475e5dd7070Spatrick addRemoval(CharSourceRange::getTokenRange(
476e5dd7070Spatrick getLocForEndOfToken(replacementRange.getEnd(),
477e5dd7070Spatrick Ctx.getSourceManager(), PP),
478e5dd7070Spatrick range.getEnd()));
479e5dd7070Spatrick }
commitReplaceText(SourceLocation loc,StringRef text,StringRef replacementText)480e5dd7070Spatrick void TransformActionsImpl::commitReplaceText(SourceLocation loc,
481e5dd7070Spatrick StringRef text,
482e5dd7070Spatrick StringRef replacementText) {
483e5dd7070Spatrick SourceManager &SM = Ctx.getSourceManager();
484e5dd7070Spatrick loc = SM.getExpansionLoc(loc);
485e5dd7070Spatrick // canReplaceText already checked if loc points at text.
486e5dd7070Spatrick SourceLocation afterText = loc.getLocWithOffset(text.size());
487e5dd7070Spatrick
488e5dd7070Spatrick addRemoval(CharSourceRange::getCharRange(loc, afterText));
489e5dd7070Spatrick commitInsert(loc, replacementText);
490e5dd7070Spatrick }
491e5dd7070Spatrick
commitIncreaseIndentation(SourceRange range,SourceLocation parentIndent)492e5dd7070Spatrick void TransformActionsImpl::commitIncreaseIndentation(SourceRange range,
493e5dd7070Spatrick SourceLocation parentIndent) {
494e5dd7070Spatrick SourceManager &SM = Ctx.getSourceManager();
495e5dd7070Spatrick IndentationRanges.push_back(
496e5dd7070Spatrick std::make_pair(CharRange(CharSourceRange::getTokenRange(range),
497e5dd7070Spatrick SM, PP),
498e5dd7070Spatrick SM.getExpansionLoc(parentIndent)));
499e5dd7070Spatrick }
500e5dd7070Spatrick
commitClearDiagnostic(ArrayRef<unsigned> IDs,SourceRange range)501e5dd7070Spatrick void TransformActionsImpl::commitClearDiagnostic(ArrayRef<unsigned> IDs,
502e5dd7070Spatrick SourceRange range) {
503e5dd7070Spatrick CapturedDiags.clearDiagnostic(IDs, range);
504e5dd7070Spatrick }
505e5dd7070Spatrick
addInsertion(SourceLocation loc,StringRef text)506e5dd7070Spatrick void TransformActionsImpl::addInsertion(SourceLocation loc, StringRef text) {
507e5dd7070Spatrick SourceManager &SM = Ctx.getSourceManager();
508e5dd7070Spatrick loc = SM.getExpansionLoc(loc);
509e5dd7070Spatrick for (const CharRange &I : llvm::reverse(Removals)) {
510e5dd7070Spatrick if (!SM.isBeforeInTranslationUnit(loc, I.End))
511e5dd7070Spatrick break;
512e5dd7070Spatrick if (I.Begin.isBeforeInTranslationUnitThan(loc))
513e5dd7070Spatrick return;
514e5dd7070Spatrick }
515e5dd7070Spatrick
516e5dd7070Spatrick Inserts[FullSourceLoc(loc, SM)].push_back(text);
517e5dd7070Spatrick }
518e5dd7070Spatrick
addRemoval(CharSourceRange range)519e5dd7070Spatrick void TransformActionsImpl::addRemoval(CharSourceRange range) {
520e5dd7070Spatrick CharRange newRange(range, Ctx.getSourceManager(), PP);
521e5dd7070Spatrick if (newRange.Begin == newRange.End)
522e5dd7070Spatrick return;
523e5dd7070Spatrick
524e5dd7070Spatrick Inserts.erase(Inserts.upper_bound(newRange.Begin),
525e5dd7070Spatrick Inserts.lower_bound(newRange.End));
526e5dd7070Spatrick
527e5dd7070Spatrick std::list<CharRange>::iterator I = Removals.end();
528e5dd7070Spatrick while (I != Removals.begin()) {
529e5dd7070Spatrick std::list<CharRange>::iterator RI = I;
530e5dd7070Spatrick --RI;
531e5dd7070Spatrick RangeComparison comp = newRange.compareWith(*RI);
532e5dd7070Spatrick switch (comp) {
533e5dd7070Spatrick case Range_Before:
534e5dd7070Spatrick --I;
535e5dd7070Spatrick break;
536e5dd7070Spatrick case Range_After:
537e5dd7070Spatrick Removals.insert(I, newRange);
538e5dd7070Spatrick return;
539e5dd7070Spatrick case Range_Contained:
540e5dd7070Spatrick return;
541e5dd7070Spatrick case Range_Contains:
542e5dd7070Spatrick RI->End = newRange.End;
543*12c85518Srobert [[fallthrough]];
544e5dd7070Spatrick case Range_ExtendsBegin:
545e5dd7070Spatrick newRange.End = RI->End;
546e5dd7070Spatrick Removals.erase(RI);
547e5dd7070Spatrick break;
548e5dd7070Spatrick case Range_ExtendsEnd:
549e5dd7070Spatrick RI->End = newRange.End;
550e5dd7070Spatrick return;
551e5dd7070Spatrick }
552e5dd7070Spatrick }
553e5dd7070Spatrick
554e5dd7070Spatrick Removals.insert(Removals.begin(), newRange);
555e5dd7070Spatrick }
556e5dd7070Spatrick
applyRewrites(TransformActions::RewriteReceiver & receiver)557e5dd7070Spatrick void TransformActionsImpl::applyRewrites(
558e5dd7070Spatrick TransformActions::RewriteReceiver &receiver) {
559e5dd7070Spatrick for (InsertsMap::iterator I = Inserts.begin(), E = Inserts.end(); I!=E; ++I) {
560e5dd7070Spatrick SourceLocation loc = I->first;
561e5dd7070Spatrick for (TextsVec::iterator
562e5dd7070Spatrick TI = I->second.begin(), TE = I->second.end(); TI != TE; ++TI) {
563e5dd7070Spatrick receiver.insert(loc, *TI);
564e5dd7070Spatrick }
565e5dd7070Spatrick }
566e5dd7070Spatrick
567e5dd7070Spatrick for (std::vector<std::pair<CharRange, SourceLocation> >::iterator
568e5dd7070Spatrick I = IndentationRanges.begin(), E = IndentationRanges.end(); I!=E; ++I) {
569e5dd7070Spatrick CharSourceRange range = CharSourceRange::getCharRange(I->first.Begin,
570e5dd7070Spatrick I->first.End);
571e5dd7070Spatrick receiver.increaseIndentation(range, I->second);
572e5dd7070Spatrick }
573e5dd7070Spatrick
574e5dd7070Spatrick for (std::list<CharRange>::iterator
575e5dd7070Spatrick I = Removals.begin(), E = Removals.end(); I != E; ++I) {
576e5dd7070Spatrick CharSourceRange range = CharSourceRange::getCharRange(I->Begin, I->End);
577e5dd7070Spatrick receiver.remove(range);
578e5dd7070Spatrick }
579e5dd7070Spatrick }
580e5dd7070Spatrick
581e5dd7070Spatrick /// Stores text passed to the transformation methods to keep the string
582e5dd7070Spatrick /// "alive". Since the vast majority of text will be the same, we also unique
583e5dd7070Spatrick /// the strings using a StringMap.
getUniqueText(StringRef text)584e5dd7070Spatrick StringRef TransformActionsImpl::getUniqueText(StringRef text) {
585e5dd7070Spatrick return UniqueText.insert(std::make_pair(text, false)).first->first();
586e5dd7070Spatrick }
587e5dd7070Spatrick
588e5dd7070Spatrick /// Computes the source location just past the end of the token at
589e5dd7070Spatrick /// the given source location. If the location points at a macro, the whole
590e5dd7070Spatrick /// macro expansion is skipped.
getLocForEndOfToken(SourceLocation loc,SourceManager & SM,Preprocessor & PP)591e5dd7070Spatrick SourceLocation TransformActionsImpl::getLocForEndOfToken(SourceLocation loc,
592e5dd7070Spatrick SourceManager &SM,
593e5dd7070Spatrick Preprocessor &PP) {
594e5dd7070Spatrick if (loc.isMacroID()) {
595e5dd7070Spatrick CharSourceRange Exp = SM.getExpansionRange(loc);
596e5dd7070Spatrick if (Exp.isCharRange())
597e5dd7070Spatrick return Exp.getEnd();
598e5dd7070Spatrick loc = Exp.getEnd();
599e5dd7070Spatrick }
600e5dd7070Spatrick return PP.getLocForEndOfToken(loc);
601e5dd7070Spatrick }
602e5dd7070Spatrick
~RewriteReceiver()603e5dd7070Spatrick TransformActions::RewriteReceiver::~RewriteReceiver() { }
604e5dd7070Spatrick
TransformActions(DiagnosticsEngine & diag,CapturedDiagList & capturedDiags,ASTContext & ctx,Preprocessor & PP)605e5dd7070Spatrick TransformActions::TransformActions(DiagnosticsEngine &diag,
606e5dd7070Spatrick CapturedDiagList &capturedDiags,
607e5dd7070Spatrick ASTContext &ctx, Preprocessor &PP)
608e5dd7070Spatrick : Diags(diag), CapturedDiags(capturedDiags) {
609e5dd7070Spatrick Impl = new TransformActionsImpl(capturedDiags, ctx, PP);
610e5dd7070Spatrick }
611e5dd7070Spatrick
~TransformActions()612e5dd7070Spatrick TransformActions::~TransformActions() {
613e5dd7070Spatrick delete static_cast<TransformActionsImpl*>(Impl);
614e5dd7070Spatrick }
615e5dd7070Spatrick
startTransaction()616e5dd7070Spatrick void TransformActions::startTransaction() {
617e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->startTransaction();
618e5dd7070Spatrick }
619e5dd7070Spatrick
commitTransaction()620e5dd7070Spatrick bool TransformActions::commitTransaction() {
621e5dd7070Spatrick return static_cast<TransformActionsImpl*>(Impl)->commitTransaction();
622e5dd7070Spatrick }
623e5dd7070Spatrick
abortTransaction()624e5dd7070Spatrick void TransformActions::abortTransaction() {
625e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->abortTransaction();
626e5dd7070Spatrick }
627e5dd7070Spatrick
628e5dd7070Spatrick
insert(SourceLocation loc,StringRef text)629e5dd7070Spatrick void TransformActions::insert(SourceLocation loc, StringRef text) {
630e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->insert(loc, text);
631e5dd7070Spatrick }
632e5dd7070Spatrick
insertAfterToken(SourceLocation loc,StringRef text)633e5dd7070Spatrick void TransformActions::insertAfterToken(SourceLocation loc,
634e5dd7070Spatrick StringRef text) {
635e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->insertAfterToken(loc, text);
636e5dd7070Spatrick }
637e5dd7070Spatrick
remove(SourceRange range)638e5dd7070Spatrick void TransformActions::remove(SourceRange range) {
639e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->remove(range);
640e5dd7070Spatrick }
641e5dd7070Spatrick
removeStmt(Stmt * S)642e5dd7070Spatrick void TransformActions::removeStmt(Stmt *S) {
643e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->removeStmt(S);
644e5dd7070Spatrick }
645e5dd7070Spatrick
replace(SourceRange range,StringRef text)646e5dd7070Spatrick void TransformActions::replace(SourceRange range, StringRef text) {
647e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->replace(range, text);
648e5dd7070Spatrick }
649e5dd7070Spatrick
replace(SourceRange range,SourceRange replacementRange)650e5dd7070Spatrick void TransformActions::replace(SourceRange range,
651e5dd7070Spatrick SourceRange replacementRange) {
652e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->replace(range, replacementRange);
653e5dd7070Spatrick }
654e5dd7070Spatrick
replaceStmt(Stmt * S,StringRef text)655e5dd7070Spatrick void TransformActions::replaceStmt(Stmt *S, StringRef text) {
656e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->replaceStmt(S, text);
657e5dd7070Spatrick }
658e5dd7070Spatrick
replaceText(SourceLocation loc,StringRef text,StringRef replacementText)659e5dd7070Spatrick void TransformActions::replaceText(SourceLocation loc, StringRef text,
660e5dd7070Spatrick StringRef replacementText) {
661e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->replaceText(loc, text,
662e5dd7070Spatrick replacementText);
663e5dd7070Spatrick }
664e5dd7070Spatrick
increaseIndentation(SourceRange range,SourceLocation parentIndent)665e5dd7070Spatrick void TransformActions::increaseIndentation(SourceRange range,
666e5dd7070Spatrick SourceLocation parentIndent) {
667e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->increaseIndentation(range,
668e5dd7070Spatrick parentIndent);
669e5dd7070Spatrick }
670e5dd7070Spatrick
clearDiagnostic(ArrayRef<unsigned> IDs,SourceRange range)671e5dd7070Spatrick bool TransformActions::clearDiagnostic(ArrayRef<unsigned> IDs,
672e5dd7070Spatrick SourceRange range) {
673e5dd7070Spatrick return static_cast<TransformActionsImpl*>(Impl)->clearDiagnostic(IDs, range);
674e5dd7070Spatrick }
675e5dd7070Spatrick
applyRewrites(RewriteReceiver & receiver)676e5dd7070Spatrick void TransformActions::applyRewrites(RewriteReceiver &receiver) {
677e5dd7070Spatrick static_cast<TransformActionsImpl*>(Impl)->applyRewrites(receiver);
678e5dd7070Spatrick }
679e5dd7070Spatrick
report(SourceLocation loc,unsigned diagId,SourceRange range)680e5dd7070Spatrick DiagnosticBuilder TransformActions::report(SourceLocation loc, unsigned diagId,
681e5dd7070Spatrick SourceRange range) {
682e5dd7070Spatrick assert(!static_cast<TransformActionsImpl *>(Impl)->isInTransaction() &&
683e5dd7070Spatrick "Errors should be emitted out of a transaction");
684e5dd7070Spatrick return Diags.Report(loc, diagId) << range;
685e5dd7070Spatrick }
686e5dd7070Spatrick
reportError(StringRef message,SourceLocation loc,SourceRange range)687e5dd7070Spatrick void TransformActions::reportError(StringRef message, SourceLocation loc,
688e5dd7070Spatrick SourceRange range) {
689e5dd7070Spatrick report(loc, diag::err_mt_message, range) << message;
690e5dd7070Spatrick }
691e5dd7070Spatrick
reportWarning(StringRef message,SourceLocation loc,SourceRange range)692e5dd7070Spatrick void TransformActions::reportWarning(StringRef message, SourceLocation loc,
693e5dd7070Spatrick SourceRange range) {
694e5dd7070Spatrick report(loc, diag::warn_mt_message, range) << message;
695e5dd7070Spatrick }
696e5dd7070Spatrick
reportNote(StringRef message,SourceLocation loc,SourceRange range)697e5dd7070Spatrick void TransformActions::reportNote(StringRef message, SourceLocation loc,
698e5dd7070Spatrick SourceRange range) {
699e5dd7070Spatrick report(loc, diag::note_mt_message, range) << message;
700e5dd7070Spatrick }
701