Rewriter.cpp revision 263508
1//===--- Rewriter.cpp - Code rewriting interface --------------------------===//
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//  This file defines the Rewriter class, which is used for code
11//  transformations.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Rewrite/Core/Rewriter.h"
16#include "clang/AST/Decl.h"
17#include "clang/AST/PrettyPrinter.h"
18#include "clang/AST/Stmt.h"
19#include "clang/Basic/DiagnosticIDs.h"
20#include "clang/Basic/FileManager.h"
21#include "clang/Basic/SourceManager.h"
22#include "clang/Lex/Lexer.h"
23#include "llvm/ADT/SmallString.h"
24#include "llvm/Support/FileSystem.h"
25#include "llvm/Support/raw_ostream.h"
26using namespace clang;
27
28raw_ostream &RewriteBuffer::write(raw_ostream &os) const {
29  // FIXME: eliminate the copy by writing out each chunk at a time
30  os << std::string(begin(), end());
31  return os;
32}
33
34/// \brief Return true if this character is non-new-line whitespace:
35/// ' ', '\\t', '\\f', '\\v', '\\r'.
36static inline bool isWhitespace(unsigned char c) {
37  switch (c) {
38  case ' ':
39  case '\t':
40  case '\f':
41  case '\v':
42  case '\r':
43    return true;
44  default:
45    return false;
46  }
47}
48
49void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size,
50                               bool removeLineIfEmpty) {
51  // Nothing to remove, exit early.
52  if (Size == 0) return;
53
54  unsigned RealOffset = getMappedOffset(OrigOffset, true);
55  assert(RealOffset+Size < Buffer.size() && "Invalid location");
56
57  // Remove the dead characters.
58  Buffer.erase(RealOffset, Size);
59
60  // Add a delta so that future changes are offset correctly.
61  AddReplaceDelta(OrigOffset, -Size);
62
63  if (removeLineIfEmpty) {
64    // Find the line that the remove occurred and if it is completely empty
65    // remove the line as well.
66
67    iterator curLineStart = begin();
68    unsigned curLineStartOffs = 0;
69    iterator posI = begin();
70    for (unsigned i = 0; i != RealOffset; ++i) {
71      if (*posI == '\n') {
72        curLineStart = posI;
73        ++curLineStart;
74        curLineStartOffs = i + 1;
75      }
76      ++posI;
77    }
78
79    unsigned lineSize = 0;
80    posI = curLineStart;
81    while (posI != end() && isWhitespace(*posI)) {
82      ++posI;
83      ++lineSize;
84    }
85    if (posI != end() && *posI == '\n') {
86      Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/);
87      AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/));
88    }
89  }
90}
91
92void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str,
93                               bool InsertAfter) {
94
95  // Nothing to insert, exit early.
96  if (Str.empty()) return;
97
98  unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter);
99  Buffer.insert(RealOffset, Str.begin(), Str.end());
100
101  // Add a delta so that future changes are offset correctly.
102  AddInsertDelta(OrigOffset, Str.size());
103}
104
105/// ReplaceText - This method replaces a range of characters in the input
106/// buffer with a new string.  This is effectively a combined "remove+insert"
107/// operation.
108void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
109                                StringRef NewStr) {
110  unsigned RealOffset = getMappedOffset(OrigOffset, true);
111  Buffer.erase(RealOffset, OrigLength);
112  Buffer.insert(RealOffset, NewStr.begin(), NewStr.end());
113  if (OrigLength != NewStr.size())
114    AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength);
115}
116
117
118//===----------------------------------------------------------------------===//
119// Rewriter class
120//===----------------------------------------------------------------------===//
121
122/// getRangeSize - Return the size in bytes of the specified range if they
123/// are in the same file.  If not, this returns -1.
124int Rewriter::getRangeSize(const CharSourceRange &Range,
125                           RewriteOptions opts) const {
126  if (!isRewritable(Range.getBegin()) ||
127      !isRewritable(Range.getEnd())) return -1;
128
129  FileID StartFileID, EndFileID;
130  unsigned StartOff, EndOff;
131
132  StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
133  EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
134
135  if (StartFileID != EndFileID)
136    return -1;
137
138  // If edits have been made to this buffer, the delta between the range may
139  // have changed.
140  std::map<FileID, RewriteBuffer>::const_iterator I =
141    RewriteBuffers.find(StartFileID);
142  if (I != RewriteBuffers.end()) {
143    const RewriteBuffer &RB = I->second;
144    EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange);
145    StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange);
146  }
147
148
149  // Adjust the end offset to the end of the last token, instead of being the
150  // start of the last token if this is a token range.
151  if (Range.isTokenRange())
152    EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
153
154  return EndOff-StartOff;
155}
156
157int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const {
158  return getRangeSize(CharSourceRange::getTokenRange(Range), opts);
159}
160
161
162/// getRewrittenText - Return the rewritten form of the text in the specified
163/// range.  If the start or end of the range was unrewritable or if they are
164/// in different buffers, this returns an empty string.
165///
166/// Note that this method is not particularly efficient.
167///
168std::string Rewriter::getRewrittenText(SourceRange Range) const {
169  if (!isRewritable(Range.getBegin()) ||
170      !isRewritable(Range.getEnd()))
171    return "";
172
173  FileID StartFileID, EndFileID;
174  unsigned StartOff, EndOff;
175  StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
176  EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
177
178  if (StartFileID != EndFileID)
179    return ""; // Start and end in different buffers.
180
181  // If edits have been made to this buffer, the delta between the range may
182  // have changed.
183  std::map<FileID, RewriteBuffer>::const_iterator I =
184    RewriteBuffers.find(StartFileID);
185  if (I == RewriteBuffers.end()) {
186    // If the buffer hasn't been rewritten, just return the text from the input.
187    const char *Ptr = SourceMgr->getCharacterData(Range.getBegin());
188
189    // Adjust the end offset to the end of the last token, instead of being the
190    // start of the last token.
191    EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
192    return std::string(Ptr, Ptr+EndOff-StartOff);
193  }
194
195  const RewriteBuffer &RB = I->second;
196  EndOff = RB.getMappedOffset(EndOff, true);
197  StartOff = RB.getMappedOffset(StartOff);
198
199  // Adjust the end offset to the end of the last token, instead of being the
200  // start of the last token.
201  EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
202
203  // Advance the iterators to the right spot, yay for linear time algorithms.
204  RewriteBuffer::iterator Start = RB.begin();
205  std::advance(Start, StartOff);
206  RewriteBuffer::iterator End = Start;
207  std::advance(End, EndOff-StartOff);
208
209  return std::string(Start, End);
210}
211
212unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
213                                              FileID &FID) const {
214  assert(Loc.isValid() && "Invalid location");
215  std::pair<FileID,unsigned> V = SourceMgr->getDecomposedLoc(Loc);
216  FID = V.first;
217  return V.second;
218}
219
220
221/// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
222///
223RewriteBuffer &Rewriter::getEditBuffer(FileID FID) {
224  std::map<FileID, RewriteBuffer>::iterator I =
225    RewriteBuffers.lower_bound(FID);
226  if (I != RewriteBuffers.end() && I->first == FID)
227    return I->second;
228  I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));
229
230  StringRef MB = SourceMgr->getBufferData(FID);
231  I->second.Initialize(MB.begin(), MB.end());
232
233  return I->second;
234}
235
236/// InsertText - Insert the specified string at the specified location in the
237/// original buffer.
238bool Rewriter::InsertText(SourceLocation Loc, StringRef Str,
239                          bool InsertAfter, bool indentNewLines) {
240  if (!isRewritable(Loc)) return true;
241  FileID FID;
242  unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
243
244  SmallString<128> indentedStr;
245  if (indentNewLines && Str.find('\n') != StringRef::npos) {
246    StringRef MB = SourceMgr->getBufferData(FID);
247
248    unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1;
249    const SrcMgr::ContentCache *
250        Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache();
251    unsigned lineOffs = Content->SourceLineCache[lineNo];
252
253    // Find the whitespace at the start of the line.
254    StringRef indentSpace;
255    {
256      unsigned i = lineOffs;
257      while (isWhitespace(MB[i]))
258        ++i;
259      indentSpace = MB.substr(lineOffs, i-lineOffs);
260    }
261
262    SmallVector<StringRef, 4> lines;
263    Str.split(lines, "\n");
264
265    for (unsigned i = 0, e = lines.size(); i != e; ++i) {
266      indentedStr += lines[i];
267      if (i < e-1) {
268        indentedStr += '\n';
269        indentedStr += indentSpace;
270      }
271    }
272    Str = indentedStr.str();
273  }
274
275  getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter);
276  return false;
277}
278
279bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) {
280  if (!isRewritable(Loc)) return true;
281  FileID FID;
282  unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
283  RewriteOptions rangeOpts;
284  rangeOpts.IncludeInsertsAtBeginOfRange = false;
285  StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts);
286  getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true);
287  return false;
288}
289
290/// RemoveText - Remove the specified text region.
291bool Rewriter::RemoveText(SourceLocation Start, unsigned Length,
292                          RewriteOptions opts) {
293  if (!isRewritable(Start)) return true;
294  FileID FID;
295  unsigned StartOffs = getLocationOffsetAndFileID(Start, FID);
296  getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty);
297  return false;
298}
299
300/// ReplaceText - This method replaces a range of characters in the input
301/// buffer with a new string.  This is effectively a combined "remove/insert"
302/// operation.
303bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
304                           StringRef NewStr) {
305  if (!isRewritable(Start)) return true;
306  FileID StartFileID;
307  unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
308
309  getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr);
310  return false;
311}
312
313bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) {
314  if (!isRewritable(range.getBegin())) return true;
315  if (!isRewritable(range.getEnd())) return true;
316  if (replacementRange.isInvalid()) return true;
317  SourceLocation start = range.getBegin();
318  unsigned origLength = getRangeSize(range);
319  unsigned newLength = getRangeSize(replacementRange);
320  FileID FID;
321  unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(),
322                                                FID);
323  StringRef MB = SourceMgr->getBufferData(FID);
324  return ReplaceText(start, origLength, MB.substr(newOffs, newLength));
325}
326
327/// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty
328/// printer to generate the replacement code.  This returns true if the input
329/// could not be rewritten, or false if successful.
330bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) {
331  // Measaure the old text.
332  int Size = getRangeSize(From->getSourceRange());
333  if (Size == -1)
334    return true;
335
336  // Get the new text.
337  std::string SStr;
338  llvm::raw_string_ostream S(SStr);
339  To->printPretty(S, 0, PrintingPolicy(*LangOpts));
340  const std::string &Str = S.str();
341
342  ReplaceText(From->getLocStart(), Size, Str);
343  return false;
344}
345
346std::string Rewriter::ConvertToString(Stmt *From) {
347  std::string SStr;
348  llvm::raw_string_ostream S(SStr);
349  From->printPretty(S, 0, PrintingPolicy(*LangOpts));
350  return S.str();
351}
352
353bool Rewriter::IncreaseIndentation(CharSourceRange range,
354                                   SourceLocation parentIndent) {
355  if (range.isInvalid()) return true;
356  if (!isRewritable(range.getBegin())) return true;
357  if (!isRewritable(range.getEnd())) return true;
358  if (!isRewritable(parentIndent)) return true;
359
360  FileID StartFileID, EndFileID, parentFileID;
361  unsigned StartOff, EndOff, parentOff;
362
363  StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID);
364  EndOff   = getLocationOffsetAndFileID(range.getEnd(), EndFileID);
365  parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID);
366
367  if (StartFileID != EndFileID || StartFileID != parentFileID)
368    return true;
369  if (StartOff > EndOff)
370    return true;
371
372  FileID FID = StartFileID;
373  StringRef MB = SourceMgr->getBufferData(FID);
374
375  unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1;
376  unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1;
377  unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1;
378
379  const SrcMgr::ContentCache *
380      Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache();
381
382  // Find where the lines start.
383  unsigned parentLineOffs = Content->SourceLineCache[parentLineNo];
384  unsigned startLineOffs = Content->SourceLineCache[startLineNo];
385
386  // Find the whitespace at the start of each line.
387  StringRef parentSpace, startSpace;
388  {
389    unsigned i = parentLineOffs;
390    while (isWhitespace(MB[i]))
391      ++i;
392    parentSpace = MB.substr(parentLineOffs, i-parentLineOffs);
393
394    i = startLineOffs;
395    while (isWhitespace(MB[i]))
396      ++i;
397    startSpace = MB.substr(startLineOffs, i-startLineOffs);
398  }
399  if (parentSpace.size() >= startSpace.size())
400    return true;
401  if (!startSpace.startswith(parentSpace))
402    return true;
403
404  StringRef indent = startSpace.substr(parentSpace.size());
405
406  // Indent the lines between start/end offsets.
407  RewriteBuffer &RB = getEditBuffer(FID);
408  for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) {
409    unsigned offs = Content->SourceLineCache[lineNo];
410    unsigned i = offs;
411    while (isWhitespace(MB[i]))
412      ++i;
413    StringRef origIndent = MB.substr(offs, i-offs);
414    if (origIndent.startswith(startSpace))
415      RB.InsertText(offs, indent, /*InsertAfter=*/false);
416  }
417
418  return false;
419}
420
421namespace {
422// A wrapper for a file stream that atomically overwrites the target.
423//
424// Creates a file output stream for a temporary file in the constructor,
425// which is later accessible via getStream() if ok() return true.
426// Flushes the stream and moves the temporary file to the target location
427// in the destructor.
428class AtomicallyMovedFile {
429public:
430  AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename,
431                      bool &AllWritten)
432    : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) {
433    TempFilename = Filename;
434    TempFilename += "-%%%%%%%%";
435    int FD;
436    if (llvm::sys::fs::createUniqueFile(TempFilename.str(), FD, TempFilename)) {
437      AllWritten = false;
438      Diagnostics.Report(clang::diag::err_unable_to_make_temp)
439        << TempFilename;
440    } else {
441      FileStream.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true));
442    }
443  }
444
445  ~AtomicallyMovedFile() {
446    if (!ok()) return;
447
448    FileStream->flush();
449#ifdef _WIN32
450    // Win32 does not allow rename/removing opened files.
451    FileStream.reset();
452#endif
453    if (llvm::error_code ec =
454          llvm::sys::fs::rename(TempFilename.str(), Filename)) {
455      AllWritten = false;
456      Diagnostics.Report(clang::diag::err_unable_to_rename_temp)
457        << TempFilename << Filename << ec.message();
458      bool existed;
459      // If the remove fails, there's not a lot we can do - this is already an
460      // error.
461      llvm::sys::fs::remove(TempFilename.str(), existed);
462    }
463  }
464
465  bool ok() { return FileStream.isValid(); }
466  raw_ostream &getStream() { return *FileStream; }
467
468private:
469  DiagnosticsEngine &Diagnostics;
470  StringRef Filename;
471  SmallString<128> TempFilename;
472  OwningPtr<llvm::raw_fd_ostream> FileStream;
473  bool &AllWritten;
474};
475} // end anonymous namespace
476
477bool Rewriter::overwriteChangedFiles() {
478  bool AllWritten = true;
479  for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
480    const FileEntry *Entry =
481        getSourceMgr().getFileEntryForID(I->first);
482    AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(),
483                             AllWritten);
484    if (File.ok()) {
485      I->second.write(File.getStream());
486    }
487  }
488  return !AllWritten;
489}
490