clang  19.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());
27  assert(Loc.isFileID());
28  return Loc;
29 }
30 
32  SourceLocation Loc = getFileLocation(SM);
34 }
35 
37  SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());
38  Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());
39  assert(Loc.isFileID());
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()).starts_with(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 }
#define SM(sm)
Definition: Cuda.cpp:83
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
SourceLocation Loc
Definition: SemaObjC.cpp:755
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Represents a character-granular source range.
static CharSourceRange getCharRange(SourceRange R)
SourceLocation getEnd() const
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:872
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:894
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:955
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:499
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:850
bool areInDifferentConditionalDirectiveRegion(SourceLocation LHS, SourceLocation RHS) const
Returns true if the given locations are in different regions, separated by conditional directive bloc...
bool rangeIntersectsConditionalDirective(SourceRange Range) const
Returns true if the given range intersects with a conditional directive.
Encodes a location in the source.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
StringRef getBufferData(FileID FID, bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID.
bool replaceWithInner(CharSourceRange range, CharSourceRange innerRange)
Definition: Commit.cpp:132
bool insertWrap(StringRef before, CharSourceRange range, StringRef after)
Definition: Commit.cpp:103
bool insertFromRange(SourceLocation loc, CharSourceRange range, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:64
bool insert(SourceLocation loc, StringRef text, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:48
Commit(EditedSource &Editor)
Definition: Commit.cpp:43
bool insertAfterToken(SourceLocation loc, StringRef text, bool beforePreviousInsertions=false)
Definition: Commit.h:73
bool remove(CharSourceRange range)
Definition: Commit.cpp:91
bool replace(CharSourceRange range, StringRef text)
Definition: Commit.cpp:116
bool replaceText(SourceLocation loc, StringRef text, StringRef replacementText)
Definition: Commit.cpp:165
bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs)
FileOffset getWithOffset(unsigned offset) const
Definition: FileOffset.h:31
unsigned getOffset() const
Definition: FileOffset.h:29
FileID getFID() const
Definition: FileOffset.h:28
RangeSelector after(RangeSelector Selector)
Selects the point immediately following Selector.
RangeSelector before(RangeSelector Selector)
Selects the (empty) range [B,B) when Selector selects the range [B,E).
RangeSelector range(RangeSelector Begin, RangeSelector End)
DEPRECATED. Use enclose.
Definition: RangeSelector.h:41
EditGenerator edit(ASTEdit E)
Generates a single (specified) edit.
Definition: RewriteRule.cpp:84
The JSON file list parser is used to communicate input to InstallAPI.
SourceLocation getFileLocation(SourceManager &SM) const
Definition: Commit.cpp:24
CharSourceRange getFileRange(SourceManager &SM) const
Definition: Commit.cpp:31
CharSourceRange getInsertFromRange(SourceManager &SM) const
Definition: Commit.cpp:36