blob: 5b7fa4ad1b67fc07b58724b4d670f329a3c6ec81 [file] [log] [blame]
Ted Kremenek30660a82012-03-06 20:06:33 +00001//===----- 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/Edit/Commit.h"
12#include "clang/Edit/EditsReceiver.h"
13#include "clang/Lex/Lexer.h"
14#include "clang/Basic/SourceManager.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 < E) {
104 if (BeginOffs >= B) {
105 BeginOffs = E;
106 ++I;
107 }
108 break;
109 }
110 }
111
112 for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
113 FileEdit &FA = I->second;
114 FileOffset B = I->first;
115 FileOffset E = B.getWithOffset(FA.RemoveLen);
116
117 if (BeginOffs < B) {
118 bool Invalid = false;
119 StringRef text = getSourceText(BeginOffs, B, Invalid);
120 if (Invalid)
121 return false;
122 StrVec += text;
123 }
124 StrVec += FA.Text;
125 BeginOffs = E;
126 }
127
128 if (BeginOffs < EndOffs) {
129 bool Invalid = false;
130 StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
131 if (Invalid)
132 return false;
133 StrVec += text;
134 }
135
136 return commitInsert(OrigLoc, Offs, StrVec.str(), beforePreviousInsertions);
137}
138
139void EditedSource::commitRemove(SourceLocation OrigLoc,
140 FileOffset BeginOffs, unsigned Len) {
141 if (Len == 0)
142 return;
143
144 FileOffset EndOffs = BeginOffs.getWithOffset(Len);
145 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
146 if (I != FileEdits.begin())
147 --I;
148
149 for (; I != FileEdits.end(); ++I) {
150 FileEdit &FA = I->second;
151 FileOffset B = I->first;
152 FileOffset E = B.getWithOffset(FA.RemoveLen);
153
154 if (BeginOffs < E)
155 break;
156 }
157
158 FileOffset TopBegin, TopEnd;
159 FileEdit *TopFA = 0;
160
161 if (I == FileEdits.end()) {
162 FileEditsTy::iterator
163 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
164 NewI->second.RemoveLen = Len;
165 return;
166 }
167
168 FileEdit &FA = I->second;
169 FileOffset B = I->first;
170 FileOffset E = B.getWithOffset(FA.RemoveLen);
171 if (BeginOffs < B) {
172 FileEditsTy::iterator
173 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
174 TopBegin = BeginOffs;
175 TopEnd = EndOffs;
176 TopFA = &NewI->second;
177 TopFA->RemoveLen = Len;
178 } else {
179 TopBegin = B;
180 TopEnd = E;
181 TopFA = &I->second;
182 if (TopEnd >= EndOffs)
183 return;
184 unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
185 TopEnd = EndOffs;
186 TopFA->RemoveLen += diff;
187 ++I;
188 }
189
190 while (I != FileEdits.end()) {
191 FileEdit &FA = I->second;
192 FileOffset B = I->first;
193 FileOffset E = B.getWithOffset(FA.RemoveLen);
194
195 if (B >= TopEnd)
196 break;
197
198 if (E <= TopEnd) {
199 FileEdits.erase(I++);
200 continue;
201 }
202
203 if (B < TopEnd) {
204 unsigned diff = E.getOffset() - TopEnd.getOffset();
205 TopEnd = E;
206 TopFA->RemoveLen += diff;
207 FileEdits.erase(I);
208 }
209
210 break;
211 }
212}
213
214bool EditedSource::commit(const Commit &commit) {
215 if (!commit.isCommitable())
216 return false;
217
218 for (edit::Commit::edit_iterator
219 I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
220 const edit::Commit::Edit &edit = *I;
221 switch (edit.Kind) {
222 case edit::Commit::Act_Insert:
223 commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
224 break;
225 case edit::Commit::Act_InsertFromRange:
226 commitInsertFromRange(edit.OrigLoc, edit.Offset,
227 edit.InsertFromRangeOffs, edit.Length,
228 edit.BeforePrev);
229 break;
230 case edit::Commit::Act_Remove:
231 commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
232 break;
233 }
234 }
235
236 return true;
237}
238
239static void applyRewrite(EditsReceiver &receiver,
240 StringRef text, FileOffset offs, unsigned len,
241 const SourceManager &SM) {
242 assert(!offs.getFID().isInvalid());
243 SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
244 Loc = Loc.getLocWithOffset(offs.getOffset());
245 assert(Loc.isFileID());
246 CharSourceRange range = CharSourceRange::getCharRange(Loc,
247 Loc.getLocWithOffset(len));
248
249 if (text.empty()) {
250 assert(len);
251 receiver.remove(range);
252 return;
253 }
254
255 if (len)
256 receiver.replace(range, text);
257 else
258 receiver.insert(Loc, text);
259}
260
261void EditedSource::applyRewrites(EditsReceiver &receiver) {
262 llvm::SmallString<128> StrVec;
263 FileOffset CurOffs, CurEnd;
264 unsigned CurLen;
265
266 if (FileEdits.empty())
267 return;
268
269 FileEditsTy::iterator I = FileEdits.begin();
270 CurOffs = I->first;
271 StrVec = I->second.Text;
272 CurLen = I->second.RemoveLen;
273 CurEnd = CurOffs.getWithOffset(CurLen);
274 ++I;
275
276 for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
277 FileOffset offs = I->first;
278 FileEdit act = I->second;
279 assert(offs >= CurEnd);
280
281 if (offs == CurEnd) {
282 StrVec += act.Text;
283 CurLen += act.RemoveLen;
284 CurEnd.getWithOffset(act.RemoveLen);
285 continue;
286 }
287
288 applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr);
289 CurOffs = offs;
290 StrVec = act.Text;
291 CurLen = act.RemoveLen;
292 CurEnd = CurOffs.getWithOffset(CurLen);
293 }
294
295 applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr);
296}
297
298void EditedSource::clearRewrites() {
299 FileEdits.clear();
300 StrAlloc.Reset();
301}
302
303StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
304 bool &Invalid) {
305 assert(BeginOffs.getFID() == EndOffs.getFID());
306 assert(BeginOffs <= EndOffs);
307 SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
308 BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
309 assert(BLoc.isFileID());
310 SourceLocation
311 ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
312 return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
313 SourceMgr, LangOpts, &Invalid);
314}
315
316EditedSource::FileEditsTy::iterator
317EditedSource::getActionForOffset(FileOffset Offs) {
318 FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
319 if (I == FileEdits.begin())
320 return FileEdits.end();
321 --I;
322 FileEdit &FA = I->second;
323 FileOffset B = I->first;
324 FileOffset E = B.getWithOffset(FA.RemoveLen);
325 if (Offs >= B && Offs < E)
326 return I;
327
328 return FileEdits.end();
329}