clang  3.9.0
Commit.cpp
Go to the documentation of this file.
1 //===----- Commit.cpp - A unit of edits -----------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "clang/Edit/Commit.h"
13 #include "clang/Lex/Lexer.h"
15 
16 using namespace clang;
17 using namespace edit;
18 
20  SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
21  Loc = Loc.getLocWithOffset(Offset.getOffset());
22  assert(Loc.isFileID());
23  return Loc;
24 }
25 
27  SourceLocation Loc = getFileLocation(SM);
29 }
30 
32  SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());
33  Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());
34  assert(Loc.isFileID());
36 }
37 
39  : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),
40  PPRec(Editor.getPPCondDirectiveRecord()),
41  Editor(&Editor), IsCommitable(true) { }
42 
43 bool Commit::insert(SourceLocation loc, StringRef text,
44  bool afterToken, bool beforePreviousInsertions) {
45  if (text.empty())
46  return true;
47 
48  FileOffset Offs;
49  if ((!afterToken && !canInsert(loc, Offs)) ||
50  ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
51  IsCommitable = false;
52  return false;
53  }
54 
55  addInsert(loc, Offs, text, beforePreviousInsertions);
56  return true;
57 }
58 
60  CharSourceRange range,
61  bool afterToken, bool beforePreviousInsertions) {
62  FileOffset RangeOffs;
63  unsigned RangeLen;
64  if (!canRemoveRange(range, RangeOffs, RangeLen)) {
65  IsCommitable = false;
66  return false;
67  }
68 
69  FileOffset Offs;
70  if ((!afterToken && !canInsert(loc, Offs)) ||
71  ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
72  IsCommitable = false;
73  return false;
74  }
75 
76  if (PPRec &&
78  IsCommitable = false;
79  return false;
80  }
81 
82  addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
83  return true;
84 }
85 
87  FileOffset Offs;
88  unsigned Len;
89  if (!canRemoveRange(range, Offs, Len)) {
90  IsCommitable = false;
91  return false;
92  }
93 
94  addRemove(range.getBegin(), Offs, Len);
95  return true;
96 }
97 
98 bool Commit::insertWrap(StringRef before, CharSourceRange range,
99  StringRef after) {
100  bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
101  /*beforePreviousInsertions=*/true);
102  bool commitableAfter;
103  if (range.isTokenRange())
104  commitableAfter = insertAfterToken(range.getEnd(), after);
105  else
106  commitableAfter = insert(range.getEnd(), after);
107 
108  return commitableBefore && commitableAfter;
109 }
110 
111 bool Commit::replace(CharSourceRange range, StringRef text) {
112  if (text.empty())
113  return remove(range);
114 
115  FileOffset Offs;
116  unsigned Len;
117  if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {
118  IsCommitable = false;
119  return false;
120  }
121 
122  addRemove(range.getBegin(), Offs, Len);
123  addInsert(range.getBegin(), Offs, text, false);
124  return true;
125 }
126 
128  CharSourceRange replacementRange) {
129  FileOffset OuterBegin;
130  unsigned OuterLen;
131  if (!canRemoveRange(range, OuterBegin, OuterLen)) {
132  IsCommitable = false;
133  return false;
134  }
135 
136  FileOffset InnerBegin;
137  unsigned InnerLen;
138  if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
139  IsCommitable = false;
140  return false;
141  }
142 
143  FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
144  FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);
145  if (OuterBegin.getFID() != InnerBegin.getFID() ||
146  InnerBegin < OuterBegin ||
147  InnerBegin > OuterEnd ||
148  InnerEnd > OuterEnd) {
149  IsCommitable = false;
150  return false;
151  }
152 
153  addRemove(range.getBegin(),
154  OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
155  addRemove(replacementRange.getEnd(),
156  InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
157  return true;
158 }
159 
161  StringRef replacementText) {
162  if (text.empty() || replacementText.empty())
163  return true;
164 
165  FileOffset Offs;
166  unsigned Len;
167  if (!canReplaceText(loc, replacementText, Offs, Len)) {
168  IsCommitable = false;
169  return false;
170  }
171 
172  addRemove(loc, Offs, Len);
173  addInsert(loc, Offs, text, false);
174  return true;
175 }
176 
177 void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,
178  bool beforePreviousInsertions) {
179  if (text.empty())
180  return;
181 
182  Edit data;
183  data.Kind = Act_Insert;
184  data.OrigLoc = OrigLoc;
185  data.Offset = Offs;
186  data.Text = text.copy(StrAlloc);
187  data.BeforePrev = beforePreviousInsertions;
188  CachedEdits.push_back(data);
189 }
190 
191 void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
192  FileOffset RangeOffs, unsigned RangeLen,
193  bool beforePreviousInsertions) {
194  if (RangeLen == 0)
195  return;
196 
197  Edit data;
198  data.Kind = Act_InsertFromRange;
199  data.OrigLoc = OrigLoc;
200  data.Offset = Offs;
201  data.InsertFromRangeOffs = RangeOffs;
202  data.Length = RangeLen;
203  data.BeforePrev = beforePreviousInsertions;
204  CachedEdits.push_back(data);
205 }
206 
207 void Commit::addRemove(SourceLocation OrigLoc,
208  FileOffset Offs, unsigned Len) {
209  if (Len == 0)
210  return;
211 
212  Edit data;
213  data.Kind = Act_Remove;
214  data.OrigLoc = OrigLoc;
215  data.Offset = Offs;
216  data.Length = Len;
217  CachedEdits.push_back(data);
218 }
219 
220 bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
221  if (loc.isInvalid())
222  return false;
223 
224  if (loc.isMacroID())
225  isAtStartOfMacroExpansion(loc, &loc);
226 
227  const SourceManager &SM = SourceMgr;
228  while (SM.isMacroArgExpansion(loc))
229  loc = SM.getImmediateSpellingLoc(loc);
230 
231  if (loc.isMacroID())
232  if (!isAtStartOfMacroExpansion(loc, &loc))
233  return false;
234 
235  if (SM.isInSystemHeader(loc))
236  return false;
237 
238  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
239  if (locInfo.first.isInvalid())
240  return false;
241  offs = FileOffset(locInfo.first, locInfo.second);
242  return canInsertInOffset(loc, offs);
243 }
244 
245 bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
246  SourceLocation &AfterLoc) {
247  if (loc.isInvalid())
248 
249  return false;
250 
251  SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
252  unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
253  AfterLoc = loc.getLocWithOffset(tokLen);
254 
255  if (loc.isMacroID())
256  isAtEndOfMacroExpansion(loc, &loc);
257 
258  const SourceManager &SM = SourceMgr;
259  while (SM.isMacroArgExpansion(loc))
260  loc = SM.getImmediateSpellingLoc(loc);
261 
262  if (loc.isMacroID())
263  if (!isAtEndOfMacroExpansion(loc, &loc))
264  return false;
265 
266  if (SM.isInSystemHeader(loc))
267  return false;
268 
269  loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
270  if (loc.isInvalid())
271  return false;
272 
273  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
274  if (locInfo.first.isInvalid())
275  return false;
276  offs = FileOffset(locInfo.first, locInfo.second);
277  return canInsertInOffset(loc, offs);
278 }
279 
280 bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
281  for (unsigned i = 0, e = CachedEdits.size(); i != e; ++i) {
282  Edit &act = CachedEdits[i];
283  if (act.Kind == Act_Remove) {
284  if (act.Offset.getFID() == Offs.getFID() &&
285  Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))
286  return false; // position has been removed.
287  }
288  }
289 
290  if (!Editor)
291  return true;
292  return Editor->canInsertInOffset(OrigLoc, Offs);
293 }
294 
295 bool Commit::canRemoveRange(CharSourceRange range,
296  FileOffset &Offs, unsigned &Len) {
297  const SourceManager &SM = SourceMgr;
298  range = Lexer::makeFileCharRange(range, SM, LangOpts);
299  if (range.isInvalid())
300  return false;
301 
302  if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
303  return false;
304  if (SM.isInSystemHeader(range.getBegin()) ||
305  SM.isInSystemHeader(range.getEnd()))
306  return false;
307 
308  if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
309  return false;
310 
311  std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
312  std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
313  if (beginInfo.first != endInfo.first ||
314  beginInfo.second > endInfo.second)
315  return false;
316 
317  Offs = FileOffset(beginInfo.first, beginInfo.second);
318  Len = endInfo.second - beginInfo.second;
319  return true;
320 }
321 
322 bool Commit::canReplaceText(SourceLocation loc, StringRef text,
323  FileOffset &Offs, unsigned &Len) {
324  assert(!text.empty());
325 
326  if (!canInsert(loc, Offs))
327  return false;
328 
329  // Try to load the file buffer.
330  bool invalidTemp = false;
331  StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);
332  if (invalidTemp)
333  return false;
334 
335  Len = text.size();
336  return file.substr(Offs.getOffset()).startswith(text);
337 }
338 
339 bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
340  SourceLocation *MacroBegin) const {
341  return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
342 }
343 bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
344  SourceLocation *MacroEnd) const {
345  return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
346 }
bool isMacroArgExpansion(SourceLocation Loc, SourceLocation *StartLoc=nullptr) const
Tests whether the given source location represents a macro argument's expansion into the function-lik...
bool remove(CharSourceRange range)
Definition: Commit.cpp:86
unsigned Length
SourceLocation getBegin() const
bool isMacroID() const
SourceLocation getImmediateSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID. ...
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID. ...
bool insertWrap(StringRef before, CharSourceRange range, StringRef after)
Definition: Commit.cpp:98
Defines the SourceManager interface.
bool insertAfterToken(SourceLocation loc, StringRef text, bool beforePreviousInsertions=false)
Definition: Commit.h:69
bool insertFromRange(SourceLocation loc, CharSourceRange range, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:59
static bool isAtStartOfMacroExpansion(SourceLocation loc, const SourceManager &SM, const LangOptions &LangOpts, SourceLocation *MacroBegin=nullptr)
Returns true if the given MacroID location points at the first token of the macro expansion...
Definition: Lexer.cpp:783
bool rangeIntersectsConditionalDirective(SourceRange Range) const
Returns true if the given range intersects with a conditional directive.
unsigned getOffset() const
Definition: FileOffset.h:29
FileOffset getWithOffset(unsigned offset) const
Definition: FileOffset.h:31
bool insert(SourceLocation loc, StringRef text, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:43
StringRef getBufferData(FileID FID, bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
bool isFileID() const
bool replace(CharSourceRange range, StringRef text)
Definition: Commit.cpp:111
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
uint32_t Offset
Definition: CacheTokens.cpp:44
bool isInvalid() const
static bool isAtEndOfMacroExpansion(SourceLocation loc, const SourceManager &SM, const LangOptions &LangOpts, SourceLocation *MacroEnd=nullptr)
Returns true if the given MacroID location points at the last token of the macro expansion.
Definition: Lexer.cpp:805
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
Definition: Lexer.cpp:761
Represents a character-granular source range.
SourceLocation getEnd() const
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...
Definition: Lexer.cpp:406
FileID getFID() const
Definition: FileOffset.h:28
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
CharSourceRange getInsertFromRange(SourceManager &SM) const
Definition: Commit.cpp:31
const SourceManager & SM
Definition: Format.cpp:1184
static CharSourceRange getCharRange(SourceRange R)
bool isTokenRange() const
Return true if the end of this range specifies the start of the last token.
bool areInDifferentConditionalDirectiveRegion(SourceLocation LHS, SourceLocation RHS) const
Returns true if the given locations are in different regions, separated by conditional directive bloc...
Encodes a location in the source.
SourceRange getAsRange() const
CharSourceRange getFileRange(SourceManager &SM) const
Definition: Commit.cpp:26
static CharSourceRange makeFileCharRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
Accepts a range and returns a character range with file locations.
Definition: Lexer.cpp:858
bool replaceText(SourceLocation loc, StringRef text, StringRef replacementText)
Definition: Commit.cpp:160
Commit(EditedSource &Editor)
Definition: Commit.cpp:38
SourceLocation getFileLocation(SourceManager &SM) const
Definition: Commit.cpp:19
bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs)
bool replaceWithInner(CharSourceRange range, CharSourceRange innerRange)
Definition: Commit.cpp:127
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file. ...
#define true
Definition: stdbool.h:32
std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
This class handles loading and caching of source files into memory.