1db914a46SEugene Zelenko //===- EditedSource.cpp - Collection of source edits ----------------------===//
2f7639e1bSTed Kremenek //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6f7639e1bSTed Kremenek //
7f7639e1bSTed Kremenek //===----------------------------------------------------------------------===//
8f7639e1bSTed Kremenek 
9f7639e1bSTed Kremenek #include "clang/Edit/EditedSource.h"
10a7d03840SJordan Rose #include "clang/Basic/CharInfo.h"
11db914a46SEugene Zelenko #include "clang/Basic/LLVM.h"
12db914a46SEugene Zelenko #include "clang/Basic/SourceLocation.h"
133a02247dSChandler Carruth #include "clang/Basic/SourceManager.h"
14f7639e1bSTed Kremenek #include "clang/Edit/Commit.h"
15f7639e1bSTed Kremenek #include "clang/Edit/EditsReceiver.h"
16db914a46SEugene Zelenko #include "clang/Edit/FileOffset.h"
17f7639e1bSTed Kremenek #include "clang/Lex/Lexer.h"
18db914a46SEugene Zelenko #include "llvm/ADT/STLExtras.h"
19f7639e1bSTed Kremenek #include "llvm/ADT/SmallString.h"
20db914a46SEugene Zelenko #include "llvm/ADT/StringRef.h"
21f7639e1bSTed Kremenek #include "llvm/ADT/Twine.h"
22db914a46SEugene Zelenko #include <algorithm>
23db914a46SEugene Zelenko #include <cassert>
24db914a46SEugene Zelenko #include <tuple>
25db914a46SEugene Zelenko #include <utility>
26f7639e1bSTed Kremenek 
27f7639e1bSTed Kremenek using namespace clang;
28f7639e1bSTed Kremenek using namespace edit;
29f7639e1bSTed Kremenek 
remove(CharSourceRange range)30f7639e1bSTed Kremenek void EditsReceiver::remove(CharSourceRange range) {
31f7639e1bSTed Kremenek   replace(range, StringRef());
32f7639e1bSTed Kremenek }
33f7639e1bSTed Kremenek 
deconstructMacroArgLoc(SourceLocation Loc,SourceLocation & ExpansionLoc,MacroArgUse & ArgUse)34822e2887SArgyrios Kyrtzidis void EditedSource::deconstructMacroArgLoc(SourceLocation Loc,
35822e2887SArgyrios Kyrtzidis                                           SourceLocation &ExpansionLoc,
36108ca94fSAlexander Shaposhnikov                                           MacroArgUse &ArgUse) {
37822e2887SArgyrios Kyrtzidis   assert(SourceMgr.isMacroArgExpansion(Loc));
38b5f8171aSRichard Smith   SourceLocation DefArgLoc =
39b5f8171aSRichard Smith       SourceMgr.getImmediateExpansionRange(Loc).getBegin();
403dbef856SAlexander Shaposhnikov   SourceLocation ImmediateExpansionLoc =
41b5f8171aSRichard Smith       SourceMgr.getImmediateExpansionRange(DefArgLoc).getBegin();
423dbef856SAlexander Shaposhnikov   ExpansionLoc = ImmediateExpansionLoc;
433dbef856SAlexander Shaposhnikov   while (SourceMgr.isMacroBodyExpansion(ExpansionLoc))
44b5f8171aSRichard Smith     ExpansionLoc =
45b5f8171aSRichard Smith         SourceMgr.getImmediateExpansionRange(ExpansionLoc).getBegin();
46822e2887SArgyrios Kyrtzidis   SmallString<20> Buf;
47822e2887SArgyrios Kyrtzidis   StringRef ArgName = Lexer::getSpelling(SourceMgr.getSpellingLoc(DefArgLoc),
48822e2887SArgyrios Kyrtzidis                                          Buf, SourceMgr, LangOpts);
493dbef856SAlexander Shaposhnikov   ArgUse = MacroArgUse{nullptr, SourceLocation(), SourceLocation()};
50108ca94fSAlexander Shaposhnikov   if (!ArgName.empty())
513dbef856SAlexander Shaposhnikov     ArgUse = {&IdentTable.get(ArgName), ImmediateExpansionLoc,
523dbef856SAlexander Shaposhnikov               SourceMgr.getSpellingLoc(DefArgLoc)};
53822e2887SArgyrios Kyrtzidis }
54822e2887SArgyrios Kyrtzidis 
startingCommit()55822e2887SArgyrios Kyrtzidis void EditedSource::startingCommit() {}
56822e2887SArgyrios Kyrtzidis 
finishedCommit()57822e2887SArgyrios Kyrtzidis void EditedSource::finishedCommit() {
58822e2887SArgyrios Kyrtzidis   for (auto &ExpArg : CurrCommitMacroArgExps) {
59822e2887SArgyrios Kyrtzidis     SourceLocation ExpLoc;
60108ca94fSAlexander Shaposhnikov     MacroArgUse ArgUse;
61108ca94fSAlexander Shaposhnikov     std::tie(ExpLoc, ArgUse) = ExpArg;
6278194118SMikhail Maltsev     auto &ArgUses = ExpansionToArgMap[ExpLoc];
63e567f37dSKazu Hirata     if (!llvm::is_contained(ArgUses, ArgUse))
64108ca94fSAlexander Shaposhnikov       ArgUses.push_back(ArgUse);
65822e2887SArgyrios Kyrtzidis   }
66822e2887SArgyrios Kyrtzidis   CurrCommitMacroArgExps.clear();
67822e2887SArgyrios Kyrtzidis }
68822e2887SArgyrios Kyrtzidis 
copyString(const Twine & twine)69f7639e1bSTed Kremenek StringRef EditedSource::copyString(const Twine &twine) {
70f857950dSDmitri Gribenko   SmallString<128> Data;
71f7639e1bSTed Kremenek   return copyString(twine.toStringRef(Data));
72f7639e1bSTed Kremenek }
73f7639e1bSTed Kremenek 
canInsertInOffset(SourceLocation OrigLoc,FileOffset Offs)74f7639e1bSTed Kremenek bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
75f7639e1bSTed Kremenek   FileEditsTy::iterator FA = getActionForOffset(Offs);
76f7639e1bSTed Kremenek   if (FA != FileEdits.end()) {
77f7639e1bSTed Kremenek     if (FA->first != Offs)
78f7639e1bSTed Kremenek       return false; // position has been removed.
79f7639e1bSTed Kremenek   }
80f7639e1bSTed Kremenek 
81f7639e1bSTed Kremenek   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
82822e2887SArgyrios Kyrtzidis     SourceLocation ExpLoc;
83108ca94fSAlexander Shaposhnikov     MacroArgUse ArgUse;
84108ca94fSAlexander Shaposhnikov     deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
8578194118SMikhail Maltsev     auto I = ExpansionToArgMap.find(ExpLoc);
86822e2887SArgyrios Kyrtzidis     if (I != ExpansionToArgMap.end() &&
87*9e88cbccSKazu Hirata         llvm::any_of(I->second, [&](const MacroArgUse &U) {
883dbef856SAlexander Shaposhnikov           return ArgUse.Identifier == U.Identifier &&
893dbef856SAlexander Shaposhnikov                  std::tie(ArgUse.ImmediateExpansionLoc, ArgUse.UseLoc) !=
903dbef856SAlexander Shaposhnikov                      std::tie(U.ImmediateExpansionLoc, U.UseLoc);
91*9e88cbccSKazu Hirata         })) {
92822e2887SArgyrios Kyrtzidis       // Trying to write in a macro argument input that has already been
93822e2887SArgyrios Kyrtzidis       // written by a previous commit for another expansion of the same macro
94822e2887SArgyrios Kyrtzidis       // argument name. For example:
95822e2887SArgyrios Kyrtzidis       //
96822e2887SArgyrios Kyrtzidis       // \code
97822e2887SArgyrios Kyrtzidis       //   #define MAC(x) ((x)+(x))
98822e2887SArgyrios Kyrtzidis       //   MAC(a)
99822e2887SArgyrios Kyrtzidis       // \endcode
100822e2887SArgyrios Kyrtzidis       //
101822e2887SArgyrios Kyrtzidis       // A commit modified the macro argument 'a' due to the first '(x)'
102822e2887SArgyrios Kyrtzidis       // expansion inside the macro definition, and a subsequent commit tried
103822e2887SArgyrios Kyrtzidis       // to modify 'a' again for the second '(x)' expansion. The edits of the
104822e2887SArgyrios Kyrtzidis       // second commit will be rejected.
105822e2887SArgyrios Kyrtzidis       return false;
106822e2887SArgyrios Kyrtzidis     }
107f7639e1bSTed Kremenek   }
108f7639e1bSTed Kremenek   return true;
109f7639e1bSTed Kremenek }
110f7639e1bSTed Kremenek 
commitInsert(SourceLocation OrigLoc,FileOffset Offs,StringRef text,bool beforePreviousInsertions)111f7639e1bSTed Kremenek bool EditedSource::commitInsert(SourceLocation OrigLoc,
112f7639e1bSTed Kremenek                                 FileOffset Offs, StringRef text,
113f7639e1bSTed Kremenek                                 bool beforePreviousInsertions) {
114f7639e1bSTed Kremenek   if (!canInsertInOffset(OrigLoc, Offs))
115f7639e1bSTed Kremenek     return false;
116f7639e1bSTed Kremenek   if (text.empty())
117f7639e1bSTed Kremenek     return true;
118f7639e1bSTed Kremenek 
119f7639e1bSTed Kremenek   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
120108ca94fSAlexander Shaposhnikov     MacroArgUse ArgUse;
1213dbef856SAlexander Shaposhnikov     SourceLocation ExpLoc;
122108ca94fSAlexander Shaposhnikov     deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
1233dbef856SAlexander Shaposhnikov     if (ArgUse.Identifier)
124108ca94fSAlexander Shaposhnikov       CurrCommitMacroArgExps.emplace_back(ExpLoc, ArgUse);
125f7639e1bSTed Kremenek   }
126f7639e1bSTed Kremenek 
127f7639e1bSTed Kremenek   FileEdit &FA = FileEdits[Offs];
128f7639e1bSTed Kremenek   if (FA.Text.empty()) {
129f7639e1bSTed Kremenek     FA.Text = copyString(text);
130f7639e1bSTed Kremenek     return true;
131f7639e1bSTed Kremenek   }
132f7639e1bSTed Kremenek 
133f7639e1bSTed Kremenek   if (beforePreviousInsertions)
134543c7430SBenjamin Kramer     FA.Text = copyString(Twine(text) + FA.Text);
135f7639e1bSTed Kremenek   else
136543c7430SBenjamin Kramer     FA.Text = copyString(Twine(FA.Text) + text);
137f7639e1bSTed Kremenek 
138f7639e1bSTed Kremenek   return true;
139f7639e1bSTed Kremenek }
140f7639e1bSTed Kremenek 
commitInsertFromRange(SourceLocation OrigLoc,FileOffset Offs,FileOffset InsertFromRangeOffs,unsigned Len,bool beforePreviousInsertions)141f7639e1bSTed Kremenek bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
142f7639e1bSTed Kremenek                                    FileOffset Offs,
143f7639e1bSTed Kremenek                                    FileOffset InsertFromRangeOffs, unsigned Len,
144f7639e1bSTed Kremenek                                    bool beforePreviousInsertions) {
145f7639e1bSTed Kremenek   if (Len == 0)
146f7639e1bSTed Kremenek     return true;
147f7639e1bSTed Kremenek 
148f857950dSDmitri Gribenko   SmallString<128> StrVec;
149f7639e1bSTed Kremenek   FileOffset BeginOffs = InsertFromRangeOffs;
150f7639e1bSTed Kremenek   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
151f7639e1bSTed Kremenek   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
152f7639e1bSTed Kremenek   if (I != FileEdits.begin())
153f7639e1bSTed Kremenek     --I;
154f7639e1bSTed Kremenek 
155f7639e1bSTed Kremenek   for (; I != FileEdits.end(); ++I) {
156f7639e1bSTed Kremenek     FileEdit &FA = I->second;
157f7639e1bSTed Kremenek     FileOffset B = I->first;
158f7639e1bSTed Kremenek     FileOffset E = B.getWithOffset(FA.RemoveLen);
159f7639e1bSTed Kremenek 
160c1dfed65SArgyrios Kyrtzidis     if (BeginOffs == B)
161c1dfed65SArgyrios Kyrtzidis       break;
162c1dfed65SArgyrios Kyrtzidis 
163f7639e1bSTed Kremenek     if (BeginOffs < E) {
164c1dfed65SArgyrios Kyrtzidis       if (BeginOffs > B) {
165f7639e1bSTed Kremenek         BeginOffs = E;
166f7639e1bSTed Kremenek         ++I;
167f7639e1bSTed Kremenek       }
168f7639e1bSTed Kremenek       break;
169f7639e1bSTed Kremenek     }
170f7639e1bSTed Kremenek   }
171f7639e1bSTed Kremenek 
172f7639e1bSTed Kremenek   for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
173f7639e1bSTed Kremenek     FileEdit &FA = I->second;
174f7639e1bSTed Kremenek     FileOffset B = I->first;
175f7639e1bSTed Kremenek     FileOffset E = B.getWithOffset(FA.RemoveLen);
176f7639e1bSTed Kremenek 
177f7639e1bSTed Kremenek     if (BeginOffs < B) {
178f7639e1bSTed Kremenek       bool Invalid = false;
179f7639e1bSTed Kremenek       StringRef text = getSourceText(BeginOffs, B, Invalid);
180f7639e1bSTed Kremenek       if (Invalid)
181f7639e1bSTed Kremenek         return false;
182f7639e1bSTed Kremenek       StrVec += text;
183f7639e1bSTed Kremenek     }
184f7639e1bSTed Kremenek     StrVec += FA.Text;
185f7639e1bSTed Kremenek     BeginOffs = E;
186f7639e1bSTed Kremenek   }
187f7639e1bSTed Kremenek 
188f7639e1bSTed Kremenek   if (BeginOffs < EndOffs) {
189f7639e1bSTed Kremenek     bool Invalid = false;
190f7639e1bSTed Kremenek     StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
191f7639e1bSTed Kremenek     if (Invalid)
192f7639e1bSTed Kremenek       return false;
193f7639e1bSTed Kremenek     StrVec += text;
194f7639e1bSTed Kremenek   }
195f7639e1bSTed Kremenek 
19692e1b62dSYaron Keren   return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
197f7639e1bSTed Kremenek }
198f7639e1bSTed Kremenek 
commitRemove(SourceLocation OrigLoc,FileOffset BeginOffs,unsigned Len)199f7639e1bSTed Kremenek void EditedSource::commitRemove(SourceLocation OrigLoc,
200f7639e1bSTed Kremenek                                 FileOffset BeginOffs, unsigned Len) {
201f7639e1bSTed Kremenek   if (Len == 0)
202f7639e1bSTed Kremenek     return;
203f7639e1bSTed Kremenek 
204f7639e1bSTed Kremenek   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
205f7639e1bSTed Kremenek   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
206f7639e1bSTed Kremenek   if (I != FileEdits.begin())
207f7639e1bSTed Kremenek     --I;
208f7639e1bSTed Kremenek 
209f7639e1bSTed Kremenek   for (; I != FileEdits.end(); ++I) {
210f7639e1bSTed Kremenek     FileEdit &FA = I->second;
211f7639e1bSTed Kremenek     FileOffset B = I->first;
212f7639e1bSTed Kremenek     FileOffset E = B.getWithOffset(FA.RemoveLen);
213f7639e1bSTed Kremenek 
214f7639e1bSTed Kremenek     if (BeginOffs < E)
215f7639e1bSTed Kremenek       break;
216f7639e1bSTed Kremenek   }
217f7639e1bSTed Kremenek 
218f7639e1bSTed Kremenek   FileOffset TopBegin, TopEnd;
2192145bc02SCraig Topper   FileEdit *TopFA = nullptr;
220f7639e1bSTed Kremenek 
221f7639e1bSTed Kremenek   if (I == FileEdits.end()) {
222f7639e1bSTed Kremenek     FileEditsTy::iterator
223f7639e1bSTed Kremenek       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
224f7639e1bSTed Kremenek     NewI->second.RemoveLen = Len;
225f7639e1bSTed Kremenek     return;
226f7639e1bSTed Kremenek   }
227f7639e1bSTed Kremenek 
228f7639e1bSTed Kremenek   FileEdit &FA = I->second;
229f7639e1bSTed Kremenek   FileOffset B = I->first;
230f7639e1bSTed Kremenek   FileOffset E = B.getWithOffset(FA.RemoveLen);
231f7639e1bSTed Kremenek   if (BeginOffs < B) {
232f7639e1bSTed Kremenek     FileEditsTy::iterator
233f7639e1bSTed Kremenek       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
234f7639e1bSTed Kremenek     TopBegin = BeginOffs;
235f7639e1bSTed Kremenek     TopEnd = EndOffs;
236f7639e1bSTed Kremenek     TopFA = &NewI->second;
237f7639e1bSTed Kremenek     TopFA->RemoveLen = Len;
238f7639e1bSTed Kremenek   } else {
239f7639e1bSTed Kremenek     TopBegin = B;
240f7639e1bSTed Kremenek     TopEnd = E;
241f7639e1bSTed Kremenek     TopFA = &I->second;
242f7639e1bSTed Kremenek     if (TopEnd >= EndOffs)
243f7639e1bSTed Kremenek       return;
244f7639e1bSTed Kremenek     unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
245f7639e1bSTed Kremenek     TopEnd = EndOffs;
246f7639e1bSTed Kremenek     TopFA->RemoveLen += diff;
247501d90bfSArgyrios Kyrtzidis     if (B == BeginOffs)
248501d90bfSArgyrios Kyrtzidis       TopFA->Text = StringRef();
249f7639e1bSTed Kremenek     ++I;
250f7639e1bSTed Kremenek   }
251f7639e1bSTed Kremenek 
252f7639e1bSTed Kremenek   while (I != FileEdits.end()) {
253f7639e1bSTed Kremenek     FileEdit &FA = I->second;
254f7639e1bSTed Kremenek     FileOffset B = I->first;
255f7639e1bSTed Kremenek     FileOffset E = B.getWithOffset(FA.RemoveLen);
256f7639e1bSTed Kremenek 
257f7639e1bSTed Kremenek     if (B >= TopEnd)
258f7639e1bSTed Kremenek       break;
259f7639e1bSTed Kremenek 
260f7639e1bSTed Kremenek     if (E <= TopEnd) {
261f7639e1bSTed Kremenek       FileEdits.erase(I++);
262f7639e1bSTed Kremenek       continue;
263f7639e1bSTed Kremenek     }
264f7639e1bSTed Kremenek 
265f7639e1bSTed Kremenek     if (B < TopEnd) {
266f7639e1bSTed Kremenek       unsigned diff = E.getOffset() - TopEnd.getOffset();
267f7639e1bSTed Kremenek       TopEnd = E;
268f7639e1bSTed Kremenek       TopFA->RemoveLen += diff;
269f7639e1bSTed Kremenek       FileEdits.erase(I);
270f7639e1bSTed Kremenek     }
271f7639e1bSTed Kremenek 
272f7639e1bSTed Kremenek     break;
273f7639e1bSTed Kremenek   }
274f7639e1bSTed Kremenek }
275f7639e1bSTed Kremenek 
commit(const Commit & commit)276f7639e1bSTed Kremenek bool EditedSource::commit(const Commit &commit) {
277f7639e1bSTed Kremenek   if (!commit.isCommitable())
278f7639e1bSTed Kremenek     return false;
279f7639e1bSTed Kremenek 
280822e2887SArgyrios Kyrtzidis   struct CommitRAII {
281822e2887SArgyrios Kyrtzidis     EditedSource &Editor;
282db914a46SEugene Zelenko 
283822e2887SArgyrios Kyrtzidis     CommitRAII(EditedSource &Editor) : Editor(Editor) {
284822e2887SArgyrios Kyrtzidis       Editor.startingCommit();
285822e2887SArgyrios Kyrtzidis     }
286db914a46SEugene Zelenko 
287822e2887SArgyrios Kyrtzidis     ~CommitRAII() {
288822e2887SArgyrios Kyrtzidis       Editor.finishedCommit();
289822e2887SArgyrios Kyrtzidis     }
290822e2887SArgyrios Kyrtzidis   } CommitRAII(*this);
291822e2887SArgyrios Kyrtzidis 
292f7639e1bSTed Kremenek   for (edit::Commit::edit_iterator
293f7639e1bSTed Kremenek          I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
294f7639e1bSTed Kremenek     const edit::Commit::Edit &edit = *I;
295f7639e1bSTed Kremenek     switch (edit.Kind) {
296f7639e1bSTed Kremenek     case edit::Commit::Act_Insert:
297f7639e1bSTed Kremenek       commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
298f7639e1bSTed Kremenek       break;
299f7639e1bSTed Kremenek     case edit::Commit::Act_InsertFromRange:
300f7639e1bSTed Kremenek       commitInsertFromRange(edit.OrigLoc, edit.Offset,
301f7639e1bSTed Kremenek                             edit.InsertFromRangeOffs, edit.Length,
302f7639e1bSTed Kremenek                             edit.BeforePrev);
303f7639e1bSTed Kremenek       break;
304f7639e1bSTed Kremenek     case edit::Commit::Act_Remove:
305f7639e1bSTed Kremenek       commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
306f7639e1bSTed Kremenek       break;
307f7639e1bSTed Kremenek     }
308f7639e1bSTed Kremenek   }
309f7639e1bSTed Kremenek 
310f7639e1bSTed Kremenek   return true;
311f7639e1bSTed Kremenek }
312f7639e1bSTed Kremenek 
3139fc8faf9SAdrian Prantl // Returns true if it is ok to make the two given characters adjacent.
canBeJoined(char left,char right,const LangOptions & LangOpts)314cbfd4d24SArgyrios Kyrtzidis static bool canBeJoined(char left, char right, const LangOptions &LangOpts) {
315a7d03840SJordan Rose   // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like
316cbfd4d24SArgyrios Kyrtzidis   // making two '<' adjacent.
317601102d2SCorentin Jabot   return !(Lexer::isAsciiIdentifierContinueChar(left, LangOpts) &&
318601102d2SCorentin Jabot            Lexer::isAsciiIdentifierContinueChar(right, LangOpts));
319cbfd4d24SArgyrios Kyrtzidis }
320cbfd4d24SArgyrios Kyrtzidis 
3219fc8faf9SAdrian Prantl /// Returns true if it is ok to eliminate the trailing whitespace between
322cbfd4d24SArgyrios Kyrtzidis /// the given characters.
canRemoveWhitespace(char left,char beforeWSpace,char right,const LangOptions & LangOpts)323cbfd4d24SArgyrios Kyrtzidis static bool canRemoveWhitespace(char left, char beforeWSpace, char right,
324cbfd4d24SArgyrios Kyrtzidis                                 const LangOptions &LangOpts) {
325cbfd4d24SArgyrios Kyrtzidis   if (!canBeJoined(left, right, LangOpts))
326cbfd4d24SArgyrios Kyrtzidis     return false;
327a7d03840SJordan Rose   if (isWhitespace(left) || isWhitespace(right))
328cbfd4d24SArgyrios Kyrtzidis     return true;
329cbfd4d24SArgyrios Kyrtzidis   if (canBeJoined(beforeWSpace, right, LangOpts))
330cbfd4d24SArgyrios Kyrtzidis     return false; // the whitespace was intentional, keep it.
331cbfd4d24SArgyrios Kyrtzidis   return true;
332cbfd4d24SArgyrios Kyrtzidis }
333cbfd4d24SArgyrios Kyrtzidis 
3349fc8faf9SAdrian Prantl /// Check the range that we are going to remove and:
335cbfd4d24SArgyrios Kyrtzidis /// -Remove any trailing whitespace if possible.
336cbfd4d24SArgyrios Kyrtzidis /// -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)337cbfd4d24SArgyrios Kyrtzidis static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts,
338cbfd4d24SArgyrios Kyrtzidis                           SourceLocation Loc, FileOffset offs,
339cbfd4d24SArgyrios Kyrtzidis                           unsigned &len, StringRef &text) {
340cbfd4d24SArgyrios Kyrtzidis   assert(len && text.empty());
341cbfd4d24SArgyrios Kyrtzidis   SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
342cbfd4d24SArgyrios Kyrtzidis   if (BeginTokLoc != Loc)
343cbfd4d24SArgyrios Kyrtzidis     return; // the range is not at the beginning of a token, keep the range.
344cbfd4d24SArgyrios Kyrtzidis 
345cbfd4d24SArgyrios Kyrtzidis   bool Invalid = false;
346cbfd4d24SArgyrios Kyrtzidis   StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);
347cbfd4d24SArgyrios Kyrtzidis   if (Invalid)
348cbfd4d24SArgyrios Kyrtzidis     return;
349cbfd4d24SArgyrios Kyrtzidis 
350cbfd4d24SArgyrios Kyrtzidis   unsigned begin = offs.getOffset();
351cbfd4d24SArgyrios Kyrtzidis   unsigned end = begin + len;
352cbfd4d24SArgyrios Kyrtzidis 
3531009df42SBenjamin Kramer   // Do not try to extend the removal if we're at the end of the buffer already.
3541009df42SBenjamin Kramer   if (end == buffer.size())
3551009df42SBenjamin Kramer     return;
3561009df42SBenjamin Kramer 
3571009df42SBenjamin Kramer   assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");
3581009df42SBenjamin Kramer 
359cbfd4d24SArgyrios Kyrtzidis   // FIXME: Remove newline.
360cbfd4d24SArgyrios Kyrtzidis 
361cbfd4d24SArgyrios Kyrtzidis   if (begin == 0) {
362cbfd4d24SArgyrios Kyrtzidis     if (buffer[end] == ' ')
363cbfd4d24SArgyrios Kyrtzidis       ++len;
364cbfd4d24SArgyrios Kyrtzidis     return;
365cbfd4d24SArgyrios Kyrtzidis   }
366cbfd4d24SArgyrios Kyrtzidis 
367cbfd4d24SArgyrios Kyrtzidis   if (buffer[end] == ' ') {
368851f310cSBenjamin Kramer     assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
369851f310cSBenjamin Kramer            "buffer not zero-terminated!");
370cbfd4d24SArgyrios Kyrtzidis     if (canRemoveWhitespace(/*left=*/buffer[begin-1],
371cbfd4d24SArgyrios Kyrtzidis                             /*beforeWSpace=*/buffer[end-1],
372851f310cSBenjamin Kramer                             /*right=*/buffer.data()[end + 1], // zero-terminated
373cbfd4d24SArgyrios Kyrtzidis                             LangOpts))
374cbfd4d24SArgyrios Kyrtzidis       ++len;
375cbfd4d24SArgyrios Kyrtzidis     return;
376cbfd4d24SArgyrios Kyrtzidis   }
377cbfd4d24SArgyrios Kyrtzidis 
378cbfd4d24SArgyrios Kyrtzidis   if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts))
379cbfd4d24SArgyrios Kyrtzidis     text = " ";
380cbfd4d24SArgyrios Kyrtzidis }
381cbfd4d24SArgyrios Kyrtzidis 
applyRewrite(EditsReceiver & receiver,StringRef text,FileOffset offs,unsigned len,const SourceManager & SM,const LangOptions & LangOpts,bool shouldAdjustRemovals)382f7639e1bSTed Kremenek static void applyRewrite(EditsReceiver &receiver,
383f7639e1bSTed Kremenek                          StringRef text, FileOffset offs, unsigned len,
3845312b667SArgyrios Kyrtzidis                          const SourceManager &SM, const LangOptions &LangOpts,
3855312b667SArgyrios Kyrtzidis                          bool shouldAdjustRemovals) {
3868b563665SYaron Keren   assert(offs.getFID().isValid());
387f7639e1bSTed Kremenek   SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
388f7639e1bSTed Kremenek   Loc = Loc.getLocWithOffset(offs.getOffset());
389f7639e1bSTed Kremenek   assert(Loc.isFileID());
390cbfd4d24SArgyrios Kyrtzidis 
3915312b667SArgyrios Kyrtzidis   if (text.empty() && shouldAdjustRemovals)
392cbfd4d24SArgyrios Kyrtzidis     adjustRemoval(SM, LangOpts, Loc, offs, len, text);
393cbfd4d24SArgyrios Kyrtzidis 
394f7639e1bSTed Kremenek   CharSourceRange range = CharSourceRange::getCharRange(Loc,
395f7639e1bSTed Kremenek                                                      Loc.getLocWithOffset(len));
396f7639e1bSTed Kremenek 
397f7639e1bSTed Kremenek   if (text.empty()) {
398f7639e1bSTed Kremenek     assert(len);
399f7639e1bSTed Kremenek     receiver.remove(range);
400f7639e1bSTed Kremenek     return;
401f7639e1bSTed Kremenek   }
402f7639e1bSTed Kremenek 
403f7639e1bSTed Kremenek   if (len)
404f7639e1bSTed Kremenek     receiver.replace(range, text);
405f7639e1bSTed Kremenek   else
406f7639e1bSTed Kremenek     receiver.insert(Loc, text);
407f7639e1bSTed Kremenek }
408f7639e1bSTed Kremenek 
applyRewrites(EditsReceiver & receiver,bool shouldAdjustRemovals)4095312b667SArgyrios Kyrtzidis void EditedSource::applyRewrites(EditsReceiver &receiver,
4105312b667SArgyrios Kyrtzidis                                  bool shouldAdjustRemovals) {
411f857950dSDmitri Gribenko   SmallString<128> StrVec;
412f7639e1bSTed Kremenek   FileOffset CurOffs, CurEnd;
413f7639e1bSTed Kremenek   unsigned CurLen;
414f7639e1bSTed Kremenek 
415f7639e1bSTed Kremenek   if (FileEdits.empty())
416f7639e1bSTed Kremenek     return;
417f7639e1bSTed Kremenek 
418f7639e1bSTed Kremenek   FileEditsTy::iterator I = FileEdits.begin();
419f7639e1bSTed Kremenek   CurOffs = I->first;
420f7639e1bSTed Kremenek   StrVec = I->second.Text;
421f7639e1bSTed Kremenek   CurLen = I->second.RemoveLen;
422f7639e1bSTed Kremenek   CurEnd = CurOffs.getWithOffset(CurLen);
423f7639e1bSTed Kremenek   ++I;
424f7639e1bSTed Kremenek 
425f7639e1bSTed Kremenek   for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
426f7639e1bSTed Kremenek     FileOffset offs = I->first;
427f7639e1bSTed Kremenek     FileEdit act = I->second;
428f7639e1bSTed Kremenek     assert(offs >= CurEnd);
429f7639e1bSTed Kremenek 
430f7639e1bSTed Kremenek     if (offs == CurEnd) {
431f7639e1bSTed Kremenek       StrVec += act.Text;
432f7639e1bSTed Kremenek       CurLen += act.RemoveLen;
433f7639e1bSTed Kremenek       CurEnd.getWithOffset(act.RemoveLen);
434f7639e1bSTed Kremenek       continue;
435f7639e1bSTed Kremenek     }
436f7639e1bSTed Kremenek 
4375312b667SArgyrios Kyrtzidis     applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
4385312b667SArgyrios Kyrtzidis                  shouldAdjustRemovals);
439f7639e1bSTed Kremenek     CurOffs = offs;
440f7639e1bSTed Kremenek     StrVec = act.Text;
441f7639e1bSTed Kremenek     CurLen = act.RemoveLen;
442f7639e1bSTed Kremenek     CurEnd = CurOffs.getWithOffset(CurLen);
443f7639e1bSTed Kremenek   }
444f7639e1bSTed Kremenek 
4455312b667SArgyrios Kyrtzidis   applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
4465312b667SArgyrios Kyrtzidis                shouldAdjustRemovals);
447f7639e1bSTed Kremenek }
448f7639e1bSTed Kremenek 
clearRewrites()449f7639e1bSTed Kremenek void EditedSource::clearRewrites() {
450f7639e1bSTed Kremenek   FileEdits.clear();
451f7639e1bSTed Kremenek   StrAlloc.Reset();
452f7639e1bSTed Kremenek }
453f7639e1bSTed Kremenek 
getSourceText(FileOffset BeginOffs,FileOffset EndOffs,bool & Invalid)454f7639e1bSTed Kremenek StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
455f7639e1bSTed Kremenek                                       bool &Invalid) {
456f7639e1bSTed Kremenek   assert(BeginOffs.getFID() == EndOffs.getFID());
457f7639e1bSTed Kremenek   assert(BeginOffs <= EndOffs);
458f7639e1bSTed Kremenek   SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
459f7639e1bSTed Kremenek   BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
460f7639e1bSTed Kremenek   assert(BLoc.isFileID());
461f7639e1bSTed Kremenek   SourceLocation
462f7639e1bSTed Kremenek     ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
463f7639e1bSTed Kremenek   return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
464f7639e1bSTed Kremenek                               SourceMgr, LangOpts, &Invalid);
465f7639e1bSTed Kremenek }
466f7639e1bSTed Kremenek 
467f7639e1bSTed Kremenek EditedSource::FileEditsTy::iterator
getActionForOffset(FileOffset Offs)468f7639e1bSTed Kremenek EditedSource::getActionForOffset(FileOffset Offs) {
469f7639e1bSTed Kremenek   FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
470f7639e1bSTed Kremenek   if (I == FileEdits.begin())
471f7639e1bSTed Kremenek     return FileEdits.end();
472f7639e1bSTed Kremenek   --I;
473f7639e1bSTed Kremenek   FileEdit &FA = I->second;
474f7639e1bSTed Kremenek   FileOffset B = I->first;
475f7639e1bSTed Kremenek   FileOffset E = B.getWithOffset(FA.RemoveLen);
476f7639e1bSTed Kremenek   if (Offs >= B && Offs < E)
477f7639e1bSTed Kremenek     return I;
478f7639e1bSTed Kremenek 
479f7639e1bSTed Kremenek   return FileEdits.end();
480f7639e1bSTed Kremenek }
481