clang  8.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"
11 #include "clang/Basic/LLVM.h"
15 #include "clang/Edit/FileOffset.h"
16 #include "clang/Lex/Lexer.h"
18 #include "llvm/ADT/StringRef.h"
19 #include <cassert>
20 #include <utility>
21 
22 using namespace clang;
23 using namespace edit;
24 
26  SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
27  Loc = Loc.getLocWithOffset(Offset.getOffset());
28  assert(Loc.isFileID());
29  return Loc;
30 }
31 
35 }
36 
40  assert(Loc.isFileID());
42 }
43 
45  : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),
46  PPRec(Editor.getPPCondDirectiveRecord()),
47  Editor(&Editor) {}
48 
49 bool Commit::insert(SourceLocation loc, StringRef text,
50  bool afterToken, bool beforePreviousInsertions) {
51  if (text.empty())
52  return true;
53 
54  FileOffset Offs;
55  if ((!afterToken && !canInsert(loc, Offs)) ||
56  ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
57  IsCommitable = false;
58  return false;
59  }
60 
61  addInsert(loc, Offs, text, beforePreviousInsertions);
62  return true;
63 }
64 
66  CharSourceRange range,
67  bool afterToken, bool beforePreviousInsertions) {
68  FileOffset RangeOffs;
69  unsigned RangeLen;
70  if (!canRemoveRange(range, RangeOffs, RangeLen)) {
71  IsCommitable = false;
72  return false;
73  }
74 
75  FileOffset Offs;
76  if ((!afterToken && !canInsert(loc, Offs)) ||
77  ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
78  IsCommitable = false;
79  return false;
80  }
81 
82  if (PPRec &&
84  IsCommitable = false;
85  return false;
86  }
87 
88  addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
89  return true;
90 }
91 
93  FileOffset Offs;
94  unsigned Len;
95  if (!canRemoveRange(range, Offs, Len)) {
96  IsCommitable = false;
97  return false;
98  }
99 
100  addRemove(range.getBegin(), Offs, Len);
101  return true;
102 }
103 
104 bool Commit::insertWrap(StringRef before, CharSourceRange range,
105  StringRef after) {
106  bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
107  /*beforePreviousInsertions=*/true);
108  bool commitableAfter;
109  if (range.isTokenRange())
110  commitableAfter = insertAfterToken(range.getEnd(), after);
111  else
112  commitableAfter = insert(range.getEnd(), after);
113 
114  return commitableBefore && commitableAfter;
115 }
116 
117 bool Commit::replace(CharSourceRange range, StringRef text) {
118  if (text.empty())
119  return remove(range);
120 
121  FileOffset Offs;
122  unsigned Len;
123  if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {
124  IsCommitable = false;
125  return false;
126  }
127 
128  addRemove(range.getBegin(), Offs, Len);
129  addInsert(range.getBegin(), Offs, text, false);
130  return true;
131 }
132 
134  CharSourceRange replacementRange) {
135  FileOffset OuterBegin;
136  unsigned OuterLen;
137  if (!canRemoveRange(range, OuterBegin, OuterLen)) {
138  IsCommitable = false;
139  return false;
140  }
141 
142  FileOffset InnerBegin;
143  unsigned InnerLen;
144  if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
145  IsCommitable = false;
146  return false;
147  }
148 
149  FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
150  FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);
151  if (OuterBegin.getFID() != InnerBegin.getFID() ||
152  InnerBegin < OuterBegin ||
153  InnerBegin > OuterEnd ||
154  InnerEnd > OuterEnd) {
155  IsCommitable = false;
156  return false;
157  }
158 
159  addRemove(range.getBegin(),
160  OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
161  addRemove(replacementRange.getEnd(),
162  InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
163  return true;
164 }
165 
167  StringRef replacementText) {
168  if (text.empty() || replacementText.empty())
169  return true;
170 
171  FileOffset Offs;
172  unsigned Len;
173  if (!canReplaceText(loc, replacementText, Offs, Len)) {
174  IsCommitable = false;
175  return false;
176  }
177 
178  addRemove(loc, Offs, Len);
179  addInsert(loc, Offs, text, false);
180  return true;
181 }
182 
183 void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,
184  bool beforePreviousInsertions) {
185  if (text.empty())
186  return;
187 
188  Edit data;
189  data.Kind = Act_Insert;
190  data.OrigLoc = OrigLoc;
191  data.Offset = Offs;
192  data.Text = text.copy(StrAlloc);
193  data.BeforePrev = beforePreviousInsertions;
194  CachedEdits.push_back(data);
195 }
196 
197 void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
198  FileOffset RangeOffs, unsigned RangeLen,
199  bool beforePreviousInsertions) {
200  if (RangeLen == 0)
201  return;
202 
203  Edit data;
204  data.Kind = Act_InsertFromRange;
205  data.OrigLoc = OrigLoc;
206  data.Offset = Offs;
207  data.InsertFromRangeOffs = RangeOffs;
208  data.Length = RangeLen;
209  data.BeforePrev = beforePreviousInsertions;
210  CachedEdits.push_back(data);
211 }
212 
213 void Commit::addRemove(SourceLocation OrigLoc,
214  FileOffset Offs, unsigned Len) {
215  if (Len == 0)
216  return;
217 
218  Edit data;
219  data.Kind = Act_Remove;
220  data.OrigLoc = OrigLoc;
221  data.Offset = Offs;
222  data.Length = Len;
223  CachedEdits.push_back(data);
224 }
225 
226 bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
227  if (loc.isInvalid())
228  return false;
229 
230  if (loc.isMacroID())
231  isAtStartOfMacroExpansion(loc, &loc);
232 
233  const SourceManager &SM = SourceMgr;
234  loc = SM.getTopMacroCallerLoc(loc);
235 
236  if (loc.isMacroID())
237  if (!isAtStartOfMacroExpansion(loc, &loc))
238  return false;
239 
240  if (SM.isInSystemHeader(loc))
241  return false;
242 
243  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
244  if (locInfo.first.isInvalid())
245  return false;
246  offs = FileOffset(locInfo.first, locInfo.second);
247  return canInsertInOffset(loc, offs);
248 }
249 
250 bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
251  SourceLocation &AfterLoc) {
252  if (loc.isInvalid())
253 
254  return false;
255 
256  SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
257  unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
258  AfterLoc = loc.getLocWithOffset(tokLen);
259 
260  if (loc.isMacroID())
261  isAtEndOfMacroExpansion(loc, &loc);
262 
263  const SourceManager &SM = SourceMgr;
264  loc = SM.getTopMacroCallerLoc(loc);
265 
266  if (loc.isMacroID())
267  if (!isAtEndOfMacroExpansion(loc, &loc))
268  return false;
269 
270  if (SM.isInSystemHeader(loc))
271  return false;
272 
273  loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
274  if (loc.isInvalid())
275  return false;
276 
277  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
278  if (locInfo.first.isInvalid())
279  return false;
280  offs = FileOffset(locInfo.first, locInfo.second);
281  return canInsertInOffset(loc, offs);
282 }
283 
284 bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
285  for (const auto &act : CachedEdits)
286  if (act.Kind == Act_Remove) {
287  if (act.Offset.getFID() == Offs.getFID() &&
288  Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))
289  return false; // position has been removed.
290  }
291 
292  if (!Editor)
293  return true;
294  return Editor->canInsertInOffset(OrigLoc, Offs);
295 }
296 
297 bool 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()))
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 
324 bool 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 
341 bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
342  SourceLocation *MacroBegin) const {
343  return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
344 }
345 
346 bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
347  SourceLocation *MacroEnd) const {
348  return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
349 }
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:92
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:104
Defines the SourceManager interface.
bool insertAfterToken(SourceLocation loc, StringRef text, bool beforePreviousInsertions=false)
Definition: Commit.h:74
bool insertFromRange(SourceLocation loc, CharSourceRange range, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:65
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:793
bool insert(SourceLocation loc, StringRef text, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:49
SourceLocation getBegin() const
bool replace(CharSourceRange range, StringRef text)
Definition: Commit.cpp:117
FileID getFID() const
Definition: FileOffset.h:29
uint32_t Offset
Definition: CacheTokens.cpp:43
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified...
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:41
CharSourceRange getFileRange(SourceManager &SM) const
Definition: Commit.cpp:32
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:815
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:771
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:43
const SourceManager & SM
Definition: Format.cpp:1472
FileOffset getWithOffset(unsigned offset) const
Definition: FileOffset.h:32
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:868
CharSourceRange getInsertFromRange(SourceManager &SM) const
Definition: Commit.cpp:37
unsigned getOffset() const
Definition: FileOffset.h:30
bool replaceText(SourceLocation loc, StringRef text, StringRef replacementText)
Definition: Commit.cpp:166
Commit(EditedSource &Editor)
Definition: Commit.cpp:44
SourceLocation getEnd() const
bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs)
Defines the clang::SourceLocation class and associated facilities.
SourceLocation getFileLocation(SourceManager &SM) const
Definition: Commit.cpp:25
bool replaceWithInner(CharSourceRange range, CharSourceRange innerRange)
Definition: Commit.cpp:133
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