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" 40 using namespace clang;
41 using namespace tooling;
48 StringRef ReplacementText)
49 : FilePath(FilePath), ReplacementRange(Offset, Length),
50 ReplacementText(ReplacementText) {}
53 unsigned Length, StringRef ReplacementText) {
54 setFromSourceLocation(Sources, Start, Length, ReplacementText);
59 StringRef ReplacementText,
61 setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
77 getLocWithOffset(ReplacementRange.
getOffset());
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 <<
"\"";
120 void 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 = ReplacementText;
139 std::pair<FileID, unsigned> Start = Sources.
getDecomposedLoc(SpellingBegin);
141 if (Start.first != End.first)
return -1;
144 return End.second - Start.second;
147 void Replacement::setFromSourceRange(
const SourceManager &Sources,
149 StringRef ReplacementText,
157 Replacements::getReplacementInChangedCode(
const Replacement &R)
const {
158 unsigned NewStart = getShiftedCodePosition(R.
getOffset());
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.");
182 if (NewReplacement.hasValue())
183 Message +=
"\nNew replacement: " + NewReplacement->toString();
184 if (ExistingReplacement.hasValue())
185 Message +=
"\nExisting replacement: " + ExistingReplacement->toString();
191 Replacements 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();
202 NewReplaces.push_back(R);
205 "Existing replacements must not overlap.");
212 ReplacementsImpl NewReplacesImpl(NewReplaces.begin(), NewReplaces.end());
213 return Replacements(NewReplacesImpl.begin(), NewReplacesImpl.end());
220 Replacements::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>(
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();
307 .overlapsWith(Range(R2.getOffset(), R2.getLength()));
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();
366 class MergedReplacement {
368 MergedReplacement(
const Replacement &R,
bool MergeSecond,
int D)
369 : MergeSecond(MergeSecond), Delta(D), FilePath(R.
getFilePath()),
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);
395 StringRef Tail = RText.substr(End - R.
getOffset());
403 DeltaFirst += RText.size() - R.
getLength();
416 bool mergeSecond()
const {
return MergeSecond; }
418 int deltaFirst()
const {
return DeltaFirst; }
435 const StringRef FilePath;
444 if (empty() || ReplacesToMerge.
empty())
445 return empty() ? ReplacesToMerge : *
this;
447 auto &
First = Replaces;
448 auto &Second = ReplacesToMerge.Replaces;
452 ReplacementsImpl Result;
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) {
492 if (Result.empty() ||
493 Result.back().getOffset() + Result.back().getLength() < R.
getOffset()) {
497 std::max(Result.back().getOffset() + Result.back().getLength(),
499 Result[Result.size() - 1] =
500 Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());
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) {
541 ChangedRanges.push_back(Range(Offset, Length));
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>"));
596 for (
auto I = Replaces.
rbegin(), E = Replaces.
rend(); I != E; ++I) {
597 Replacement Replace(
"<stdin>", I->getOffset(), I->getLength(),
598 I->getReplacementText());
599 if (!Replace.apply(Rewrite))
600 return llvm::make_error<ReplacementError>(
604 llvm::raw_string_ostream OS(Result);
612 const std::map<std::string, Replacements> &FileToReplaces) {
613 std::map<std::string, Replacements> Result;
614 llvm::SmallPtrSet<const FileEntry *, 16> ProcessedFileEntries;
615 for (
const auto &Entry : FileToReplaces) {
618 llvm::errs() <<
"File path " << Entry.first <<
" is invalid.\n";
619 else if (ProcessedFileEntries.insert(FE).second)
620 Result[Entry.first] = std::move(Entry.second);
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file. ...
Implements support for file system lookup, file system caching, and directory search management...
Defines the clang::FileManager interface and associated types.
Defines the SourceManager interface.
RewriteBuffer & getEditBuffer(FileID FID)
getEditBuffer - This is like getRewriteBufferFor, but always returns a buffer, and allows you to writ...
FileManager & getFileManager() const
SourceLocation getBegin() const
bool ReplaceText(SourceLocation Start, unsigned OrigLength, StringRef NewStr)
ReplaceText - This method replaces a range of characters in the input buffer with a new string...
__DEVICE__ int max(int __a, int __b)
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
SourceManager & getSourceMgr() const
Concrete class used by the front-end to report problems and issues.
Defines the Diagnostic-related interfaces.
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID. ...
const FileEntry * getFile(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)
Lookup, cache, and verify the specified file (real or virtual).
FileID getOrCreateFileID(const FileEntry *SourceFile, SrcMgr::CharacteristicKind FileCharacter)
Get the FileID for SourceFile if it exists.
static const char *const InvalidLocation
Represents a character-granular source range.
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 ...
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
FileID createFileID(const FileEntry *SourceFile, SourceLocation IncludePos, SrcMgr::CharacteristicKind FileCharacter, int LoadedID=0, unsigned LoadedOffset=0)
Create a new FileID that represents the specified file being #included from the specified IncludePosi...
Encodes a location in the source.
raw_ostream & write(raw_ostream &Stream) const
Write to Stream the result of applying all changes to the original buffer.
StringRef getName() const
Options for controlling the compiler diagnostics engine.
Cached information about one file (either on disk or in the virtual file system). ...
bool isTokenRange() const
Return true if the end of this range specifies the start of the last token.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Dataflow Directional Tag Classes.
Used for handling and querying diagnostic IDs.
static int getRangeSize(const SourceManager &Sources, const CharSourceRange &Range, const LangOptions &LangOpts)
Defines the clang::FileSystemOptions interface.
SourceLocation getEnd() const
Keeps track of options that affect how file operations are performed.
Rewriter - This is the main interface to the rewrite buffers.
Defines the clang::SourceLocation class and associated facilities.
static std::string getReplacementErrString(replacement_error Err)
Defines the Diagnostic IDs-related interfaces.
This class handles loading and caching of source files into memory.
std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
static std::vector< Range > combineAndSortRanges(std::vector< Range > Ranges)