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;
41using namespace tooling;
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
66}
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
96namespace tooling {
97
101
104
108}
109
115}
116
117}
118}
119
120void Replacement::setFromSourceLocation(const SourceManager &Sources,
122 StringRef ReplacementText) {
123 const std::pair<FileID, unsigned> DecomposedLocation =
128 this->ReplacementRange = Range(DecomposedLocation.second, Length);
129 this->ReplacementText = std::string(ReplacementText);
130}
131
132
133
134
140 std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
141 std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
142 if (Start.first != End.first) return -1;
143 if (Range.isTokenRange())
145 return End.second - Start.second;
146}
147
148void Replacement::setFromSourceRange(const SourceManager &Sources,
150 StringRef ReplacementText,
154 ReplacementText);
155}
156
158Replacements::getReplacementInChangedCode(const Replacement &R) const {
163}
164
166 switch (Err) {
168 return "Failed to apply a replacement.";
170 return "The new replacement's file path is different from the file path of "
171 "existing replacements";
173 return "The new replacement overlaps with an existing replacement.";
175 return "The new insertion has the same insert location as an existing "
176 "replacement.";
177 }
178 llvm_unreachable("A value of replacement_error has no message.");
179}
180
183 if (NewReplacement)
184 Message += "\nNew replacement: " + NewReplacement->toString();
185 if (ExistingReplacement)
186 Message += "\nExisting replacement: " + ExistingReplacement->toString();
187 return Message;
188}
189
191
192Replacements Replacements::getCanonicalReplacements() const {
193 std::vector NewReplaces;
194
195 for (const auto &R : Replaces) {
196 if (NewReplaces.empty()) {
197 NewReplaces.push_back(R);
198 continue;
199 }
200 auto &Prev = NewReplaces.back();
201 unsigned PrevEnd = Prev.getOffset() + Prev.getLength();
202 if (PrevEnd < R.getOffset()) {
203 NewReplaces.push_back(R);
204 } else {
205 assert(PrevEnd == R.getOffset() &&
206 "Existing replacements must not overlap.");
208 R.getFilePath(), Prev.getOffset(), Prev.getLength() + R.getLength(),
209 (Prev.getReplacementText() + R.getReplacementText()).str());
210 Prev = NewR;
211 }
212 }
213 ReplacementsImpl NewReplacesImpl(NewReplaces.begin(), NewReplaces.end());
214 return Replacements(NewReplacesImpl.begin(), NewReplacesImpl.end());
215}
216
217
218
219
221Replacements::mergeIfOrderIndependent(const Replacement &R) const {
223
224
225 Replacements RsShiftedByReplaces(getReplacementInChangedCode(R));
226
227
229 for (const auto &Replace : Replaces)
230 ReplacesShiftedByRs.Replaces.insert(
231 Rs.getReplacementInChangedCode(Replace));
232
233 auto MergeShiftedRs = merge(RsShiftedByReplaces);
234
235 auto MergeShiftedReplaces = Rs.merge(ReplacesShiftedByRs);
236
237
238
239 if (MergeShiftedRs.getCanonicalReplacements() ==
240 MergeShiftedReplaces.getCanonicalReplacements())
241 return MergeShiftedRs;
243 R, *Replaces.begin());
244}
245
247
248 if (!Replaces.empty() && R.getFilePath() != Replaces.begin()->getFilePath())
249 return llvm::make_error(
251
252
253 if (R.getOffset() == std::numeric_limits::max()) {
254 Replaces.insert(R);
255 return llvm::Error::success();
256 }
257
258
259
260
261
262
264
265
266
267
268 auto I = Replaces.lower_bound(AtEnd);
269
270 if (I != Replaces.end() && R.getOffset() == I->getOffset()) {
272
273 if (I->getLength() == 0) {
274
275
278 return llvm::make_error(
280
284 Replaces.erase(I);
285 Replaces.insert(std::move(NewR));
286 return llvm::Error::success();
287 }
288
289
290
291
292
293
294
295 Replaces.insert(R);
296 return llvm::Error::success();
297 }
298
299
300
301 if (I == Replaces.begin()) {
302 Replaces.insert(R);
303 return llvm::Error::success();
304 }
305 --I;
309 };
310
311
312 if (!Overlap(R, *I)) {
313
314
315
316
317
318
319
320 Replaces.insert(R);
321 } else {
322
323
324
325 auto MergeBegin = I;
326 auto MergeEnd = std::next(I);
327 while (I != Replaces.begin()) {
328 --I;
329
330 if (!Overlap(R, *I))
331 break;
332 MergeBegin = I;
333 }
334 Replacements OverlapReplaces(MergeBegin, MergeEnd);
336 OverlapReplaces.mergeIfOrderIndependent(R);
337 if (!Merged)
338 return Merged.takeError();
339 Replaces.erase(MergeBegin, MergeEnd);
340 Replaces.insert(Merged->begin(), Merged->end());
341 }
342 return llvm::Error::success();
343}
344
345namespace {
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367class MergedReplacement {
368public:
369 MergedReplacement(const Replacement &R, bool MergeSecond, int D)
370 : MergeSecond(MergeSecond), Delta(D), FilePath(R.getFilePath()),
371 Offset(R.getOffset() + (MergeSecond ? 0 : Delta)),
372 Length(R.getLength()), Text(std::string(R.getReplacementText())) {
373 Delta += MergeSecond ? 0 : Text.size() - Length;
374 DeltaFirst = MergeSecond ? Text.size() - Length : 0;
375 }
376
377
378
379
381 if (MergeSecond) {
383 unsigned End = Offset + Text.size();
384 if (REnd > End) {
385 Length += REnd - End;
386 MergeSecond = false;
387 }
388 StringRef TextRef = Text;
389 StringRef Head = TextRef.substr(0, R.getOffset() + Delta - Offset);
390 StringRef Tail = TextRef.substr(REnd - Offset);
393 } else {
394 unsigned End = Offset + Length;
396 StringRef Tail = RText.substr(End - R.getOffset());
398 if (R.getOffset() + RText.size() > End) {
400 MergeSecond = true;
401 } else {
402 Length += R.getLength() - RText.size();
403 }
404 DeltaFirst += RText.size() - R.getLength();
405 }
406 }
407
408
409
410 bool endsBefore(const Replacement &R) const {
411 if (MergeSecond)
412 return Offset + Text.size() < R.getOffset() + Delta;
413 return Offset + Length < R.getOffset();
414 }
415
416
417 bool mergeSecond() const { return MergeSecond; }
418
419 int deltaFirst() const { return DeltaFirst; }
420 Replacement asReplacement() const { return {FilePath, Offset, Length, Text}; }
421
422private:
423 bool MergeSecond;
424
425
426
427 int Delta;
428
429
430
431
432 int DeltaFirst;
433
434
435
436 const StringRef FilePath;
437 const unsigned Offset;
438 unsigned Length;
439 std::string Text;
440};
441
442}
443
445 if (empty() || ReplacesToMerge.empty())
446 return empty() ? ReplacesToMerge : *this;
447
448 auto &First = Replaces;
449 auto &Second = ReplacesToMerge.Replaces;
450
451
452 int Delta = 0;
453 ReplacementsImpl Result;
454
455
456
457
458
459 for (auto FirstI = First.begin(), SecondI = Second.begin();
460 FirstI != First.end() || SecondI != Second.end();) {
461 bool NextIsFirst = SecondI == Second.end() ||
462 (FirstI != First.end() &&
463 FirstI->getOffset() < SecondI->getOffset() + Delta);
464 MergedReplacement Merged(NextIsFirst ? *FirstI : *SecondI, NextIsFirst,
465 Delta);
466 ++(NextIsFirst ? FirstI : SecondI);
467
468 while ((Merged.mergeSecond() && SecondI != Second.end()) ||
469 (!Merged.mergeSecond() && FirstI != First.end())) {
470 auto &I = Merged.mergeSecond() ? SecondI : FirstI;
471 if (Merged.endsBefore(*I))
472 break;
473 Merged.merge(*I);
474 ++I;
475 }
476 Delta -= Merged.deltaFirst();
477 Result.insert(Merged.asReplacement());
478 }
480}
481
482
483
484
486 llvm::sort(Ranges, [](const Range &LHS, const Range &RHS) {
490 });
491 std::vector Result;
492 for (const auto &R : Ranges) {
493 if (Result.empty() ||
496 } else {
497 unsigned NewEnd =
498 std::max(Result.back().getOffset() + Result.back().getLength(),
501 Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());
502 }
503 }
505}
506
508namespace tooling {
509
510std::vector
512 const std::vector &Ranges) {
513
514
515
516
517
519 if (Replaces.empty())
520 return MergedRanges;
522 for (const auto &R : MergedRanges) {
523 llvm::cantFail(
526 std::string(R.getLength(), ' '))),
527 "Replacements must not conflict since ranges have been merged.");
528 }
530}
531
532}
533}
534
536 std::vector ChangedRanges;
537 int Shift = 0;
538 for (const auto &R : Replaces) {
539 unsigned Offset = R.getOffset() + Shift;
542 ChangedRanges.push_back(Range(Offset, Length));
543 }
545}
546
548 unsigned Offset = 0;
549 for (const auto &R : Replaces) {
552 continue;
553 }
558 Position--;
559 }
560 break;
561 }
562 return Position + Offset;
563}
564
566namespace tooling {
567
569 bool Result = true;
570 for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) {
571 if (I->isApplicable()) {
572 Result = I->apply(Rewrite) && Result;
573 } else {
574 Result = false;
575 }
576 }
577 return Result;
578}
579
582 if (Replaces.empty())
583 return Code.str();
584
586 new llvm::vfs::InMemoryFileSystem);
593 InMemoryFileSystem->addFile(
594 "", 0, llvm::MemoryBuffer::getMemBuffer(Code, ""));
598 for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) {
599 Replacement Replace("", I->getOffset(), I->getLength(),
600 I->getReplacementText());
602 return llvm::make_error(
603 replacement_error::fail_to_apply, Replace);
604 }
605 std::string Result;
606 llvm::raw_string_ostream OS(Result);
607 Rewrite.getEditBuffer(ID).write(OS);
608 return Result;
609}
610
613 const std::map<std::string, Replacements> &FileToReplaces) {
614 std::map<std::string, Replacements> Result;
616 for (const auto &Entry : FileToReplaces) {
618 if (!FE)
619 llvm::errs() << "File path " << Entry.first << " is invalid.\n";
620 else if (ProcessedFileEntries.insert(*FE).second)
621 Result[Entry.first] = std::move(Entry.second);
622 }
623 return Result;
624}
625
626}
627}
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)
static const char *const InvalidLocation
static std::string getReplacementErrString(replacement_error Err)
static std::vector< Range > combineAndSortRanges(std::vector< Range > Ranges)
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Represents a character-granular source range.
Used for handling and querying diagnostic IDs.
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.
OptionalFileEntryRef getFileEntryRefForID(FileID FID) const
Returns the FileEntryRef for the provided FileID.
FileID createFileID(FileEntryRef SourceFile, SourceLocation IncludePos, SrcMgr::CharacteristicKind FileCharacter, int LoadedID=0, SourceLocation::UIntTy LoadedOffset=0)
Create a new FileID that represents the specified file being #included from the specified IncludePosi...
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID.
std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
The JSON file list parser is used to communicate input to InstallAPI.
@ Rewrite
We are substituting template parameters for (typically) other template parameters in order to rewrite...
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
bool operator<(DeclarationName LHS, DeclarationName RHS)
Ordering on two declaration names.
@ Result
The result type of a method or function.