clang: lib/Edit/EditedSource.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/ADT/SmallString.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/ADT/Twine.h"
22#include
23#include
24#include
25#include
26
27using namespace clang;
28using namespace edit;
29
31 replace(range, StringRef());
32}
33
36 MacroArgUse &ArgUse) {
42 ExpansionLoc = ImmediateExpansionLoc;
44 ExpansionLoc =
48 Buf, SourceMgr, LangOpts);
50 if (!ArgName.empty())
51 ArgUse = {&IdentTable.get(ArgName), ImmediateExpansionLoc,
53}
54
55void EditedSource::startingCommit() {}
56
57void EditedSource::finishedCommit() {
58 for (auto &ExpArg : CurrCommitMacroArgExps) {
60 MacroArgUse ArgUse;
61 std::tie(ExpLoc, ArgUse) = ExpArg;
62 auto &ArgUses = ExpansionToArgMap[ExpLoc];
63 if (!llvm::is_contained(ArgUses, ArgUse))
64 ArgUses.push_back(ArgUse);
65 }
66 CurrCommitMacroArgExps.clear();
67}
68
72}
73
75 FileEditsTy::iterator FA = getActionForOffset(Offs);
76 if (FA != FileEdits.end()) {
77 if (FA->first != Offs)
78 return false;
79 }
80
83 MacroArgUse ArgUse;
84 deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
85 auto I = ExpansionToArgMap.find(ExpLoc);
86 if (I != ExpansionToArgMap.end() &&
87 llvm::any_of(I->second, [&](const MacroArgUse &U) {
88 return ArgUse.Identifier == U.Identifier &&
89 std::tie(ArgUse.ImmediateExpansionLoc, ArgUse.UseLoc) !=
90 std::tie(U.ImmediateExpansionLoc, U.UseLoc);
91 })) {
92
93
94
95
96
97
98
99
100
101
102
103
104
105 return false;
106 }
107 }
108 return true;
109}
110
111bool EditedSource::commitInsert(SourceLocation OrigLoc,
113 bool beforePreviousInsertions) {
115 return false;
116 if (text.empty())
117 return true;
118
120 MacroArgUse ArgUse;
122 deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
123 if (ArgUse.Identifier)
124 CurrCommitMacroArgExps.emplace_back(ExpLoc, ArgUse);
125 }
126
127 FileEdit &FA = FileEdits[Offs];
128 if (FA.Text.empty()) {
130 return true;
131 }
132
133 if (beforePreviousInsertions)
134 FA.Text = copyString(Twine(text) + FA.Text);
135 else
136 FA.Text = copyString(Twine(FA.Text) + text);
137
138 return true;
139}
140
141bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
143 FileOffset InsertFromRangeOffs, unsigned Len,
144 bool beforePreviousInsertions) {
145 if (Len == 0)
146 return true;
147
149 FileOffset BeginOffs = InsertFromRangeOffs;
151 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
152 if (I != FileEdits.begin())
153 --I;
154
155 for (; I != FileEdits.end(); ++I) {
156 FileEdit &FA = I->second;
159
160 if (BeginOffs == B)
161 break;
162
163 if (BeginOffs < E) {
164 if (BeginOffs > B) {
165 BeginOffs = E;
166 ++I;
167 }
168 break;
169 }
170 }
171
172 for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
173 FileEdit &FA = I->second;
176
177 if (BeginOffs < B) {
179 StringRef text = getSourceText(BeginOffs, B, Invalid);
181 return false;
182 StrVec += text;
183 }
184 StrVec += FA.Text;
185 BeginOffs = E;
186 }
187
188 if (BeginOffs < EndOffs) {
190 StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
192 return false;
193 StrVec += text;
194 }
195
196 return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
197}
198
199void EditedSource::commitRemove(SourceLocation OrigLoc,
200 FileOffset BeginOffs, unsigned Len) {
201 if (Len == 0)
202 return;
203
205 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
206 if (I != FileEdits.begin())
207 --I;
208
209 for (; I != FileEdits.end(); ++I) {
210 FileEdit &FA = I->second;
213
214 if (BeginOffs < E)
215 break;
216 }
217
219 FileEdit *TopFA = nullptr;
220
221 if (I == FileEdits.end()) {
222 FileEditsTy::iterator
223 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
224 NewI->second.RemoveLen = Len;
225 return;
226 }
227
228 FileEdit &FA = I->second;
231 if (BeginOffs < B) {
232 FileEditsTy::iterator
233 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
234 TopBegin = BeginOffs;
235 TopEnd = EndOffs;
236 TopFA = &NewI->second;
237 TopFA->RemoveLen = Len;
238 } else {
239 TopBegin = B;
240 TopEnd = E;
241 TopFA = &I->second;
242 if (TopEnd >= EndOffs)
243 return;
245 TopEnd = EndOffs;
246 TopFA->RemoveLen += diff;
247 if (B == BeginOffs)
248 TopFA->Text = StringRef();
249 ++I;
250 }
251
252 while (I != FileEdits.end()) {
253 FileEdit &FA = I->second;
256
257 if (B >= TopEnd)
258 break;
259
260 if (E <= TopEnd) {
261 FileEdits.erase(I++);
262 continue;
263 }
264
265 if (B < TopEnd) {
266 unsigned diff = E.getOffset() - TopEnd.getOffset();
267 TopEnd = E;
268 TopFA->RemoveLen += diff;
269 FileEdits.erase(I);
270 }
271
272 break;
273 }
274}
275
277 if (.isCommitable())
278 return false;
279
280 struct CommitRAII {
282
283 CommitRAII(EditedSource &Editor) : Editor(Editor) {
284 Editor.startingCommit();
285 }
286
287 ~CommitRAII() {
288 Editor.finishedCommit();
289 }
290 } CommitRAII(*this);
291
293 I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
295 switch (edit.Kind) {
297 commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
298 break;
300 commitInsertFromRange(edit.OrigLoc, edit.Offset,
301 edit.InsertFromRangeOffs, edit.Length,
302 edit.BeforePrev);
303 break;
305 commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
306 break;
307 }
308 }
309
310 return true;
311}
312
313
315
316
319}
320
321
322
326 return false;
328 return true;
329 if (canBeJoined(beforeWSpace, right, LangOpts))
330 return false;
331 return true;
332}
333
334
335
336
339 unsigned &len, StringRef &text) {
340 assert(len && text.empty());
342 if (BeginTokLoc != Loc)
343 return;
344
346 StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);
348 return;
349
350 unsigned begin = offs.getOffset();
351 unsigned end = begin + len;
352
353
354 if (end == buffer.size())
355 return;
356
357 assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");
358
359
360
361 if (begin == 0) {
362 if (buffer[end] == ' ')
363 ++len;
364 return;
365 }
366
367 if (buffer[end] == ' ') {
368 assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
369 "buffer not zero-terminated!");
371 buffer[end-1],
372 buffer.data()[end + 1],
373 LangOpts))
374 ++len;
375 return;
376 }
377
378 if ((buffer[begin-1], buffer[end], LangOpts))
379 text = " ";
380}
381
383 StringRef text, FileOffset offs, unsigned len,
385 bool shouldAdjustRemovals) {
390
391 if (text.empty() && shouldAdjustRemovals)
393
396
397 if (text.empty()) {
398 assert(len);
399 receiver.remove(range);
400 return;
401 }
402
403 if (len)
404 receiver.replace(range, text);
405 else
407}
408
410 bool shouldAdjustRemovals) {
413 unsigned CurLen;
414
415 if (FileEdits.empty())
416 return;
417
418 FileEditsTy::iterator I = FileEdits.begin();
419 CurOffs = I->first;
420 StrVec = I->second.Text;
421 CurLen = I->second.RemoveLen;
423 ++I;
424
425 for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
427 FileEdit act = I->second;
428 assert(offs >= CurEnd);
429
430 if (offs == CurEnd) {
431 StrVec += act.Text;
432 CurLen += act.RemoveLen;
434 continue;
435 }
436
437 applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
438 shouldAdjustRemovals);
439 CurOffs = offs;
440 StrVec = act.Text;
441 CurLen = act.RemoveLen;
443 }
444
445 applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
446 shouldAdjustRemovals);
447}
448
450 FileEdits.clear();
451 StrAlloc.Reset();
452}
453
454StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
456 assert(BeginOffs.getFID() == EndOffs.getFID());
457 assert(BeginOffs <= EndOffs);
464 SourceMgr, LangOpts, &Invalid);
465}
466
467EditedSource::FileEditsTy::iterator
468EditedSource::getActionForOffset(FileOffset Offs) {
469 FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
470 if (I == FileEdits.begin())
471 return FileEdits.end();
472 --I;
473 FileEdit &FA = I->second;
476 if (Offs >= B && Offs < E)
477 return I;
478
479 return FileEdits.end();
480}
static bool canBeJoined(char left, char right, const LangOptions &LangOpts)
static void applyRewrite(EditsReceiver &receiver, StringRef text, FileOffset offs, unsigned len, const SourceManager &SM, const LangOptions &LangOpts, bool shouldAdjustRemovals)
static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation Loc, FileOffset offs, unsigned &len, StringRef &text)
Check the range that we are going to remove and: -Remove any trailing whitespace if possible.
static bool canRemoveWhitespace(char left, char beforeWSpace, char right, const LangOptions &LangOpts)
Returns true if it is ok to eliminate the trailing whitespace between the given characters.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Represents a character-granular source range.
static CharSourceRange getCharRange(SourceRange R)
SourceLocation getBegin() const
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)
Returns a string for the source that the range encompasses.
static unsigned getSpelling(const Token &Tok, const char *&Buffer, const SourceManager &SourceMgr, const LangOptions &LangOpts, bool *Invalid=nullptr)
getSpelling - This method is used to get the spelling of a token into a preallocated buffer,...
static bool isAsciiIdentifierContinueChar(char c, const LangOptions &LangOpts)
Returns true if the given character could appear in an identifier.
static SourceLocation GetBeginningOfToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Given a location any where in a source buffer, find the location that corresponds to the beginning of...
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.
bool isMacroBodyExpansion(SourceLocation Loc) const
Tests whether the given source location represents the expansion of a macro body.
bool isMacroArgExpansion(SourceLocation Loc, SourceLocation *StartLoc=nullptr) const
Tests whether the given source location represents a macro argument's expansion into the function-lik...
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID.
CharSourceRange getImmediateExpansionRange(SourceLocation Loc) const
Return the start/end of the expansion information for an expansion location.
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file.
SmallVectorImpl< Edit >::const_iterator edit_iterator
StringRef copyString(StringRef str)
bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs)
void applyRewrites(EditsReceiver &receiver, bool adjustRemovals=true)
bool commit(const Commit &commit)
virtual void insert(SourceLocation loc, StringRef text)=0
virtual void remove(CharSourceRange range)
By default it calls replace with an empty string.
virtual void replace(CharSourceRange range, StringRef text)=0
FileOffset getWithOffset(unsigned offset) const
unsigned getOffset() const
EditGenerator edit(ASTEdit E)
Generates a single (specified) edit.
The JSON file list parser is used to communicate input to InstallAPI.
LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...