EditedSource.cpp revision 55fc873017f10f6f566b182b70f6fc22aefa3464
1//===----- EditedSource.cpp - Collection of source edits ------------------===//
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 "clang/Edit/EditedSource.h"
11#include "clang/Basic/SourceManager.h"
12#include "clang/Edit/Commit.h"
13#include "clang/Edit/EditsReceiver.h"
14#include "clang/Lex/Lexer.h"
15#include "llvm/ADT/SmallString.h"
16#include "llvm/ADT/Twine.h"
17
18using namespace clang;
19using namespace edit;
20
21void EditsReceiver::remove(CharSourceRange range) {
22  replace(range, StringRef());
23}
24
25StringRef EditedSource::copyString(const Twine &twine) {
26  llvm::SmallString<128> Data;
27  return copyString(twine.toStringRef(Data));
28}
29
30bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
31  FileEditsTy::iterator FA = getActionForOffset(Offs);
32  if (FA != FileEdits.end()) {
33    if (FA->first != Offs)
34      return false; // position has been removed.
35  }
36
37  if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
38    SourceLocation
39      DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first;
40    SourceLocation
41      ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
42    llvm::DenseMap<unsigned, SourceLocation>::iterator
43      I = ExpansionToArgMap.find(ExpLoc.getRawEncoding());
44    if (I != ExpansionToArgMap.end() && I->second != DefArgLoc)
45      return false; // Trying to write in a macro argument input that has
46                 // already been written for another argument of the same macro.
47  }
48
49  return true;
50}
51
52bool EditedSource::commitInsert(SourceLocation OrigLoc,
53                                FileOffset Offs, StringRef text,
54                                bool beforePreviousInsertions) {
55  if (!canInsertInOffset(OrigLoc, Offs))
56    return false;
57  if (text.empty())
58    return true;
59
60  if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
61    SourceLocation
62      DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first;
63    SourceLocation
64      ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
65    ExpansionToArgMap[ExpLoc.getRawEncoding()] = DefArgLoc;
66  }
67
68  FileEdit &FA = FileEdits[Offs];
69  if (FA.Text.empty()) {
70    FA.Text = copyString(text);
71    return true;
72  }
73
74  Twine concat;
75  if (beforePreviousInsertions)
76    concat = Twine(text) + FA.Text;
77  else
78    concat = Twine(FA.Text) +  text;
79
80  FA.Text = copyString(concat);
81  return true;
82}
83
84bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
85                                   FileOffset Offs,
86                                   FileOffset InsertFromRangeOffs, unsigned Len,
87                                   bool beforePreviousInsertions) {
88  if (Len == 0)
89    return true;
90
91  llvm::SmallString<128> StrVec;
92  FileOffset BeginOffs = InsertFromRangeOffs;
93  FileOffset EndOffs = BeginOffs.getWithOffset(Len);
94  FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
95  if (I != FileEdits.begin())
96    --I;
97
98  for (; I != FileEdits.end(); ++I) {
99    FileEdit &FA = I->second;
100    FileOffset B = I->first;
101    FileOffset E = B.getWithOffset(FA.RemoveLen);
102
103    if (BeginOffs == B)
104      break;
105
106    if (BeginOffs < E) {
107      if (BeginOffs > B) {
108        BeginOffs = E;
109        ++I;
110      }
111      break;
112    }
113  }
114
115  for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
116    FileEdit &FA = I->second;
117    FileOffset B = I->first;
118    FileOffset E = B.getWithOffset(FA.RemoveLen);
119
120    if (BeginOffs < B) {
121      bool Invalid = false;
122      StringRef text = getSourceText(BeginOffs, B, Invalid);
123      if (Invalid)
124        return false;
125      StrVec += text;
126    }
127    StrVec += FA.Text;
128    BeginOffs = E;
129  }
130
131  if (BeginOffs < EndOffs) {
132    bool Invalid = false;
133    StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
134    if (Invalid)
135      return false;
136    StrVec += text;
137  }
138
139  return commitInsert(OrigLoc, Offs, StrVec.str(), beforePreviousInsertions);
140}
141
142void EditedSource::commitRemove(SourceLocation OrigLoc,
143                                FileOffset BeginOffs, unsigned Len) {
144  if (Len == 0)
145    return;
146
147  FileOffset EndOffs = BeginOffs.getWithOffset(Len);
148  FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
149  if (I != FileEdits.begin())
150    --I;
151
152  for (; I != FileEdits.end(); ++I) {
153    FileEdit &FA = I->second;
154    FileOffset B = I->first;
155    FileOffset E = B.getWithOffset(FA.RemoveLen);
156
157    if (BeginOffs < E)
158      break;
159  }
160
161  FileOffset TopBegin, TopEnd;
162  FileEdit *TopFA = 0;
163
164  if (I == FileEdits.end()) {
165    FileEditsTy::iterator
166      NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
167    NewI->second.RemoveLen = Len;
168    return;
169  }
170
171  FileEdit &FA = I->second;
172  FileOffset B = I->first;
173  FileOffset E = B.getWithOffset(FA.RemoveLen);
174  if (BeginOffs < B) {
175    FileEditsTy::iterator
176      NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
177    TopBegin = BeginOffs;
178    TopEnd = EndOffs;
179    TopFA = &NewI->second;
180    TopFA->RemoveLen = Len;
181  } else {
182    TopBegin = B;
183    TopEnd = E;
184    TopFA = &I->second;
185    if (TopEnd >= EndOffs)
186      return;
187    unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
188    TopEnd = EndOffs;
189    TopFA->RemoveLen += diff;
190    ++I;
191  }
192
193  while (I != FileEdits.end()) {
194    FileEdit &FA = I->second;
195    FileOffset B = I->first;
196    FileOffset E = B.getWithOffset(FA.RemoveLen);
197
198    if (B >= TopEnd)
199      break;
200
201    if (E <= TopEnd) {
202      FileEdits.erase(I++);
203      continue;
204    }
205
206    if (B < TopEnd) {
207      unsigned diff = E.getOffset() - TopEnd.getOffset();
208      TopEnd = E;
209      TopFA->RemoveLen += diff;
210      FileEdits.erase(I);
211    }
212
213    break;
214  }
215}
216
217bool EditedSource::commit(const Commit &commit) {
218  if (!commit.isCommitable())
219    return false;
220
221  for (edit::Commit::edit_iterator
222         I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
223    const edit::Commit::Edit &edit = *I;
224    switch (edit.Kind) {
225    case edit::Commit::Act_Insert:
226      commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
227      break;
228    case edit::Commit::Act_InsertFromRange:
229      commitInsertFromRange(edit.OrigLoc, edit.Offset,
230                            edit.InsertFromRangeOffs, edit.Length,
231                            edit.BeforePrev);
232      break;
233    case edit::Commit::Act_Remove:
234      commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
235      break;
236    }
237  }
238
239  return true;
240}
241
242static void applyRewrite(EditsReceiver &receiver,
243                         StringRef text, FileOffset offs, unsigned len,
244                         const SourceManager &SM) {
245  assert(!offs.getFID().isInvalid());
246  SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
247  Loc = Loc.getLocWithOffset(offs.getOffset());
248  assert(Loc.isFileID());
249  CharSourceRange range = CharSourceRange::getCharRange(Loc,
250                                                     Loc.getLocWithOffset(len));
251
252  if (text.empty()) {
253    assert(len);
254    receiver.remove(range);
255    return;
256  }
257
258  if (len)
259    receiver.replace(range, text);
260  else
261    receiver.insert(Loc, text);
262}
263
264void EditedSource::applyRewrites(EditsReceiver &receiver) {
265  llvm::SmallString<128> StrVec;
266  FileOffset CurOffs, CurEnd;
267  unsigned CurLen;
268
269  if (FileEdits.empty())
270    return;
271
272  FileEditsTy::iterator I = FileEdits.begin();
273  CurOffs = I->first;
274  StrVec = I->second.Text;
275  CurLen = I->second.RemoveLen;
276  CurEnd = CurOffs.getWithOffset(CurLen);
277  ++I;
278
279  for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
280    FileOffset offs = I->first;
281    FileEdit act = I->second;
282    assert(offs >= CurEnd);
283
284    if (offs == CurEnd) {
285      StrVec += act.Text;
286      CurLen += act.RemoveLen;
287      CurEnd.getWithOffset(act.RemoveLen);
288      continue;
289    }
290
291    applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr);
292    CurOffs = offs;
293    StrVec = act.Text;
294    CurLen = act.RemoveLen;
295    CurEnd = CurOffs.getWithOffset(CurLen);
296  }
297
298  applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr);
299}
300
301void EditedSource::clearRewrites() {
302  FileEdits.clear();
303  StrAlloc.Reset();
304}
305
306StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
307                                      bool &Invalid) {
308  assert(BeginOffs.getFID() == EndOffs.getFID());
309  assert(BeginOffs <= EndOffs);
310  SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
311  BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
312  assert(BLoc.isFileID());
313  SourceLocation
314    ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
315  return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
316                              SourceMgr, LangOpts, &Invalid);
317}
318
319EditedSource::FileEditsTy::iterator
320EditedSource::getActionForOffset(FileOffset Offs) {
321  FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
322  if (I == FileEdits.begin())
323    return FileEdits.end();
324  --I;
325  FileEdit &FA = I->second;
326  FileOffset B = I->first;
327  FileOffset E = B.getWithOffset(FA.RemoveLen);
328  if (Offs >= B && Offs < E)
329    return I;
330
331  return FileEdits.end();
332}
333