Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Edit/Commit.cpp
35262 views
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"
11
#include "clang/Basic/SourceLocation.h"
12
#include "clang/Basic/SourceManager.h"
13
#include "clang/Edit/EditedSource.h"
14
#include "clang/Edit/FileOffset.h"
15
#include "clang/Lex/Lexer.h"
16
#include "clang/Lex/PPConditionalDirectiveRecord.h"
17
#include "llvm/ADT/StringRef.h"
18
#include <cassert>
19
#include <utility>
20
21
using namespace clang;
22
using namespace edit;
23
24
SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {
25
SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
26
Loc = Loc.getLocWithOffset(Offset.getOffset());
27
assert(Loc.isFileID());
28
return Loc;
29
}
30
31
CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {
32
SourceLocation Loc = getFileLocation(SM);
33
return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
34
}
35
36
CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const {
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
43
Commit::Commit(EditedSource &Editor)
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
64
bool Commit::insertFromRange(SourceLocation loc,
65
CharSourceRange range,
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
91
bool Commit::remove(CharSourceRange range) {
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
103
bool Commit::insertWrap(StringRef before, CharSourceRange range,
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
116
bool Commit::replace(CharSourceRange range, StringRef text) {
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
132
bool Commit::replaceWithInner(CharSourceRange range,
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
165
bool Commit::replaceText(SourceLocation loc, StringRef text,
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
}
349
350