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"
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().getOptionalFileRef(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 =
128 this->ReplacementRange =
Range(DecomposedLocation.second, Length);
129 this->ReplacementText = std::string(ReplacementText);
140 std::pair<FileID, unsigned> Start = Sources.
getDecomposedLoc(SpellingBegin);
142 if (Start.first != End.first)
return -1;
143 if (
Range.isTokenRange())
145 return End.second - Start.second;
148void Replacement::setFromSourceRange(
const SourceManager &Sources,
150 StringRef ReplacementText,
158Replacements::getReplacementInChangedCode(
const Replacement &R)
const {
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 "
178 llvm_unreachable(
"A value of replacement_error has no message.");
184 Message +=
"\nNew replacement: " + NewReplacement->toString();
185 if (ExistingReplacement)
186 Message +=
"\nExisting replacement: " + ExistingReplacement->toString();
192Replacements Replacements::getCanonicalReplacements()
const {
193 std::vector<Replacement> NewReplaces;
195 for (
const auto &R : Replaces) {
196 if (NewReplaces.empty()) {
197 NewReplaces.push_back(R);
200 auto &Prev = NewReplaces.back();
201 unsigned PrevEnd = Prev.getOffset() + Prev.getLength();
202 if (PrevEnd < R.getOffset()) {
203 NewReplaces.push_back(R);
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());
213 ReplacementsImpl NewReplacesImpl(NewReplaces.begin(), NewReplaces.end());
214 return Replacements(NewReplacesImpl.begin(), NewReplacesImpl.end());
221Replacements::mergeIfOrderIndependent(
const Replacement &R)
const {
225 Replacements RsShiftedByReplaces(getReplacementInChangedCode(R));
229 for (
const auto &Replace : Replaces)
230 ReplacesShiftedByRs.Replaces.insert(
231 Rs.getReplacementInChangedCode(Replace));
233 auto MergeShiftedRs =
merge(RsShiftedByReplaces);
235 auto MergeShiftedReplaces = Rs.merge(ReplacesShiftedByRs);
239 if (MergeShiftedRs.getCanonicalReplacements() ==
240 MergeShiftedReplaces.getCanonicalReplacements())
241 return MergeShiftedRs;
243 R, *Replaces.begin());
248 if (!Replaces.empty() && R.
getFilePath() != Replaces.begin()->getFilePath())
249 return llvm::make_error<ReplacementError>(
253 if (R.
getOffset() == std::numeric_limits<unsigned>::max()) {
255 return llvm::Error::success();
268 auto I = Replaces.lower_bound(AtEnd);
270 if (I != Replaces.end() && R.
getOffset() == I->getOffset()) {
273 if (I->getLength() == 0) {
278 return llvm::make_error<ReplacementError>(
285 Replaces.insert(std::move(NewR));
286 return llvm::Error::success();
296 return llvm::Error::success();
301 if (I == Replaces.begin()) {
303 return llvm::Error::success();
312 if (!Overlap(R, *I)) {
326 auto MergeEnd = std::next(I);
327 while (I != Replaces.begin()) {
336 OverlapReplaces.mergeIfOrderIndependent(R);
338 return Merged.takeError();
339 Replaces.erase(MergeBegin, MergeEnd);
340 Replaces.insert(Merged->begin(), Merged->end());
342 return llvm::Error::success();
367class MergedReplacement {
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;
383 unsigned End = Offset +
Text.size();
385 Length += REnd - End;
388 StringRef TextRef =
Text;
389 StringRef Head = TextRef.substr(0, R.
getOffset() + Delta - Offset);
390 StringRef Tail = TextRef.substr(REnd - Offset);
394 unsigned End = Offset + Length;
396 StringRef Tail = RText.substr(End - R.
getOffset());
398 if (R.
getOffset() + RText.size() > End) {
404 DeltaFirst += RText.size() - R.
getLength();
417 bool mergeSecond()
const {
return MergeSecond; }
419 int deltaFirst()
const {
return DeltaFirst; }
420 Replacement asReplacement()
const {
return {FilePath, Offset, Length,
Text}; }
436 const StringRef FilePath;
437 const unsigned Offset;
446 return empty() ? ReplacesToMerge : *
this;
448 auto &
First = Replaces;
449 auto &Second = ReplacesToMerge.Replaces;
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,
466 ++(NextIsFirst ? FirstI : SecondI);
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))
476 Delta -= Merged.deltaFirst();
477 Result.insert(Merged.asReplacement());
486 llvm::sort(Ranges, [](
const Range &LHS,
const Range &RHS) {
491 std::vector<Range>
Result;
492 for (
const auto &R : Ranges) {
498 std::max(
Result.back().getOffset() +
Result.back().getLength(),
512 const std::vector<Range> &Ranges) {
519 if (Replaces.
empty())
522 for (
const auto &R : MergedRanges) {
527 "Replacements must not conflict since ranges have been merged.");
536 std::vector<Range> ChangedRanges;
538 for (
const auto &R : Replaces) {
542 ChangedRanges.push_back(
Range(Offset, Length));
549 for (
const auto &R : Replaces) {
562 return Position + Offset;
570 for (
auto I = Replaces.
rbegin(),
E = Replaces.
rend(); I !=
E; ++I) {
571 if (I->isApplicable()) {
572 Result = I->apply(
Rewrite) && Result;
582 if (Replaces.
empty())
586 new llvm::vfs::InMemoryFileSystem);
593 InMemoryFileSystem->addFile(
594 "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code,
"<stdin>"));
598 for (
auto I = Replaces.
rbegin(),
E = Replaces.
rend(); I !=
E; ++I) {
599 Replacement Replace(
"<stdin>", I->getOffset(), I->getLength(),
600 I->getReplacementText());
602 return llvm::make_error<ReplacementError>(
603 replacement_error::fail_to_apply, Replace);
606 llvm::raw_string_ostream OS(Result);
614 const std::map<std::string, Replacements> &FileToReplaces) {
615 std::map<std::string, Replacements> Result;
617 for (
const auto &Entry : FileToReplaces) {
618 auto FE = FileMgr.
getFile(Entry.first);
620 llvm::errs() <<
"File path " << Entry.first <<
" is invalid.\n";
621 else if (ProcessedFileEntries.insert(*FE).second)
622 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.
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.
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.
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.