123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- //===--- Replacement.cpp - Framework for clang refactoring tools ----------===//
- //
- // The LLVM Compiler Infrastructure
- //
- // This file is distributed under the University of Illinois Open Source
- // License. See LICENSE.TXT for details.
- //
- //===----------------------------------------------------------------------===//
- //
- // Implements classes to support/store refactorings.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/Basic/Diagnostic.h"
- #include "clang/Basic/DiagnosticIDs.h"
- #include "clang/Basic/DiagnosticOptions.h"
- #include "clang/Basic/FileManager.h"
- #include "clang/Basic/SourceManager.h"
- #include "clang/Lex/Lexer.h"
- #include "clang/Rewrite/Core/Rewriter.h"
- #include "clang/Tooling/Core/Replacement.h"
- #include "llvm/Support/FileSystem.h"
- #include "llvm/Support/Path.h"
- #include "llvm/Support/raw_os_ostream.h"
- namespace clang {
- namespace tooling {
- static const char * const InvalidLocation = "";
- Replacement::Replacement()
- : FilePath(InvalidLocation) {}
- Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
- StringRef ReplacementText)
- : FilePath(FilePath), ReplacementRange(Offset, Length),
- ReplacementText(ReplacementText) {}
- Replacement::Replacement(const SourceManager &Sources, SourceLocation Start,
- unsigned Length, StringRef ReplacementText) {
- setFromSourceLocation(Sources, Start, Length, ReplacementText);
- }
- Replacement::Replacement(const SourceManager &Sources,
- const CharSourceRange &Range,
- StringRef ReplacementText,
- const LangOptions &LangOpts) {
- setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
- }
- bool Replacement::isApplicable() const {
- return FilePath != InvalidLocation;
- }
- bool Replacement::apply(Rewriter &Rewrite) const {
- SourceManager &SM = Rewrite.getSourceMgr();
- const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
- if (!Entry)
- return false;
- FileID ID;
- // FIXME: Use SM.translateFile directly.
- SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1);
- ID = Location.isValid() ?
- SM.getFileID(Location) :
- SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
- // FIXME: We cannot check whether Offset + Length is in the file, as
- // the remapping API is not public in the RewriteBuffer.
- const SourceLocation Start =
- SM.getLocForStartOfFile(ID).
- getLocWithOffset(ReplacementRange.getOffset());
- // ReplaceText returns false on success.
- // ReplaceText only fails if the source location is not a file location, in
- // which case we already returned false earlier.
- bool RewriteSucceeded = !Rewrite.ReplaceText(
- Start, ReplacementRange.getLength(), ReplacementText);
- assert(RewriteSucceeded);
- return RewriteSucceeded;
- }
- std::string Replacement::toString() const {
- std::string Result;
- llvm::raw_string_ostream Stream(Result);
- Stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"
- << ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";
- return Stream.str();
- }
- bool operator<(const Replacement &LHS, const Replacement &RHS) {
- if (LHS.getOffset() != RHS.getOffset())
- return LHS.getOffset() < RHS.getOffset();
- // Apply longer replacements first, specifically so that deletions are
- // executed before insertions. It is (hopefully) never the intention to
- // delete parts of newly inserted code.
- if (LHS.getLength() != RHS.getLength())
- return LHS.getLength() > RHS.getLength();
- if (LHS.getFilePath() != RHS.getFilePath())
- return LHS.getFilePath() < RHS.getFilePath();
- return LHS.getReplacementText() < RHS.getReplacementText();
- }
- bool operator==(const Replacement &LHS, const Replacement &RHS) {
- return LHS.getOffset() == RHS.getOffset() &&
- LHS.getLength() == RHS.getLength() &&
- LHS.getFilePath() == RHS.getFilePath() &&
- LHS.getReplacementText() == RHS.getReplacementText();
- }
- void Replacement::setFromSourceLocation(const SourceManager &Sources,
- SourceLocation Start, unsigned Length,
- StringRef ReplacementText) {
- const std::pair<FileID, unsigned> DecomposedLocation =
- Sources.getDecomposedLoc(Start);
- const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
- if (Entry) {
- // Make FilePath absolute so replacements can be applied correctly when
- // relative paths for files are used.
- llvm::SmallString<256> FilePath(Entry->getName());
- std::error_code EC = llvm::sys::fs::make_absolute(FilePath);
- this->FilePath = EC ? FilePath.c_str() : Entry->getName();
- } else {
- this->FilePath = InvalidLocation;
- }
- this->ReplacementRange = Range(DecomposedLocation.second, Length);
- this->ReplacementText = ReplacementText;
- }
- // FIXME: This should go into the Lexer, but we need to figure out how
- // to handle ranges for refactoring in general first - there is no obvious
- // good way how to integrate this into the Lexer yet.
- static int getRangeSize(const SourceManager &Sources,
- const CharSourceRange &Range,
- const LangOptions &LangOpts) {
- SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
- SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
- std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
- std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
- if (Start.first != End.first) return -1;
- if (Range.isTokenRange())
- End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, LangOpts);
- return End.second - Start.second;
- }
- void Replacement::setFromSourceRange(const SourceManager &Sources,
- const CharSourceRange &Range,
- StringRef ReplacementText,
- const LangOptions &LangOpts) {
- setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
- getRangeSize(Sources, Range, LangOpts),
- ReplacementText);
- }
- unsigned shiftedCodePosition(const Replacements &Replaces, unsigned Position) {
- unsigned NewPosition = Position;
- for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E;
- ++I) {
- if (I->getOffset() >= Position)
- break;
- if (I->getOffset() + I->getLength() > Position)
- NewPosition += I->getOffset() + I->getLength() - Position;
- NewPosition += I->getReplacementText().size() - I->getLength();
- }
- return NewPosition;
- }
- // FIXME: Remove this function when Replacements is implemented as std::vector
- // instead of std::set.
- unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces,
- unsigned Position) {
- unsigned NewPosition = Position;
- for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
- E = Replaces.end();
- I != E; ++I) {
- if (I->getOffset() >= Position)
- break;
- if (I->getOffset() + I->getLength() > Position)
- NewPosition += I->getOffset() + I->getLength() - Position;
- NewPosition += I->getReplacementText().size() - I->getLength();
- }
- return NewPosition;
- }
- void deduplicate(std::vector<Replacement> &Replaces,
- std::vector<Range> &Conflicts) {
- if (Replaces.empty())
- return;
- auto LessNoPath = [](const Replacement &LHS, const Replacement &RHS) {
- if (LHS.getOffset() != RHS.getOffset())
- return LHS.getOffset() < RHS.getOffset();
- if (LHS.getLength() != RHS.getLength())
- return LHS.getLength() < RHS.getLength();
- return LHS.getReplacementText() < RHS.getReplacementText();
- };
- auto EqualNoPath = [](const Replacement &LHS, const Replacement &RHS) {
- return LHS.getOffset() == RHS.getOffset() &&
- LHS.getLength() == RHS.getLength() &&
- LHS.getReplacementText() == RHS.getReplacementText();
- };
- // Deduplicate. We don't want to deduplicate based on the path as we assume
- // that all replacements refer to the same file (or are symlinks).
- std::sort(Replaces.begin(), Replaces.end(), LessNoPath);
- Replaces.erase(std::unique(Replaces.begin(), Replaces.end(), EqualNoPath),
- Replaces.end());
- // Detect conflicts
- Range ConflictRange(Replaces.front().getOffset(),
- Replaces.front().getLength());
- unsigned ConflictStart = 0;
- unsigned ConflictLength = 1;
- for (unsigned i = 1; i < Replaces.size(); ++i) {
- Range Current(Replaces[i].getOffset(), Replaces[i].getLength());
- if (ConflictRange.overlapsWith(Current)) {
- // Extend conflicted range
- ConflictRange = Range(ConflictRange.getOffset(),
- std::max(ConflictRange.getLength(),
- Current.getOffset() + Current.getLength() -
- ConflictRange.getOffset()));
- ++ConflictLength;
- } else {
- if (ConflictLength > 1)
- Conflicts.push_back(Range(ConflictStart, ConflictLength));
- ConflictRange = Current;
- ConflictStart = i;
- ConflictLength = 1;
- }
- }
- if (ConflictLength > 1)
- Conflicts.push_back(Range(ConflictStart, ConflictLength));
- }
- bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) {
- bool Result = true;
- for (Replacements::const_iterator I = Replaces.begin(),
- E = Replaces.end();
- I != E; ++I) {
- if (I->isApplicable()) {
- Result = I->apply(Rewrite) && Result;
- } else {
- Result = false;
- }
- }
- return Result;
- }
- // FIXME: Remove this function when Replacements is implemented as std::vector
- // instead of std::set.
- bool applyAllReplacements(const std::vector<Replacement> &Replaces,
- Rewriter &Rewrite) {
- bool Result = true;
- for (std::vector<Replacement>::const_iterator I = Replaces.begin(),
- E = Replaces.end();
- I != E; ++I) {
- if (I->isApplicable()) {
- Result = I->apply(Rewrite) && Result;
- } else {
- Result = false;
- }
- }
- return Result;
- }
- std::string applyAllReplacements(StringRef Code, const Replacements &Replaces) {
- FileManager Files((FileSystemOptions()));
- DiagnosticsEngine Diagnostics(
- IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
- new DiagnosticOptions);
- SourceManager SourceMgr(Diagnostics, Files);
- Rewriter Rewrite(SourceMgr, LangOptions());
- std::unique_ptr<llvm::MemoryBuffer> Buf =
- llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>");
- const clang::FileEntry *Entry =
- Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0);
- SourceMgr.overrideFileContents(Entry, std::move(Buf));
- FileID ID =
- SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User);
- for (Replacements::const_iterator I = Replaces.begin(), E = Replaces.end();
- I != E; ++I) {
- Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
- I->getReplacementText());
- if (!Replace.apply(Rewrite))
- return "";
- }
- std::string Result;
- llvm::raw_string_ostream OS(Result);
- Rewrite.getEditBuffer(ID).write(OS);
- OS.flush();
- return Result;
- }
- } // end namespace tooling
- } // end namespace clang
|