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