clang  14.0.0git
Commit.cpp
Go to the documentation of this file.
1 //===- Commit.cpp - A unit of edits ---------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "clang/Edit/Commit.h"
10 #include "clang/Basic/LLVM.h"
14 #include "clang/Edit/FileOffset.h"
15 #include "clang/Lex/Lexer.h"
17 #include "llvm/ADT/StringRef.h"
18 #include <cassert>
19 #include <utility>
20 
21 using namespace clang;
22 using namespace edit;
23 
25  SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
26  Loc = Loc.getLocWithOffset(Offset.getOffset());
27  assert(Loc.isFileID());
28  return Loc;
29 }
30 
32  SourceLocation Loc = getFileLocation(SM);
33  return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
34 }
35 
37  SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());
38  Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());
39  assert(Loc.isFileID());
40  return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
41 }
42 
44  : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),
45  PPRec(Editor.getPPCondDirectiveRecord()),
46  Editor(&Editor) {}
47 
48 bool Commit::insert(SourceLocation loc, StringRef text,
49  bool afterToken, bool beforePreviousInsertions) {
50  if (text.empty())
51  return true;
52 
53  FileOffset Offs;
54  if ((!afterToken && !canInsert(loc, Offs)) ||
55  ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
56  IsCommitable = false;
57  return false;
58  }
59 
60  addInsert(loc, Offs, text, beforePreviousInsertions);
61  return true;
62 }
63 
66  bool afterToken, bool beforePreviousInsertions) {
67  FileOffset RangeOffs;
68  unsigned RangeLen;
69  if (!canRemoveRange(range, RangeOffs, RangeLen)) {
70  IsCommitable = false;
71  return false;
72  }
73 
74  FileOffset Offs;
75  if ((!afterToken && !canInsert(loc, Offs)) ||
76  ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
77  IsCommitable = false;
78  return false;
79  }
80 
81  if (PPRec &&
82  PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) {
83  IsCommitable = false;
84  return false;
85  }
86 
87  addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
88  return true;
89 }
90 
92  FileOffset Offs;
93  unsigned Len;
94  if (!canRemoveRange(range, Offs, Len)) {
95  IsCommitable = false;
96  return false;
97  }
98 
99  addRemove(range.getBegin(), Offs, Len);
100  return true;
101 }
102 
104  StringRef after) {
105  bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
106  /*beforePreviousInsertions=*/true);
107  bool commitableAfter;
108  if (range.isTokenRange())
109  commitableAfter = insertAfterToken(range.getEnd(), after);
110  else
111  commitableAfter = insert(range.getEnd(), after);
112 
113  return commitableBefore && commitableAfter;
114 }
115 
117  if (text.empty())
118  return remove(range);
119 
120  FileOffset Offs;
121  unsigned Len;
122  if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {
123  IsCommitable = false;
124  return false;
125  }
126 
127  addRemove(range.getBegin(), Offs, Len);
128  addInsert(range.getBegin(), Offs, text, false);
129  return true;
130 }
131 
133  CharSourceRange replacementRange) {
134  FileOffset OuterBegin;
135  unsigned OuterLen;
136  if (!canRemoveRange(range, OuterBegin, OuterLen)) {
137  IsCommitable = false;
138  return false;
139  }
140 
141  FileOffset InnerBegin;
142  unsigned InnerLen;
143  if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
144  IsCommitable = false;
145  return false;
146  }
147 
148  FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
149  FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);
150  if (OuterBegin.getFID() != InnerBegin.getFID() ||
151  InnerBegin < OuterBegin ||
152  InnerBegin > OuterEnd ||
153  InnerEnd > OuterEnd) {
154  IsCommitable = false;
155  return false;
156  }
157 
158  addRemove(range.getBegin(),
159  OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
160  addRemove(replacementRange.getEnd(),
161  InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
162  return true;
163 }
164 
166  StringRef replacementText) {
167  if (text.empty() || replacementText.empty())
168  return true;
169 
170  FileOffset Offs;
171  unsigned Len;
172  if (!canReplaceText(loc, replacementText, Offs, Len)) {
173  IsCommitable = false;
174  return false;
175  }
176 
177  addRemove(loc, Offs, Len);
178  addInsert(loc, Offs, text, false);
179  return true;
180 }
181 
182 void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,
183  bool beforePreviousInsertions) {
184  if (text.empty())
185  return;
186 
187  Edit data;
188  data.Kind = Act_Insert;
189  data.OrigLoc = OrigLoc;
190  data.Offset = Offs;
191  data.Text = text.copy(StrAlloc);
192  data.BeforePrev = beforePreviousInsertions;
193  CachedEdits.push_back(data);
194 }
195 
196 void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
197  FileOffset RangeOffs, unsigned RangeLen,
198  bool beforePreviousInsertions) {
199  if (RangeLen == 0)
200  return;
201 
202  Edit data;
203  data.Kind = Act_InsertFromRange;
204  data.OrigLoc = OrigLoc;
205  data.Offset = Offs;
206  data.InsertFromRangeOffs = RangeOffs;
207  data.Length = RangeLen;
208  data.BeforePrev = beforePreviousInsertions;
209  CachedEdits.push_back(data);
210 }
211 
212 void Commit::addRemove(SourceLocation OrigLoc,
213  FileOffset Offs, unsigned Len) {
214  if (Len == 0)
215  return;
216 
217  Edit data;
218  data.Kind = Act_Remove;
219  data.OrigLoc = OrigLoc;
220  data.Offset = Offs;
221  data.Length = Len;
222  CachedEdits.push_back(data);
223 }
224 
225 bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
226  if (loc.isInvalid())
227  return false;
228 
229  if (loc.isMacroID())
230  isAtStartOfMacroExpansion(loc, &loc);
231 
232  const SourceManager &SM = SourceMgr;
233  loc = SM.getTopMacroCallerLoc(loc);
234 
235  if (loc.isMacroID())
236  if (!isAtStartOfMacroExpansion(loc, &loc))
237  return false;
238 
239  if (SM.isInSystemHeader(loc))
240  return false;
241 
242  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
243  if (locInfo.first.isInvalid())
244  return false;
245  offs = FileOffset(locInfo.first, locInfo.second);
246  return canInsertInOffset(loc, offs);
247 }
248 
249 bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
250  SourceLocation &AfterLoc) {
251  if (loc.isInvalid())
252 
253  return false;
254 
255  SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
256  unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
257  AfterLoc = loc.getLocWithOffset(tokLen);
258 
259  if (loc.isMacroID())
260  isAtEndOfMacroExpansion(loc, &loc);
261 
262  const SourceManager &SM = SourceMgr;
263  loc = SM.getTopMacroCallerLoc(loc);
264 
265  if (loc.isMacroID())
266  if (!isAtEndOfMacroExpansion(loc, &loc))
267  return false;
268 
269  if (SM.isInSystemHeader(loc))
270  return false;
271 
272  loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
273  if (loc.isInvalid())
274  return false;
275 
276  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
277  if (locInfo.first.isInvalid())
278  return false;
279  offs = FileOffset(locInfo.first, locInfo.second);
280  return canInsertInOffset(loc, offs);
281 }
282 
283 bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
284  for (const auto &act : CachedEdits)
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  if (!Editor)
292  return true;
293  return Editor->canInsertInOffset(OrigLoc, Offs);
294 }
295 
296 bool Commit::canRemoveRange(CharSourceRange range,
297  FileOffset &Offs, unsigned &Len) {
298  const SourceManager &SM = SourceMgr;
299  range = Lexer::makeFileCharRange(range, SM, LangOpts);
300  if (range.isInvalid())
301  return false;
302 
303  if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
304  return false;
305  if (SM.isInSystemHeader(range.getBegin()) ||
306  SM.isInSystemHeader(range.getEnd()))
307  return false;
308 
309  if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
310  return false;
311 
312  std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
313  std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
314  if (beginInfo.first != endInfo.first ||
315  beginInfo.second > endInfo.second)
316  return false;
317 
318  Offs = FileOffset(beginInfo.first, beginInfo.second);
319  Len = endInfo.second - beginInfo.second;
320  return true;
321 }
322 
323 bool Commit::canReplaceText(SourceLocation loc, StringRef text,
324  FileOffset &Offs, unsigned &Len) {
325  assert(!text.empty());
326 
327  if (!canInsert(loc, Offs))
328  return false;
329 
330  // Try to load the file buffer.
331  bool invalidTemp = false;
332  StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);
333  if (invalidTemp)
334  return false;
335 
336  Len = text.size();
337  return file.substr(Offs.getOffset()).startswith(text);
338 }
339 
340 bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
341  SourceLocation *MacroBegin) const {
342  return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
343 }
344 
345 bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
346  SourceLocation *MacroEnd) const {
347  return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
348 }
clang::Lexer::isAtEndOfMacroExpansion
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:827
clang::Lexer::makeFileCharRange
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:888
clang::comments::tok::text
@ text
Definition: CommentLexer.h:35
clang::edit::EditedSource::canInsertInOffset
bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs)
Definition: EditedSource.cpp:74
clang::PPConditionalDirectiveRecord::rangeIntersectsConditionalDirective
bool rangeIntersectsConditionalDirective(SourceRange Range) const
Returns true if the given range intersects with a conditional directive.
Definition: PPConditionalDirectiveRecord.cpp:23
clang::edit::FileOffset::getOffset
unsigned getOffset() const
Definition: FileOffset.h:29
clang::edit::Commit::insertWrap
bool insertWrap(StringRef before, CharSourceRange range, StringRef after)
Definition: Commit.cpp:103
clang::edit::Commit::insertFromRange
bool insertFromRange(SourceLocation loc, CharSourceRange range, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:64
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:88
clang::SourceLocation::getLocWithOffset
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
Definition: SourceLocation.h:136
clang::edit::Commit::insert
bool insert(SourceLocation loc, StringRef text, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:48
clang::edit::Commit::replace
bool replace(CharSourceRange range, StringRef text)
Definition: Commit.cpp:116
FileOffset.h
SourceManager.h
clang::Lexer::MeasureTokenLength
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:447
clang::edit::Commit::insertAfterToken
bool insertAfterToken(SourceLocation loc, StringRef text, bool beforePreviousInsertions=false)
Definition: Commit.h:73
clang::edit::Commit::Act_InsertFromRange
@ Act_InsertFromRange
Definition: Commit.h:33
clang::transformer::edit
EditGenerator edit(ASTEdit E)
Generates a single (specified) edit.
Definition: RewriteRule.cpp:75
clang::SourceManager
This class handles loading and caching of source files into memory.
Definition: SourceManager.h:626
clang::edit::Commit::Edit::getInsertFromRange
CharSourceRange getInsertFromRange(SourceManager &SM) const
Definition: Commit.cpp:36
clang::edit::EditedSource
Definition: EditedSource.h:35
clang::CharSourceRange::getCharRange
static CharSourceRange getCharRange(SourceRange R)
Definition: SourceLocation.h:267
clang::edit::Commit::Edit::getFileLocation
SourceLocation getFileLocation(SourceManager &SM) const
Definition: Commit.cpp:24
clang::SourceLocation::isFileID
bool isFileID() const
Definition: SourceLocation.h:104
clang::edit::Commit::Edit::Offset
FileOffset Offset
Definition: Commit.h:41
clang::PPConditionalDirectiveRecord::areInDifferentConditionalDirectiveRegion
bool areInDifferentConditionalDirectiveRegion(SourceLocation LHS, SourceLocation RHS) const
Returns true if the given locations are in different regions, separated by conditional directive bloc...
Definition: PPConditionalDirectiveRecord.h:79
SourceLocation.h
PPConditionalDirectiveRecord.h
Lexer.h
clang::CharSourceRange
Represents a character-granular source range.
Definition: SourceLocation.h:255
LLVM.h
Commit.h
clang::edit::FileOffset::getWithOffset
FileOffset getWithOffset(unsigned offset) const
Definition: FileOffset.h:31
clang::SourceManager::getBufferData
StringRef getBufferData(FileID FID, bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
Definition: SourceManager.cpp:735
clang::CharSourceRange::getEnd
SourceLocation getEnd() const
Definition: SourceLocation.h:286
clang::transformer::after
RangeSelector after(RangeSelector Selector)
Selects the point immediately following Selector.
Definition: RangeSelector.cpp:115
clang::SourceLocation::isMacroID
bool isMacroID() const
Definition: SourceLocation.h:105
clang::SourceLocation::isInvalid
bool isInvalid() const
Definition: SourceLocation.h:113
clang
Definition: CalledOnceCheck.h:17
clang::Lexer::isAtStartOfMacroExpansion
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:805
clang::edit::Commit::Act_Insert
@ Act_Insert
Definition: Commit.h:32
clang::transformer::range
RangeSelector range(RangeSelector Begin, RangeSelector End)
DEPRECATED. Use enclose.
Definition: RangeSelector.h:41
clang::edit::FileOffset::getFID
FileID getFID() const
Definition: FileOffset.h:28
clang::edit::Commit::Commit
Commit(EditedSource &Editor)
Definition: Commit.cpp:43
clang::edit::Commit::Act_Remove
@ Act_Remove
Definition: Commit.h:34
clang::Lexer::getLocForEndOfToken
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:783
clang::edit::Commit::Edit::getFileRange
CharSourceRange getFileRange(SourceManager &SM) const
Definition: Commit.cpp:31
clang::edit::FileOffset
Definition: FileOffset.h:18
SM
#define SM(sm)
Definition: Cuda.cpp:78
EditedSource.h
clang::transformer::before
RangeSelector before(RangeSelector Selector)
Selects the (empty) range [B,B) when Selector selects the range [B,E).
Definition: RangeSelector.cpp:106
clang::edit::Commit::remove
bool remove(CharSourceRange range)
Definition: Commit.cpp:91
clang::edit::Commit::replaceWithInner
bool replaceWithInner(CharSourceRange range, CharSourceRange innerRange)
Definition: Commit.cpp:132
clang::edit::Commit::replaceText
bool replaceText(SourceLocation loc, StringRef text, StringRef replacementText)
Definition: Commit.cpp:165
clang::SourceManager::getSpellingLoc
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID.
Definition: SourceManager.h:1206