clang  7.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  loc = SM.getTopMacroCallerLoc(loc);
229 
230  if (loc.isMacroID())
231  if (!isAtStartOfMacroExpansion(loc, &loc))
232  return false;
233 
234  if (SM.isInSystemHeader(loc))
235  return false;
236 
237  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
238  if (locInfo.first.isInvalid())
239  return false;
240  offs = FileOffset(locInfo.first, locInfo.second);
241  return canInsertInOffset(loc, offs);
242 }
243 
244 bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
245  SourceLocation &AfterLoc) {
246  if (loc.isInvalid())
247 
248  return false;
249 
250  SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
251  unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
252  AfterLoc = loc.getLocWithOffset(tokLen);
253 
254  if (loc.isMacroID())
255  isAtEndOfMacroExpansion(loc, &loc);
256 
257  const SourceManager &SM = SourceMgr;
258  loc = SM.getTopMacroCallerLoc(loc);
259 
260  if (loc.isMacroID())
261  if (!isAtEndOfMacroExpansion(loc, &loc))
262  return false;
263 
264  if (SM.isInSystemHeader(loc))
265  return false;
266 
267  loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
268  if (loc.isInvalid())
269  return false;
270 
271  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
272  if (locInfo.first.isInvalid())
273  return false;
274  offs = FileOffset(locInfo.first, locInfo.second);
275  return canInsertInOffset(loc, offs);
276 }
277 
278 bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
279  for (unsigned i = 0, e = CachedEdits.size(); i != e; ++i) {
280  Edit &act = CachedEdits[i];
281  if (act.Kind == Act_Remove) {
282  if (act.Offset.getFID() == Offs.getFID() &&
283  Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))
284  return false; // position has been removed.
285  }
286  }
287 
288  if (!Editor)
289  return true;
290  return Editor->canInsertInOffset(OrigLoc, Offs);
291 }
292 
293 bool Commit::canRemoveRange(CharSourceRange range,
294  FileOffset &Offs, unsigned &Len) {
295  const SourceManager &SM = SourceMgr;
296  range = Lexer::makeFileCharRange(range, SM, LangOpts);
297  if (range.isInvalid())
298  return false;
299 
300  if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
301  return false;
302  if (SM.isInSystemHeader(range.getBegin()) ||
303  SM.isInSystemHeader(range.getEnd()))
304  return false;
305 
306  if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
307  return false;
308 
309  std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
310  std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
311  if (beginInfo.first != endInfo.first ||
312  beginInfo.second > endInfo.second)
313  return false;
314 
315  Offs = FileOffset(beginInfo.first, beginInfo.second);
316  Len = endInfo.second - beginInfo.second;
317  return true;
318 }
319 
320 bool Commit::canReplaceText(SourceLocation loc, StringRef text,
321  FileOffset &Offs, unsigned &Len) {
322  assert(!text.empty());
323 
324  if (!canInsert(loc, Offs))
325  return false;
326 
327  // Try to load the file buffer.
328  bool invalidTemp = false;
329  StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);
330  if (invalidTemp)
331  return false;
332 
333  Len = text.size();
334  return file.substr(Offs.getOffset()).startswith(text);
335 }
336 
337 bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
338  SourceLocation *MacroBegin) const {
339  return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
340 }
341 bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
342  SourceLocation *MacroEnd) const {
343  return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
344 }
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:796
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:818
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:774
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:436
FileOffset InsertFromRangeOffs
Definition: Commit.h:38
const SourceManager & SM
Definition: Format.cpp:1412
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:871
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
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.
SourceLocation getTopMacroCallerLoc(SourceLocation Loc) const