blob: b38f8fd0d9cbaac727642c1902a405b63b91e4d5 [file] [log] [blame]
Eugene Zelenkodb914a42018-03-27 00:01:49 +00001//===- EditedSource.cpp - Collection of source edits ----------------------===//
Ted Kremenekf7639e12012-03-06 20:06:33 +00002//
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"
Eugene Zelenkodb914a42018-03-27 00:01:49 +000012#include "clang/Basic/LLVM.h"
13#include "clang/Basic/SourceLocation.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000014#include "clang/Basic/SourceManager.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000015#include "clang/Edit/Commit.h"
16#include "clang/Edit/EditsReceiver.h"
Eugene Zelenkodb914a42018-03-27 00:01:49 +000017#include "clang/Edit/FileOffset.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000018#include "clang/Lex/Lexer.h"
Eugene Zelenkodb914a42018-03-27 00:01:49 +000019#include "llvm/ADT/STLExtras.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000020#include "llvm/ADT/SmallString.h"
Eugene Zelenkodb914a42018-03-27 00:01:49 +000021#include "llvm/ADT/StringRef.h"
Ted Kremenekf7639e12012-03-06 20:06:33 +000022#include "llvm/ADT/Twine.h"
Eugene Zelenkodb914a42018-03-27 00:01:49 +000023#include <algorithm>
24#include <cassert>
25#include <tuple>
26#include <utility>
Ted Kremenekf7639e12012-03-06 20:06:33 +000027
28using namespace clang;
29using namespace edit;
30
31void EditsReceiver::remove(CharSourceRange range) {
32 replace(range, StringRef());
33}
34
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000035void EditedSource::deconstructMacroArgLoc(SourceLocation Loc,
36 SourceLocation &ExpansionLoc,
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +000037 MacroArgUse &ArgUse) {
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000038 assert(SourceMgr.isMacroArgExpansion(Loc));
Richard Smithb5f81712018-04-30 05:25:48 +000039 SourceLocation DefArgLoc =
40 SourceMgr.getImmediateExpansionRange(Loc).getBegin();
Alexander Shaposhnikov3dbef852017-06-20 20:46:58 +000041 SourceLocation ImmediateExpansionLoc =
Richard Smithb5f81712018-04-30 05:25:48 +000042 SourceMgr.getImmediateExpansionRange(DefArgLoc).getBegin();
Alexander Shaposhnikov3dbef852017-06-20 20:46:58 +000043 ExpansionLoc = ImmediateExpansionLoc;
44 while (SourceMgr.isMacroBodyExpansion(ExpansionLoc))
Richard Smithb5f81712018-04-30 05:25:48 +000045 ExpansionLoc =
46 SourceMgr.getImmediateExpansionRange(ExpansionLoc).getBegin();
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000047 SmallString<20> Buf;
48 StringRef ArgName = Lexer::getSpelling(SourceMgr.getSpellingLoc(DefArgLoc),
49 Buf, SourceMgr, LangOpts);
Alexander Shaposhnikov3dbef852017-06-20 20:46:58 +000050 ArgUse = MacroArgUse{nullptr, SourceLocation(), SourceLocation()};
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +000051 if (!ArgName.empty())
Alexander Shaposhnikov3dbef852017-06-20 20:46:58 +000052 ArgUse = {&IdentTable.get(ArgName), ImmediateExpansionLoc,
53 SourceMgr.getSpellingLoc(DefArgLoc)};
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000054}
55
56void EditedSource::startingCommit() {}
57
58void EditedSource::finishedCommit() {
59 for (auto &ExpArg : CurrCommitMacroArgExps) {
60 SourceLocation ExpLoc;
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +000061 MacroArgUse ArgUse;
62 std::tie(ExpLoc, ArgUse) = ExpArg;
63 auto &ArgUses = ExpansionToArgMap[ExpLoc.getRawEncoding()];
64 if (std::find(ArgUses.begin(), ArgUses.end(), ArgUse) == ArgUses.end())
65 ArgUses.push_back(ArgUse);
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000066 }
67 CurrCommitMacroArgExps.clear();
68}
69
Ted Kremenekf7639e12012-03-06 20:06:33 +000070StringRef EditedSource::copyString(const Twine &twine) {
Dmitri Gribenkof8579502013-01-12 19:30:44 +000071 SmallString<128> Data;
Ted Kremenekf7639e12012-03-06 20:06:33 +000072 return copyString(twine.toStringRef(Data));
73}
74
75bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
76 FileEditsTy::iterator FA = getActionForOffset(Offs);
77 if (FA != FileEdits.end()) {
78 if (FA->first != Offs)
79 return false; // position has been removed.
80 }
81
82 if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000083 SourceLocation ExpLoc;
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +000084 MacroArgUse ArgUse;
85 deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000086 auto I = ExpansionToArgMap.find(ExpLoc.getRawEncoding());
87 if (I != ExpansionToArgMap.end() &&
Alexander Shaposhnikov3dbef852017-06-20 20:46:58 +000088 find_if(I->second, [&](const MacroArgUse &U) {
89 return ArgUse.Identifier == U.Identifier &&
90 std::tie(ArgUse.ImmediateExpansionLoc, ArgUse.UseLoc) !=
91 std::tie(U.ImmediateExpansionLoc, U.UseLoc);
92 }) != I->second.end()) {
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +000093 // Trying to write in a macro argument input that has already been
94 // written by a previous commit for another expansion of the same macro
95 // argument name. For example:
96 //
97 // \code
98 // #define MAC(x) ((x)+(x))
99 // MAC(a)
100 // \endcode
101 //
102 // A commit modified the macro argument 'a' due to the first '(x)'
103 // expansion inside the macro definition, and a subsequent commit tried
104 // to modify 'a' again for the second '(x)' expansion. The edits of the
105 // second commit will be rejected.
106 return false;
107 }
Ted Kremenekf7639e12012-03-06 20:06:33 +0000108 }
Ted Kremenekf7639e12012-03-06 20:06:33 +0000109 return true;
110}
111
112bool EditedSource::commitInsert(SourceLocation OrigLoc,
113 FileOffset Offs, StringRef text,
114 bool beforePreviousInsertions) {
115 if (!canInsertInOffset(OrigLoc, Offs))
116 return false;
117 if (text.empty())
118 return true;
119
120 if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +0000121 MacroArgUse ArgUse;
Alexander Shaposhnikov3dbef852017-06-20 20:46:58 +0000122 SourceLocation ExpLoc;
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +0000123 deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
Alexander Shaposhnikov3dbef852017-06-20 20:46:58 +0000124 if (ArgUse.Identifier)
Alexander Shaposhnikov108ca942017-06-08 21:44:45 +0000125 CurrCommitMacroArgExps.emplace_back(ExpLoc, ArgUse);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000126 }
Alexander Shaposhnikov3dbef852017-06-20 20:46:58 +0000127
Ted Kremenekf7639e12012-03-06 20:06:33 +0000128 FileEdit &FA = FileEdits[Offs];
129 if (FA.Text.empty()) {
130 FA.Text = copyString(text);
131 return true;
132 }
133
Ted Kremenekf7639e12012-03-06 20:06:33 +0000134 if (beforePreviousInsertions)
Benjamin Kramer543c7432014-03-29 16:54:24 +0000135 FA.Text = copyString(Twine(text) + FA.Text);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000136 else
Benjamin Kramer543c7432014-03-29 16:54:24 +0000137 FA.Text = copyString(Twine(FA.Text) + text);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000138
Ted Kremenekf7639e12012-03-06 20:06:33 +0000139 return true;
140}
141
142bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
143 FileOffset Offs,
144 FileOffset InsertFromRangeOffs, unsigned Len,
145 bool beforePreviousInsertions) {
146 if (Len == 0)
147 return true;
148
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000149 SmallString<128> StrVec;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000150 FileOffset BeginOffs = InsertFromRangeOffs;
151 FileOffset EndOffs = BeginOffs.getWithOffset(Len);
152 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
153 if (I != FileEdits.begin())
154 --I;
155
156 for (; I != FileEdits.end(); ++I) {
157 FileEdit &FA = I->second;
158 FileOffset B = I->first;
159 FileOffset E = B.getWithOffset(FA.RemoveLen);
160
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000161 if (BeginOffs == B)
162 break;
163
Ted Kremenekf7639e12012-03-06 20:06:33 +0000164 if (BeginOffs < E) {
Argyrios Kyrtzidisc1dfed62012-05-14 22:01:53 +0000165 if (BeginOffs > B) {
Ted Kremenekf7639e12012-03-06 20:06:33 +0000166 BeginOffs = E;
167 ++I;
168 }
169 break;
170 }
171 }
172
173 for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
174 FileEdit &FA = I->second;
175 FileOffset B = I->first;
176 FileOffset E = B.getWithOffset(FA.RemoveLen);
177
178 if (BeginOffs < B) {
179 bool Invalid = false;
180 StringRef text = getSourceText(BeginOffs, B, Invalid);
181 if (Invalid)
182 return false;
183 StrVec += text;
184 }
185 StrVec += FA.Text;
186 BeginOffs = E;
187 }
188
189 if (BeginOffs < EndOffs) {
190 bool Invalid = false;
191 StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
192 if (Invalid)
193 return false;
194 StrVec += text;
195 }
196
Yaron Keren92e1b622015-03-18 10:17:07 +0000197 return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000198}
199
200void EditedSource::commitRemove(SourceLocation OrigLoc,
201 FileOffset BeginOffs, unsigned Len) {
202 if (Len == 0)
203 return;
204
205 FileOffset EndOffs = BeginOffs.getWithOffset(Len);
206 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
207 if (I != FileEdits.begin())
208 --I;
209
210 for (; I != FileEdits.end(); ++I) {
211 FileEdit &FA = I->second;
212 FileOffset B = I->first;
213 FileOffset E = B.getWithOffset(FA.RemoveLen);
214
215 if (BeginOffs < E)
216 break;
217 }
218
219 FileOffset TopBegin, TopEnd;
Craig Topper2145bc02014-05-09 08:15:10 +0000220 FileEdit *TopFA = nullptr;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000221
222 if (I == FileEdits.end()) {
223 FileEditsTy::iterator
224 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
225 NewI->second.RemoveLen = Len;
226 return;
227 }
228
229 FileEdit &FA = I->second;
230 FileOffset B = I->first;
231 FileOffset E = B.getWithOffset(FA.RemoveLen);
232 if (BeginOffs < B) {
233 FileEditsTy::iterator
234 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
235 TopBegin = BeginOffs;
236 TopEnd = EndOffs;
237 TopFA = &NewI->second;
238 TopFA->RemoveLen = Len;
239 } else {
240 TopBegin = B;
241 TopEnd = E;
242 TopFA = &I->second;
243 if (TopEnd >= EndOffs)
244 return;
245 unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
246 TopEnd = EndOffs;
247 TopFA->RemoveLen += diff;
Argyrios Kyrtzidis501d90b2013-04-06 01:13:17 +0000248 if (B == BeginOffs)
249 TopFA->Text = StringRef();
Ted Kremenekf7639e12012-03-06 20:06:33 +0000250 ++I;
251 }
252
253 while (I != FileEdits.end()) {
254 FileEdit &FA = I->second;
255 FileOffset B = I->first;
256 FileOffset E = B.getWithOffset(FA.RemoveLen);
257
258 if (B >= TopEnd)
259 break;
260
261 if (E <= TopEnd) {
262 FileEdits.erase(I++);
263 continue;
264 }
265
266 if (B < TopEnd) {
267 unsigned diff = E.getOffset() - TopEnd.getOffset();
268 TopEnd = E;
269 TopFA->RemoveLen += diff;
270 FileEdits.erase(I);
271 }
272
273 break;
274 }
275}
276
277bool EditedSource::commit(const Commit &commit) {
278 if (!commit.isCommitable())
279 return false;
280
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +0000281 struct CommitRAII {
282 EditedSource &Editor;
Eugene Zelenkodb914a42018-03-27 00:01:49 +0000283
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +0000284 CommitRAII(EditedSource &Editor) : Editor(Editor) {
285 Editor.startingCommit();
286 }
Eugene Zelenkodb914a42018-03-27 00:01:49 +0000287
Argyrios Kyrtzidis822e2882015-09-11 20:09:11 +0000288 ~CommitRAII() {
289 Editor.finishedCommit();
290 }
291 } CommitRAII(*this);
292
Ted Kremenekf7639e12012-03-06 20:06:33 +0000293 for (edit::Commit::edit_iterator
294 I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
295 const edit::Commit::Edit &edit = *I;
296 switch (edit.Kind) {
297 case edit::Commit::Act_Insert:
298 commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
299 break;
300 case edit::Commit::Act_InsertFromRange:
301 commitInsertFromRange(edit.OrigLoc, edit.Offset,
302 edit.InsertFromRangeOffs, edit.Length,
303 edit.BeforePrev);
304 break;
305 case edit::Commit::Act_Remove:
306 commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
307 break;
308 }
309 }
310
311 return true;
312}
313
Adrian Prantl9fc8faf2018-05-09 01:00:01 +0000314// Returns true if it is ok to make the two given characters adjacent.
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000315static bool canBeJoined(char left, char right, const LangOptions &LangOpts) {
Jordan Rosea7d03842013-02-08 22:30:41 +0000316 // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000317 // making two '<' adjacent.
Jordan Rosea7d03842013-02-08 22:30:41 +0000318 return !(Lexer::isIdentifierBodyChar(left, LangOpts) &&
319 Lexer::isIdentifierBodyChar(right, LangOpts));
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000320}
321
Adrian Prantl9fc8faf2018-05-09 01:00:01 +0000322/// Returns true if it is ok to eliminate the trailing whitespace between
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000323/// the given characters.
324static bool canRemoveWhitespace(char left, char beforeWSpace, char right,
325 const LangOptions &LangOpts) {
326 if (!canBeJoined(left, right, LangOpts))
327 return false;
Jordan Rosea7d03842013-02-08 22:30:41 +0000328 if (isWhitespace(left) || isWhitespace(right))
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000329 return true;
330 if (canBeJoined(beforeWSpace, right, LangOpts))
331 return false; // the whitespace was intentional, keep it.
332 return true;
333}
334
Adrian Prantl9fc8faf2018-05-09 01:00:01 +0000335/// Check the range that we are going to remove and:
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000336/// -Remove any trailing whitespace if possible.
337/// -Insert a space if removing the range is going to mess up the source tokens.
338static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts,
339 SourceLocation Loc, FileOffset offs,
340 unsigned &len, StringRef &text) {
341 assert(len && text.empty());
342 SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
343 if (BeginTokLoc != Loc)
344 return; // the range is not at the beginning of a token, keep the range.
345
346 bool Invalid = false;
347 StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);
348 if (Invalid)
349 return;
350
351 unsigned begin = offs.getOffset();
352 unsigned end = begin + len;
353
Benjamin Kramer1009df42014-09-15 11:47:10 +0000354 // Do not try to extend the removal if we're at the end of the buffer already.
355 if (end == buffer.size())
356 return;
357
358 assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");
359
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000360 // FIXME: Remove newline.
361
362 if (begin == 0) {
363 if (buffer[end] == ' ')
364 ++len;
365 return;
366 }
367
368 if (buffer[end] == ' ') {
Benjamin Kramer851f3102015-03-29 18:07:29 +0000369 assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
370 "buffer not zero-terminated!");
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000371 if (canRemoveWhitespace(/*left=*/buffer[begin-1],
372 /*beforeWSpace=*/buffer[end-1],
Benjamin Kramer851f3102015-03-29 18:07:29 +0000373 /*right=*/buffer.data()[end + 1], // zero-terminated
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000374 LangOpts))
375 ++len;
376 return;
377 }
378
379 if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts))
380 text = " ";
381}
382
Ted Kremenekf7639e12012-03-06 20:06:33 +0000383static void applyRewrite(EditsReceiver &receiver,
384 StringRef text, FileOffset offs, unsigned len,
Argyrios Kyrtzidis5312b662017-04-28 00:25:06 +0000385 const SourceManager &SM, const LangOptions &LangOpts,
386 bool shouldAdjustRemovals) {
Yaron Keren8b563662015-10-03 10:46:20 +0000387 assert(offs.getFID().isValid());
Ted Kremenekf7639e12012-03-06 20:06:33 +0000388 SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
389 Loc = Loc.getLocWithOffset(offs.getOffset());
390 assert(Loc.isFileID());
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000391
Argyrios Kyrtzidis5312b662017-04-28 00:25:06 +0000392 if (text.empty() && shouldAdjustRemovals)
Argyrios Kyrtzidiscbfd4d22012-12-20 21:05:53 +0000393 adjustRemoval(SM, LangOpts, Loc, offs, len, text);
394
Ted Kremenekf7639e12012-03-06 20:06:33 +0000395 CharSourceRange range = CharSourceRange::getCharRange(Loc,
396 Loc.getLocWithOffset(len));
397
398 if (text.empty()) {
399 assert(len);
400 receiver.remove(range);
401 return;
402 }
403
404 if (len)
405 receiver.replace(range, text);
406 else
407 receiver.insert(Loc, text);
408}
409
Argyrios Kyrtzidis5312b662017-04-28 00:25:06 +0000410void EditedSource::applyRewrites(EditsReceiver &receiver,
411 bool shouldAdjustRemovals) {
Dmitri Gribenkof8579502013-01-12 19:30:44 +0000412 SmallString<128> StrVec;
Ted Kremenekf7639e12012-03-06 20:06:33 +0000413 FileOffset CurOffs, CurEnd;
414 unsigned CurLen;
415
416 if (FileEdits.empty())
417 return;
418
419 FileEditsTy::iterator I = FileEdits.begin();
420 CurOffs = I->first;
421 StrVec = I->second.Text;
422 CurLen = I->second.RemoveLen;
423 CurEnd = CurOffs.getWithOffset(CurLen);
424 ++I;
425
426 for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
427 FileOffset offs = I->first;
428 FileEdit act = I->second;
429 assert(offs >= CurEnd);
430
431 if (offs == CurEnd) {
432 StrVec += act.Text;
433 CurLen += act.RemoveLen;
434 CurEnd.getWithOffset(act.RemoveLen);
435 continue;
436 }
437
Argyrios Kyrtzidis5312b662017-04-28 00:25:06 +0000438 applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
439 shouldAdjustRemovals);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000440 CurOffs = offs;
441 StrVec = act.Text;
442 CurLen = act.RemoveLen;
443 CurEnd = CurOffs.getWithOffset(CurLen);
444 }
445
Argyrios Kyrtzidis5312b662017-04-28 00:25:06 +0000446 applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
447 shouldAdjustRemovals);
Ted Kremenekf7639e12012-03-06 20:06:33 +0000448}
449
450void EditedSource::clearRewrites() {
451 FileEdits.clear();
452 StrAlloc.Reset();
453}
454
455StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
456 bool &Invalid) {
457 assert(BeginOffs.getFID() == EndOffs.getFID());
458 assert(BeginOffs <= EndOffs);
459 SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
460 BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
461 assert(BLoc.isFileID());
462 SourceLocation
463 ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
464 return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
465 SourceMgr, LangOpts, &Invalid);
466}
467
468EditedSource::FileEditsTy::iterator
469EditedSource::getActionForOffset(FileOffset Offs) {
470 FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
471 if (I == FileEdits.begin())
472 return FileEdits.end();
473 --I;
474 FileEdit &FA = I->second;
475 FileOffset B = I->first;
476 FileOffset E = B.getWithOffset(FA.RemoveLen);
477 if (Offs >= B && Offs < E)
478 return I;
479
480 return FileEdits.end();
481}