xref: /openbsd-src/gnu/llvm/clang/lib/ARCMigrate/TransformActions.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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