237 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- 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;
 | 
						|
}
 |