xref: /aosp_15_r20/external/clang/lib/Edit/Commit.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
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