clang  6.0.0svn
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 
29 }
30 
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 }
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file. ...
bool remove(CharSourceRange range)
Definition: Commit.cpp:86
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
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
StringRef getBufferData(FileID FID, bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
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:795
bool insert(SourceLocation loc, StringRef text, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:43
SourceLocation getBegin() const
bool replace(CharSourceRange range, StringRef text)
Definition: Commit.cpp:111
FileID getFID() const
Definition: FileOffset.h:28
uint32_t Offset
Definition: CacheTokens.cpp:43
bool areInDifferentConditionalDirectiveRegion(SourceLocation LHS, SourceLocation RHS) const
Returns true if the given locations are in different regions, separated by conditional directive bloc...
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID. ...
SourceLocation OrigLoc
Definition: Commit.h:36
CharSourceRange getFileRange(SourceManager &SM) const
Definition: Commit.cpp:26
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:817
SourceLocation getImmediateSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID. ...
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:773
Represents a character-granular source range.
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
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:435
FileOffset InsertFromRangeOffs
Definition: Commit.h:38
const SourceManager & SM
Definition: Format.cpp:1337
FileOffset getWithOffset(unsigned offset) const
Definition: FileOffset.h:31
static CharSourceRange getCharRange(SourceRange R)
Encodes a location in the source.
bool rangeIntersectsConditionalDirective(SourceRange Range) const
Returns true if the given range intersects with a conditional directive.
bool isTokenRange() const
Return true if the end of this range specifies the start of the last token.
SourceRange getAsRange() const
Dataflow Directional Tag Classes.
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:870
CharSourceRange getInsertFromRange(SourceManager &SM) const
Definition: Commit.cpp:31
unsigned getOffset() const
Definition: FileOffset.h:29
bool replaceText(SourceLocation loc, StringRef text, StringRef replacementText)
Definition: Commit.cpp:160
bool isMacroID() const
bool isMacroArgExpansion(SourceLocation Loc, SourceLocation *StartLoc=nullptr) const
Tests whether the given source location represents a macro argument&#39;s expansion into the function-lik...
Commit(EditedSource &Editor)
Definition: Commit.cpp:38
SourceLocation getEnd() const
bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs)
SourceLocation getFileLocation(SourceManager &SM) const
Definition: Commit.cpp:19
bool replaceWithInner(CharSourceRange range, CharSourceRange innerRange)
Definition: Commit.cpp:127
#define true
Definition: stdbool.h:32
This class handles loading and caching of source files into memory.
std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.