1353358Sdim//===-- TransformActions.cpp - Migration to ARC mode ----------------------===//
2224135Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6224135Sdim//
7224135Sdim//===----------------------------------------------------------------------===//
8224135Sdim
9224135Sdim#include "Internals.h"
10239462Sdim#include "clang/AST/ASTContext.h"
11224135Sdim#include "clang/AST/Expr.h"
12249423Sdim#include "clang/Basic/SourceManager.h"
13224135Sdim#include "clang/Lex/Preprocessor.h"
14224135Sdim#include "llvm/ADT/DenseSet.h"
15224135Sdim#include <map>
16224135Sdimusing namespace clang;
17224135Sdimusing namespace arcmt;
18224135Sdim
19224135Sdimnamespace {
20224135Sdim
21341825Sdim/// Collects transformations and merges them before applying them with
22224135Sdim/// with applyRewrites(). E.g. if the same source range
23224135Sdim/// is requested to be removed twice, only one rewriter remove will be invoked.
24224135Sdim/// Rewrites happen in "transactions"; if one rewrite in the transaction cannot
25224135Sdim/// be done (e.g. it resides in a macro) all rewrites in the transaction are
26224135Sdim/// aborted.
27224135Sdim/// FIXME: "Transactional" rewrites support should be baked in the Rewriter.
28224135Sdimclass TransformActionsImpl {
29224135Sdim  CapturedDiagList &CapturedDiags;
30224135Sdim  ASTContext &Ctx;
31224135Sdim  Preprocessor &PP;
32224135Sdim
33224135Sdim  bool IsInTransaction;
34224135Sdim
35224135Sdim  enum ActionKind {
36224135Sdim    Act_Insert, Act_InsertAfterToken,
37224135Sdim    Act_Remove, Act_RemoveStmt,
38224135Sdim    Act_Replace, Act_ReplaceText,
39224135Sdim    Act_IncreaseIndentation,
40224135Sdim    Act_ClearDiagnostic
41224135Sdim  };
42224135Sdim
43224135Sdim  struct ActionData {
44224135Sdim    ActionKind Kind;
45224135Sdim    SourceLocation Loc;
46224135Sdim    SourceRange R1, R2;
47226633Sdim    StringRef Text1, Text2;
48224135Sdim    Stmt *S;
49226633Sdim    SmallVector<unsigned, 2> DiagIDs;
50224135Sdim  };
51224135Sdim
52224135Sdim  std::vector<ActionData> CachedActions;
53224135Sdim
54224135Sdim  enum RangeComparison {
55224135Sdim    Range_Before,
56224135Sdim    Range_After,
57224135Sdim    Range_Contains,
58224135Sdim    Range_Contained,
59224135Sdim    Range_ExtendsBegin,
60224135Sdim    Range_ExtendsEnd
61224135Sdim  };
62224135Sdim
63341825Sdim  /// A range to remove. It is a character range.
64224135Sdim  struct CharRange {
65224135Sdim    FullSourceLoc Begin, End;
66224135Sdim
67224135Sdim    CharRange(CharSourceRange range, SourceManager &srcMgr, Preprocessor &PP) {
68224135Sdim      SourceLocation beginLoc = range.getBegin(), endLoc = range.getEnd();
69224135Sdim      assert(beginLoc.isValid() && endLoc.isValid());
70224135Sdim      if (range.isTokenRange()) {
71226633Sdim        Begin = FullSourceLoc(srcMgr.getExpansionLoc(beginLoc), srcMgr);
72224135Sdim        End = FullSourceLoc(getLocForEndOfToken(endLoc, srcMgr, PP), srcMgr);
73224135Sdim      } else {
74226633Sdim        Begin = FullSourceLoc(srcMgr.getExpansionLoc(beginLoc), srcMgr);
75226633Sdim        End = FullSourceLoc(srcMgr.getExpansionLoc(endLoc), srcMgr);
76224135Sdim      }
77224135Sdim      assert(Begin.isValid() && End.isValid());
78341825Sdim    }
79224135Sdim
80224135Sdim    RangeComparison compareWith(const CharRange &RHS) const {
81224135Sdim      if (End.isBeforeInTranslationUnitThan(RHS.Begin))
82224135Sdim        return Range_Before;
83224135Sdim      if (RHS.End.isBeforeInTranslationUnitThan(Begin))
84224135Sdim        return Range_After;
85224135Sdim      if (!Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&
86224135Sdim          !RHS.End.isBeforeInTranslationUnitThan(End))
87224135Sdim        return Range_Contained;
88224135Sdim      if (Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&
89224135Sdim          RHS.End.isBeforeInTranslationUnitThan(End))
90224135Sdim        return Range_Contains;
91224135Sdim      if (Begin.isBeforeInTranslationUnitThan(RHS.Begin))
92224135Sdim        return Range_ExtendsBegin;
93224135Sdim      else
94224135Sdim        return Range_ExtendsEnd;
95224135Sdim    }
96341825Sdim
97224135Sdim    static RangeComparison compare(SourceRange LHS, SourceRange RHS,
98224135Sdim                                   SourceManager &SrcMgr, Preprocessor &PP) {
99224135Sdim      return CharRange(CharSourceRange::getTokenRange(LHS), SrcMgr, PP)
100224135Sdim                  .compareWith(CharRange(CharSourceRange::getTokenRange(RHS),
101224135Sdim                                            SrcMgr, PP));
102224135Sdim    }
103224135Sdim  };
104224135Sdim
105226633Sdim  typedef SmallVector<StringRef, 2> TextsVec;
106224135Sdim  typedef std::map<FullSourceLoc, TextsVec, FullSourceLoc::BeforeThanCompare>
107224135Sdim      InsertsMap;
108224135Sdim  InsertsMap Inserts;
109341825Sdim  /// A list of ranges to remove. They are always sorted and they never
110224135Sdim  /// intersect with each other.
111224135Sdim  std::list<CharRange> Removals;
112224135Sdim
113224135Sdim  llvm::DenseSet<Stmt *> StmtRemovals;
114224135Sdim
115224135Sdim  std::vector<std::pair<CharRange, SourceLocation> > IndentationRanges;
116224135Sdim
117341825Sdim  /// Keeps text passed to transformation methods.
118224135Sdim  llvm::StringMap<bool> UniqueText;
119224135Sdim
120224135Sdimpublic:
121224135Sdim  TransformActionsImpl(CapturedDiagList &capturedDiags,
122224135Sdim                       ASTContext &ctx, Preprocessor &PP)
123224135Sdim    : CapturedDiags(capturedDiags), Ctx(ctx), PP(PP), IsInTransaction(false) { }
124224135Sdim
125234353Sdim  ASTContext &getASTContext() { return Ctx; }
126234353Sdim
127224135Sdim  void startTransaction();
128224135Sdim  bool commitTransaction();
129224135Sdim  void abortTransaction();
130224135Sdim
131224135Sdim  bool isInTransaction() const { return IsInTransaction; }
132224135Sdim
133226633Sdim  void insert(SourceLocation loc, StringRef text);
134226633Sdim  void insertAfterToken(SourceLocation loc, StringRef text);
135224135Sdim  void remove(SourceRange range);
136224135Sdim  void removeStmt(Stmt *S);
137226633Sdim  void replace(SourceRange range, StringRef text);
138224135Sdim  void replace(SourceRange range, SourceRange replacementRange);
139226633Sdim  void replaceStmt(Stmt *S, StringRef text);
140226633Sdim  void replaceText(SourceLocation loc, StringRef text,
141226633Sdim                   StringRef replacementText);
142224135Sdim  void increaseIndentation(SourceRange range,
143224135Sdim                           SourceLocation parentIndent);
144224135Sdim
145226633Sdim  bool clearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);
146224135Sdim
147224135Sdim  void applyRewrites(TransformActions::RewriteReceiver &receiver);
148224135Sdim
149224135Sdimprivate:
150224135Sdim  bool canInsert(SourceLocation loc);
151224135Sdim  bool canInsertAfterToken(SourceLocation loc);
152224135Sdim  bool canRemoveRange(SourceRange range);
153224135Sdim  bool canReplaceRange(SourceRange range, SourceRange replacementRange);
154226633Sdim  bool canReplaceText(SourceLocation loc, StringRef text);
155224135Sdim
156224135Sdim  void commitInsert(SourceLocation loc, StringRef text);
157224135Sdim  void commitInsertAfterToken(SourceLocation loc, StringRef text);
158224135Sdim  void commitRemove(SourceRange range);
159224135Sdim  void commitRemoveStmt(Stmt *S);
160224135Sdim  void commitReplace(SourceRange range, SourceRange replacementRange);
161226633Sdim  void commitReplaceText(SourceLocation loc, StringRef text,
162226633Sdim                         StringRef replacementText);
163224135Sdim  void commitIncreaseIndentation(SourceRange range,SourceLocation parentIndent);
164226633Sdim  void commitClearDiagnostic(ArrayRef<unsigned> IDs, SourceRange range);
165224135Sdim
166224135Sdim  void addRemoval(CharSourceRange range);
167224135Sdim  void addInsertion(SourceLocation loc, StringRef text);
168224135Sdim
169341825Sdim  /// Stores text passed to the transformation methods to keep the string
170224135Sdim  /// "alive". Since the vast majority of text will be the same, we also unique
171224135Sdim  /// the strings using a StringMap.
172224135Sdim  StringRef getUniqueText(StringRef text);
173224135Sdim
174341825Sdim  /// Computes the source location just past the end of the token at
175224135Sdim  /// the given source location. If the location points at a macro, the whole
176224135Sdim  /// macro expansion is skipped.
177224135Sdim  static SourceLocation getLocForEndOfToken(SourceLocation loc,
178224135Sdim                                            SourceManager &SM,Preprocessor &PP);
179224135Sdim};
180224135Sdim
181224135Sdim} // anonymous namespace
182224135Sdim
183224135Sdimvoid TransformActionsImpl::startTransaction() {
184224135Sdim  assert(!IsInTransaction &&
185224135Sdim         "Cannot start a transaction in the middle of another one");
186224135Sdim  IsInTransaction = true;
187224135Sdim}
188224135Sdim
189224135Sdimbool TransformActionsImpl::commitTransaction() {
190224135Sdim  assert(IsInTransaction && "No transaction started");
191224135Sdim
192224135Sdim  if (CachedActions.empty()) {
193224135Sdim    IsInTransaction = false;
194224135Sdim    return false;
195224135Sdim  }
196224135Sdim
197224135Sdim  // Verify that all actions are possible otherwise abort the whole transaction.
198224135Sdim  bool AllActionsPossible = true;
199224135Sdim  for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) {
200224135Sdim    ActionData &act = CachedActions[i];
201224135Sdim    switch (act.Kind) {
202224135Sdim    case Act_Insert:
203224135Sdim      if (!canInsert(act.Loc))
204224135Sdim        AllActionsPossible = false;
205224135Sdim      break;
206224135Sdim    case Act_InsertAfterToken:
207224135Sdim      if (!canInsertAfterToken(act.Loc))
208224135Sdim        AllActionsPossible = false;
209224135Sdim      break;
210224135Sdim    case Act_Remove:
211224135Sdim      if (!canRemoveRange(act.R1))
212224135Sdim        AllActionsPossible = false;
213224135Sdim      break;
214224135Sdim    case Act_RemoveStmt:
215224135Sdim      assert(act.S);
216224135Sdim      if (!canRemoveRange(act.S->getSourceRange()))
217224135Sdim        AllActionsPossible = false;
218224135Sdim      break;
219224135Sdim    case Act_Replace:
220224135Sdim      if (!canReplaceRange(act.R1, act.R2))
221224135Sdim        AllActionsPossible = false;
222224135Sdim      break;
223224135Sdim    case Act_ReplaceText:
224224135Sdim      if (!canReplaceText(act.Loc, act.Text1))
225224135Sdim        AllActionsPossible = false;
226224135Sdim      break;
227224135Sdim    case Act_IncreaseIndentation:
228224135Sdim      // This is not important, we don't care if it will fail.
229224135Sdim      break;
230224135Sdim    case Act_ClearDiagnostic:
231224135Sdim      // We are just checking source rewrites.
232224135Sdim      break;
233224135Sdim    }
234224135Sdim    if (!AllActionsPossible)
235224135Sdim      break;
236224135Sdim  }
237224135Sdim
238224135Sdim  if (!AllActionsPossible) {
239224135Sdim    abortTransaction();
240224135Sdim    return true;
241224135Sdim  }
242224135Sdim
243224135Sdim  for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) {
244224135Sdim    ActionData &act = CachedActions[i];
245224135Sdim    switch (act.Kind) {
246224135Sdim    case Act_Insert:
247224135Sdim      commitInsert(act.Loc, act.Text1);
248224135Sdim      break;
249224135Sdim    case Act_InsertAfterToken:
250224135Sdim      commitInsertAfterToken(act.Loc, act.Text1);
251224135Sdim      break;
252224135Sdim    case Act_Remove:
253224135Sdim      commitRemove(act.R1);
254224135Sdim      break;
255224135Sdim    case Act_RemoveStmt:
256224135Sdim      commitRemoveStmt(act.S);
257224135Sdim      break;
258224135Sdim    case Act_Replace:
259224135Sdim      commitReplace(act.R1, act.R2);
260224135Sdim      break;
261224135Sdim    case Act_ReplaceText:
262224135Sdim      commitReplaceText(act.Loc, act.Text1, act.Text2);
263224135Sdim      break;
264224135Sdim    case Act_IncreaseIndentation:
265224135Sdim      commitIncreaseIndentation(act.R1, act.Loc);
266224135Sdim      break;
267224135Sdim    case Act_ClearDiagnostic:
268224135Sdim      commitClearDiagnostic(act.DiagIDs, act.R1);
269224135Sdim      break;
270224135Sdim    }
271224135Sdim  }
272224135Sdim
273224135Sdim  CachedActions.clear();
274224135Sdim  IsInTransaction = false;
275224135Sdim  return false;
276224135Sdim}
277224135Sdim
278224135Sdimvoid TransformActionsImpl::abortTransaction() {
279224135Sdim  assert(IsInTransaction && "No transaction started");
280224135Sdim  CachedActions.clear();
281224135Sdim  IsInTransaction = false;
282224135Sdim}
283224135Sdim
284224135Sdimvoid TransformActionsImpl::insert(SourceLocation loc, StringRef text) {
285224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
286224135Sdim  text = getUniqueText(text);
287224135Sdim  ActionData data;
288224135Sdim  data.Kind = Act_Insert;
289224135Sdim  data.Loc = loc;
290224135Sdim  data.Text1 = text;
291224135Sdim  CachedActions.push_back(data);
292224135Sdim}
293224135Sdim
294224135Sdimvoid TransformActionsImpl::insertAfterToken(SourceLocation loc, StringRef text) {
295224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
296224135Sdim  text = getUniqueText(text);
297224135Sdim  ActionData data;
298224135Sdim  data.Kind = Act_InsertAfterToken;
299224135Sdim  data.Loc = loc;
300224135Sdim  data.Text1 = text;
301224135Sdim  CachedActions.push_back(data);
302224135Sdim}
303224135Sdim
304224135Sdimvoid TransformActionsImpl::remove(SourceRange range) {
305224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
306224135Sdim  ActionData data;
307224135Sdim  data.Kind = Act_Remove;
308224135Sdim  data.R1 = range;
309224135Sdim  CachedActions.push_back(data);
310224135Sdim}
311224135Sdim
312224135Sdimvoid TransformActionsImpl::removeStmt(Stmt *S) {
313224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
314224135Sdim  ActionData data;
315224135Sdim  data.Kind = Act_RemoveStmt;
316353358Sdim  if (auto *E = dyn_cast<Expr>(S))
317353358Sdim    S = E->IgnoreImplicit(); // important for uniquing
318353358Sdim  data.S = S;
319224135Sdim  CachedActions.push_back(data);
320224135Sdim}
321224135Sdim
322224135Sdimvoid TransformActionsImpl::replace(SourceRange range, StringRef text) {
323224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
324224135Sdim  text = getUniqueText(text);
325224135Sdim  remove(range);
326224135Sdim  insert(range.getBegin(), text);
327224135Sdim}
328224135Sdim
329224135Sdimvoid TransformActionsImpl::replace(SourceRange range,
330224135Sdim                                   SourceRange replacementRange) {
331224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
332224135Sdim  ActionData data;
333224135Sdim  data.Kind = Act_Replace;
334224135Sdim  data.R1 = range;
335224135Sdim  data.R2 = replacementRange;
336224135Sdim  CachedActions.push_back(data);
337224135Sdim}
338224135Sdim
339224135Sdimvoid TransformActionsImpl::replaceText(SourceLocation loc, StringRef text,
340224135Sdim                                       StringRef replacementText) {
341224135Sdim  text = getUniqueText(text);
342224135Sdim  replacementText = getUniqueText(replacementText);
343224135Sdim  ActionData data;
344224135Sdim  data.Kind = Act_ReplaceText;
345224135Sdim  data.Loc = loc;
346224135Sdim  data.Text1 = text;
347224135Sdim  data.Text2 = replacementText;
348224135Sdim  CachedActions.push_back(data);
349224135Sdim}
350224135Sdim
351224135Sdimvoid TransformActionsImpl::replaceStmt(Stmt *S, StringRef text) {
352224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
353224135Sdim  text = getUniqueText(text);
354344779Sdim  insert(S->getBeginLoc(), text);
355224135Sdim  removeStmt(S);
356224135Sdim}
357224135Sdim
358224135Sdimvoid TransformActionsImpl::increaseIndentation(SourceRange range,
359224135Sdim                                               SourceLocation parentIndent) {
360224135Sdim  if (range.isInvalid()) return;
361224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
362224135Sdim  ActionData data;
363224135Sdim  data.Kind = Act_IncreaseIndentation;
364224135Sdim  data.R1 = range;
365224135Sdim  data.Loc = parentIndent;
366224135Sdim  CachedActions.push_back(data);
367224135Sdim}
368224135Sdim
369226633Sdimbool TransformActionsImpl::clearDiagnostic(ArrayRef<unsigned> IDs,
370224135Sdim                                           SourceRange range) {
371224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
372224135Sdim  if (!CapturedDiags.hasDiagnostic(IDs, range))
373224135Sdim    return false;
374224135Sdim
375224135Sdim  ActionData data;
376224135Sdim  data.Kind = Act_ClearDiagnostic;
377224135Sdim  data.R1 = range;
378224135Sdim  data.DiagIDs.append(IDs.begin(), IDs.end());
379224135Sdim  CachedActions.push_back(data);
380224135Sdim  return true;
381224135Sdim}
382224135Sdim
383224135Sdimbool TransformActionsImpl::canInsert(SourceLocation loc) {
384224135Sdim  if (loc.isInvalid())
385224135Sdim    return false;
386224135Sdim
387224135Sdim  SourceManager &SM = Ctx.getSourceManager();
388226633Sdim  if (SM.isInSystemHeader(SM.getExpansionLoc(loc)))
389224135Sdim    return false;
390224135Sdim
391224135Sdim  if (loc.isFileID())
392224135Sdim    return true;
393224135Sdim  return PP.isAtStartOfMacroExpansion(loc);
394224135Sdim}
395224135Sdim
396224135Sdimbool TransformActionsImpl::canInsertAfterToken(SourceLocation loc) {
397224135Sdim  if (loc.isInvalid())
398224135Sdim    return false;
399224135Sdim
400224135Sdim  SourceManager &SM = Ctx.getSourceManager();
401226633Sdim  if (SM.isInSystemHeader(SM.getExpansionLoc(loc)))
402224135Sdim    return false;
403224135Sdim
404224135Sdim  if (loc.isFileID())
405224135Sdim    return true;
406224135Sdim  return PP.isAtEndOfMacroExpansion(loc);
407224135Sdim}
408224135Sdim
409224135Sdimbool TransformActionsImpl::canRemoveRange(SourceRange range) {
410224135Sdim  return canInsert(range.getBegin()) && canInsertAfterToken(range.getEnd());
411224135Sdim}
412224135Sdim
413224135Sdimbool TransformActionsImpl::canReplaceRange(SourceRange range,
414224135Sdim                                           SourceRange replacementRange) {
415224135Sdim  return canRemoveRange(range) && canRemoveRange(replacementRange);
416224135Sdim}
417224135Sdim
418224135Sdimbool TransformActionsImpl::canReplaceText(SourceLocation loc, StringRef text) {
419224135Sdim  if (!canInsert(loc))
420224135Sdim    return false;
421224135Sdim
422224135Sdim  SourceManager &SM = Ctx.getSourceManager();
423226633Sdim  loc = SM.getExpansionLoc(loc);
424224135Sdim
425224135Sdim  // Break down the source location.
426224135Sdim  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
427224135Sdim
428224135Sdim  // Try to load the file buffer.
429224135Sdim  bool invalidTemp = false;
430226633Sdim  StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
431224135Sdim  if (invalidTemp)
432224135Sdim    return false;
433224135Sdim
434224135Sdim  return file.substr(locInfo.second).startswith(text);
435224135Sdim}
436224135Sdim
437224135Sdimvoid TransformActionsImpl::commitInsert(SourceLocation loc, StringRef text) {
438224135Sdim  addInsertion(loc, text);
439224135Sdim}
440224135Sdim
441224135Sdimvoid TransformActionsImpl::commitInsertAfterToken(SourceLocation loc,
442224135Sdim                                                  StringRef text) {
443224135Sdim  addInsertion(getLocForEndOfToken(loc, Ctx.getSourceManager(), PP), text);
444224135Sdim}
445224135Sdim
446224135Sdimvoid TransformActionsImpl::commitRemove(SourceRange range) {
447224135Sdim  addRemoval(CharSourceRange::getTokenRange(range));
448224135Sdim}
449224135Sdim
450224135Sdimvoid TransformActionsImpl::commitRemoveStmt(Stmt *S) {
451224135Sdim  assert(S);
452224135Sdim  if (StmtRemovals.count(S))
453224135Sdim    return; // already removed.
454224135Sdim
455224135Sdim  if (Expr *E = dyn_cast<Expr>(S)) {
456224135Sdim    commitRemove(E->getSourceRange());
457224135Sdim    commitInsert(E->getSourceRange().getBegin(), getARCMTMacroName());
458224135Sdim  } else
459224135Sdim    commitRemove(S->getSourceRange());
460224135Sdim
461224135Sdim  StmtRemovals.insert(S);
462224135Sdim}
463224135Sdim
464224135Sdimvoid TransformActionsImpl::commitReplace(SourceRange range,
465224135Sdim                                         SourceRange replacementRange) {
466224135Sdim  RangeComparison comp = CharRange::compare(replacementRange, range,
467224135Sdim                                               Ctx.getSourceManager(), PP);
468224135Sdim  assert(comp == Range_Contained);
469224135Sdim  if (comp != Range_Contained)
470224135Sdim    return; // Although we asserted, be extra safe for release build.
471224135Sdim  if (range.getBegin() != replacementRange.getBegin())
472224135Sdim    addRemoval(CharSourceRange::getCharRange(range.getBegin(),
473224135Sdim                                             replacementRange.getBegin()));
474224135Sdim  if (replacementRange.getEnd() != range.getEnd())
475224135Sdim    addRemoval(CharSourceRange::getTokenRange(
476224135Sdim                                  getLocForEndOfToken(replacementRange.getEnd(),
477224135Sdim                                                      Ctx.getSourceManager(), PP),
478224135Sdim                                  range.getEnd()));
479224135Sdim}
480224135Sdimvoid TransformActionsImpl::commitReplaceText(SourceLocation loc,
481224135Sdim                                             StringRef text,
482224135Sdim                                             StringRef replacementText) {
483224135Sdim  SourceManager &SM = Ctx.getSourceManager();
484226633Sdim  loc = SM.getExpansionLoc(loc);
485224135Sdim  // canReplaceText already checked if loc points at text.
486226633Sdim  SourceLocation afterText = loc.getLocWithOffset(text.size());
487224135Sdim
488224135Sdim  addRemoval(CharSourceRange::getCharRange(loc, afterText));
489341825Sdim  commitInsert(loc, replacementText);
490224135Sdim}
491224135Sdim
492224135Sdimvoid TransformActionsImpl::commitIncreaseIndentation(SourceRange range,
493224135Sdim                                                  SourceLocation parentIndent) {
494224135Sdim  SourceManager &SM = Ctx.getSourceManager();
495224135Sdim  IndentationRanges.push_back(
496224135Sdim                 std::make_pair(CharRange(CharSourceRange::getTokenRange(range),
497224135Sdim                                          SM, PP),
498226633Sdim                                SM.getExpansionLoc(parentIndent)));
499224135Sdim}
500224135Sdim
501226633Sdimvoid TransformActionsImpl::commitClearDiagnostic(ArrayRef<unsigned> IDs,
502224135Sdim                                                 SourceRange range) {
503224135Sdim  CapturedDiags.clearDiagnostic(IDs, range);
504224135Sdim}
505224135Sdim
506224135Sdimvoid TransformActionsImpl::addInsertion(SourceLocation loc, StringRef text) {
507224135Sdim  SourceManager &SM = Ctx.getSourceManager();
508226633Sdim  loc = SM.getExpansionLoc(loc);
509296417Sdim  for (const CharRange &I : llvm::reverse(Removals)) {
510296417Sdim    if (!SM.isBeforeInTranslationUnit(loc, I.End))
511224135Sdim      break;
512296417Sdim    if (I.Begin.isBeforeInTranslationUnitThan(loc))
513224135Sdim      return;
514224135Sdim  }
515224135Sdim
516224135Sdim  Inserts[FullSourceLoc(loc, SM)].push_back(text);
517224135Sdim}
518224135Sdim
519224135Sdimvoid TransformActionsImpl::addRemoval(CharSourceRange range) {
520224135Sdim  CharRange newRange(range, Ctx.getSourceManager(), PP);
521224135Sdim  if (newRange.Begin == newRange.End)
522224135Sdim    return;
523224135Sdim
524224135Sdim  Inserts.erase(Inserts.upper_bound(newRange.Begin),
525224135Sdim                Inserts.lower_bound(newRange.End));
526224135Sdim
527224135Sdim  std::list<CharRange>::iterator I = Removals.end();
528224135Sdim  while (I != Removals.begin()) {
529224135Sdim    std::list<CharRange>::iterator RI = I;
530224135Sdim    --RI;
531224135Sdim    RangeComparison comp = newRange.compareWith(*RI);
532224135Sdim    switch (comp) {
533224135Sdim    case Range_Before:
534224135Sdim      --I;
535224135Sdim      break;
536224135Sdim    case Range_After:
537224135Sdim      Removals.insert(I, newRange);
538224135Sdim      return;
539224135Sdim    case Range_Contained:
540224135Sdim      return;
541224135Sdim    case Range_Contains:
542224135Sdim      RI->End = newRange.End;
543321369Sdim      LLVM_FALLTHROUGH;
544224135Sdim    case Range_ExtendsBegin:
545224135Sdim      newRange.End = RI->End;
546224135Sdim      Removals.erase(RI);
547224135Sdim      break;
548224135Sdim    case Range_ExtendsEnd:
549224135Sdim      RI->End = newRange.End;
550224135Sdim      return;
551224135Sdim    }
552224135Sdim  }
553224135Sdim
554224135Sdim  Removals.insert(Removals.begin(), newRange);
555224135Sdim}
556224135Sdim
557224135Sdimvoid TransformActionsImpl::applyRewrites(
558224135Sdim                                  TransformActions::RewriteReceiver &receiver) {
559224135Sdim  for (InsertsMap::iterator I = Inserts.begin(), E = Inserts.end(); I!=E; ++I) {
560224135Sdim    SourceLocation loc = I->first;
561224135Sdim    for (TextsVec::iterator
562224135Sdim           TI = I->second.begin(), TE = I->second.end(); TI != TE; ++TI) {
563224135Sdim      receiver.insert(loc, *TI);
564224135Sdim    }
565224135Sdim  }
566224135Sdim
567224135Sdim  for (std::vector<std::pair<CharRange, SourceLocation> >::iterator
568224135Sdim       I = IndentationRanges.begin(), E = IndentationRanges.end(); I!=E; ++I) {
569224135Sdim    CharSourceRange range = CharSourceRange::getCharRange(I->first.Begin,
570224135Sdim                                                          I->first.End);
571224135Sdim    receiver.increaseIndentation(range, I->second);
572224135Sdim  }
573224135Sdim
574224135Sdim  for (std::list<CharRange>::iterator
575224135Sdim         I = Removals.begin(), E = Removals.end(); I != E; ++I) {
576224135Sdim    CharSourceRange range = CharSourceRange::getCharRange(I->Begin, I->End);
577224135Sdim    receiver.remove(range);
578224135Sdim  }
579224135Sdim}
580224135Sdim
581341825Sdim/// Stores text passed to the transformation methods to keep the string
582224135Sdim/// "alive". Since the vast majority of text will be the same, we also unique
583224135Sdim/// the strings using a StringMap.
584224135SdimStringRef TransformActionsImpl::getUniqueText(StringRef text) {
585280031Sdim  return UniqueText.insert(std::make_pair(text, false)).first->first();
586224135Sdim}
587224135Sdim
588341825Sdim/// Computes the source location just past the end of the token at
589224135Sdim/// the given source location. If the location points at a macro, the whole
590224135Sdim/// macro expansion is skipped.
591224135SdimSourceLocation TransformActionsImpl::getLocForEndOfToken(SourceLocation loc,
592224135Sdim                                                         SourceManager &SM,
593224135Sdim                                                         Preprocessor &PP) {
594341825Sdim  if (loc.isMacroID()) {
595341825Sdim    CharSourceRange Exp = SM.getExpansionRange(loc);
596341825Sdim    if (Exp.isCharRange())
597341825Sdim      return Exp.getEnd();
598341825Sdim    loc = Exp.getEnd();
599341825Sdim  }
600224135Sdim  return PP.getLocForEndOfToken(loc);
601224135Sdim}
602224135Sdim
603224135SdimTransformActions::RewriteReceiver::~RewriteReceiver() { }
604224135Sdim
605226633SdimTransformActions::TransformActions(DiagnosticsEngine &diag,
606224135Sdim                                   CapturedDiagList &capturedDiags,
607224135Sdim                                   ASTContext &ctx, Preprocessor &PP)
608276479Sdim    : Diags(diag), CapturedDiags(capturedDiags) {
609224135Sdim  Impl = new TransformActionsImpl(capturedDiags, ctx, PP);
610224135Sdim}
611224135Sdim
612224135SdimTransformActions::~TransformActions() {
613224135Sdim  delete static_cast<TransformActionsImpl*>(Impl);
614224135Sdim}
615224135Sdim
616224135Sdimvoid TransformActions::startTransaction() {
617224135Sdim  static_cast<TransformActionsImpl*>(Impl)->startTransaction();
618224135Sdim}
619224135Sdim
620224135Sdimbool TransformActions::commitTransaction() {
621224135Sdim  return static_cast<TransformActionsImpl*>(Impl)->commitTransaction();
622224135Sdim}
623224135Sdim
624224135Sdimvoid TransformActions::abortTransaction() {
625224135Sdim  static_cast<TransformActionsImpl*>(Impl)->abortTransaction();
626224135Sdim}
627224135Sdim
628224135Sdim
629226633Sdimvoid TransformActions::insert(SourceLocation loc, StringRef text) {
630224135Sdim  static_cast<TransformActionsImpl*>(Impl)->insert(loc, text);
631224135Sdim}
632224135Sdim
633224135Sdimvoid TransformActions::insertAfterToken(SourceLocation loc,
634226633Sdim                                        StringRef text) {
635224135Sdim  static_cast<TransformActionsImpl*>(Impl)->insertAfterToken(loc, text);
636224135Sdim}
637224135Sdim
638224135Sdimvoid TransformActions::remove(SourceRange range) {
639224135Sdim  static_cast<TransformActionsImpl*>(Impl)->remove(range);
640224135Sdim}
641224135Sdim
642224135Sdimvoid TransformActions::removeStmt(Stmt *S) {
643224135Sdim  static_cast<TransformActionsImpl*>(Impl)->removeStmt(S);
644224135Sdim}
645224135Sdim
646226633Sdimvoid TransformActions::replace(SourceRange range, StringRef text) {
647224135Sdim  static_cast<TransformActionsImpl*>(Impl)->replace(range, text);
648224135Sdim}
649224135Sdim
650224135Sdimvoid TransformActions::replace(SourceRange range,
651224135Sdim                               SourceRange replacementRange) {
652224135Sdim  static_cast<TransformActionsImpl*>(Impl)->replace(range, replacementRange);
653224135Sdim}
654224135Sdim
655226633Sdimvoid TransformActions::replaceStmt(Stmt *S, StringRef text) {
656224135Sdim  static_cast<TransformActionsImpl*>(Impl)->replaceStmt(S, text);
657224135Sdim}
658224135Sdim
659226633Sdimvoid TransformActions::replaceText(SourceLocation loc, StringRef text,
660226633Sdim                                   StringRef replacementText) {
661224135Sdim  static_cast<TransformActionsImpl*>(Impl)->replaceText(loc, text,
662224135Sdim                                                        replacementText);
663224135Sdim}
664224135Sdim
665224135Sdimvoid TransformActions::increaseIndentation(SourceRange range,
666224135Sdim                                           SourceLocation parentIndent) {
667224135Sdim  static_cast<TransformActionsImpl*>(Impl)->increaseIndentation(range,
668224135Sdim                                                                parentIndent);
669224135Sdim}
670224135Sdim
671226633Sdimbool TransformActions::clearDiagnostic(ArrayRef<unsigned> IDs,
672224135Sdim                                       SourceRange range) {
673224135Sdim  return static_cast<TransformActionsImpl*>(Impl)->clearDiagnostic(IDs, range);
674224135Sdim}
675224135Sdim
676224135Sdimvoid TransformActions::applyRewrites(RewriteReceiver &receiver) {
677224135Sdim  static_cast<TransformActionsImpl*>(Impl)->applyRewrites(receiver);
678224135Sdim}
679224135Sdim
680276479SdimDiagnosticBuilder TransformActions::report(SourceLocation loc, unsigned diagId,
681276479Sdim                                           SourceRange range) {
682276479Sdim  assert(!static_cast<TransformActionsImpl *>(Impl)->isInTransaction() &&
683224135Sdim         "Errors should be emitted out of a transaction");
684276479Sdim  return Diags.Report(loc, diagId) << range;
685224135Sdim}
686224135Sdim
687276479Sdimvoid TransformActions::reportError(StringRef message, SourceLocation loc,
688234353Sdim                                   SourceRange range) {
689276479Sdim  report(loc, diag::err_mt_message, range) << message;
690234353Sdim}
691234353Sdim
692276479Sdimvoid TransformActions::reportWarning(StringRef message, SourceLocation loc,
693276479Sdim                                     SourceRange range) {
694276479Sdim  report(loc, diag::warn_mt_message, range) << message;
695276479Sdim}
696276479Sdim
697276479Sdimvoid TransformActions::reportNote(StringRef message, SourceLocation loc,
698224135Sdim                                  SourceRange range) {
699276479Sdim  report(loc, diag::note_mt_message, range) << message;
700224135Sdim}
701