]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - contrib/llvm/tools/clang/lib/Rewrite/Rewriter.cpp
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / contrib / llvm / tools / clang / lib / Rewrite / Rewriter.cpp
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/Rewriter.h"
16 #include "clang/AST/Stmt.h"
17 #include "clang/AST/Decl.h"
18 #include "clang/Lex/Lexer.h"
19 #include "clang/Basic/SourceManager.h"
20 using namespace clang;
21
22 raw_ostream &RewriteBuffer::write(raw_ostream &os) const {
23   // FIXME: eliminate the copy by writing out each chunk at a time
24   os << std::string(begin(), end());
25   return os;
26 }
27
28 /// \brief Return true if this character is non-new-line whitespace:
29 /// ' ', '\t', '\f', '\v', '\r'.
30 static inline bool isWhitespace(unsigned char c) {
31   switch (c) {
32   case ' ':
33   case '\t':
34   case '\f':
35   case '\v':
36   case '\r':
37     return true;
38   default:
39     return false;
40   }
41 }
42
43 void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size,
44                                bool removeLineIfEmpty) {
45   // Nothing to remove, exit early.
46   if (Size == 0) return;
47
48   unsigned RealOffset = getMappedOffset(OrigOffset, true);
49   assert(RealOffset+Size < Buffer.size() && "Invalid location");
50
51   // Remove the dead characters.
52   Buffer.erase(RealOffset, Size);
53
54   // Add a delta so that future changes are offset correctly.
55   AddReplaceDelta(OrigOffset, -Size);
56
57   if (removeLineIfEmpty) {
58     // Find the line that the remove occurred and if it is completely empty
59     // remove the line as well.
60
61     iterator curLineStart = begin();
62     unsigned curLineStartOffs = 0;
63     iterator posI = begin();
64     for (unsigned i = 0; i != RealOffset; ++i) {
65       if (*posI == '\n') {
66         curLineStart = posI;
67         ++curLineStart;
68         curLineStartOffs = i + 1;
69       }
70       ++posI;
71     }
72   
73     unsigned lineSize = 0;
74     posI = curLineStart;
75     while (posI != end() && isWhitespace(*posI)) {
76       ++posI;
77       ++lineSize;
78     }
79     if (posI != end() && *posI == '\n') {
80       Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/);
81       AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/));
82     }
83   }
84 }
85
86 void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str,
87                                bool InsertAfter) {
88
89   // Nothing to insert, exit early.
90   if (Str.empty()) return;
91
92   unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter);
93   Buffer.insert(RealOffset, Str.begin(), Str.end());
94
95   // Add a delta so that future changes are offset correctly.
96   AddInsertDelta(OrigOffset, Str.size());
97 }
98
99 /// ReplaceText - This method replaces a range of characters in the input
100 /// buffer with a new string.  This is effectively a combined "remove+insert"
101 /// operation.
102 void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
103                                 StringRef NewStr) {
104   unsigned RealOffset = getMappedOffset(OrigOffset, true);
105   Buffer.erase(RealOffset, OrigLength);
106   Buffer.insert(RealOffset, NewStr.begin(), NewStr.end());
107   if (OrigLength != NewStr.size())
108     AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength);
109 }
110
111
112 //===----------------------------------------------------------------------===//
113 // Rewriter class
114 //===----------------------------------------------------------------------===//
115
116 /// getRangeSize - Return the size in bytes of the specified range if they
117 /// are in the same file.  If not, this returns -1.
118 int Rewriter::getRangeSize(const CharSourceRange &Range,
119                            RewriteOptions opts) const {
120   if (!isRewritable(Range.getBegin()) ||
121       !isRewritable(Range.getEnd())) return -1;
122
123   FileID StartFileID, EndFileID;
124   unsigned StartOff, EndOff;
125
126   StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
127   EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
128
129   if (StartFileID != EndFileID)
130     return -1;
131
132   // If edits have been made to this buffer, the delta between the range may
133   // have changed.
134   std::map<FileID, RewriteBuffer>::const_iterator I =
135     RewriteBuffers.find(StartFileID);
136   if (I != RewriteBuffers.end()) {
137     const RewriteBuffer &RB = I->second;
138     EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange);
139     StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange);
140   }
141
142
143   // Adjust the end offset to the end of the last token, instead of being the
144   // start of the last token if this is a token range.
145   if (Range.isTokenRange())
146     EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
147
148   return EndOff-StartOff;
149 }
150
151 int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const {
152   return getRangeSize(CharSourceRange::getTokenRange(Range), opts);
153 }
154
155
156 /// getRewrittenText - Return the rewritten form of the text in the specified
157 /// range.  If the start or end of the range was unrewritable or if they are
158 /// in different buffers, this returns an empty string.
159 ///
160 /// Note that this method is not particularly efficient.
161 ///
162 std::string Rewriter::getRewrittenText(SourceRange Range) const {
163   if (!isRewritable(Range.getBegin()) ||
164       !isRewritable(Range.getEnd()))
165     return "";
166
167   FileID StartFileID, EndFileID;
168   unsigned StartOff, EndOff;
169   StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
170   EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
171
172   if (StartFileID != EndFileID)
173     return ""; // Start and end in different buffers.
174
175   // If edits have been made to this buffer, the delta between the range may
176   // have changed.
177   std::map<FileID, RewriteBuffer>::const_iterator I =
178     RewriteBuffers.find(StartFileID);
179   if (I == RewriteBuffers.end()) {
180     // If the buffer hasn't been rewritten, just return the text from the input.
181     const char *Ptr = SourceMgr->getCharacterData(Range.getBegin());
182
183     // Adjust the end offset to the end of the last token, instead of being the
184     // start of the last token.
185     EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
186     return std::string(Ptr, Ptr+EndOff-StartOff);
187   }
188
189   const RewriteBuffer &RB = I->second;
190   EndOff = RB.getMappedOffset(EndOff, true);
191   StartOff = RB.getMappedOffset(StartOff);
192
193   // Adjust the end offset to the end of the last token, instead of being the
194   // start of the last token.
195   EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
196
197   // Advance the iterators to the right spot, yay for linear time algorithms.
198   RewriteBuffer::iterator Start = RB.begin();
199   std::advance(Start, StartOff);
200   RewriteBuffer::iterator End = Start;
201   std::advance(End, EndOff-StartOff);
202
203   return std::string(Start, End);
204 }
205
206 unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
207                                               FileID &FID) const {
208   assert(Loc.isValid() && "Invalid location");
209   std::pair<FileID,unsigned> V = SourceMgr->getDecomposedLoc(Loc);
210   FID = V.first;
211   return V.second;
212 }
213
214
215 /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
216 ///
217 RewriteBuffer &Rewriter::getEditBuffer(FileID FID) {
218   std::map<FileID, RewriteBuffer>::iterator I =
219     RewriteBuffers.lower_bound(FID);
220   if (I != RewriteBuffers.end() && I->first == FID)
221     return I->second;
222   I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));
223
224   StringRef MB = SourceMgr->getBufferData(FID);
225   I->second.Initialize(MB.begin(), MB.end());
226
227   return I->second;
228 }
229
230 /// InsertText - Insert the specified string at the specified location in the
231 /// original buffer.
232 bool Rewriter::InsertText(SourceLocation Loc, StringRef Str,
233                           bool InsertAfter, bool indentNewLines) {
234   if (!isRewritable(Loc)) return true;
235   FileID FID;
236   unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
237
238   llvm::SmallString<128> indentedStr;
239   if (indentNewLines && Str.find('\n') != StringRef::npos) {
240     StringRef MB = SourceMgr->getBufferData(FID);
241
242     unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1;
243     const SrcMgr::ContentCache *
244         Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache();
245     unsigned lineOffs = Content->SourceLineCache[lineNo];
246
247     // Find the whitespace at the start of the line.
248     StringRef indentSpace;
249     {
250       unsigned i = lineOffs;
251       while (isWhitespace(MB[i]))
252         ++i;
253       indentSpace = MB.substr(lineOffs, i-lineOffs);
254     }
255
256     SmallVector<StringRef, 4> lines;
257     Str.split(lines, "\n");
258
259     for (unsigned i = 0, e = lines.size(); i != e; ++i) {
260       indentedStr += lines[i];
261       if (i < e-1) {
262         indentedStr += '\n';
263         indentedStr += indentSpace;
264       }
265     }
266     Str = indentedStr.str();
267   }
268
269   getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter);
270   return false;
271 }
272
273 bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) {
274   if (!isRewritable(Loc)) return true;
275   FileID FID;
276   unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
277   RewriteOptions rangeOpts;
278   rangeOpts.IncludeInsertsAtBeginOfRange = false;
279   StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts);
280   getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true);
281   return false;
282 }
283
284 /// RemoveText - Remove the specified text region.
285 bool Rewriter::RemoveText(SourceLocation Start, unsigned Length,
286                           RewriteOptions opts) {
287   if (!isRewritable(Start)) return true;
288   FileID FID;
289   unsigned StartOffs = getLocationOffsetAndFileID(Start, FID);
290   getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty);
291   return false;
292 }
293
294 /// ReplaceText - This method replaces a range of characters in the input
295 /// buffer with a new string.  This is effectively a combined "remove/insert"
296 /// operation.
297 bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
298                            StringRef NewStr) {
299   if (!isRewritable(Start)) return true;
300   FileID StartFileID;
301   unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
302
303   getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr);
304   return false;
305 }
306
307 bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) {
308   if (!isRewritable(range.getBegin())) return true;
309   if (!isRewritable(range.getEnd())) return true;
310   if (replacementRange.isInvalid()) return true;
311   SourceLocation start = range.getBegin();
312   unsigned origLength = getRangeSize(range);
313   unsigned newLength = getRangeSize(replacementRange);
314   FileID FID;
315   unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(),
316                                                 FID);
317   StringRef MB = SourceMgr->getBufferData(FID);
318   return ReplaceText(start, origLength, MB.substr(newOffs, newLength));
319 }
320
321 /// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty
322 /// printer to generate the replacement code.  This returns true if the input
323 /// could not be rewritten, or false if successful.
324 bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) {
325   // Measaure the old text.
326   int Size = getRangeSize(From->getSourceRange());
327   if (Size == -1)
328     return true;
329
330   // Get the new text.
331   std::string SStr;
332   llvm::raw_string_ostream S(SStr);
333   To->printPretty(S, 0, PrintingPolicy(*LangOpts));
334   const std::string &Str = S.str();
335
336   ReplaceText(From->getLocStart(), Size, Str);
337   return false;
338 }
339
340 std::string Rewriter::ConvertToString(Stmt *From) {
341   std::string SStr;
342   llvm::raw_string_ostream S(SStr);
343   From->printPretty(S, 0, PrintingPolicy(*LangOpts));
344   return S.str();
345 }
346
347 bool Rewriter::IncreaseIndentation(CharSourceRange range,
348                                    SourceLocation parentIndent) {
349   if (range.isInvalid()) return true;
350   if (!isRewritable(range.getBegin())) return true;
351   if (!isRewritable(range.getEnd())) return true;
352   if (!isRewritable(parentIndent)) return true;
353
354   FileID StartFileID, EndFileID, parentFileID;
355   unsigned StartOff, EndOff, parentOff;
356
357   StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID);
358   EndOff   = getLocationOffsetAndFileID(range.getEnd(), EndFileID);
359   parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID);
360
361   if (StartFileID != EndFileID || StartFileID != parentFileID)
362     return true;
363   if (StartOff > EndOff)
364     return true;
365
366   FileID FID = StartFileID;
367   StringRef MB = SourceMgr->getBufferData(FID);
368
369   unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1;
370   unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1;
371   unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1;
372   
373   const SrcMgr::ContentCache *
374       Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache();
375   
376   // Find where the lines start.
377   unsigned parentLineOffs = Content->SourceLineCache[parentLineNo];
378   unsigned startLineOffs = Content->SourceLineCache[startLineNo];
379
380   // Find the whitespace at the start of each line.
381   StringRef parentSpace, startSpace;
382   {
383     unsigned i = parentLineOffs;
384     while (isWhitespace(MB[i]))
385       ++i;
386     parentSpace = MB.substr(parentLineOffs, i-parentLineOffs);
387
388     i = startLineOffs;
389     while (isWhitespace(MB[i]))
390       ++i;
391     startSpace = MB.substr(startLineOffs, i-startLineOffs);
392   }
393   if (parentSpace.size() >= startSpace.size())
394     return true;
395   if (!startSpace.startswith(parentSpace))
396     return true;
397
398   StringRef indent = startSpace.substr(parentSpace.size());
399
400   // Indent the lines between start/end offsets.
401   RewriteBuffer &RB = getEditBuffer(FID);
402   for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) {
403     unsigned offs = Content->SourceLineCache[lineNo];
404     unsigned i = offs;
405     while (isWhitespace(MB[i]))
406       ++i;
407     StringRef origIndent = MB.substr(offs, i-offs);
408     if (origIndent.startswith(startSpace))
409       RB.InsertText(offs, indent, /*InsertAfter=*/false);
410   }
411
412   return false;
413 }