Commit.cpp revision 6badc76787dc9480fd7c21d3eb75aab79d2df3f5
196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//===----- Commit.cpp - A unit of edits -----------------------------------===//
257f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler//
396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//                     The LLVM Compiler Infrastructure
496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//
596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// This file is distributed under the University of Illinois Open Source
696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// License. See LICENSE.TXT for details.
796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//
896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//===----------------------------------------------------------------------===//
996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project#include "clang/Edit/Commit.h"
1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project#include "clang/Basic/SourceManager.h"
1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project#include "clang/Edit/EditedSource.h"
1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project#include "clang/Lex/Lexer.h"
1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project#include "clang/Lex/PPConditionalDirectiveRecord.h"
1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectusing namespace clang;
1757f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadlerusing namespace edit;
1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
1957f125a01b5fbb5860b144b3057153a50d07ddd1Andrew StadlerSourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {
208ee17c7012105fd7a5bad644b455d3e894b557dcMakoto Onuki  SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
2196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project  Loc = Loc.getLocWithOffset(Offset.getOffset());
2257f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler  assert(Loc.isFileID());
2357f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler  return Loc;
2457f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler}
251a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki
2696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source ProjectCharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {
2796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project  SourceLocation Loc = getFileLocation(SM);
2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project  return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
29f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank}
30f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
31f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc BlankCharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const {
32f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank  SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());
33f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank  Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());
34f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank  assert(Loc.isFileID());
35f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank  return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
36560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy}
3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
381a0b0092e0deba07b7dcb543a837fbc116584389Makoto OnukiCommit::Commit(EditedSource &Editor)
391a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),
4096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    PPRec(Editor.getPPCondDirectiveRecord()),
4196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    Editor(&Editor),
427985b43ab7310f76c170266da346f993d2f86051Paul Westbrook    ForceCommitInSystemHeader(Editor.getForceCommitInSystemHeader()),
437985b43ab7310f76c170266da346f993d2f86051Paul Westbrook    IsCommitable(true) { }
447985b43ab7310f76c170266da346f993d2f86051Paul Westbrook
4596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectbool Commit::insert(SourceLocation loc, StringRef text,
4657f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler                    bool afterToken, bool beforePreviousInsertions) {
4757f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler  if (text.empty())
48f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    return true;
49560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy
5057f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler  FileOffset Offs;
5157f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler  if ((!afterToken && !canInsert(loc, Offs)) ||
5296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project      ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
5357f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler    IsCommitable = false;
5457f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler    return false;
5596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project  }
565bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler
575bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler  addInsert(loc, Offs, text, beforePreviousInsertions);
5891237e9dcb0a948f17488b464edabcea0f259d31Makoto Onuki  return true;
595bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler}
605bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler
61e36b6ff4fa808afa84dc09fa82587dae106c8d3dAndrew Stadlerbool Commit::insertFromRange(SourceLocation loc,
62e36b6ff4fa808afa84dc09fa82587dae106c8d3dAndrew Stadler                             CharSourceRange range,
635bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler                             bool afterToken, bool beforePreviousInsertions) {
643a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank  FileOffset RangeOffs;
65f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank  unsigned RangeLen;
665bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler  if (!canRemoveRange(range, RangeOffs, RangeLen)) {
675bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler    IsCommitable = false;
685bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler    return false;
695bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler  }
703a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank
715bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler  FileOffset Offs;
725bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler  if ((!afterToken && !canInsert(loc, Offs)) ||
733a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank      ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
7457f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler    IsCommitable = false;
752fbb3db5d86210d03175ce77ff08c989a96c5864Makoto Onuki    return false;
761a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  }
775bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler
782fbb3db5d86210d03175ce77ff08c989a96c5864Makoto Onuki  if (PPRec &&
795bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler      PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) {
805bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler    IsCommitable = false;
8119b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki    return false;
8257f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler  }
8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project  addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
851a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  return true;
8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project}
8776a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler
8876a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadlerbool Commit::remove(CharSourceRange range) {
893a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank  FileOffset Offs;
90f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank  unsigned Len;
91f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank  if (!canRemoveRange(range, Offs, Len)) {
9276a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler    IsCommitable = false;
93f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank    return false;
9476a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler  }
95f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
9676a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler  addRemove(range.getBegin(), Offs, Len);
97f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank  return true;
9876a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler}
99f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank
10076a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadlerbool Commit::insertWrap(StringRef before, CharSourceRange range,
10119b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki                        StringRef after) {
10219b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki  bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
103f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank                                 /*beforePreviousInsertions=*/true);
10419b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki  bool commitableAfter;
10576a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler  if (range.isTokenRange())
10676a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler    commitableAfter = insertAfterToken(range.getEnd(), after);
107f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank  else
1088ee17c7012105fd7a5bad644b455d3e894b557dcMakoto Onuki    commitableAfter = insert(range.getEnd(), after);
1091a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki
1101a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  return commitableBefore && commitableAfter;
1111a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki}
1121a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki
1131a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onukibool Commit::replace(CharSourceRange range, StringRef text) {
1141a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  if (text.empty())
1151a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki    return remove(range);
1161a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki
1171a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  FileOffset Offs;
1181a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  unsigned Len;
1191a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {
1201a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki    IsCommitable = false;
1211a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki    return false;
1221a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  }
123560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy
1241a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  addRemove(range.getBegin(), Offs, Len);
1251a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  addInsert(range.getBegin(), Offs, text, false);
1261a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki  return true;
1271a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki}
12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
129bool Commit::replaceWithInner(CharSourceRange range,
130                              CharSourceRange replacementRange) {
131  FileOffset OuterBegin;
132  unsigned OuterLen;
133  if (!canRemoveRange(range, OuterBegin, OuterLen)) {
134    IsCommitable = false;
135    return false;
136  }
137
138  FileOffset InnerBegin;
139  unsigned InnerLen;
140  if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
141    IsCommitable = false;
142    return false;
143  }
144
145  FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
146  FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);
147  if (OuterBegin.getFID() != InnerBegin.getFID() ||
148      InnerBegin < OuterBegin ||
149      InnerBegin > OuterEnd ||
150      InnerEnd > OuterEnd) {
151    IsCommitable = false;
152    return false;
153  }
154
155  addRemove(range.getBegin(),
156            OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
157  addRemove(replacementRange.getEnd(),
158            InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
159  return true;
160}
161
162bool Commit::replaceText(SourceLocation loc, StringRef text,
163                         StringRef replacementText) {
164  if (text.empty() || replacementText.empty())
165    return true;
166
167  FileOffset Offs;
168  unsigned Len;
169  if (!canReplaceText(loc, replacementText, Offs, Len)) {
170    IsCommitable = false;
171    return false;
172  }
173
174  addRemove(loc, Offs, Len);
175  addInsert(loc, Offs, text, false);
176  return true;
177}
178
179void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,
180                       bool beforePreviousInsertions) {
181  if (text.empty())
182    return;
183
184  Edit data;
185  data.Kind = Act_Insert;
186  data.OrigLoc = OrigLoc;
187  data.Offset = Offs;
188  data.Text = copyString(text);
189  data.BeforePrev = beforePreviousInsertions;
190  CachedEdits.push_back(data);
191}
192
193void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
194                                FileOffset RangeOffs, unsigned RangeLen,
195                                bool beforePreviousInsertions) {
196  if (RangeLen == 0)
197    return;
198
199  Edit data;
200  data.Kind = Act_InsertFromRange;
201  data.OrigLoc = OrigLoc;
202  data.Offset = Offs;
203  data.InsertFromRangeOffs = RangeOffs;
204  data.Length = RangeLen;
205  data.BeforePrev = beforePreviousInsertions;
206  CachedEdits.push_back(data);
207}
208
209void Commit::addRemove(SourceLocation OrigLoc,
210                       FileOffset Offs, unsigned Len) {
211  if (Len == 0)
212    return;
213
214  Edit data;
215  data.Kind = Act_Remove;
216  data.OrigLoc = OrigLoc;
217  data.Offset = Offs;
218  data.Length = Len;
219  CachedEdits.push_back(data);
220}
221
222bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
223  if (loc.isInvalid())
224    return false;
225
226  if (loc.isMacroID())
227    isAtStartOfMacroExpansion(loc, &loc);
228
229  const SourceManager &SM = SourceMgr;
230  while (SM.isMacroArgExpansion(loc))
231    loc = SM.getImmediateSpellingLoc(loc);
232
233  if (loc.isMacroID())
234    if (!isAtStartOfMacroExpansion(loc, &loc))
235      return false;
236
237  if (SM.isInSystemHeader(loc) && ForceCommitInSystemHeader)
238    return false;
239
240  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
241  if (locInfo.first.isInvalid())
242    return false;
243  offs = FileOffset(locInfo.first, locInfo.second);
244  return canInsertInOffset(loc, offs);
245}
246
247bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
248                                 SourceLocation &AfterLoc) {
249  if (loc.isInvalid())
250
251    return false;
252
253  SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
254  unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
255  AfterLoc = loc.getLocWithOffset(tokLen);
256
257  if (loc.isMacroID())
258    isAtEndOfMacroExpansion(loc, &loc);
259
260  const SourceManager &SM = SourceMgr;
261  while (SM.isMacroArgExpansion(loc))
262    loc = SM.getImmediateSpellingLoc(loc);
263
264  if (loc.isMacroID())
265    if (!isAtEndOfMacroExpansion(loc, &loc))
266      return false;
267
268  if (SM.isInSystemHeader(loc) && ForceCommitInSystemHeader)
269    return false;
270
271  loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
272  if (loc.isInvalid())
273    return false;
274
275  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
276  if (locInfo.first.isInvalid())
277    return false;
278  offs = FileOffset(locInfo.first, locInfo.second);
279  return canInsertInOffset(loc, offs);
280}
281
282bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
283  for (unsigned i = 0, e = CachedEdits.size(); i != e; ++i) {
284    Edit &act = CachedEdits[i];
285    if (act.Kind == Act_Remove) {
286      if (act.Offset.getFID() == Offs.getFID() &&
287          Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))
288        return false; // position has been removed.
289    }
290  }
291
292  if (!Editor)
293    return true;
294  return Editor->canInsertInOffset(OrigLoc, Offs);
295}
296
297bool Commit::canRemoveRange(CharSourceRange range,
298                            FileOffset &Offs, unsigned &Len) {
299  const SourceManager &SM = SourceMgr;
300  range = Lexer::makeFileCharRange(range, SM, LangOpts);
301  if (range.isInvalid())
302    return false;
303
304  if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
305    return false;
306  if ((SM.isInSystemHeader(range.getBegin()) ||
307       SM.isInSystemHeader(range.getEnd())) && ForceCommitInSystemHeader)
308    return false;
309
310  if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
311    return false;
312
313  std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
314  std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
315  if (beginInfo.first != endInfo.first ||
316      beginInfo.second > endInfo.second)
317    return false;
318
319  Offs = FileOffset(beginInfo.first, beginInfo.second);
320  Len = endInfo.second - beginInfo.second;
321  return true;
322}
323
324bool Commit::canReplaceText(SourceLocation loc, StringRef text,
325                            FileOffset &Offs, unsigned &Len) {
326  assert(!text.empty());
327
328  if (!canInsert(loc, Offs))
329    return false;
330
331  // Try to load the file buffer.
332  bool invalidTemp = false;
333  StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);
334  if (invalidTemp)
335    return false;
336
337  Len = text.size();
338  return file.substr(Offs.getOffset()).startswith(text);
339}
340
341bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
342                                       SourceLocation *MacroBegin) const {
343  return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
344}
345bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
346                                     SourceLocation *MacroEnd) const {
347  return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
348}
349