Rewriter.cpp revision 88ad97f17790b753e2e113b149d7f164e42fa2ba
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/Lex/Lexer.h"
18#include "clang/Basic/SourceManager.h"
19#include <sstream>
20using namespace clang;
21
22/// getMappedOffset - Given an offset into the original SourceBuffer that this
23/// RewriteBuffer is based on, map it into the offset space of the
24/// RewriteBuffer.
25unsigned RewriteBuffer::getMappedOffset(unsigned OrigOffset,
26                                        bool AfterInserts) const {
27  unsigned ResultOffset = OrigOffset;
28  unsigned DeltaIdx = 0;
29
30  // Move past any deltas that are relevant.
31  // FIXME: binary search.
32  for (; DeltaIdx != Deltas.size() &&
33       Deltas[DeltaIdx].FileLoc < OrigOffset; ++DeltaIdx)
34    ResultOffset += Deltas[DeltaIdx].Delta;
35
36  if (AfterInserts)
37    for (; DeltaIdx != Deltas.size() &&
38         OrigOffset == Deltas[DeltaIdx].FileLoc; ++DeltaIdx)
39      ResultOffset += Deltas[DeltaIdx].Delta;
40
41  return ResultOffset;
42}
43
44/// AddDelta - When a change is made that shifts around the text buffer, this
45/// method is used to record that info.
46void RewriteBuffer::AddDelta(unsigned OrigOffset, int Change) {
47  assert(Change != 0 && "Not changing anything");
48  unsigned DeltaIdx = 0;
49
50  // Skip over any unrelated deltas.
51  for (; DeltaIdx != Deltas.size() &&
52       Deltas[DeltaIdx].FileLoc < OrigOffset; ++DeltaIdx)
53    ;
54
55  // If there is no a delta for this offset, insert a new delta record.
56  if (DeltaIdx == Deltas.size() || OrigOffset != Deltas[DeltaIdx].FileLoc) {
57    // If this is a removal, check to see if this can be folded into
58    // a delta at the end of the deletion.  For example, if we have:
59    //  ABCXDEF (X inserted after C) and delete C, we want to end up with no
60    // delta because X basically replaced C.
61    if (Change < 0 && DeltaIdx != Deltas.size() &&
62        OrigOffset-Change == Deltas[DeltaIdx].FileLoc) {
63      // Adjust the start of the delta to be the start of the deleted region.
64      Deltas[DeltaIdx].FileLoc += Change;
65      Deltas[DeltaIdx].Delta += Change;
66
67      // If the delta becomes a noop, remove it.
68      if (Deltas[DeltaIdx].Delta == 0)
69        Deltas.erase(Deltas.begin()+DeltaIdx);
70      return;
71    }
72
73    // Otherwise, create an entry and return.
74    Deltas.insert(Deltas.begin()+DeltaIdx,
75                  SourceDelta::get(OrigOffset, Change));
76    return;
77  }
78
79  // Otherwise, we found a delta record at this offset, adjust it.
80  Deltas[DeltaIdx].Delta += Change;
81
82  // If it is now dead, remove it.
83  if (Deltas[DeltaIdx].Delta == 0)
84    Deltas.erase(Deltas.begin()+DeltaIdx);
85}
86
87
88void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size) {
89  // Nothing to remove, exit early.
90  if (Size == 0) return;
91
92  unsigned RealOffset = getMappedOffset(OrigOffset, true);
93  assert(RealOffset+Size < Buffer.size() && "Invalid location");
94
95  // Remove the dead characters.
96  RewriteRope::iterator I = Buffer.getAtOffset(RealOffset);
97  Buffer.erase(I, I+Size);
98
99  // Add a delta so that future changes are offset correctly.
100  AddDelta(OrigOffset, -Size);
101}
102
103void RewriteBuffer::InsertText(unsigned OrigOffset,
104                               const char *StrData, unsigned StrLen) {
105  // Nothing to insert, exit early.
106  if (StrLen == 0) return;
107
108  unsigned RealOffset = getMappedOffset(OrigOffset, true);
109  assert(RealOffset <= Buffer.size() && "Invalid location");
110
111  // Insert the new characters.
112  Buffer.insert(Buffer.getAtOffset(RealOffset), StrData, StrData+StrLen);
113
114  // Add a delta so that future changes are offset correctly.
115  AddDelta(OrigOffset, StrLen);
116}
117
118/// ReplaceText - This method replaces a range of characters in the input
119/// buffer with a new string.  This is effectively a combined "remove/insert"
120/// operation.
121void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
122                                const char *NewStr, unsigned NewLength) {
123  unsigned RealOffset = getMappedOffset(OrigOffset, true);
124  assert(RealOffset+OrigLength <= Buffer.size() && "Invalid location");
125
126  // Overwrite the common piece.
127  unsigned CommonLength = std::min(OrigLength, NewLength);
128  std::copy(NewStr, NewStr+CommonLength, Buffer.getAtOffset(RealOffset));
129
130  // If replacing without shifting around, just overwrite the text.
131  if (OrigLength == NewLength)
132    return;
133
134  // If inserting more than existed before, this is like an insertion.
135  if (NewLength > OrigLength) {
136    Buffer.insert(Buffer.getAtOffset(RealOffset+OrigLength),
137                  NewStr+OrigLength, NewStr+NewLength);
138  } else {
139    // If inserting less than existed before, this is like a removal.
140    RewriteRope::iterator I = Buffer.getAtOffset(RealOffset+NewLength);
141    Buffer.erase(I, I+(OrigLength-NewLength));
142  }
143  AddDelta(OrigOffset, NewLength-OrigLength);
144}
145
146
147//===----------------------------------------------------------------------===//
148// Rewriter class
149//===----------------------------------------------------------------------===//
150
151/// getRangeSize - Return the size in bytes of the specified range if they
152/// are in the same file.  If not, this returns -1.
153int Rewriter::getRangeSize(SourceRange Range) const {
154  if (!isRewritable(Range.getBegin()) ||
155      !isRewritable(Range.getEnd())) return -1;
156
157  unsigned StartOff, StartFileID;
158  unsigned EndOff  , EndFileID;
159
160  StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
161  EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
162
163  if (StartFileID != EndFileID)
164    return -1;
165
166  // If edits have been made to this buffer, the delta between the range may
167  // have changed.
168  std::map<unsigned, RewriteBuffer>::const_iterator I =
169    RewriteBuffers.find(StartFileID);
170  if (I != RewriteBuffers.end()) {
171    const RewriteBuffer &RB = I->second;
172    EndOff = RB.getMappedOffset(EndOff, true);
173    StartOff = RB.getMappedOffset(StartOff);
174  }
175
176
177  // Adjust the end offset to the end of the last token, instead of being the
178  // start of the last token.
179  EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr);
180
181  return EndOff-StartOff;
182}
183
184
185unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
186                                              unsigned &FileID) const {
187  std::pair<unsigned,unsigned> V = SourceMgr->getDecomposedFileLoc(Loc);
188  FileID = V.first;
189  return V.second;
190}
191
192
193/// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
194///
195RewriteBuffer &Rewriter::getEditBuffer(unsigned FileID) {
196  std::map<unsigned, RewriteBuffer>::iterator I =
197    RewriteBuffers.lower_bound(FileID);
198  if (I != RewriteBuffers.end() && I->first == FileID)
199    return I->second;
200  I = RewriteBuffers.insert(I, std::make_pair(FileID, RewriteBuffer()));
201
202  std::pair<const char*, const char*> MB = SourceMgr->getBufferData(FileID);
203  I->second.Initialize(MB.first, MB.second);
204
205  return I->second;
206}
207
208/// InsertText - Insert the specified string at the specified location in the
209/// original buffer.
210bool Rewriter::InsertText(SourceLocation Loc,
211                          const char *StrData, unsigned StrLen) {
212  if (!isRewritable(Loc)) return true;
213  unsigned FileID;
214  unsigned StartOffs = getLocationOffsetAndFileID(Loc, FileID);
215  getEditBuffer(FileID).InsertText(StartOffs, StrData, StrLen);
216  return false;
217}
218
219/// RemoveText - Remove the specified text region.
220bool Rewriter::RemoveText(SourceLocation Start, unsigned Length) {
221  if (!isRewritable(Start)) return true;
222  unsigned FileID;
223  unsigned StartOffs = getLocationOffsetAndFileID(Start, FileID);
224  getEditBuffer(FileID).RemoveText(StartOffs, Length);
225  return false;
226}
227
228/// ReplaceText - This method replaces a range of characters in the input
229/// buffer with a new string.  This is effectively a combined "remove/insert"
230/// operation.
231bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
232                           const char *NewStr, unsigned NewLength) {
233  if (!isRewritable(Start)) return true;
234  unsigned StartFileID;
235  unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
236
237  getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength,
238                                         NewStr, NewLength);
239  return false;
240}
241
242/// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty
243/// printer to generate the replacement code.  This returns true if the input
244/// could not be rewritten, or false if successful.
245bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) {
246  // Measaure the old text.
247  int Size = getRangeSize(From->getSourceRange());
248  if (Size == -1)
249    return true;
250
251  // Get the new text.
252  std::ostringstream S;
253  To->printPretty(S);
254  const std::string &Str = S.str();
255
256  ReplaceText(From->getLocStart(), Size, &Str[0], Str.size());
257  return false;
258}
259
260
261