| //===--- Rewriter.cpp - Code rewriting interface --------------------------===// | 
 | // | 
 | //                     The LLVM Compiler Infrastructure | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | // | 
 | //  This file defines the Rewriter class, which is used for code | 
 | //  transformations. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "clang/Rewrite/Rewriter.h" | 
 | #include "clang/AST/Stmt.h" | 
 | #include "clang/AST/Decl.h" | 
 | #include "clang/Lex/Lexer.h" | 
 | #include "clang/Basic/SourceManager.h" | 
 | #include "llvm/Support/raw_ostream.h" | 
 | using namespace clang; | 
 |  | 
 | llvm::raw_ostream &RewriteBuffer::write(llvm::raw_ostream &os) const { | 
 |   // FIXME: eliminate the copy by writing out each chunk at a time | 
 |   os << std::string(begin(), end()); | 
 |   return os; | 
 | } | 
 |  | 
 | void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size) { | 
 |   // Nothing to remove, exit early. | 
 |   if (Size == 0) return; | 
 |  | 
 |   unsigned RealOffset = getMappedOffset(OrigOffset, true); | 
 |   assert(RealOffset+Size < Buffer.size() && "Invalid location"); | 
 |  | 
 |   // Remove the dead characters. | 
 |   Buffer.erase(RealOffset, Size); | 
 |  | 
 |   // Add a delta so that future changes are offset correctly. | 
 |   AddReplaceDelta(OrigOffset, -Size); | 
 | } | 
 |  | 
 | void RewriteBuffer::InsertText(unsigned OrigOffset, llvm::StringRef Str, | 
 |                                bool InsertAfter) { | 
 |  | 
 |   // Nothing to insert, exit early. | 
 |   if (Str.empty()) return; | 
 |  | 
 |   unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter); | 
 |   Buffer.insert(RealOffset, Str.begin(), Str.end()); | 
 |  | 
 |   // Add a delta so that future changes are offset correctly. | 
 |   AddInsertDelta(OrigOffset, Str.size()); | 
 | } | 
 |  | 
 | /// ReplaceText - This method replaces a range of characters in the input | 
 | /// buffer with a new string.  This is effectively a combined "remove+insert" | 
 | /// operation. | 
 | void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, | 
 |                                 llvm::StringRef NewStr) { | 
 |   unsigned RealOffset = getMappedOffset(OrigOffset, true); | 
 |   Buffer.erase(RealOffset, OrigLength); | 
 |   Buffer.insert(RealOffset, NewStr.begin(), NewStr.end()); | 
 |   if (OrigLength != NewStr.size()) | 
 |     AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength); | 
 | } | 
 |  | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // Rewriter class | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | /// getRangeSize - Return the size in bytes of the specified range if they | 
 | /// are in the same file.  If not, this returns -1. | 
 | int Rewriter::getRangeSize(const CharSourceRange &Range) const { | 
 |   if (!isRewritable(Range.getBegin()) || | 
 |       !isRewritable(Range.getEnd())) return -1; | 
 |  | 
 |   FileID StartFileID, EndFileID; | 
 |   unsigned StartOff, EndOff; | 
 |  | 
 |   StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); | 
 |   EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); | 
 |  | 
 |   if (StartFileID != EndFileID) | 
 |     return -1; | 
 |  | 
 |   // If edits have been made to this buffer, the delta between the range may | 
 |   // have changed. | 
 |   std::map<FileID, RewriteBuffer>::const_iterator I = | 
 |     RewriteBuffers.find(StartFileID); | 
 |   if (I != RewriteBuffers.end()) { | 
 |     const RewriteBuffer &RB = I->second; | 
 |     EndOff = RB.getMappedOffset(EndOff, true); | 
 |     StartOff = RB.getMappedOffset(StartOff); | 
 |   } | 
 |  | 
 |  | 
 |   // Adjust the end offset to the end of the last token, instead of being the | 
 |   // start of the last token if this is a token range. | 
 |   if (Range.isTokenRange()) | 
 |     EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); | 
 |  | 
 |   return EndOff-StartOff; | 
 | } | 
 |  | 
 | int Rewriter::getRangeSize(SourceRange Range) const { | 
 |   return getRangeSize(CharSourceRange::getTokenRange(Range)); | 
 | } | 
 |  | 
 |  | 
 | /// getRewrittenText - Return the rewritten form of the text in the specified | 
 | /// range.  If the start or end of the range was unrewritable or if they are | 
 | /// in different buffers, this returns an empty string. | 
 | /// | 
 | /// Note that this method is not particularly efficient. | 
 | /// | 
 | std::string Rewriter::getRewrittenText(SourceRange Range) const { | 
 |   if (!isRewritable(Range.getBegin()) || | 
 |       !isRewritable(Range.getEnd())) | 
 |     return ""; | 
 |  | 
 |   FileID StartFileID, EndFileID; | 
 |   unsigned StartOff, EndOff; | 
 |   StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); | 
 |   EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); | 
 |  | 
 |   if (StartFileID != EndFileID) | 
 |     return ""; // Start and end in different buffers. | 
 |  | 
 |   // If edits have been made to this buffer, the delta between the range may | 
 |   // have changed. | 
 |   std::map<FileID, RewriteBuffer>::const_iterator I = | 
 |     RewriteBuffers.find(StartFileID); | 
 |   if (I == RewriteBuffers.end()) { | 
 |     // If the buffer hasn't been rewritten, just return the text from the input. | 
 |     const char *Ptr = SourceMgr->getCharacterData(Range.getBegin()); | 
 |  | 
 |     // Adjust the end offset to the end of the last token, instead of being the | 
 |     // start of the last token. | 
 |     EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); | 
 |     return std::string(Ptr, Ptr+EndOff-StartOff); | 
 |   } | 
 |  | 
 |   const RewriteBuffer &RB = I->second; | 
 |   EndOff = RB.getMappedOffset(EndOff, true); | 
 |   StartOff = RB.getMappedOffset(StartOff); | 
 |  | 
 |   // Adjust the end offset to the end of the last token, instead of being the | 
 |   // start of the last token. | 
 |   EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); | 
 |  | 
 |   // Advance the iterators to the right spot, yay for linear time algorithms. | 
 |   RewriteBuffer::iterator Start = RB.begin(); | 
 |   std::advance(Start, StartOff); | 
 |   RewriteBuffer::iterator End = Start; | 
 |   std::advance(End, EndOff-StartOff); | 
 |  | 
 |   return std::string(Start, End); | 
 | } | 
 |  | 
 | unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc, | 
 |                                               FileID &FID) const { | 
 |   assert(Loc.isValid() && "Invalid location"); | 
 |   std::pair<FileID,unsigned> V = SourceMgr->getDecomposedLoc(Loc); | 
 |   FID = V.first; | 
 |   return V.second; | 
 | } | 
 |  | 
 |  | 
 | /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID. | 
 | /// | 
 | RewriteBuffer &Rewriter::getEditBuffer(FileID FID) { | 
 |   std::map<FileID, RewriteBuffer>::iterator I = | 
 |     RewriteBuffers.lower_bound(FID); | 
 |   if (I != RewriteBuffers.end() && I->first == FID) | 
 |     return I->second; | 
 |   I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer())); | 
 |  | 
 |   llvm::StringRef MB = SourceMgr->getBufferData(FID); | 
 |   I->second.Initialize(MB.begin(), MB.end()); | 
 |  | 
 |   return I->second; | 
 | } | 
 |  | 
 | /// InsertText - Insert the specified string at the specified location in the | 
 | /// original buffer. | 
 | bool Rewriter::InsertText(SourceLocation Loc, llvm::StringRef Str, | 
 |                           bool InsertAfter) { | 
 |   if (!isRewritable(Loc)) return true; | 
 |   FileID FID; | 
 |   unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); | 
 |   getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter); | 
 |   return false; | 
 | } | 
 |  | 
 | /// RemoveText - Remove the specified text region. | 
 | bool Rewriter::RemoveText(SourceLocation Start, unsigned Length) { | 
 |   if (!isRewritable(Start)) return true; | 
 |   FileID FID; | 
 |   unsigned StartOffs = getLocationOffsetAndFileID(Start, FID); | 
 |   getEditBuffer(FID).RemoveText(StartOffs, Length); | 
 |   return false; | 
 | } | 
 |  | 
 | /// ReplaceText - This method replaces a range of characters in the input | 
 | /// buffer with a new string.  This is effectively a combined "remove/insert" | 
 | /// operation. | 
 | bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength, | 
 |                            llvm::StringRef NewStr) { | 
 |   if (!isRewritable(Start)) return true; | 
 |   FileID StartFileID; | 
 |   unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID); | 
 |  | 
 |   getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr); | 
 |   return false; | 
 | } | 
 |  | 
 | /// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty | 
 | /// printer to generate the replacement code.  This returns true if the input | 
 | /// could not be rewritten, or false if successful. | 
 | bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) { | 
 |   // Measaure the old text. | 
 |   int Size = getRangeSize(From->getSourceRange()); | 
 |   if (Size == -1) | 
 |     return true; | 
 |  | 
 |   // Get the new text. | 
 |   std::string SStr; | 
 |   llvm::raw_string_ostream S(SStr); | 
 |   To->printPretty(S, 0, PrintingPolicy(*LangOpts)); | 
 |   const std::string &Str = S.str(); | 
 |  | 
 |   ReplaceText(From->getLocStart(), Size, Str); | 
 |   return false; | 
 | } |