TransformActions.cpp revision 224135
1224135Sdim//===--- ARCMT.cpp - Migration to ARC mode --------------------------------===//
2224135Sdim//
3224135Sdim//                     The LLVM Compiler Infrastructure
4224135Sdim//
5224135Sdim// This file is distributed under the University of Illinois Open Source
6224135Sdim// License. See LICENSE.TXT for details.
7224135Sdim//
8224135Sdim//===----------------------------------------------------------------------===//
9224135Sdim
10224135Sdim#include "Internals.h"
11224135Sdim#include "clang/AST/Expr.h"
12224135Sdim#include "clang/Lex/Preprocessor.h"
13224135Sdim#include "clang/Basic/SourceManager.h"
14224135Sdim#include "llvm/ADT/DenseSet.h"
15224135Sdim#include <map>
16224135Sdim
17224135Sdimusing namespace clang;
18224135Sdimusing namespace arcmt;
19224135Sdimusing llvm::StringRef;
20224135Sdim
21224135Sdimnamespace {
22224135Sdim
23224135Sdim/// \brief Collects transformations and merges them before applying them with
24224135Sdim/// with applyRewrites(). E.g. if the same source range
25224135Sdim/// is requested to be removed twice, only one rewriter remove will be invoked.
26224135Sdim/// Rewrites happen in "transactions"; if one rewrite in the transaction cannot
27224135Sdim/// be done (e.g. it resides in a macro) all rewrites in the transaction are
28224135Sdim/// aborted.
29224135Sdim/// FIXME: "Transactional" rewrites support should be baked in the Rewriter.
30224135Sdimclass TransformActionsImpl {
31224135Sdim  CapturedDiagList &CapturedDiags;
32224135Sdim  ASTContext &Ctx;
33224135Sdim  Preprocessor &PP;
34224135Sdim
35224135Sdim  bool IsInTransaction;
36224135Sdim
37224135Sdim  enum ActionKind {
38224135Sdim    Act_Insert, Act_InsertAfterToken,
39224135Sdim    Act_Remove, Act_RemoveStmt,
40224135Sdim    Act_Replace, Act_ReplaceText,
41224135Sdim    Act_IncreaseIndentation,
42224135Sdim    Act_ClearDiagnostic
43224135Sdim  };
44224135Sdim
45224135Sdim  struct ActionData {
46224135Sdim    ActionKind Kind;
47224135Sdim    SourceLocation Loc;
48224135Sdim    SourceRange R1, R2;
49224135Sdim    llvm::StringRef Text1, Text2;
50224135Sdim    Stmt *S;
51224135Sdim    llvm::SmallVector<unsigned, 2> DiagIDs;
52224135Sdim  };
53224135Sdim
54224135Sdim  std::vector<ActionData> CachedActions;
55224135Sdim
56224135Sdim  enum RangeComparison {
57224135Sdim    Range_Before,
58224135Sdim    Range_After,
59224135Sdim    Range_Contains,
60224135Sdim    Range_Contained,
61224135Sdim    Range_ExtendsBegin,
62224135Sdim    Range_ExtendsEnd
63224135Sdim  };
64224135Sdim
65224135Sdim  /// \brief A range to remove. It is a character range.
66224135Sdim  struct CharRange {
67224135Sdim    FullSourceLoc Begin, End;
68224135Sdim
69224135Sdim    CharRange(CharSourceRange range, SourceManager &srcMgr, Preprocessor &PP) {
70224135Sdim      SourceLocation beginLoc = range.getBegin(), endLoc = range.getEnd();
71224135Sdim      assert(beginLoc.isValid() && endLoc.isValid());
72224135Sdim      if (range.isTokenRange()) {
73224135Sdim        Begin = FullSourceLoc(srcMgr.getInstantiationLoc(beginLoc), srcMgr);
74224135Sdim        End = FullSourceLoc(getLocForEndOfToken(endLoc, srcMgr, PP), srcMgr);
75224135Sdim      } else {
76224135Sdim        Begin = FullSourceLoc(srcMgr.getInstantiationLoc(beginLoc), srcMgr);
77224135Sdim        End = FullSourceLoc(srcMgr.getInstantiationLoc(endLoc), srcMgr);
78224135Sdim      }
79224135Sdim      assert(Begin.isValid() && End.isValid());
80224135Sdim    }
81224135Sdim
82224135Sdim    RangeComparison compareWith(const CharRange &RHS) const {
83224135Sdim      if (End.isBeforeInTranslationUnitThan(RHS.Begin))
84224135Sdim        return Range_Before;
85224135Sdim      if (RHS.End.isBeforeInTranslationUnitThan(Begin))
86224135Sdim        return Range_After;
87224135Sdim      if (!Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&
88224135Sdim          !RHS.End.isBeforeInTranslationUnitThan(End))
89224135Sdim        return Range_Contained;
90224135Sdim      if (Begin.isBeforeInTranslationUnitThan(RHS.Begin) &&
91224135Sdim          RHS.End.isBeforeInTranslationUnitThan(End))
92224135Sdim        return Range_Contains;
93224135Sdim      if (Begin.isBeforeInTranslationUnitThan(RHS.Begin))
94224135Sdim        return Range_ExtendsBegin;
95224135Sdim      else
96224135Sdim        return Range_ExtendsEnd;
97224135Sdim    }
98224135Sdim
99224135Sdim    static RangeComparison compare(SourceRange LHS, SourceRange RHS,
100224135Sdim                                   SourceManager &SrcMgr, Preprocessor &PP) {
101224135Sdim      return CharRange(CharSourceRange::getTokenRange(LHS), SrcMgr, PP)
102224135Sdim                  .compareWith(CharRange(CharSourceRange::getTokenRange(RHS),
103224135Sdim                                            SrcMgr, PP));
104224135Sdim    }
105224135Sdim  };
106224135Sdim
107224135Sdim  typedef llvm::SmallVector<StringRef, 2> TextsVec;
108224135Sdim  typedef std::map<FullSourceLoc, TextsVec, FullSourceLoc::BeforeThanCompare>
109224135Sdim      InsertsMap;
110224135Sdim  InsertsMap Inserts;
111224135Sdim  /// \brief A list of ranges to remove. They are always sorted and they never
112224135Sdim  /// intersect with each other.
113224135Sdim  std::list<CharRange> Removals;
114224135Sdim
115224135Sdim  llvm::DenseSet<Stmt *> StmtRemovals;
116224135Sdim
117224135Sdim  std::vector<std::pair<CharRange, SourceLocation> > IndentationRanges;
118224135Sdim
119224135Sdim  /// \brief Keeps text passed to transformation methods.
120224135Sdim  llvm::StringMap<bool> UniqueText;
121224135Sdim
122224135Sdimpublic:
123224135Sdim  TransformActionsImpl(CapturedDiagList &capturedDiags,
124224135Sdim                       ASTContext &ctx, Preprocessor &PP)
125224135Sdim    : CapturedDiags(capturedDiags), Ctx(ctx), PP(PP), IsInTransaction(false) { }
126224135Sdim
127224135Sdim  void startTransaction();
128224135Sdim  bool commitTransaction();
129224135Sdim  void abortTransaction();
130224135Sdim
131224135Sdim  bool isInTransaction() const { return IsInTransaction; }
132224135Sdim
133224135Sdim  void insert(SourceLocation loc, llvm::StringRef text);
134224135Sdim  void insertAfterToken(SourceLocation loc, llvm::StringRef text);
135224135Sdim  void remove(SourceRange range);
136224135Sdim  void removeStmt(Stmt *S);
137224135Sdim  void replace(SourceRange range, llvm::StringRef text);
138224135Sdim  void replace(SourceRange range, SourceRange replacementRange);
139224135Sdim  void replaceStmt(Stmt *S, llvm::StringRef text);
140224135Sdim  void replaceText(SourceLocation loc, llvm::StringRef text,
141224135Sdim                   llvm::StringRef replacementText);
142224135Sdim  void increaseIndentation(SourceRange range,
143224135Sdim                           SourceLocation parentIndent);
144224135Sdim
145224135Sdim  bool clearDiagnostic(llvm::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);
154224135Sdim  bool canReplaceText(SourceLocation loc, llvm::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);
161224135Sdim  void commitReplaceText(SourceLocation loc, llvm::StringRef text,
162224135Sdim                         llvm::StringRef replacementText);
163224135Sdim  void commitIncreaseIndentation(SourceRange range,SourceLocation parentIndent);
164224135Sdim  void commitClearDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range);
165224135Sdim
166224135Sdim  void addRemoval(CharSourceRange range);
167224135Sdim  void addInsertion(SourceLocation loc, StringRef text);
168224135Sdim
169224135Sdim  /// \brief 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
174224135Sdim  /// \brief 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;
316224135Sdim  data.S = S->IgnoreImplicit(); // important for uniquing
317224135Sdim  CachedActions.push_back(data);
318224135Sdim}
319224135Sdim
320224135Sdimvoid TransformActionsImpl::replace(SourceRange range, StringRef text) {
321224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
322224135Sdim  text = getUniqueText(text);
323224135Sdim  remove(range);
324224135Sdim  insert(range.getBegin(), text);
325224135Sdim}
326224135Sdim
327224135Sdimvoid TransformActionsImpl::replace(SourceRange range,
328224135Sdim                                   SourceRange replacementRange) {
329224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
330224135Sdim  ActionData data;
331224135Sdim  data.Kind = Act_Replace;
332224135Sdim  data.R1 = range;
333224135Sdim  data.R2 = replacementRange;
334224135Sdim  CachedActions.push_back(data);
335224135Sdim}
336224135Sdim
337224135Sdimvoid TransformActionsImpl::replaceText(SourceLocation loc, StringRef text,
338224135Sdim                                       StringRef replacementText) {
339224135Sdim  text = getUniqueText(text);
340224135Sdim  replacementText = getUniqueText(replacementText);
341224135Sdim  ActionData data;
342224135Sdim  data.Kind = Act_ReplaceText;
343224135Sdim  data.Loc = loc;
344224135Sdim  data.Text1 = text;
345224135Sdim  data.Text2 = replacementText;
346224135Sdim  CachedActions.push_back(data);
347224135Sdim}
348224135Sdim
349224135Sdimvoid TransformActionsImpl::replaceStmt(Stmt *S, StringRef text) {
350224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
351224135Sdim  text = getUniqueText(text);
352224135Sdim  insert(S->getLocStart(), text);
353224135Sdim  removeStmt(S);
354224135Sdim}
355224135Sdim
356224135Sdimvoid TransformActionsImpl::increaseIndentation(SourceRange range,
357224135Sdim                                               SourceLocation parentIndent) {
358224135Sdim  if (range.isInvalid()) return;
359224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
360224135Sdim  ActionData data;
361224135Sdim  data.Kind = Act_IncreaseIndentation;
362224135Sdim  data.R1 = range;
363224135Sdim  data.Loc = parentIndent;
364224135Sdim  CachedActions.push_back(data);
365224135Sdim}
366224135Sdim
367224135Sdimbool TransformActionsImpl::clearDiagnostic(llvm::ArrayRef<unsigned> IDs,
368224135Sdim                                           SourceRange range) {
369224135Sdim  assert(IsInTransaction && "Actions only allowed during a transaction");
370224135Sdim  if (!CapturedDiags.hasDiagnostic(IDs, range))
371224135Sdim    return false;
372224135Sdim
373224135Sdim  ActionData data;
374224135Sdim  data.Kind = Act_ClearDiagnostic;
375224135Sdim  data.R1 = range;
376224135Sdim  data.DiagIDs.append(IDs.begin(), IDs.end());
377224135Sdim  CachedActions.push_back(data);
378224135Sdim  return true;
379224135Sdim}
380224135Sdim
381224135Sdimbool TransformActionsImpl::canInsert(SourceLocation loc) {
382224135Sdim  if (loc.isInvalid())
383224135Sdim    return false;
384224135Sdim
385224135Sdim  SourceManager &SM = Ctx.getSourceManager();
386224135Sdim  if (SM.isInSystemHeader(SM.getInstantiationLoc(loc)))
387224135Sdim    return false;
388224135Sdim
389224135Sdim  if (loc.isFileID())
390224135Sdim    return true;
391224135Sdim  return PP.isAtStartOfMacroExpansion(loc);
392224135Sdim}
393224135Sdim
394224135Sdimbool TransformActionsImpl::canInsertAfterToken(SourceLocation loc) {
395224135Sdim  if (loc.isInvalid())
396224135Sdim    return false;
397224135Sdim
398224135Sdim  SourceManager &SM = Ctx.getSourceManager();
399224135Sdim  if (SM.isInSystemHeader(SM.getInstantiationLoc(loc)))
400224135Sdim    return false;
401224135Sdim
402224135Sdim  if (loc.isFileID())
403224135Sdim    return true;
404224135Sdim  return PP.isAtEndOfMacroExpansion(loc);
405224135Sdim}
406224135Sdim
407224135Sdimbool TransformActionsImpl::canRemoveRange(SourceRange range) {
408224135Sdim  return canInsert(range.getBegin()) && canInsertAfterToken(range.getEnd());
409224135Sdim}
410224135Sdim
411224135Sdimbool TransformActionsImpl::canReplaceRange(SourceRange range,
412224135Sdim                                           SourceRange replacementRange) {
413224135Sdim  return canRemoveRange(range) && canRemoveRange(replacementRange);
414224135Sdim}
415224135Sdim
416224135Sdimbool TransformActionsImpl::canReplaceText(SourceLocation loc, StringRef text) {
417224135Sdim  if (!canInsert(loc))
418224135Sdim    return false;
419224135Sdim
420224135Sdim  SourceManager &SM = Ctx.getSourceManager();
421224135Sdim  loc = SM.getInstantiationLoc(loc);
422224135Sdim
423224135Sdim  // Break down the source location.
424224135Sdim  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
425224135Sdim
426224135Sdim  // Try to load the file buffer.
427224135Sdim  bool invalidTemp = false;
428224135Sdim  llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
429224135Sdim  if (invalidTemp)
430224135Sdim    return false;
431224135Sdim
432224135Sdim  return file.substr(locInfo.second).startswith(text);
433224135Sdim}
434224135Sdim
435224135Sdimvoid TransformActionsImpl::commitInsert(SourceLocation loc, StringRef text) {
436224135Sdim  addInsertion(loc, text);
437224135Sdim}
438224135Sdim
439224135Sdimvoid TransformActionsImpl::commitInsertAfterToken(SourceLocation loc,
440224135Sdim                                                  StringRef text) {
441224135Sdim  addInsertion(getLocForEndOfToken(loc, Ctx.getSourceManager(), PP), text);
442224135Sdim}
443224135Sdim
444224135Sdimvoid TransformActionsImpl::commitRemove(SourceRange range) {
445224135Sdim  addRemoval(CharSourceRange::getTokenRange(range));
446224135Sdim}
447224135Sdim
448224135Sdimvoid TransformActionsImpl::commitRemoveStmt(Stmt *S) {
449224135Sdim  assert(S);
450224135Sdim  if (StmtRemovals.count(S))
451224135Sdim    return; // already removed.
452224135Sdim
453224135Sdim  if (Expr *E = dyn_cast<Expr>(S)) {
454224135Sdim    commitRemove(E->getSourceRange());
455224135Sdim    commitInsert(E->getSourceRange().getBegin(), getARCMTMacroName());
456224135Sdim  } else
457224135Sdim    commitRemove(S->getSourceRange());
458224135Sdim
459224135Sdim  StmtRemovals.insert(S);
460224135Sdim}
461224135Sdim
462224135Sdimvoid TransformActionsImpl::commitReplace(SourceRange range,
463224135Sdim                                         SourceRange replacementRange) {
464224135Sdim  RangeComparison comp = CharRange::compare(replacementRange, range,
465224135Sdim                                               Ctx.getSourceManager(), PP);
466224135Sdim  assert(comp == Range_Contained);
467224135Sdim  if (comp != Range_Contained)
468224135Sdim    return; // Although we asserted, be extra safe for release build.
469224135Sdim  if (range.getBegin() != replacementRange.getBegin())
470224135Sdim    addRemoval(CharSourceRange::getCharRange(range.getBegin(),
471224135Sdim                                             replacementRange.getBegin()));
472224135Sdim  if (replacementRange.getEnd() != range.getEnd())
473224135Sdim    addRemoval(CharSourceRange::getTokenRange(
474224135Sdim                                  getLocForEndOfToken(replacementRange.getEnd(),
475224135Sdim                                                      Ctx.getSourceManager(), PP),
476224135Sdim                                  range.getEnd()));
477224135Sdim}
478224135Sdimvoid TransformActionsImpl::commitReplaceText(SourceLocation loc,
479224135Sdim                                             StringRef text,
480224135Sdim                                             StringRef replacementText) {
481224135Sdim  SourceManager &SM = Ctx.getSourceManager();
482224135Sdim  loc = SM.getInstantiationLoc(loc);
483224135Sdim  // canReplaceText already checked if loc points at text.
484224135Sdim  SourceLocation afterText = loc.getFileLocWithOffset(text.size());
485224135Sdim
486224135Sdim  addRemoval(CharSourceRange::getCharRange(loc, afterText));
487224135Sdim  commitInsert(loc, replacementText);
488224135Sdim}
489224135Sdim
490224135Sdimvoid TransformActionsImpl::commitIncreaseIndentation(SourceRange range,
491224135Sdim                                                  SourceLocation parentIndent) {
492224135Sdim  SourceManager &SM = Ctx.getSourceManager();
493224135Sdim  IndentationRanges.push_back(
494224135Sdim                 std::make_pair(CharRange(CharSourceRange::getTokenRange(range),
495224135Sdim                                          SM, PP),
496224135Sdim                                SM.getInstantiationLoc(parentIndent)));
497224135Sdim}
498224135Sdim
499224135Sdimvoid TransformActionsImpl::commitClearDiagnostic(llvm::ArrayRef<unsigned> IDs,
500224135Sdim                                                 SourceRange range) {
501224135Sdim  CapturedDiags.clearDiagnostic(IDs, range);
502224135Sdim}
503224135Sdim
504224135Sdimvoid TransformActionsImpl::addInsertion(SourceLocation loc, StringRef text) {
505224135Sdim  SourceManager &SM = Ctx.getSourceManager();
506224135Sdim  loc = SM.getInstantiationLoc(loc);
507224135Sdim  for (std::list<CharRange>::reverse_iterator
508224135Sdim         I = Removals.rbegin(), E = Removals.rend(); I != E; ++I) {
509224135Sdim    if (!SM.isBeforeInTranslationUnit(loc, I->End))
510224135Sdim      break;
511224135Sdim    if (I->Begin.isBeforeInTranslationUnitThan(loc))
512224135Sdim      return;
513224135Sdim  }
514224135Sdim
515224135Sdim  Inserts[FullSourceLoc(loc, SM)].push_back(text);
516224135Sdim}
517224135Sdim
518224135Sdimvoid TransformActionsImpl::addRemoval(CharSourceRange range) {
519224135Sdim  CharRange newRange(range, Ctx.getSourceManager(), PP);
520224135Sdim  if (newRange.Begin == newRange.End)
521224135Sdim    return;
522224135Sdim
523224135Sdim  Inserts.erase(Inserts.upper_bound(newRange.Begin),
524224135Sdim                Inserts.lower_bound(newRange.End));
525224135Sdim
526224135Sdim  std::list<CharRange>::iterator I = Removals.end();
527224135Sdim  while (I != Removals.begin()) {
528224135Sdim    std::list<CharRange>::iterator RI = I;
529224135Sdim    --RI;
530224135Sdim    RangeComparison comp = newRange.compareWith(*RI);
531224135Sdim    switch (comp) {
532224135Sdim    case Range_Before:
533224135Sdim      --I;
534224135Sdim      break;
535224135Sdim    case Range_After:
536224135Sdim      Removals.insert(I, newRange);
537224135Sdim      return;
538224135Sdim    case Range_Contained:
539224135Sdim      return;
540224135Sdim    case Range_Contains:
541224135Sdim      RI->End = newRange.End;
542224135Sdim    case Range_ExtendsBegin:
543224135Sdim      newRange.End = RI->End;
544224135Sdim      Removals.erase(RI);
545224135Sdim      break;
546224135Sdim    case Range_ExtendsEnd:
547224135Sdim      RI->End = newRange.End;
548224135Sdim      return;
549224135Sdim    }
550224135Sdim  }
551224135Sdim
552224135Sdim  Removals.insert(Removals.begin(), newRange);
553224135Sdim}
554224135Sdim
555224135Sdimvoid TransformActionsImpl::applyRewrites(
556224135Sdim                                  TransformActions::RewriteReceiver &receiver) {
557224135Sdim  for (InsertsMap::iterator I = Inserts.begin(), E = Inserts.end(); I!=E; ++I) {
558224135Sdim    SourceLocation loc = I->first;
559224135Sdim    for (TextsVec::iterator
560224135Sdim           TI = I->second.begin(), TE = I->second.end(); TI != TE; ++TI) {
561224135Sdim      receiver.insert(loc, *TI);
562224135Sdim    }
563224135Sdim  }
564224135Sdim
565224135Sdim  for (std::vector<std::pair<CharRange, SourceLocation> >::iterator
566224135Sdim       I = IndentationRanges.begin(), E = IndentationRanges.end(); I!=E; ++I) {
567224135Sdim    CharSourceRange range = CharSourceRange::getCharRange(I->first.Begin,
568224135Sdim                                                          I->first.End);
569224135Sdim    receiver.increaseIndentation(range, I->second);
570224135Sdim  }
571224135Sdim
572224135Sdim  for (std::list<CharRange>::iterator
573224135Sdim         I = Removals.begin(), E = Removals.end(); I != E; ++I) {
574224135Sdim    CharSourceRange range = CharSourceRange::getCharRange(I->Begin, I->End);
575224135Sdim    receiver.remove(range);
576224135Sdim  }
577224135Sdim}
578224135Sdim
579224135Sdim/// \brief Stores text passed to the transformation methods to keep the string
580224135Sdim/// "alive". Since the vast majority of text will be the same, we also unique
581224135Sdim/// the strings using a StringMap.
582224135SdimStringRef TransformActionsImpl::getUniqueText(StringRef text) {
583224135Sdim  llvm::StringMapEntry<bool> &entry = UniqueText.GetOrCreateValue(text);
584224135Sdim  return entry.getKey();
585224135Sdim}
586224135Sdim
587224135Sdim/// \brief Computes the source location just past the end of the token at
588224135Sdim/// the given source location. If the location points at a macro, the whole
589224135Sdim/// macro expansion is skipped.
590224135SdimSourceLocation TransformActionsImpl::getLocForEndOfToken(SourceLocation loc,
591224135Sdim                                                         SourceManager &SM,
592224135Sdim                                                         Preprocessor &PP) {
593224135Sdim  if (loc.isMacroID())
594224135Sdim    loc = SM.getInstantiationRange(loc).second;
595224135Sdim  return PP.getLocForEndOfToken(loc);
596224135Sdim}
597224135Sdim
598224135SdimTransformActions::RewriteReceiver::~RewriteReceiver() { }
599224135Sdim
600224135SdimTransformActions::TransformActions(Diagnostic &diag,
601224135Sdim                                   CapturedDiagList &capturedDiags,
602224135Sdim                                   ASTContext &ctx, Preprocessor &PP)
603224135Sdim  : Diags(diag), CapturedDiags(capturedDiags) {
604224135Sdim  Impl = new TransformActionsImpl(capturedDiags, ctx, PP);
605224135Sdim}
606224135Sdim
607224135SdimTransformActions::~TransformActions() {
608224135Sdim  delete static_cast<TransformActionsImpl*>(Impl);
609224135Sdim}
610224135Sdim
611224135Sdimvoid TransformActions::startTransaction() {
612224135Sdim  static_cast<TransformActionsImpl*>(Impl)->startTransaction();
613224135Sdim}
614224135Sdim
615224135Sdimbool TransformActions::commitTransaction() {
616224135Sdim  return static_cast<TransformActionsImpl*>(Impl)->commitTransaction();
617224135Sdim}
618224135Sdim
619224135Sdimvoid TransformActions::abortTransaction() {
620224135Sdim  static_cast<TransformActionsImpl*>(Impl)->abortTransaction();
621224135Sdim}
622224135Sdim
623224135Sdim
624224135Sdimvoid TransformActions::insert(SourceLocation loc, llvm::StringRef text) {
625224135Sdim  static_cast<TransformActionsImpl*>(Impl)->insert(loc, text);
626224135Sdim}
627224135Sdim
628224135Sdimvoid TransformActions::insertAfterToken(SourceLocation loc,
629224135Sdim                                        llvm::StringRef text) {
630224135Sdim  static_cast<TransformActionsImpl*>(Impl)->insertAfterToken(loc, text);
631224135Sdim}
632224135Sdim
633224135Sdimvoid TransformActions::remove(SourceRange range) {
634224135Sdim  static_cast<TransformActionsImpl*>(Impl)->remove(range);
635224135Sdim}
636224135Sdim
637224135Sdimvoid TransformActions::removeStmt(Stmt *S) {
638224135Sdim  static_cast<TransformActionsImpl*>(Impl)->removeStmt(S);
639224135Sdim}
640224135Sdim
641224135Sdimvoid TransformActions::replace(SourceRange range, llvm::StringRef text) {
642224135Sdim  static_cast<TransformActionsImpl*>(Impl)->replace(range, text);
643224135Sdim}
644224135Sdim
645224135Sdimvoid TransformActions::replace(SourceRange range,
646224135Sdim                               SourceRange replacementRange) {
647224135Sdim  static_cast<TransformActionsImpl*>(Impl)->replace(range, replacementRange);
648224135Sdim}
649224135Sdim
650224135Sdimvoid TransformActions::replaceStmt(Stmt *S, llvm::StringRef text) {
651224135Sdim  static_cast<TransformActionsImpl*>(Impl)->replaceStmt(S, text);
652224135Sdim}
653224135Sdim
654224135Sdimvoid TransformActions::replaceText(SourceLocation loc, llvm::StringRef text,
655224135Sdim                                   llvm::StringRef replacementText) {
656224135Sdim  static_cast<TransformActionsImpl*>(Impl)->replaceText(loc, text,
657224135Sdim                                                        replacementText);
658224135Sdim}
659224135Sdim
660224135Sdimvoid TransformActions::increaseIndentation(SourceRange range,
661224135Sdim                                           SourceLocation parentIndent) {
662224135Sdim  static_cast<TransformActionsImpl*>(Impl)->increaseIndentation(range,
663224135Sdim                                                                parentIndent);
664224135Sdim}
665224135Sdim
666224135Sdimbool TransformActions::clearDiagnostic(llvm::ArrayRef<unsigned> IDs,
667224135Sdim                                       SourceRange range) {
668224135Sdim  return static_cast<TransformActionsImpl*>(Impl)->clearDiagnostic(IDs, range);
669224135Sdim}
670224135Sdim
671224135Sdimvoid TransformActions::applyRewrites(RewriteReceiver &receiver) {
672224135Sdim  static_cast<TransformActionsImpl*>(Impl)->applyRewrites(receiver);
673224135Sdim}
674224135Sdim
675224135Sdimvoid TransformActions::reportError(llvm::StringRef error, SourceLocation loc,
676224135Sdim                                   SourceRange range) {
677224135Sdim  assert(!static_cast<TransformActionsImpl*>(Impl)->isInTransaction() &&
678224135Sdim         "Errors should be emitted out of a transaction");
679224135Sdim  // FIXME: Use a custom category name to distinguish rewriter errors.
680224135Sdim  std::string rewriteErr = "[rewriter] ";
681224135Sdim  rewriteErr += error;
682224135Sdim  unsigned diagID
683224135Sdim     = Diags.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error,
684224135Sdim                                                 rewriteErr);
685224135Sdim  Diags.Report(loc, diagID) << range;
686224135Sdim}
687224135Sdim
688224135Sdimvoid TransformActions::reportNote(llvm::StringRef note, SourceLocation loc,
689224135Sdim                                  SourceRange range) {
690224135Sdim  assert(!static_cast<TransformActionsImpl*>(Impl)->isInTransaction() &&
691224135Sdim         "Errors should be emitted out of a transaction");
692224135Sdim  // FIXME: Use a custom category name to distinguish rewriter errors.
693224135Sdim  std::string rewriteNote = "[rewriter] ";
694224135Sdim  rewriteNote += note;
695224135Sdim  unsigned diagID
696224135Sdim     = Diags.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note,
697224135Sdim                                                 rewriteNote);
698224135Sdim  Diags.Report(loc, diagID) << range;
699224135Sdim}
700