1*67e74705SXin Li //===----- Commit.cpp - A unit of 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/Commit.h"
11*67e74705SXin Li #include "clang/Basic/SourceManager.h"
12*67e74705SXin Li #include "clang/Edit/EditedSource.h"
13*67e74705SXin Li #include "clang/Lex/Lexer.h"
14*67e74705SXin Li #include "clang/Lex/PPConditionalDirectiveRecord.h"
15*67e74705SXin Li
16*67e74705SXin Li using namespace clang;
17*67e74705SXin Li using namespace edit;
18*67e74705SXin Li
getFileLocation(SourceManager & SM) const19*67e74705SXin Li SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {
20*67e74705SXin Li SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
21*67e74705SXin Li Loc = Loc.getLocWithOffset(Offset.getOffset());
22*67e74705SXin Li assert(Loc.isFileID());
23*67e74705SXin Li return Loc;
24*67e74705SXin Li }
25*67e74705SXin Li
getFileRange(SourceManager & SM) const26*67e74705SXin Li CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {
27*67e74705SXin Li SourceLocation Loc = getFileLocation(SM);
28*67e74705SXin Li return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
29*67e74705SXin Li }
30*67e74705SXin Li
getInsertFromRange(SourceManager & SM) const31*67e74705SXin Li CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const {
32*67e74705SXin Li SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());
33*67e74705SXin Li Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());
34*67e74705SXin Li assert(Loc.isFileID());
35*67e74705SXin Li return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
36*67e74705SXin Li }
37*67e74705SXin Li
Commit(EditedSource & Editor)38*67e74705SXin Li Commit::Commit(EditedSource &Editor)
39*67e74705SXin Li : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),
40*67e74705SXin Li PPRec(Editor.getPPCondDirectiveRecord()),
41*67e74705SXin Li Editor(&Editor), IsCommitable(true) { }
42*67e74705SXin Li
insert(SourceLocation loc,StringRef text,bool afterToken,bool beforePreviousInsertions)43*67e74705SXin Li bool Commit::insert(SourceLocation loc, StringRef text,
44*67e74705SXin Li bool afterToken, bool beforePreviousInsertions) {
45*67e74705SXin Li if (text.empty())
46*67e74705SXin Li return true;
47*67e74705SXin Li
48*67e74705SXin Li FileOffset Offs;
49*67e74705SXin Li if ((!afterToken && !canInsert(loc, Offs)) ||
50*67e74705SXin Li ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
51*67e74705SXin Li IsCommitable = false;
52*67e74705SXin Li return false;
53*67e74705SXin Li }
54*67e74705SXin Li
55*67e74705SXin Li addInsert(loc, Offs, text, beforePreviousInsertions);
56*67e74705SXin Li return true;
57*67e74705SXin Li }
58*67e74705SXin Li
insertFromRange(SourceLocation loc,CharSourceRange range,bool afterToken,bool beforePreviousInsertions)59*67e74705SXin Li bool Commit::insertFromRange(SourceLocation loc,
60*67e74705SXin Li CharSourceRange range,
61*67e74705SXin Li bool afterToken, bool beforePreviousInsertions) {
62*67e74705SXin Li FileOffset RangeOffs;
63*67e74705SXin Li unsigned RangeLen;
64*67e74705SXin Li if (!canRemoveRange(range, RangeOffs, RangeLen)) {
65*67e74705SXin Li IsCommitable = false;
66*67e74705SXin Li return false;
67*67e74705SXin Li }
68*67e74705SXin Li
69*67e74705SXin Li FileOffset Offs;
70*67e74705SXin Li if ((!afterToken && !canInsert(loc, Offs)) ||
71*67e74705SXin Li ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
72*67e74705SXin Li IsCommitable = false;
73*67e74705SXin Li return false;
74*67e74705SXin Li }
75*67e74705SXin Li
76*67e74705SXin Li if (PPRec &&
77*67e74705SXin Li PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) {
78*67e74705SXin Li IsCommitable = false;
79*67e74705SXin Li return false;
80*67e74705SXin Li }
81*67e74705SXin Li
82*67e74705SXin Li addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
83*67e74705SXin Li return true;
84*67e74705SXin Li }
85*67e74705SXin Li
remove(CharSourceRange range)86*67e74705SXin Li bool Commit::remove(CharSourceRange range) {
87*67e74705SXin Li FileOffset Offs;
88*67e74705SXin Li unsigned Len;
89*67e74705SXin Li if (!canRemoveRange(range, Offs, Len)) {
90*67e74705SXin Li IsCommitable = false;
91*67e74705SXin Li return false;
92*67e74705SXin Li }
93*67e74705SXin Li
94*67e74705SXin Li addRemove(range.getBegin(), Offs, Len);
95*67e74705SXin Li return true;
96*67e74705SXin Li }
97*67e74705SXin Li
insertWrap(StringRef before,CharSourceRange range,StringRef after)98*67e74705SXin Li bool Commit::insertWrap(StringRef before, CharSourceRange range,
99*67e74705SXin Li StringRef after) {
100*67e74705SXin Li bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
101*67e74705SXin Li /*beforePreviousInsertions=*/true);
102*67e74705SXin Li bool commitableAfter;
103*67e74705SXin Li if (range.isTokenRange())
104*67e74705SXin Li commitableAfter = insertAfterToken(range.getEnd(), after);
105*67e74705SXin Li else
106*67e74705SXin Li commitableAfter = insert(range.getEnd(), after);
107*67e74705SXin Li
108*67e74705SXin Li return commitableBefore && commitableAfter;
109*67e74705SXin Li }
110*67e74705SXin Li
replace(CharSourceRange range,StringRef text)111*67e74705SXin Li bool Commit::replace(CharSourceRange range, StringRef text) {
112*67e74705SXin Li if (text.empty())
113*67e74705SXin Li return remove(range);
114*67e74705SXin Li
115*67e74705SXin Li FileOffset Offs;
116*67e74705SXin Li unsigned Len;
117*67e74705SXin Li if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {
118*67e74705SXin Li IsCommitable = false;
119*67e74705SXin Li return false;
120*67e74705SXin Li }
121*67e74705SXin Li
122*67e74705SXin Li addRemove(range.getBegin(), Offs, Len);
123*67e74705SXin Li addInsert(range.getBegin(), Offs, text, false);
124*67e74705SXin Li return true;
125*67e74705SXin Li }
126*67e74705SXin Li
replaceWithInner(CharSourceRange range,CharSourceRange replacementRange)127*67e74705SXin Li bool Commit::replaceWithInner(CharSourceRange range,
128*67e74705SXin Li CharSourceRange replacementRange) {
129*67e74705SXin Li FileOffset OuterBegin;
130*67e74705SXin Li unsigned OuterLen;
131*67e74705SXin Li if (!canRemoveRange(range, OuterBegin, OuterLen)) {
132*67e74705SXin Li IsCommitable = false;
133*67e74705SXin Li return false;
134*67e74705SXin Li }
135*67e74705SXin Li
136*67e74705SXin Li FileOffset InnerBegin;
137*67e74705SXin Li unsigned InnerLen;
138*67e74705SXin Li if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
139*67e74705SXin Li IsCommitable = false;
140*67e74705SXin Li return false;
141*67e74705SXin Li }
142*67e74705SXin Li
143*67e74705SXin Li FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
144*67e74705SXin Li FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);
145*67e74705SXin Li if (OuterBegin.getFID() != InnerBegin.getFID() ||
146*67e74705SXin Li InnerBegin < OuterBegin ||
147*67e74705SXin Li InnerBegin > OuterEnd ||
148*67e74705SXin Li InnerEnd > OuterEnd) {
149*67e74705SXin Li IsCommitable = false;
150*67e74705SXin Li return false;
151*67e74705SXin Li }
152*67e74705SXin Li
153*67e74705SXin Li addRemove(range.getBegin(),
154*67e74705SXin Li OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
155*67e74705SXin Li addRemove(replacementRange.getEnd(),
156*67e74705SXin Li InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
157*67e74705SXin Li return true;
158*67e74705SXin Li }
159*67e74705SXin Li
replaceText(SourceLocation loc,StringRef text,StringRef replacementText)160*67e74705SXin Li bool Commit::replaceText(SourceLocation loc, StringRef text,
161*67e74705SXin Li StringRef replacementText) {
162*67e74705SXin Li if (text.empty() || replacementText.empty())
163*67e74705SXin Li return true;
164*67e74705SXin Li
165*67e74705SXin Li FileOffset Offs;
166*67e74705SXin Li unsigned Len;
167*67e74705SXin Li if (!canReplaceText(loc, replacementText, Offs, Len)) {
168*67e74705SXin Li IsCommitable = false;
169*67e74705SXin Li return false;
170*67e74705SXin Li }
171*67e74705SXin Li
172*67e74705SXin Li addRemove(loc, Offs, Len);
173*67e74705SXin Li addInsert(loc, Offs, text, false);
174*67e74705SXin Li return true;
175*67e74705SXin Li }
176*67e74705SXin Li
addInsert(SourceLocation OrigLoc,FileOffset Offs,StringRef text,bool beforePreviousInsertions)177*67e74705SXin Li void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,
178*67e74705SXin Li bool beforePreviousInsertions) {
179*67e74705SXin Li if (text.empty())
180*67e74705SXin Li return;
181*67e74705SXin Li
182*67e74705SXin Li Edit data;
183*67e74705SXin Li data.Kind = Act_Insert;
184*67e74705SXin Li data.OrigLoc = OrigLoc;
185*67e74705SXin Li data.Offset = Offs;
186*67e74705SXin Li data.Text = text.copy(StrAlloc);
187*67e74705SXin Li data.BeforePrev = beforePreviousInsertions;
188*67e74705SXin Li CachedEdits.push_back(data);
189*67e74705SXin Li }
190*67e74705SXin Li
addInsertFromRange(SourceLocation OrigLoc,FileOffset Offs,FileOffset RangeOffs,unsigned RangeLen,bool beforePreviousInsertions)191*67e74705SXin Li void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
192*67e74705SXin Li FileOffset RangeOffs, unsigned RangeLen,
193*67e74705SXin Li bool beforePreviousInsertions) {
194*67e74705SXin Li if (RangeLen == 0)
195*67e74705SXin Li return;
196*67e74705SXin Li
197*67e74705SXin Li Edit data;
198*67e74705SXin Li data.Kind = Act_InsertFromRange;
199*67e74705SXin Li data.OrigLoc = OrigLoc;
200*67e74705SXin Li data.Offset = Offs;
201*67e74705SXin Li data.InsertFromRangeOffs = RangeOffs;
202*67e74705SXin Li data.Length = RangeLen;
203*67e74705SXin Li data.BeforePrev = beforePreviousInsertions;
204*67e74705SXin Li CachedEdits.push_back(data);
205*67e74705SXin Li }
206*67e74705SXin Li
addRemove(SourceLocation OrigLoc,FileOffset Offs,unsigned Len)207*67e74705SXin Li void Commit::addRemove(SourceLocation OrigLoc,
208*67e74705SXin Li FileOffset Offs, unsigned Len) {
209*67e74705SXin Li if (Len == 0)
210*67e74705SXin Li return;
211*67e74705SXin Li
212*67e74705SXin Li Edit data;
213*67e74705SXin Li data.Kind = Act_Remove;
214*67e74705SXin Li data.OrigLoc = OrigLoc;
215*67e74705SXin Li data.Offset = Offs;
216*67e74705SXin Li data.Length = Len;
217*67e74705SXin Li CachedEdits.push_back(data);
218*67e74705SXin Li }
219*67e74705SXin Li
canInsert(SourceLocation loc,FileOffset & offs)220*67e74705SXin Li bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
221*67e74705SXin Li if (loc.isInvalid())
222*67e74705SXin Li return false;
223*67e74705SXin Li
224*67e74705SXin Li if (loc.isMacroID())
225*67e74705SXin Li isAtStartOfMacroExpansion(loc, &loc);
226*67e74705SXin Li
227*67e74705SXin Li const SourceManager &SM = SourceMgr;
228*67e74705SXin Li while (SM.isMacroArgExpansion(loc))
229*67e74705SXin Li loc = SM.getImmediateSpellingLoc(loc);
230*67e74705SXin Li
231*67e74705SXin Li if (loc.isMacroID())
232*67e74705SXin Li if (!isAtStartOfMacroExpansion(loc, &loc))
233*67e74705SXin Li return false;
234*67e74705SXin Li
235*67e74705SXin Li if (SM.isInSystemHeader(loc))
236*67e74705SXin Li return false;
237*67e74705SXin Li
238*67e74705SXin Li std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
239*67e74705SXin Li if (locInfo.first.isInvalid())
240*67e74705SXin Li return false;
241*67e74705SXin Li offs = FileOffset(locInfo.first, locInfo.second);
242*67e74705SXin Li return canInsertInOffset(loc, offs);
243*67e74705SXin Li }
244*67e74705SXin Li
canInsertAfterToken(SourceLocation loc,FileOffset & offs,SourceLocation & AfterLoc)245*67e74705SXin Li bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
246*67e74705SXin Li SourceLocation &AfterLoc) {
247*67e74705SXin Li if (loc.isInvalid())
248*67e74705SXin Li
249*67e74705SXin Li return false;
250*67e74705SXin Li
251*67e74705SXin Li SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
252*67e74705SXin Li unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
253*67e74705SXin Li AfterLoc = loc.getLocWithOffset(tokLen);
254*67e74705SXin Li
255*67e74705SXin Li if (loc.isMacroID())
256*67e74705SXin Li isAtEndOfMacroExpansion(loc, &loc);
257*67e74705SXin Li
258*67e74705SXin Li const SourceManager &SM = SourceMgr;
259*67e74705SXin Li while (SM.isMacroArgExpansion(loc))
260*67e74705SXin Li loc = SM.getImmediateSpellingLoc(loc);
261*67e74705SXin Li
262*67e74705SXin Li if (loc.isMacroID())
263*67e74705SXin Li if (!isAtEndOfMacroExpansion(loc, &loc))
264*67e74705SXin Li return false;
265*67e74705SXin Li
266*67e74705SXin Li if (SM.isInSystemHeader(loc))
267*67e74705SXin Li return false;
268*67e74705SXin Li
269*67e74705SXin Li loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
270*67e74705SXin Li if (loc.isInvalid())
271*67e74705SXin Li return false;
272*67e74705SXin Li
273*67e74705SXin Li std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
274*67e74705SXin Li if (locInfo.first.isInvalid())
275*67e74705SXin Li return false;
276*67e74705SXin Li offs = FileOffset(locInfo.first, locInfo.second);
277*67e74705SXin Li return canInsertInOffset(loc, offs);
278*67e74705SXin Li }
279*67e74705SXin Li
canInsertInOffset(SourceLocation OrigLoc,FileOffset Offs)280*67e74705SXin Li bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
281*67e74705SXin Li for (unsigned i = 0, e = CachedEdits.size(); i != e; ++i) {
282*67e74705SXin Li Edit &act = CachedEdits[i];
283*67e74705SXin Li if (act.Kind == Act_Remove) {
284*67e74705SXin Li if (act.Offset.getFID() == Offs.getFID() &&
285*67e74705SXin Li Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))
286*67e74705SXin Li return false; // position has been removed.
287*67e74705SXin Li }
288*67e74705SXin Li }
289*67e74705SXin Li
290*67e74705SXin Li if (!Editor)
291*67e74705SXin Li return true;
292*67e74705SXin Li return Editor->canInsertInOffset(OrigLoc, Offs);
293*67e74705SXin Li }
294*67e74705SXin Li
canRemoveRange(CharSourceRange range,FileOffset & Offs,unsigned & Len)295*67e74705SXin Li bool Commit::canRemoveRange(CharSourceRange range,
296*67e74705SXin Li FileOffset &Offs, unsigned &Len) {
297*67e74705SXin Li const SourceManager &SM = SourceMgr;
298*67e74705SXin Li range = Lexer::makeFileCharRange(range, SM, LangOpts);
299*67e74705SXin Li if (range.isInvalid())
300*67e74705SXin Li return false;
301*67e74705SXin Li
302*67e74705SXin Li if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
303*67e74705SXin Li return false;
304*67e74705SXin Li if (SM.isInSystemHeader(range.getBegin()) ||
305*67e74705SXin Li SM.isInSystemHeader(range.getEnd()))
306*67e74705SXin Li return false;
307*67e74705SXin Li
308*67e74705SXin Li if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
309*67e74705SXin Li return false;
310*67e74705SXin Li
311*67e74705SXin Li std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
312*67e74705SXin Li std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
313*67e74705SXin Li if (beginInfo.first != endInfo.first ||
314*67e74705SXin Li beginInfo.second > endInfo.second)
315*67e74705SXin Li return false;
316*67e74705SXin Li
317*67e74705SXin Li Offs = FileOffset(beginInfo.first, beginInfo.second);
318*67e74705SXin Li Len = endInfo.second - beginInfo.second;
319*67e74705SXin Li return true;
320*67e74705SXin Li }
321*67e74705SXin Li
canReplaceText(SourceLocation loc,StringRef text,FileOffset & Offs,unsigned & Len)322*67e74705SXin Li bool Commit::canReplaceText(SourceLocation loc, StringRef text,
323*67e74705SXin Li FileOffset &Offs, unsigned &Len) {
324*67e74705SXin Li assert(!text.empty());
325*67e74705SXin Li
326*67e74705SXin Li if (!canInsert(loc, Offs))
327*67e74705SXin Li return false;
328*67e74705SXin Li
329*67e74705SXin Li // Try to load the file buffer.
330*67e74705SXin Li bool invalidTemp = false;
331*67e74705SXin Li StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);
332*67e74705SXin Li if (invalidTemp)
333*67e74705SXin Li return false;
334*67e74705SXin Li
335*67e74705SXin Li Len = text.size();
336*67e74705SXin Li return file.substr(Offs.getOffset()).startswith(text);
337*67e74705SXin Li }
338*67e74705SXin Li
isAtStartOfMacroExpansion(SourceLocation loc,SourceLocation * MacroBegin) const339*67e74705SXin Li bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
340*67e74705SXin Li SourceLocation *MacroBegin) const {
341*67e74705SXin Li return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
342*67e74705SXin Li }
isAtEndOfMacroExpansion(SourceLocation loc,SourceLocation * MacroEnd) const343*67e74705SXin Li bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
344*67e74705SXin Li SourceLocation *MacroEnd) const {
345*67e74705SXin Li return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
346*67e74705SXin Li }
347