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