xref: /aosp_15_r20/external/clang/lib/Edit/EditedSource.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li //===----- EditedSource.cpp - Collection of source edits ------------------===//
2*67e74705SXin Li //
3*67e74705SXin Li //                     The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li 
10*67e74705SXin Li #include "clang/Edit/EditedSource.h"
11*67e74705SXin Li #include "clang/Basic/CharInfo.h"
12*67e74705SXin Li #include "clang/Basic/SourceManager.h"
13*67e74705SXin Li #include "clang/Edit/Commit.h"
14*67e74705SXin Li #include "clang/Edit/EditsReceiver.h"
15*67e74705SXin Li #include "clang/Lex/Lexer.h"
16*67e74705SXin Li #include "llvm/ADT/SmallString.h"
17*67e74705SXin Li #include "llvm/ADT/Twine.h"
18*67e74705SXin Li 
19*67e74705SXin Li using namespace clang;
20*67e74705SXin Li using namespace edit;
21*67e74705SXin Li 
remove(CharSourceRange range)22*67e74705SXin Li void EditsReceiver::remove(CharSourceRange range) {
23*67e74705SXin Li   replace(range, StringRef());
24*67e74705SXin Li }
25*67e74705SXin Li 
deconstructMacroArgLoc(SourceLocation Loc,SourceLocation & ExpansionLoc,IdentifierInfo * & II)26*67e74705SXin Li void EditedSource::deconstructMacroArgLoc(SourceLocation Loc,
27*67e74705SXin Li                                           SourceLocation &ExpansionLoc,
28*67e74705SXin Li                                           IdentifierInfo *&II) {
29*67e74705SXin Li   assert(SourceMgr.isMacroArgExpansion(Loc));
30*67e74705SXin Li   SourceLocation DefArgLoc = SourceMgr.getImmediateExpansionRange(Loc).first;
31*67e74705SXin Li   ExpansionLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
32*67e74705SXin Li   SmallString<20> Buf;
33*67e74705SXin Li   StringRef ArgName = Lexer::getSpelling(SourceMgr.getSpellingLoc(DefArgLoc),
34*67e74705SXin Li                                          Buf, SourceMgr, LangOpts);
35*67e74705SXin Li   II = nullptr;
36*67e74705SXin Li   if (!ArgName.empty()) {
37*67e74705SXin Li     II = &IdentTable.get(ArgName);
38*67e74705SXin Li   }
39*67e74705SXin Li }
40*67e74705SXin Li 
startingCommit()41*67e74705SXin Li void EditedSource::startingCommit() {}
42*67e74705SXin Li 
finishedCommit()43*67e74705SXin Li void EditedSource::finishedCommit() {
44*67e74705SXin Li   for (auto &ExpArg : CurrCommitMacroArgExps) {
45*67e74705SXin Li     SourceLocation ExpLoc;
46*67e74705SXin Li     IdentifierInfo *II;
47*67e74705SXin Li     std::tie(ExpLoc, II) = ExpArg;
48*67e74705SXin Li     auto &ArgNames = ExpansionToArgMap[ExpLoc.getRawEncoding()];
49*67e74705SXin Li     if (std::find(ArgNames.begin(), ArgNames.end(), II) == ArgNames.end()) {
50*67e74705SXin Li       ArgNames.push_back(II);
51*67e74705SXin Li     }
52*67e74705SXin Li   }
53*67e74705SXin Li   CurrCommitMacroArgExps.clear();
54*67e74705SXin Li }
55*67e74705SXin Li 
copyString(const Twine & twine)56*67e74705SXin Li StringRef EditedSource::copyString(const Twine &twine) {
57*67e74705SXin Li   SmallString<128> Data;
58*67e74705SXin Li   return copyString(twine.toStringRef(Data));
59*67e74705SXin Li }
60*67e74705SXin Li 
canInsertInOffset(SourceLocation OrigLoc,FileOffset Offs)61*67e74705SXin Li bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
62*67e74705SXin Li   FileEditsTy::iterator FA = getActionForOffset(Offs);
63*67e74705SXin Li   if (FA != FileEdits.end()) {
64*67e74705SXin Li     if (FA->first != Offs)
65*67e74705SXin Li       return false; // position has been removed.
66*67e74705SXin Li   }
67*67e74705SXin Li 
68*67e74705SXin Li   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
69*67e74705SXin Li     IdentifierInfo *II;
70*67e74705SXin Li     SourceLocation ExpLoc;
71*67e74705SXin Li     deconstructMacroArgLoc(OrigLoc, ExpLoc, II);
72*67e74705SXin Li     auto I = ExpansionToArgMap.find(ExpLoc.getRawEncoding());
73*67e74705SXin Li     if (I != ExpansionToArgMap.end() &&
74*67e74705SXin Li         std::find(I->second.begin(), I->second.end(), II) != I->second.end()) {
75*67e74705SXin Li       // Trying to write in a macro argument input that has already been
76*67e74705SXin Li       // written by a previous commit for another expansion of the same macro
77*67e74705SXin Li       // argument name. For example:
78*67e74705SXin Li       //
79*67e74705SXin Li       // \code
80*67e74705SXin Li       //   #define MAC(x) ((x)+(x))
81*67e74705SXin Li       //   MAC(a)
82*67e74705SXin Li       // \endcode
83*67e74705SXin Li       //
84*67e74705SXin Li       // A commit modified the macro argument 'a' due to the first '(x)'
85*67e74705SXin Li       // expansion inside the macro definition, and a subsequent commit tried
86*67e74705SXin Li       // to modify 'a' again for the second '(x)' expansion. The edits of the
87*67e74705SXin Li       // second commit will be rejected.
88*67e74705SXin Li       return false;
89*67e74705SXin Li     }
90*67e74705SXin Li   }
91*67e74705SXin Li 
92*67e74705SXin Li   return true;
93*67e74705SXin Li }
94*67e74705SXin Li 
commitInsert(SourceLocation OrigLoc,FileOffset Offs,StringRef text,bool beforePreviousInsertions)95*67e74705SXin Li bool EditedSource::commitInsert(SourceLocation OrigLoc,
96*67e74705SXin Li                                 FileOffset Offs, StringRef text,
97*67e74705SXin Li                                 bool beforePreviousInsertions) {
98*67e74705SXin Li   if (!canInsertInOffset(OrigLoc, Offs))
99*67e74705SXin Li     return false;
100*67e74705SXin Li   if (text.empty())
101*67e74705SXin Li     return true;
102*67e74705SXin Li 
103*67e74705SXin Li   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
104*67e74705SXin Li     IdentifierInfo *II;
105*67e74705SXin Li     SourceLocation ExpLoc;
106*67e74705SXin Li     deconstructMacroArgLoc(OrigLoc, ExpLoc, II);
107*67e74705SXin Li     if (II)
108*67e74705SXin Li       CurrCommitMacroArgExps.emplace_back(ExpLoc, II);
109*67e74705SXin Li   }
110*67e74705SXin Li 
111*67e74705SXin Li   FileEdit &FA = FileEdits[Offs];
112*67e74705SXin Li   if (FA.Text.empty()) {
113*67e74705SXin Li     FA.Text = copyString(text);
114*67e74705SXin Li     return true;
115*67e74705SXin Li   }
116*67e74705SXin Li 
117*67e74705SXin Li   if (beforePreviousInsertions)
118*67e74705SXin Li     FA.Text = copyString(Twine(text) + FA.Text);
119*67e74705SXin Li   else
120*67e74705SXin Li     FA.Text = copyString(Twine(FA.Text) + text);
121*67e74705SXin Li 
122*67e74705SXin Li   return true;
123*67e74705SXin Li }
124*67e74705SXin Li 
commitInsertFromRange(SourceLocation OrigLoc,FileOffset Offs,FileOffset InsertFromRangeOffs,unsigned Len,bool beforePreviousInsertions)125*67e74705SXin Li bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
126*67e74705SXin Li                                    FileOffset Offs,
127*67e74705SXin Li                                    FileOffset InsertFromRangeOffs, unsigned Len,
128*67e74705SXin Li                                    bool beforePreviousInsertions) {
129*67e74705SXin Li   if (Len == 0)
130*67e74705SXin Li     return true;
131*67e74705SXin Li 
132*67e74705SXin Li   SmallString<128> StrVec;
133*67e74705SXin Li   FileOffset BeginOffs = InsertFromRangeOffs;
134*67e74705SXin Li   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
135*67e74705SXin Li   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
136*67e74705SXin Li   if (I != FileEdits.begin())
137*67e74705SXin Li     --I;
138*67e74705SXin Li 
139*67e74705SXin Li   for (; I != FileEdits.end(); ++I) {
140*67e74705SXin Li     FileEdit &FA = I->second;
141*67e74705SXin Li     FileOffset B = I->first;
142*67e74705SXin Li     FileOffset E = B.getWithOffset(FA.RemoveLen);
143*67e74705SXin Li 
144*67e74705SXin Li     if (BeginOffs == B)
145*67e74705SXin Li       break;
146*67e74705SXin Li 
147*67e74705SXin Li     if (BeginOffs < E) {
148*67e74705SXin Li       if (BeginOffs > B) {
149*67e74705SXin Li         BeginOffs = E;
150*67e74705SXin Li         ++I;
151*67e74705SXin Li       }
152*67e74705SXin Li       break;
153*67e74705SXin Li     }
154*67e74705SXin Li   }
155*67e74705SXin Li 
156*67e74705SXin Li   for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
157*67e74705SXin Li     FileEdit &FA = I->second;
158*67e74705SXin Li     FileOffset B = I->first;
159*67e74705SXin Li     FileOffset E = B.getWithOffset(FA.RemoveLen);
160*67e74705SXin Li 
161*67e74705SXin Li     if (BeginOffs < B) {
162*67e74705SXin Li       bool Invalid = false;
163*67e74705SXin Li       StringRef text = getSourceText(BeginOffs, B, Invalid);
164*67e74705SXin Li       if (Invalid)
165*67e74705SXin Li         return false;
166*67e74705SXin Li       StrVec += text;
167*67e74705SXin Li     }
168*67e74705SXin Li     StrVec += FA.Text;
169*67e74705SXin Li     BeginOffs = E;
170*67e74705SXin Li   }
171*67e74705SXin Li 
172*67e74705SXin Li   if (BeginOffs < EndOffs) {
173*67e74705SXin Li     bool Invalid = false;
174*67e74705SXin Li     StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
175*67e74705SXin Li     if (Invalid)
176*67e74705SXin Li       return false;
177*67e74705SXin Li     StrVec += text;
178*67e74705SXin Li   }
179*67e74705SXin Li 
180*67e74705SXin Li   return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
181*67e74705SXin Li }
182*67e74705SXin Li 
commitRemove(SourceLocation OrigLoc,FileOffset BeginOffs,unsigned Len)183*67e74705SXin Li void EditedSource::commitRemove(SourceLocation OrigLoc,
184*67e74705SXin Li                                 FileOffset BeginOffs, unsigned Len) {
185*67e74705SXin Li   if (Len == 0)
186*67e74705SXin Li     return;
187*67e74705SXin Li 
188*67e74705SXin Li   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
189*67e74705SXin Li   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
190*67e74705SXin Li   if (I != FileEdits.begin())
191*67e74705SXin Li     --I;
192*67e74705SXin Li 
193*67e74705SXin Li   for (; I != FileEdits.end(); ++I) {
194*67e74705SXin Li     FileEdit &FA = I->second;
195*67e74705SXin Li     FileOffset B = I->first;
196*67e74705SXin Li     FileOffset E = B.getWithOffset(FA.RemoveLen);
197*67e74705SXin Li 
198*67e74705SXin Li     if (BeginOffs < E)
199*67e74705SXin Li       break;
200*67e74705SXin Li   }
201*67e74705SXin Li 
202*67e74705SXin Li   FileOffset TopBegin, TopEnd;
203*67e74705SXin Li   FileEdit *TopFA = nullptr;
204*67e74705SXin Li 
205*67e74705SXin Li   if (I == FileEdits.end()) {
206*67e74705SXin Li     FileEditsTy::iterator
207*67e74705SXin Li       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
208*67e74705SXin Li     NewI->second.RemoveLen = Len;
209*67e74705SXin Li     return;
210*67e74705SXin Li   }
211*67e74705SXin Li 
212*67e74705SXin Li   FileEdit &FA = I->second;
213*67e74705SXin Li   FileOffset B = I->first;
214*67e74705SXin Li   FileOffset E = B.getWithOffset(FA.RemoveLen);
215*67e74705SXin Li   if (BeginOffs < B) {
216*67e74705SXin Li     FileEditsTy::iterator
217*67e74705SXin Li       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
218*67e74705SXin Li     TopBegin = BeginOffs;
219*67e74705SXin Li     TopEnd = EndOffs;
220*67e74705SXin Li     TopFA = &NewI->second;
221*67e74705SXin Li     TopFA->RemoveLen = Len;
222*67e74705SXin Li   } else {
223*67e74705SXin Li     TopBegin = B;
224*67e74705SXin Li     TopEnd = E;
225*67e74705SXin Li     TopFA = &I->second;
226*67e74705SXin Li     if (TopEnd >= EndOffs)
227*67e74705SXin Li       return;
228*67e74705SXin Li     unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
229*67e74705SXin Li     TopEnd = EndOffs;
230*67e74705SXin Li     TopFA->RemoveLen += diff;
231*67e74705SXin Li     if (B == BeginOffs)
232*67e74705SXin Li       TopFA->Text = StringRef();
233*67e74705SXin Li     ++I;
234*67e74705SXin Li   }
235*67e74705SXin Li 
236*67e74705SXin Li   while (I != FileEdits.end()) {
237*67e74705SXin Li     FileEdit &FA = I->second;
238*67e74705SXin Li     FileOffset B = I->first;
239*67e74705SXin Li     FileOffset E = B.getWithOffset(FA.RemoveLen);
240*67e74705SXin Li 
241*67e74705SXin Li     if (B >= TopEnd)
242*67e74705SXin Li       break;
243*67e74705SXin Li 
244*67e74705SXin Li     if (E <= TopEnd) {
245*67e74705SXin Li       FileEdits.erase(I++);
246*67e74705SXin Li       continue;
247*67e74705SXin Li     }
248*67e74705SXin Li 
249*67e74705SXin Li     if (B < TopEnd) {
250*67e74705SXin Li       unsigned diff = E.getOffset() - TopEnd.getOffset();
251*67e74705SXin Li       TopEnd = E;
252*67e74705SXin Li       TopFA->RemoveLen += diff;
253*67e74705SXin Li       FileEdits.erase(I);
254*67e74705SXin Li     }
255*67e74705SXin Li 
256*67e74705SXin Li     break;
257*67e74705SXin Li   }
258*67e74705SXin Li }
259*67e74705SXin Li 
commit(const Commit & commit)260*67e74705SXin Li bool EditedSource::commit(const Commit &commit) {
261*67e74705SXin Li   if (!commit.isCommitable())
262*67e74705SXin Li     return false;
263*67e74705SXin Li 
264*67e74705SXin Li   struct CommitRAII {
265*67e74705SXin Li     EditedSource &Editor;
266*67e74705SXin Li     CommitRAII(EditedSource &Editor) : Editor(Editor) {
267*67e74705SXin Li       Editor.startingCommit();
268*67e74705SXin Li     }
269*67e74705SXin Li     ~CommitRAII() {
270*67e74705SXin Li       Editor.finishedCommit();
271*67e74705SXin Li     }
272*67e74705SXin Li   } CommitRAII(*this);
273*67e74705SXin Li 
274*67e74705SXin Li   for (edit::Commit::edit_iterator
275*67e74705SXin Li          I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
276*67e74705SXin Li     const edit::Commit::Edit &edit = *I;
277*67e74705SXin Li     switch (edit.Kind) {
278*67e74705SXin Li     case edit::Commit::Act_Insert:
279*67e74705SXin Li       commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
280*67e74705SXin Li       break;
281*67e74705SXin Li     case edit::Commit::Act_InsertFromRange:
282*67e74705SXin Li       commitInsertFromRange(edit.OrigLoc, edit.Offset,
283*67e74705SXin Li                             edit.InsertFromRangeOffs, edit.Length,
284*67e74705SXin Li                             edit.BeforePrev);
285*67e74705SXin Li       break;
286*67e74705SXin Li     case edit::Commit::Act_Remove:
287*67e74705SXin Li       commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
288*67e74705SXin Li       break;
289*67e74705SXin Li     }
290*67e74705SXin Li   }
291*67e74705SXin Li 
292*67e74705SXin Li   return true;
293*67e74705SXin Li }
294*67e74705SXin Li 
295*67e74705SXin Li // \brief Returns true if it is ok to make the two given characters adjacent.
canBeJoined(char left,char right,const LangOptions & LangOpts)296*67e74705SXin Li static bool canBeJoined(char left, char right, const LangOptions &LangOpts) {
297*67e74705SXin Li   // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like
298*67e74705SXin Li   // making two '<' adjacent.
299*67e74705SXin Li   return !(Lexer::isIdentifierBodyChar(left, LangOpts) &&
300*67e74705SXin Li            Lexer::isIdentifierBodyChar(right, LangOpts));
301*67e74705SXin Li }
302*67e74705SXin Li 
303*67e74705SXin Li /// \brief Returns true if it is ok to eliminate the trailing whitespace between
304*67e74705SXin Li /// the given characters.
canRemoveWhitespace(char left,char beforeWSpace,char right,const LangOptions & LangOpts)305*67e74705SXin Li static bool canRemoveWhitespace(char left, char beforeWSpace, char right,
306*67e74705SXin Li                                 const LangOptions &LangOpts) {
307*67e74705SXin Li   if (!canBeJoined(left, right, LangOpts))
308*67e74705SXin Li     return false;
309*67e74705SXin Li   if (isWhitespace(left) || isWhitespace(right))
310*67e74705SXin Li     return true;
311*67e74705SXin Li   if (canBeJoined(beforeWSpace, right, LangOpts))
312*67e74705SXin Li     return false; // the whitespace was intentional, keep it.
313*67e74705SXin Li   return true;
314*67e74705SXin Li }
315*67e74705SXin Li 
316*67e74705SXin Li /// \brief Check the range that we are going to remove and:
317*67e74705SXin Li /// -Remove any trailing whitespace if possible.
318*67e74705SXin Li /// -Insert a space if removing the range is going to mess up the source tokens.
adjustRemoval(const SourceManager & SM,const LangOptions & LangOpts,SourceLocation Loc,FileOffset offs,unsigned & len,StringRef & text)319*67e74705SXin Li static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts,
320*67e74705SXin Li                           SourceLocation Loc, FileOffset offs,
321*67e74705SXin Li                           unsigned &len, StringRef &text) {
322*67e74705SXin Li   assert(len && text.empty());
323*67e74705SXin Li   SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
324*67e74705SXin Li   if (BeginTokLoc != Loc)
325*67e74705SXin Li     return; // the range is not at the beginning of a token, keep the range.
326*67e74705SXin Li 
327*67e74705SXin Li   bool Invalid = false;
328*67e74705SXin Li   StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);
329*67e74705SXin Li   if (Invalid)
330*67e74705SXin Li     return;
331*67e74705SXin Li 
332*67e74705SXin Li   unsigned begin = offs.getOffset();
333*67e74705SXin Li   unsigned end = begin + len;
334*67e74705SXin Li 
335*67e74705SXin Li   // Do not try to extend the removal if we're at the end of the buffer already.
336*67e74705SXin Li   if (end == buffer.size())
337*67e74705SXin Li     return;
338*67e74705SXin Li 
339*67e74705SXin Li   assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");
340*67e74705SXin Li 
341*67e74705SXin Li   // FIXME: Remove newline.
342*67e74705SXin Li 
343*67e74705SXin Li   if (begin == 0) {
344*67e74705SXin Li     if (buffer[end] == ' ')
345*67e74705SXin Li       ++len;
346*67e74705SXin Li     return;
347*67e74705SXin Li   }
348*67e74705SXin Li 
349*67e74705SXin Li   if (buffer[end] == ' ') {
350*67e74705SXin Li     assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
351*67e74705SXin Li            "buffer not zero-terminated!");
352*67e74705SXin Li     if (canRemoveWhitespace(/*left=*/buffer[begin-1],
353*67e74705SXin Li                             /*beforeWSpace=*/buffer[end-1],
354*67e74705SXin Li                             /*right=*/buffer.data()[end + 1], // zero-terminated
355*67e74705SXin Li                             LangOpts))
356*67e74705SXin Li       ++len;
357*67e74705SXin Li     return;
358*67e74705SXin Li   }
359*67e74705SXin Li 
360*67e74705SXin Li   if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts))
361*67e74705SXin Li     text = " ";
362*67e74705SXin Li }
363*67e74705SXin Li 
applyRewrite(EditsReceiver & receiver,StringRef text,FileOffset offs,unsigned len,const SourceManager & SM,const LangOptions & LangOpts)364*67e74705SXin Li static void applyRewrite(EditsReceiver &receiver,
365*67e74705SXin Li                          StringRef text, FileOffset offs, unsigned len,
366*67e74705SXin Li                          const SourceManager &SM, const LangOptions &LangOpts) {
367*67e74705SXin Li   assert(offs.getFID().isValid());
368*67e74705SXin Li   SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
369*67e74705SXin Li   Loc = Loc.getLocWithOffset(offs.getOffset());
370*67e74705SXin Li   assert(Loc.isFileID());
371*67e74705SXin Li 
372*67e74705SXin Li   if (text.empty())
373*67e74705SXin Li     adjustRemoval(SM, LangOpts, Loc, offs, len, text);
374*67e74705SXin Li 
375*67e74705SXin Li   CharSourceRange range = CharSourceRange::getCharRange(Loc,
376*67e74705SXin Li                                                      Loc.getLocWithOffset(len));
377*67e74705SXin Li 
378*67e74705SXin Li   if (text.empty()) {
379*67e74705SXin Li     assert(len);
380*67e74705SXin Li     receiver.remove(range);
381*67e74705SXin Li     return;
382*67e74705SXin Li   }
383*67e74705SXin Li 
384*67e74705SXin Li   if (len)
385*67e74705SXin Li     receiver.replace(range, text);
386*67e74705SXin Li   else
387*67e74705SXin Li     receiver.insert(Loc, text);
388*67e74705SXin Li }
389*67e74705SXin Li 
applyRewrites(EditsReceiver & receiver)390*67e74705SXin Li void EditedSource::applyRewrites(EditsReceiver &receiver) {
391*67e74705SXin Li   SmallString<128> StrVec;
392*67e74705SXin Li   FileOffset CurOffs, CurEnd;
393*67e74705SXin Li   unsigned CurLen;
394*67e74705SXin Li 
395*67e74705SXin Li   if (FileEdits.empty())
396*67e74705SXin Li     return;
397*67e74705SXin Li 
398*67e74705SXin Li   FileEditsTy::iterator I = FileEdits.begin();
399*67e74705SXin Li   CurOffs = I->first;
400*67e74705SXin Li   StrVec = I->second.Text;
401*67e74705SXin Li   CurLen = I->second.RemoveLen;
402*67e74705SXin Li   CurEnd = CurOffs.getWithOffset(CurLen);
403*67e74705SXin Li   ++I;
404*67e74705SXin Li 
405*67e74705SXin Li   for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
406*67e74705SXin Li     FileOffset offs = I->first;
407*67e74705SXin Li     FileEdit act = I->second;
408*67e74705SXin Li     assert(offs >= CurEnd);
409*67e74705SXin Li 
410*67e74705SXin Li     if (offs == CurEnd) {
411*67e74705SXin Li       StrVec += act.Text;
412*67e74705SXin Li       CurLen += act.RemoveLen;
413*67e74705SXin Li       CurEnd.getWithOffset(act.RemoveLen);
414*67e74705SXin Li       continue;
415*67e74705SXin Li     }
416*67e74705SXin Li 
417*67e74705SXin Li     applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts);
418*67e74705SXin Li     CurOffs = offs;
419*67e74705SXin Li     StrVec = act.Text;
420*67e74705SXin Li     CurLen = act.RemoveLen;
421*67e74705SXin Li     CurEnd = CurOffs.getWithOffset(CurLen);
422*67e74705SXin Li   }
423*67e74705SXin Li 
424*67e74705SXin Li   applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts);
425*67e74705SXin Li }
426*67e74705SXin Li 
clearRewrites()427*67e74705SXin Li void EditedSource::clearRewrites() {
428*67e74705SXin Li   FileEdits.clear();
429*67e74705SXin Li   StrAlloc.Reset();
430*67e74705SXin Li }
431*67e74705SXin Li 
getSourceText(FileOffset BeginOffs,FileOffset EndOffs,bool & Invalid)432*67e74705SXin Li StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
433*67e74705SXin Li                                       bool &Invalid) {
434*67e74705SXin Li   assert(BeginOffs.getFID() == EndOffs.getFID());
435*67e74705SXin Li   assert(BeginOffs <= EndOffs);
436*67e74705SXin Li   SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
437*67e74705SXin Li   BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
438*67e74705SXin Li   assert(BLoc.isFileID());
439*67e74705SXin Li   SourceLocation
440*67e74705SXin Li     ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
441*67e74705SXin Li   return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
442*67e74705SXin Li                               SourceMgr, LangOpts, &Invalid);
443*67e74705SXin Li }
444*67e74705SXin Li 
445*67e74705SXin Li EditedSource::FileEditsTy::iterator
getActionForOffset(FileOffset Offs)446*67e74705SXin Li EditedSource::getActionForOffset(FileOffset Offs) {
447*67e74705SXin Li   FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
448*67e74705SXin Li   if (I == FileEdits.begin())
449*67e74705SXin Li     return FileEdits.end();
450*67e74705SXin Li   --I;
451*67e74705SXin Li   FileEdit &FA = I->second;
452*67e74705SXin Li   FileOffset B = I->first;
453*67e74705SXin Li   FileOffset E = B.getWithOffset(FA.RemoveLen);
454*67e74705SXin Li   if (Offs >= B && Offs < E)
455*67e74705SXin Li     return I;
456*67e74705SXin Li 
457*67e74705SXin Li   return FileEdits.end();
458*67e74705SXin Li }
459