clang: lib/Tooling/Core/Replacement.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
23#include "llvm/ADT/IntrusiveRefCntPtr.h"
24#include "llvm/ADT/RewriteBuffer.h"
25#include "llvm/ADT/SmallPtrSet.h"
26#include "llvm/ADT/StringRef.h"
27#include "llvm/Support/Error.h"
28#include "llvm/Support/ErrorHandling.h"
29#include "llvm/Support/MemoryBuffer.h"
30#include "llvm/Support/VirtualFileSystem.h"
31#include "llvm/Support/raw_ostream.h"
32#include
33#include
34#include
35#include
36#include
37#include
38#include
39
40using namespace clang;
42
44
46
48 StringRef ReplacementText)
49 : FilePath(std::string(FilePath)), ReplacementRange(Offset, Length),
50 ReplacementText(std::string(ReplacementText)) {}
51
53 unsigned Length, StringRef ReplacementText) {
54 setFromSourceLocation(Sources, Start, Length, ReplacementText);
55}
56
59 StringRef ReplacementText,
61 setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
62}
63
67
70 auto Entry = SM.getFileManager().getOptionalFileRef(FilePath);
71 if (!Entry)
72 return false;
73
76 SM.getLocForStartOfFile(ID).
77 getLocWithOffset(ReplacementRange.getOffset());
78
79
80
81 bool RewriteSucceeded = .ReplaceText(
82 Start, ReplacementRange.getLength(), ReplacementText);
83 assert(RewriteSucceeded);
84 return RewriteSucceeded;
85}
86
89 llvm::raw_string_ostream Stream(Result);
90 Stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"
91 << ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";
92 return Stream.str();
93}
94
97
109
116
117}
118}
119
120void Replacement::setFromSourceLocation(const SourceManager &Sources,
121 SourceLocation Start, unsigned Length,
122 StringRef ReplacementText) {
127 this->ReplacementRange = Range(DecomposedLocation.second, Length);
128 this->ReplacementText = std::string(ReplacementText);
129}
130
131
132
133
141 if (Start.first != End.first) return -1;
142 if (Range.isTokenRange())
144 return End.second - Start.second;
145}
146
147void Replacement::setFromSourceRange(const SourceManager &Sources,
148 const CharSourceRange &Range,
149 StringRef ReplacementText,
150 const LangOptions &LangOpts) {
153 ReplacementText);
154}
155
156Replacement
157Replacements::getReplacementInChangedCode(const Replacement &R) const {
158 unsigned NewStart = getShiftedCodePosition(R.getOffset());
159 unsigned NewEnd = getShiftedCodePosition(R.getOffset() + R.getLength());
160 return Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
162}
163
165 switch (Err) {
166 case replacement_error::fail_to_apply:
167 return "Failed to apply a replacement.";
168 case replacement_error::wrong_file_path:
169 return "The new replacement's file path is different from the file path of "
170 "existing replacements";
171 case replacement_error::overlap_conflict:
172 return "The new replacement overlaps with an existing replacement.";
173 case replacement_error::insert_conflict:
174 return "The new insertion has the same insert location as an existing "
175 "replacement.";
176 }
177 llvm_unreachable("A value of replacement_error has no message.");
178}
179
182 if (NewReplacement)
183 Message += "\nNew replacement: " + NewReplacement->toString();
184 if (ExistingReplacement)
185 Message += "\nExisting replacement: " + ExistingReplacement->toString();
186 return Message;
187}
188
190
191Replacements Replacements::getCanonicalReplacements() const {
192 std::vector NewReplaces;
193
194 for (const auto &R : Replaces) {
195 if (NewReplaces.empty()) {
196 NewReplaces.push_back(R);
197 continue;
198 }
199 auto &Prev = NewReplaces.back();
200 unsigned PrevEnd = Prev.getOffset() + Prev.getLength();
201 if (PrevEnd < R.getOffset()) {
202 NewReplaces.push_back(R);
203 } else {
204 assert(PrevEnd == R.getOffset() &&
205 "Existing replacements must not overlap.");
207 R.getFilePath(), Prev.getOffset(), Prev.getLength() + R.getLength(),
208 (Prev.getReplacementText() + R.getReplacementText()).str());
209 Prev = NewR;
210 }
211 }
212 ReplacementsImpl NewReplacesImpl(NewReplaces.begin(), NewReplaces.end());
213 return Replacements(NewReplacesImpl.begin(), NewReplacesImpl.end());
214}
215
216
217
218
219llvm::Expected
220Replacements::mergeIfOrderIndependent(const Replacement &R) const {
221 Replacements Rs(R);
222
223
224 Replacements RsShiftedByReplaces(getReplacementInChangedCode(R));
225
226
227 Replacements ReplacesShiftedByRs;
228 for (const auto &Replace : Replaces)
229 ReplacesShiftedByRs.Replaces.insert(
230 Rs.getReplacementInChangedCode(Replace));
231
232 auto MergeShiftedRs = merge(RsShiftedByReplaces);
233
234 auto MergeShiftedReplaces = Rs.merge(ReplacesShiftedByRs);
235
236
237
238 if (MergeShiftedRs.getCanonicalReplacements() ==
239 MergeShiftedReplaces.getCanonicalReplacements())
240 return MergeShiftedRs;
241 return llvm::make_error(replacement_error::overlap_conflict,
242 R, *Replaces.begin());
243}
244
246
247 if (!Replaces.empty() && R.getFilePath() != Replaces.begin()->getFilePath())
248 return llvm::make_error(
250
251
252 if (R.getOffset() == std::numeric_limits::max()) {
253 Replaces.insert(R);
254 return llvm::Error::success();
255 }
256
257
258
259
260
261
263
264
265
266
267 auto I = Replaces.lower_bound(AtEnd);
268
269 if (I != Replaces.end() && R.getOffset() == I->getOffset()) {
271
272 if (I->getLength() == 0) {
273
274
277 return llvm::make_error(
279
283 Replaces.erase(I);
284 Replaces.insert(std::move(NewR));
285 return llvm::Error::success();
286 }
287
288
289
290
291
292
293
294 Replaces.insert(R);
295 return llvm::Error::success();
296 }
297
298
299
300 if (I == Replaces.begin()) {
301 Replaces.insert(R);
302 return llvm::Error::success();
303 }
304 --I;
308 };
309
310
311 if (!Overlap(R, *I)) {
312
313
314
315
316
317
318
319 Replaces.insert(R);
320 } else {
321
322
323
324 auto MergeBegin = I;
325 auto MergeEnd = std::next(I);
326 while (I != Replaces.begin()) {
327 --I;
328
329 if (!Overlap(R, *I))
330 break;
331 MergeBegin = I;
332 }
333 Replacements OverlapReplaces(MergeBegin, MergeEnd);
335 OverlapReplaces.mergeIfOrderIndependent(R);
336 if (!Merged)
337 return Merged.takeError();
338 Replaces.erase(MergeBegin, MergeEnd);
339 Replaces.insert(Merged->begin(), Merged->end());
340 }
341 return llvm::Error::success();
342}
343
344namespace {
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366class MergedReplacement {
367public:
368 MergedReplacement(const Replacement &R, bool MergeSecond, int D)
369 : MergeSecond(MergeSecond), Delta(D), FilePath(R.getFilePath()),
370 Offset(R.getOffset() + (MergeSecond ? 0 : Delta)),
371 Length(R.getLength()), Text(std::string(R.getReplacementText())) {
372 Delta += MergeSecond ? 0 : Text.size() - Length;
373 DeltaFirst = MergeSecond ? Text.size() - Length : 0;
374 }
375
376
377
378
380 if (MergeSecond) {
382 unsigned End = Offset + Text.size();
383 if (REnd > End) {
384 Length += REnd - End;
385 MergeSecond = false;
386 }
387 StringRef TextRef = Text;
388 StringRef Head = TextRef.substr(0, R.getOffset() + Delta - Offset);
389 StringRef Tail = TextRef.substr(REnd - Offset);
392 } else {
393 unsigned End = Offset + Length;
395 StringRef Tail = RText.substr(End - R.getOffset());
397 if (R.getOffset() + RText.size() > End) {
399 MergeSecond = true;
400 } else {
401 Length += R.getLength() - RText.size();
402 }
403 DeltaFirst += RText.size() - R.getLength();
404 }
405 }
406
407
408
409 bool endsBefore(const Replacement &R) const {
410 if (MergeSecond)
411 return Offset + Text.size() < R.getOffset() + Delta;
412 return Offset + Length < R.getOffset();
413 }
414
415
416 bool mergeSecond() const { return MergeSecond; }
417
418 int deltaFirst() const { return DeltaFirst; }
419 Replacement asReplacement() const { return {FilePath, Offset, Length, Text}; }
420
421private:
422 bool MergeSecond;
423
424
425
426 int Delta;
427
428
429
430
431 int DeltaFirst;
432
433
434
435 const StringRef FilePath;
436 const unsigned Offset;
437 unsigned Length;
438 std::string Text;
439};
440
441}
442
444 if (empty() || ReplacesToMerge.empty())
445 return empty() ? ReplacesToMerge : *this;
446
447 auto &First = Replaces;
448 auto &Second = ReplacesToMerge.Replaces;
449
450
451 int Delta = 0;
452 ReplacementsImpl Result;
453
454
455
456
457
458 for (auto FirstI = First.begin(), SecondI = Second.begin();
459 FirstI != First.end() || SecondI != Second.end();) {
460 bool NextIsFirst = SecondI == Second.end() ||
461 (FirstI != First.end() &&
462 FirstI->getOffset() < SecondI->getOffset() + Delta);
463 MergedReplacement Merged(NextIsFirst ? *FirstI : *SecondI, NextIsFirst,
464 Delta);
465 ++(NextIsFirst ? FirstI : SecondI);
466
467 while ((Merged.mergeSecond() && SecondI != Second.end()) ||
468 (!Merged.mergeSecond() && FirstI != First.end())) {
469 auto &I = Merged.mergeSecond() ? SecondI : FirstI;
470 if (Merged.endsBefore(*I))
471 break;
472 Merged.merge(*I);
473 ++I;
474 }
475 Delta -= Merged.deltaFirst();
476 Result.insert(Merged.asReplacement());
477 }
479}
480
481
482
483
485 llvm::sort(Ranges, [](const Range &LHS, const Range &RHS) {
489 });
490 std::vector Result;
491 for (const auto &R : Ranges) {
492 if (Result.empty() ||
493 Result.back().getOffset() + Result.back().getLength() < R.getOffset()) {
494 Result.push_back(R);
495 } else {
496 unsigned NewEnd =
497 std::max(Result.back().getOffset() + Result.back().getLength(),
499 Result[Result.size() - 1] =
500 Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());
501 }
502 }
503 return Result;
504}
505
506namespace clang {
507namespace tooling {
508
509std::vector
511 const std::vector &Ranges) {
512
513
514
515
516
518 if (Replaces.empty())
519 return MergedRanges;
521 for (const auto &R : MergedRanges) {
522 llvm::cantFail(
525 std::string(R.getLength(), ' '))),
526 "Replacements must not conflict since ranges have been merged.");
527 }
529}
530
531}
532}
533
535 std::vector ChangedRanges;
536 int Shift = 0;
537 for (const auto &R : Replaces) {
538 unsigned Offset = R.getOffset() + Shift;
541 ChangedRanges.push_back(Range(Offset, Length));
542 }
544}
545
547 unsigned Offset = 0;
548 for (const auto &R : Replaces) {
551 continue;
552 }
557 Position--;
558 }
559 break;
560 }
561 return Position + Offset;
562}
563
566
569 for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) {
570 if (I->isApplicable()) {
572 } else {
574 }
575 }
577}
578
581 if (Replaces.empty())
582 return Code.str();
583
584 auto InMemoryFileSystem =
585 llvm::makeIntrusiveRefCntllvm::vfs::InMemoryFileSystem();
591 InMemoryFileSystem->addFile(
592 "", 0, llvm::MemoryBuffer::getMemBuffer(Code, ""));
596 for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) {
597 Replacement Replace("", I->getOffset(), I->getLength(),
598 I->getReplacementText());
600 return llvm::make_error(
602 }
604 llvm::raw_string_ostream OS(Result);
605 Rewrite.getEditBuffer(ID).write(OS);
607}
608
611 const std::map<std::string, Replacements> &FileToReplaces) {
612 std::map<std::string, Replacements> Result;
614 for (const auto &Entry : FileToReplaces) {
615 auto FE = FileMgr.getOptionalFileRef(Entry.first);
616 if (!FE)
617 llvm::errs() << "File path " << Entry.first << " is invalid.\n";
618 else if (ProcessedFileEntries.insert(*FE).second)
619 Result[Entry.first] = std::move(Entry.second);
620 }
622}
623
624}
625}
Defines the Diagnostic-related interfaces.
Defines the Diagnostic IDs-related interfaces.
Defines the clang::FileManager interface and associated types.
Defines the clang::FileSystemOptions interface.
static int getRangeSize(const SourceManager &Sources, const CharSourceRange &Range, const LangOptions &LangOpts)
Definition Replacement.cpp:134
static const char *const InvalidLocation
Definition Replacement.cpp:43
static std::string getReplacementErrString(replacement_error Err)
Definition Replacement.cpp:164
static std::vector< Range > combineAndSortRanges(std::vector< Range > Ranges)
Definition Replacement.cpp:484
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Represents a character-granular source range.
static llvm::IntrusiveRefCntPtr< DiagnosticIDs > create()
Options for controlling the compiler diagnostics engine.
Concrete class used by the front-end to report problems and issues.
StringRef getName() const
The name of this FileEntry.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Implements support for file system lookup, file system caching, and directory search management.
OptionalFileEntryRef getOptionalFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)
Get a FileEntryRef if it exists, without doing anything on error.
Keeps track of options that affect how file operations are performed.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
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 ...
Rewriter - This is the main interface to the rewrite buffers.
Encodes a location in the source.
This class handles loading and caching of source files into memory.
FileIDAndOffset getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
OptionalFileEntryRef getFileEntryRefForID(FileID FID) const
Returns the FileEntryRef for the provided FileID.
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID.
RangeSelector merge(RangeSelector First, RangeSelector Second)
Selects the merge of the two ranges, i.e.
The JSON file list parser is used to communicate input to InstallAPI.
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
@ Rewrite
We are substituting template parameters for (typically) other template parameters in order to rewrite...
std::pair< FileID, unsigned > FileIDAndOffset
@ Result
The result type of a method or function.