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