blob: 03d45cf185c9f718c1867951f9f6a3e58fc3c3c6 [file] [log] [blame]
Ted Kremenekf7639e12012-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"
Jordan Rosea7d03842013-02-08 22:30:41 +000011#include "clang/Basic/CharInfo.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000012#include "clang/Basic/SourceManager.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000013#include "clang/Edit/Commit.h"
14#include "clang/Edit/EditsReceiver.h"
15#include "clang/Lex/Lexer.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000016#include "llvm/ADT/SmallString.h"
17#include "llvm/ADT/Twine.h"
18
19using namespace clang;
20using namespace edit;
21
22void EditsReceiver::remove(CharSourceRange range) {
23 replace(range, StringRef());
24}
25
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000026void EditedSource::deconstructMacroArgLoc(SourceLocation Loc,
27 SourceLocation &ExpansionLoc,
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +000028 MacroArgUse &ArgUse) {
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000029 assert(SourceMgr.isMacroArgExpansion(Loc));
30 SourceLocation DefArgLoc = SourceMgr.getImmediateExpansionRange(Loc).first;
31 ExpansionLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
32 SmallString<20> Buf;
33 StringRef ArgName = Lexer::getSpelling(SourceMgr.getSpellingLoc(DefArgLoc),
34 Buf, SourceMgr, LangOpts);
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +000035 ArgUse = {nullptr, SourceLocation()};
36 if (!ArgName.empty())
37 ArgUse = {&IdentTable.get(ArgName), SourceMgr.getSpellingLoc(DefArgLoc)};
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000038}
39
40void EditedSource::startingCommit() {}
41
42void EditedSource::finishedCommit() {
43 for (auto &ExpArg : CurrCommitMacroArgExps) {
44 SourceLocation ExpLoc;
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +000045 MacroArgUse ArgUse;
46 std::tie(ExpLoc, ArgUse) = ExpArg;
47 auto &ArgUses = ExpansionToArgMap[ExpLoc.getRawEncoding()];
48 if (std::find(ArgUses.begin(), ArgUses.end(), ArgUse) == ArgUses.end())
49 ArgUses.push_back(ArgUse);
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000050 }
51 CurrCommitMacroArgExps.clear();
52}
53
Ted Kremenekf7639e12012-03-06 20:06:33 +000054StringRef EditedSource::copyString(const Twine &twine) {
Dmitri Gribenkof8579502013-01-12 19:30:44 +000055 SmallString<128> Data;
Ted Kremenekf7639e12012-03-06 20:06:33 +000056 return copyString(twine.toStringRef(Data));
57}
58
59bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
60 FileEditsTy::iterator FA = getActionForOffset(Offs);
61 if (FA != FileEdits.end()) {
62 if (FA->first != Offs)
63 return false; // position has been removed.
64 }
65
66 if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000067 SourceLocation ExpLoc;
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +000068 MacroArgUse ArgUse;
69 deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000070 auto I = ExpansionToArgMap.find(ExpLoc.getRawEncoding());
71 if (I != ExpansionToArgMap.end() &&
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +000072 std::find_if(
73 I->second.begin(), I->second.end(), [&](const MacroArgUse &U) {
74 return ArgUse.first == U.first && ArgUse.second != U.second;
75 }) != I->second.end()) {
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000076 // Trying to write in a macro argument input that has already been
77 // written by a previous commit for another expansion of the same macro
78 // argument name. For example:
79 //
80 // \code
81 // #define MAC(x) ((x)+(x))
82 // MAC(a)
83 // \endcode
84 //
85 // A commit modified the macro argument 'a' due to the first '(x)'
86 // expansion inside the macro definition, and a subsequent commit tried
87 // to modify 'a' again for the second '(x)' expansion. The edits of the
88 // second commit will be rejected.
89 return false;
90 }
Ted Kremenekf7639e12012-03-06 20:06:33 +000091 }
92
93 return true;
94}
95
96bool EditedSource::commitInsert(SourceLocation OrigLoc,
97 FileOffset Offs, StringRef text,
98 bool beforePreviousInsertions) {
99 if (!canInsertInOffset(OrigLoc, Offs))
100 return false;
101 if (text.empty())
102 return true;
103
104 if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +0000105 SourceLocation ExpLoc;
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +0000106 MacroArgUse ArgUse;
107 deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
108 if (ArgUse.first)
109 CurrCommitMacroArgExps.emplace_back(ExpLoc, ArgUse);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000110 }
111
112 FileEdit &FA = FileEdits[Offs];
113 if (FA.Text.empty()) {
114 FA.Text = copyString(text);
115 return true;
116 }
117
Ted Kremenekf7639e12012-03-06 20:06:33 +0000118 if (beforePreviousInsertions)
Benjamin Kramer543c7432014-03-29 16:54:24 +0000119 FA.Text = copyString(Twine(text) + FA.Text);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000120 else
Benjamin Kramer543c7432014-03-29 16:54:24 +0000121 FA.Text = copyString(Twine(FA.Text) + text);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000122
Ted Kremenekf7639e12012-03-06 20:06:33 +0000123 return true;
124}
125
126bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
127 FileOffset Offs,
128 FileOffset InsertFromRangeOffs, unsigned Len,
129 bool beforePreviousInsertions) {
130 if (Len == 0)
131 return true;
132
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000133 SmallString<128> StrVec;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000134 FileOffset BeginOffs = InsertFromRangeOffs;
135 FileOffset EndOffs = BeginOffs.getWithOffset(Len);
136 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
137 if (I != FileEdits.begin())
138 --I;
139
140 for (; I != FileEdits.end(); ++I) {
141 FileEdit &FA = I->second;
142 FileOffset B = I->first;
143 FileOffset E = B.getWithOffset(FA.RemoveLen);
144
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000145 if (BeginOffs == B)
146 break;
147
Ted Kremenekf7639e12012-03-06 20:06:33 +0000148 if (BeginOffs < E) {
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000149 if (BeginOffs > B) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000150 BeginOffs = E;
151 ++I;
152 }
153 break;
154 }
155 }
156
157 for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
158 FileEdit &FA = I->second;
159 FileOffset B = I->first;
160 FileOffset E = B.getWithOffset(FA.RemoveLen);
161
162 if (BeginOffs < B) {
163 bool Invalid = false;
164 StringRef text = getSourceText(BeginOffs, B, Invalid);
165 if (Invalid)
166 return false;
167 StrVec += text;
168 }
169 StrVec += FA.Text;
170 BeginOffs = E;
171 }
172
173 if (BeginOffs < EndOffs) {
174 bool Invalid = false;
175 StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
176 if (Invalid)
177 return false;
178 StrVec += text;
179 }
180
Yaron Keren92e1b622015-03-18 10:17:07 +0000181 return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000182}
183
184void EditedSource::commitRemove(SourceLocation OrigLoc,
185 FileOffset BeginOffs, unsigned Len) {
186 if (Len == 0)
187 return;
188
189 FileOffset EndOffs = BeginOffs.getWithOffset(Len);
190 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
191 if (I != FileEdits.begin())
192 --I;
193
194 for (; I != FileEdits.end(); ++I) {
195 FileEdit &FA = I->second;
196 FileOffset B = I->first;
197 FileOffset E = B.getWithOffset(FA.RemoveLen);
198
199 if (BeginOffs < E)
200 break;
201 }
202
203 FileOffset TopBegin, TopEnd;
Craig Topper2145bc02014-05-09 08:15:10 +0000204 FileEdit *TopFA = nullptr;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000205
206 if (I == FileEdits.end()) {
207 FileEditsTy::iterator
208 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
209 NewI->second.RemoveLen = Len;
210 return;
211 }
212
213 FileEdit &FA = I->second;
214 FileOffset B = I->first;
215 FileOffset E = B.getWithOffset(FA.RemoveLen);
216 if (BeginOffs < B) {
217 FileEditsTy::iterator
218 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
219 TopBegin = BeginOffs;
220 TopEnd = EndOffs;
221 TopFA = &NewI->second;
222 TopFA->RemoveLen = Len;
223 } else {
224 TopBegin = B;
225 TopEnd = E;
226 TopFA = &I->second;
227 if (TopEnd >= EndOffs)
228 return;
229 unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
230 TopEnd = EndOffs;
231 TopFA->RemoveLen += diff;
Argyrios Kyrtzidis501d90b2013-04-06 01:13:17 +0000232 if (B == BeginOffs)
233 TopFA->Text = StringRef();
Ted Kremenekf7639e12012-03-06 20:06:33 +0000234 ++I;
235 }
236
237 while (I != FileEdits.end()) {
238 FileEdit &FA = I->second;
239 FileOffset B = I->first;
240 FileOffset E = B.getWithOffset(FA.RemoveLen);
241
242 if (B >= TopEnd)
243 break;
244
245 if (E <= TopEnd) {
246 FileEdits.erase(I++);
247 continue;
248 }
249
250 if (B < TopEnd) {
251 unsigned diff = E.getOffset() - TopEnd.getOffset();
252 TopEnd = E;
253 TopFA->RemoveLen += diff;
254 FileEdits.erase(I);
255 }
256
257 break;
258 }
259}
260
261bool EditedSource::commit(const Commit &commit) {
262 if (!commit.isCommitable())
263 return false;
264
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +0000265 struct CommitRAII {
266 EditedSource &Editor;
267 CommitRAII(EditedSource &Editor) : Editor(Editor) {
268 Editor.startingCommit();
269 }
270 ~CommitRAII() {
271 Editor.finishedCommit();
272 }
273 } CommitRAII(*this);
274
Ted Kremenekf7639e12012-03-06 20:06:33 +0000275 for (edit::Commit::edit_iterator
276 I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
277 const edit::Commit::Edit &edit = *I;
278 switch (edit.Kind) {
279 case edit::Commit::Act_Insert:
280 commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
281 break;
282 case edit::Commit::Act_InsertFromRange:
283 commitInsertFromRange(edit.OrigLoc, edit.Offset,
284 edit.InsertFromRangeOffs, edit.Length,
285 edit.BeforePrev);
286 break;
287 case edit::Commit::Act_Remove:
288 commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
289 break;
290 }
291 }
292
293 return true;
294}
295
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000296// \brief Returns true if it is ok to make the two given characters adjacent.
297static bool canBeJoined(char left, char right, const LangOptions &LangOpts) {
Jordan Rosea7d03842013-02-08 22:30:41 +0000298 // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000299 // making two '<' adjacent.
Jordan Rosea7d03842013-02-08 22:30:41 +0000300 return !(Lexer::isIdentifierBodyChar(left, LangOpts) &&
301 Lexer::isIdentifierBodyChar(right, LangOpts));
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000302}
303
304/// \brief Returns true if it is ok to eliminate the trailing whitespace between
305/// the given characters.
306static bool canRemoveWhitespace(char left, char beforeWSpace, char right,
307 const LangOptions &LangOpts) {
308 if (!canBeJoined(left, right, LangOpts))
309 return false;
Jordan Rosea7d03842013-02-08 22:30:41 +0000310 if (isWhitespace(left) || isWhitespace(right))
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000311 return true;
312 if (canBeJoined(beforeWSpace, right, LangOpts))
313 return false; // the whitespace was intentional, keep it.
314 return true;
315}
316
317/// \brief Check the range that we are going to remove and:
318/// -Remove any trailing whitespace if possible.
319/// -Insert a space if removing the range is going to mess up the source tokens.
320static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts,
321 SourceLocation Loc, FileOffset offs,
322 unsigned &len, StringRef &text) {
323 assert(len && text.empty());
324 SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
325 if (BeginTokLoc != Loc)
326 return; // the range is not at the beginning of a token, keep the range.
327
328 bool Invalid = false;
329 StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);
330 if (Invalid)
331 return;
332
333 unsigned begin = offs.getOffset();
334 unsigned end = begin + len;
335
Benjamin Kramer1009df42014-09-15 11:47:10 +0000336 // Do not try to extend the removal if we're at the end of the buffer already.
337 if (end == buffer.size())
338 return;
339
340 assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");
341
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000342 // FIXME: Remove newline.
343
344 if (begin == 0) {
345 if (buffer[end] == ' ')
346 ++len;
347 return;
348 }
349
350 if (buffer[end] == ' ') {
Benjamin Kramer851f3102015-03-29 18:07:29 +0000351 assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
352 "buffer not zero-terminated!");
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000353 if (canRemoveWhitespace(/*left=*/buffer[begin-1],
354 /*beforeWSpace=*/buffer[end-1],
Benjamin Kramer851f3102015-03-29 18:07:29 +0000355 /*right=*/buffer.data()[end + 1], // zero-terminated
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000356 LangOpts))
357 ++len;
358 return;
359 }
360
361 if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts))
362 text = " ";
363}
364
Ted Kremenekf7639e12012-03-06 20:06:33 +0000365static void applyRewrite(EditsReceiver &receiver,
366 StringRef text, FileOffset offs, unsigned len,
Argyrios Kyrtzidis5312b662017-04-28 00:25:06 +0000367 const SourceManager &SM, const LangOptions &LangOpts,
368 bool shouldAdjustRemovals) {
Yaron Keren8b563662015-10-03 10:46:20 +0000369 assert(offs.getFID().isValid());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000370 SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
371 Loc = Loc.getLocWithOffset(offs.getOffset());
372 assert(Loc.isFileID());
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000373
Argyrios Kyrtzidis5312b662017-04-28 00:25:06 +0000374 if (text.empty() && shouldAdjustRemovals)
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000375 adjustRemoval(SM, LangOpts, Loc, offs, len, text);
376
Ted Kremenekf7639e12012-03-06 20:06:33 +0000377 CharSourceRange range = CharSourceRange::getCharRange(Loc,
378 Loc.getLocWithOffset(len));
379
380 if (text.empty()) {
381 assert(len);
382 receiver.remove(range);
383 return;
384 }
385
386 if (len)
387 receiver.replace(range, text);
388 else
389 receiver.insert(Loc, text);
390}
391
Argyrios Kyrtzidis5312b662017-04-28 00:25:06 +0000392void EditedSource::applyRewrites(EditsReceiver &receiver,
393 bool shouldAdjustRemovals) {
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000394 SmallString<128> StrVec;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000395 FileOffset CurOffs, CurEnd;
396 unsigned CurLen;
397
398 if (FileEdits.empty())
399 return;
400
401 FileEditsTy::iterator I = FileEdits.begin();
402 CurOffs = I->first;
403 StrVec = I->second.Text;
404 CurLen = I->second.RemoveLen;
405 CurEnd = CurOffs.getWithOffset(CurLen);
406 ++I;
407
408 for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
409 FileOffset offs = I->first;
410 FileEdit act = I->second;
411 assert(offs >= CurEnd);
412
413 if (offs == CurEnd) {
414 StrVec += act.Text;
415 CurLen += act.RemoveLen;
416 CurEnd.getWithOffset(act.RemoveLen);
417 continue;
418 }
419
Argyrios Kyrtzidis5312b662017-04-28 00:25:06 +0000420 applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
421 shouldAdjustRemovals);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000422 CurOffs = offs;
423 StrVec = act.Text;
424 CurLen = act.RemoveLen;
425 CurEnd = CurOffs.getWithOffset(CurLen);
426 }
427
Argyrios Kyrtzidis5312b662017-04-28 00:25:06 +0000428 applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
429 shouldAdjustRemovals);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000430}
431
432void EditedSource::clearRewrites() {
433 FileEdits.clear();
434 StrAlloc.Reset();
435}
436
437StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
438 bool &Invalid) {
439 assert(BeginOffs.getFID() == EndOffs.getFID());
440 assert(BeginOffs <= EndOffs);
441 SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
442 BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
443 assert(BLoc.isFileID());
444 SourceLocation
445 ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
446 return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
447 SourceMgr, LangOpts, &Invalid);
448}
449
450EditedSource::FileEditsTy::iterator
451EditedSource::getActionForOffset(FileOffset Offs) {
452 FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
453 if (I == FileEdits.begin())
454 return FileEdits.end();
455 --I;
456 FileEdit &FA = I->second;
457 FileOffset B = I->first;
458 FileOffset E = B.getWithOffset(FA.RemoveLen);
459 if (Offs >= B && Offs < E)
460 return I;
461
462 return FileEdits.end();
463}