24#include "llvm/ADT/IntrusiveRefCntPtr.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"
41using namespace tooling;
48 StringRef ReplacementText)
49 : FilePath(
std::string(FilePath)), ReplacementRange(
Offset, Length),
50 ReplacementText(
std::string(ReplacementText)) {}
53 unsigned Length, StringRef ReplacementText) {
54 setFromSourceLocation(Sources, Start, Length, ReplacementText);
59 StringRef ReplacementText,
61 setFromSourceRange(Sources,
Range, ReplacementText, LangOpts);
70 auto Entry =
SM.getFileManager().getFile(FilePath);
76 SM.getLocForStartOfFile(ID).
77 getLocWithOffset(ReplacementRange.
getOffset());
81 bool RewriteSucceeded = !
Rewrite.ReplaceText(
82 Start, ReplacementRange.
getLength(), ReplacementText);
83 assert(RewriteSucceeded);
84 return RewriteSucceeded;
89 llvm::raw_string_ostream Stream(
Result);
90 Stream << FilePath <<
": " << ReplacementRange.
getOffset() <<
":+"
91 << ReplacementRange.
getLength() <<
":\"" << ReplacementText <<
"\"";
120void Replacement::setFromSourceLocation(
const SourceManager &Sources,
122 StringRef ReplacementText) {
123 const std::pair<FileID, unsigned> DecomposedLocation =
127 this->ReplacementRange =
Range(DecomposedLocation.second, Length);
128 this->ReplacementText = std::string(ReplacementText);
139 std::pair<FileID, unsigned> Start = Sources.
getDecomposedLoc(SpellingBegin);
141 if (Start.first != End.first)
return -1;
142 if (
Range.isTokenRange())
144 return End.second - Start.second;
147void Replacement::setFromSourceRange(
const SourceManager &Sources,
149 StringRef ReplacementText,
157Replacements::getReplacementInChangedCode(
const Replacement &R)
const {
167 return "Failed to apply a replacement.";
169 return "The new replacement's file path is different from the file path of "
170 "existing replacements";
172 return "The new replacement overlaps with an existing replacement.";
174 return "The new insertion has the same insert location as an existing "
177 llvm_unreachable(
"A value of replacement_error has no message.");
183 Message +=
"\nNew replacement: " + NewReplacement->toString();
184 if (ExistingReplacement)
185 Message +=
"\nExisting replacement: " + ExistingReplacement->toString();
191Replacements Replacements::getCanonicalReplacements()
const {
192 std::vector<Replacement> NewReplaces;
194 for (
const auto &R : Replaces) {
195 if (NewReplaces.empty()) {
196 NewReplaces.push_back(R);
199 auto &Prev = NewReplaces.back();
200 unsigned PrevEnd = Prev.getOffset() + Prev.getLength();
201 if (PrevEnd < R.getOffset()) {
202 NewReplaces.push_back(R);
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());
212 ReplacementsImpl NewReplacesImpl(NewReplaces.begin(), NewReplaces.end());
213 return Replacements(NewReplacesImpl.begin(), NewReplacesImpl.end());
220Replacements::mergeIfOrderIndependent(
const Replacement &R)
const {
224 Replacements RsShiftedByReplaces(getReplacementInChangedCode(R));
228 for (
const auto &Replace : Replaces)
229 ReplacesShiftedByRs.Replaces.insert(
230 Rs.getReplacementInChangedCode(Replace));
232 auto MergeShiftedRs =
merge(RsShiftedByReplaces);
234 auto MergeShiftedReplaces = Rs.merge(ReplacesShiftedByRs);
238 if (MergeShiftedRs.getCanonicalReplacements() ==
239 MergeShiftedReplaces.getCanonicalReplacements())
240 return MergeShiftedRs;
242 R, *Replaces.begin());
247 if (!Replaces.empty() && R.
getFilePath() != Replaces.begin()->getFilePath())
248 return llvm::make_error<ReplacementError>(
252 if (R.
getOffset() == std::numeric_limits<unsigned>::max()) {
254 return llvm::Error::success();
267 auto I = Replaces.lower_bound(AtEnd);
269 if (I != Replaces.end() && R.
getOffset() == I->getOffset()) {
272 if (I->getLength() == 0) {
277 return llvm::make_error<ReplacementError>(
284 Replaces.insert(std::move(NewR));
285 return llvm::Error::success();
295 return llvm::Error::success();
300 if (I == Replaces.begin()) {
302 return llvm::Error::success();
311 if (!Overlap(R, *I)) {
325 auto MergeEnd = std::next(I);
326 while (I != Replaces.begin()) {
335 OverlapReplaces.mergeIfOrderIndependent(R);
337 return Merged.takeError();
338 Replaces.erase(MergeBegin, MergeEnd);
339 Replaces.insert(Merged->begin(), Merged->end());
341 return llvm::Error::success();
366class MergedReplacement {
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;
384 Length += REnd - End;
387 StringRef TextRef =
Text;
389 StringRef Tail = TextRef.substr(REnd -
Offset);
393 unsigned End =
Offset + Length;
395 StringRef Tail = RText.substr(End - R.
getOffset());
397 if (R.
getOffset() + RText.size() > End) {
403 DeltaFirst += RText.size() - R.
getLength();
416 bool mergeSecond()
const {
return MergeSecond; }
418 int deltaFirst()
const {
return DeltaFirst; }
435 const StringRef FilePath;
445 return empty() ? ReplacesToMerge : *
this;
447 auto &
First = Replaces;
448 auto &Second = ReplacesToMerge.Replaces;
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,
465 ++(NextIsFirst ? FirstI : SecondI);
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))
475 Delta -= Merged.deltaFirst();
476 Result.insert(Merged.asReplacement());
485 llvm::sort(Ranges, [](
const Range &LHS,
const Range &RHS) {
490 std::vector<Range>
Result;
491 for (
const auto &R : Ranges) {
497 std::max(
Result.back().getOffset() +
Result.back().getLength(),
511 const std::vector<Range> &Ranges) {
518 if (Replaces.
empty())
521 for (
const auto &R : MergedRanges) {
526 "Replacements must not conflict since ranges have been merged.");
535 std::vector<Range> ChangedRanges;
537 for (
const auto &R : Replaces) {
548 for (
const auto &R : Replaces) {
569 for (
auto I = Replaces.
rbegin(), E = Replaces.
rend(); I != E; ++I) {
570 if (I->isApplicable()) {
571 Result = I->apply(
Rewrite) && Result;
581 if (Replaces.
empty())
585 new llvm::vfs::InMemoryFileSystem);
592 InMemoryFileSystem->addFile(
593 "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code,
"<stdin>"));
597 for (
auto I = Replaces.
rbegin(), E = Replaces.
rend(); I != E; ++I) {
598 Replacement Replace(
"<stdin>", I->getOffset(), I->getLength(),
599 I->getReplacementText());
601 return llvm::make_error<ReplacementError>(
602 replacement_error::fail_to_apply, Replace);
605 llvm::raw_string_ostream OS(Result);
606 Rewrite.getEditBuffer(ID).write(OS);
613 const std::map<std::string, Replacements> &FileToReplaces) {
614 std::map<std::string, Replacements> Result;
616 for (
const auto &Entry : FileToReplaces) {
617 auto FE = FileMgr.
getFile(Entry.first);
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);
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.
Cached information about one file (either on disk or in the virtual file system).
StringRef getName() const
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.
llvm::ErrorOr< const FileEntry * > getFile(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)
Lookup, cache, and verify the specified file (real or virtual).
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.
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID.
FileID createFileID(const FileEntry *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...
std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
@ 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.