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