26 #include "llvm/ADT/IntrusiveRefCntPtr.h" 27 #include "llvm/ADT/SmallPtrSet.h" 28 #include "llvm/ADT/StringRef.h" 29 #include "llvm/Support/Error.h" 30 #include "llvm/Support/ErrorHandling.h" 31 #include "llvm/Support/MemoryBuffer.h" 32 #include "llvm/Support/raw_ostream.h" 41 using namespace clang;
42 using namespace tooling;
49 StringRef ReplacementText)
50 : FilePath(FilePath), ReplacementRange(Offset, Length),
51 ReplacementText(ReplacementText) {}
54 unsigned Length, StringRef ReplacementText) {
55 setFromSourceLocation(Sources, Start, Length, ReplacementText);
60 StringRef ReplacementText,
62 setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
78 getLocWithOffset(ReplacementRange.
getOffset());
83 Start, ReplacementRange.
getLength(), ReplacementText);
84 assert(RewriteSucceeded);
85 return RewriteSucceeded;
90 llvm::raw_string_ostream Stream(Result);
91 Stream << FilePath <<
": " << ReplacementRange.
getOffset() <<
":+" 92 << ReplacementRange.
getLength() <<
":\"" << ReplacementText <<
"\"";
121 void Replacement::setFromSourceLocation(
const SourceManager &Sources,
123 StringRef ReplacementText) {
124 const std::pair<FileID, unsigned> DecomposedLocation =
128 this->ReplacementRange =
Range(DecomposedLocation.second, Length);
129 this->ReplacementText = ReplacementText;
140 std::pair<FileID, unsigned> Start = Sources.
getDecomposedLoc(SpellingBegin);
142 if (Start.first != End.first)
return -1;
145 return End.second - Start.second;
148 void Replacement::setFromSourceRange(
const SourceManager &Sources,
150 StringRef ReplacementText,
158 Replacements::getReplacementInChangedCode(
const Replacement &R)
const {
159 unsigned NewStart = getShiftedCodePosition(R.
getOffset());
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.");
183 if (NewReplacement.hasValue())
184 Message +=
"\nNew replacement: " + NewReplacement->toString();
185 if (ExistingReplacement.hasValue())
186 Message +=
"\nExisting replacement: " + ExistingReplacement->toString();
192 Replacements 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();
203 NewReplaces.push_back(R);
206 "Existing replacements must not overlap.");
213 ReplacementsImpl NewReplacesImpl(NewReplaces.begin(), NewReplaces.end());
214 return Replacements(NewReplacesImpl.begin(), NewReplacesImpl.end());
221 Replacements::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>(
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();
308 .overlapsWith(Range(R2.getOffset(), R2.getLength()));
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();
367 class MergedReplacement {
369 MergedReplacement(
const Replacement &R,
bool MergeSecond,
int D)
370 : MergeSecond(MergeSecond), Delta(D), FilePath(R.
getFilePath()),
373 Delta += MergeSecond ? 0 :
Text.size() - Length;
374 DeltaFirst = MergeSecond ?
Text.size() - Length : 0;
385 Length += REnd -
End;
388 StringRef TextRef =
Text;
390 StringRef Tail = TextRef.substr(REnd -
Offset);
396 StringRef Tail = RText.substr(End - R.
getOffset());
404 DeltaFirst += RText.size() - R.
getLength();
417 bool mergeSecond()
const {
return MergeSecond; }
419 int deltaFirst()
const {
return DeltaFirst; }
436 const StringRef FilePath;
445 if (empty() || ReplacesToMerge.
empty())
446 return empty() ? ReplacesToMerge : *
this;
448 auto &
First = Replaces;
449 auto &Second = ReplacesToMerge.Replaces;
453 ReplacementsImpl Result;
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.begin(), Ranges.end(),
487 [](
const Range &LHS,
const Range &RHS) {
489 return LHS.
getOffset() < RHS.getOffset();
490 return LHS.
getLength() < RHS.getLength();
492 std::vector<Range> Result;
493 for (
const auto &R : Ranges) {
494 if (Result.empty() ||
495 Result.back().getOffset() + Result.back().getLength() < R.
getOffset()) {
499 std::max(Result.back().getOffset() + Result.back().getLength(),
501 Result[Result.size() - 1] =
502 Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());
513 const std::vector<Range> &Ranges) {
520 if (Replaces.
empty())
523 for (
const auto &R : MergedRanges) {
528 "Replacements must not conflict since ranges have been merged.");
529 llvm::consumeError(std::move(Err));
538 std::vector<Range> ChangedRanges;
540 for (
const auto &R : Replaces) {
544 ChangedRanges.push_back(Range(Offset, Length));
551 for (
const auto &R : Replaces) {
572 for (
auto I = Replaces.
rbegin(), E = Replaces.
rend(); I != E; ++I) {
573 if (I->isApplicable()) {
574 Result = I->apply(Rewrite) && Result;
584 if (Replaces.
empty())
595 InMemoryFileSystem->addFile(
596 "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code,
"<stdin>"));
599 for (
auto I = Replaces.
rbegin(), E = Replaces.
rend(); I != E; ++I) {
600 Replacement Replace(
"<stdin>", I->getOffset(), I->getLength(),
601 I->getReplacementText());
602 if (!Replace.apply(Rewrite))
603 return llvm::make_error<ReplacementError>(
607 llvm::raw_string_ostream OS(Result);
615 const std::map<std::string, Replacements> &FileToReplaces) {
616 std::map<std::string, Replacements> Result;
617 llvm::SmallPtrSet<const FileEntry *, 16> ProcessedFileEntries;
618 for (
const auto &Entry : FileToReplaces) {
621 llvm::errs() <<
"File path " << Entry.first <<
" is invalid.\n";
622 else if (ProcessedFileEntries.insert(FE).second)
623 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...
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
An in-memory file system.
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.
Defines the virtual file system interface vfs::FileSystem.
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.
__DEVICE__ int max(int __a, int __b)
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)