Rewriter.cpp revision 88ad97f17790b753e2e113b149d7f164e42fa2ba
1//===--- Rewriter.cpp - Code rewriting interface --------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file defines the Rewriter class, which is used for code 11// transformations. 12// 13//===----------------------------------------------------------------------===// 14 15#include "clang/Rewrite/Rewriter.h" 16#include "clang/AST/Stmt.h" 17#include "clang/Lex/Lexer.h" 18#include "clang/Basic/SourceManager.h" 19#include <sstream> 20using namespace clang; 21 22/// getMappedOffset - Given an offset into the original SourceBuffer that this 23/// RewriteBuffer is based on, map it into the offset space of the 24/// RewriteBuffer. 25unsigned RewriteBuffer::getMappedOffset(unsigned OrigOffset, 26 bool AfterInserts) const { 27 unsigned ResultOffset = OrigOffset; 28 unsigned DeltaIdx = 0; 29 30 // Move past any deltas that are relevant. 31 // FIXME: binary search. 32 for (; DeltaIdx != Deltas.size() && 33 Deltas[DeltaIdx].FileLoc < OrigOffset; ++DeltaIdx) 34 ResultOffset += Deltas[DeltaIdx].Delta; 35 36 if (AfterInserts) 37 for (; DeltaIdx != Deltas.size() && 38 OrigOffset == Deltas[DeltaIdx].FileLoc; ++DeltaIdx) 39 ResultOffset += Deltas[DeltaIdx].Delta; 40 41 return ResultOffset; 42} 43 44/// AddDelta - When a change is made that shifts around the text buffer, this 45/// method is used to record that info. 46void RewriteBuffer::AddDelta(unsigned OrigOffset, int Change) { 47 assert(Change != 0 && "Not changing anything"); 48 unsigned DeltaIdx = 0; 49 50 // Skip over any unrelated deltas. 51 for (; DeltaIdx != Deltas.size() && 52 Deltas[DeltaIdx].FileLoc < OrigOffset; ++DeltaIdx) 53 ; 54 55 // If there is no a delta for this offset, insert a new delta record. 56 if (DeltaIdx == Deltas.size() || OrigOffset != Deltas[DeltaIdx].FileLoc) { 57 // If this is a removal, check to see if this can be folded into 58 // a delta at the end of the deletion. For example, if we have: 59 // ABCXDEF (X inserted after C) and delete C, we want to end up with no 60 // delta because X basically replaced C. 61 if (Change < 0 && DeltaIdx != Deltas.size() && 62 OrigOffset-Change == Deltas[DeltaIdx].FileLoc) { 63 // Adjust the start of the delta to be the start of the deleted region. 64 Deltas[DeltaIdx].FileLoc += Change; 65 Deltas[DeltaIdx].Delta += Change; 66 67 // If the delta becomes a noop, remove it. 68 if (Deltas[DeltaIdx].Delta == 0) 69 Deltas.erase(Deltas.begin()+DeltaIdx); 70 return; 71 } 72 73 // Otherwise, create an entry and return. 74 Deltas.insert(Deltas.begin()+DeltaIdx, 75 SourceDelta::get(OrigOffset, Change)); 76 return; 77 } 78 79 // Otherwise, we found a delta record at this offset, adjust it. 80 Deltas[DeltaIdx].Delta += Change; 81 82 // If it is now dead, remove it. 83 if (Deltas[DeltaIdx].Delta == 0) 84 Deltas.erase(Deltas.begin()+DeltaIdx); 85} 86 87 88void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size) { 89 // Nothing to remove, exit early. 90 if (Size == 0) return; 91 92 unsigned RealOffset = getMappedOffset(OrigOffset, true); 93 assert(RealOffset+Size < Buffer.size() && "Invalid location"); 94 95 // Remove the dead characters. 96 RewriteRope::iterator I = Buffer.getAtOffset(RealOffset); 97 Buffer.erase(I, I+Size); 98 99 // Add a delta so that future changes are offset correctly. 100 AddDelta(OrigOffset, -Size); 101} 102 103void RewriteBuffer::InsertText(unsigned OrigOffset, 104 const char *StrData, unsigned StrLen) { 105 // Nothing to insert, exit early. 106 if (StrLen == 0) return; 107 108 unsigned RealOffset = getMappedOffset(OrigOffset, true); 109 assert(RealOffset <= Buffer.size() && "Invalid location"); 110 111 // Insert the new characters. 112 Buffer.insert(Buffer.getAtOffset(RealOffset), StrData, StrData+StrLen); 113 114 // Add a delta so that future changes are offset correctly. 115 AddDelta(OrigOffset, StrLen); 116} 117 118/// ReplaceText - This method replaces a range of characters in the input 119/// buffer with a new string. This is effectively a combined "remove/insert" 120/// operation. 121void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, 122 const char *NewStr, unsigned NewLength) { 123 unsigned RealOffset = getMappedOffset(OrigOffset, true); 124 assert(RealOffset+OrigLength <= Buffer.size() && "Invalid location"); 125 126 // Overwrite the common piece. 127 unsigned CommonLength = std::min(OrigLength, NewLength); 128 std::copy(NewStr, NewStr+CommonLength, Buffer.getAtOffset(RealOffset)); 129 130 // If replacing without shifting around, just overwrite the text. 131 if (OrigLength == NewLength) 132 return; 133 134 // If inserting more than existed before, this is like an insertion. 135 if (NewLength > OrigLength) { 136 Buffer.insert(Buffer.getAtOffset(RealOffset+OrigLength), 137 NewStr+OrigLength, NewStr+NewLength); 138 } else { 139 // If inserting less than existed before, this is like a removal. 140 RewriteRope::iterator I = Buffer.getAtOffset(RealOffset+NewLength); 141 Buffer.erase(I, I+(OrigLength-NewLength)); 142 } 143 AddDelta(OrigOffset, NewLength-OrigLength); 144} 145 146 147//===----------------------------------------------------------------------===// 148// Rewriter class 149//===----------------------------------------------------------------------===// 150 151/// getRangeSize - Return the size in bytes of the specified range if they 152/// are in the same file. If not, this returns -1. 153int Rewriter::getRangeSize(SourceRange Range) const { 154 if (!isRewritable(Range.getBegin()) || 155 !isRewritable(Range.getEnd())) return -1; 156 157 unsigned StartOff, StartFileID; 158 unsigned EndOff , EndFileID; 159 160 StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); 161 EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); 162 163 if (StartFileID != EndFileID) 164 return -1; 165 166 // If edits have been made to this buffer, the delta between the range may 167 // have changed. 168 std::map<unsigned, RewriteBuffer>::const_iterator I = 169 RewriteBuffers.find(StartFileID); 170 if (I != RewriteBuffers.end()) { 171 const RewriteBuffer &RB = I->second; 172 EndOff = RB.getMappedOffset(EndOff, true); 173 StartOff = RB.getMappedOffset(StartOff); 174 } 175 176 177 // Adjust the end offset to the end of the last token, instead of being the 178 // start of the last token. 179 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr); 180 181 return EndOff-StartOff; 182} 183 184 185unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc, 186 unsigned &FileID) const { 187 std::pair<unsigned,unsigned> V = SourceMgr->getDecomposedFileLoc(Loc); 188 FileID = V.first; 189 return V.second; 190} 191 192 193/// getEditBuffer - Get or create a RewriteBuffer for the specified FileID. 194/// 195RewriteBuffer &Rewriter::getEditBuffer(unsigned FileID) { 196 std::map<unsigned, RewriteBuffer>::iterator I = 197 RewriteBuffers.lower_bound(FileID); 198 if (I != RewriteBuffers.end() && I->first == FileID) 199 return I->second; 200 I = RewriteBuffers.insert(I, std::make_pair(FileID, RewriteBuffer())); 201 202 std::pair<const char*, const char*> MB = SourceMgr->getBufferData(FileID); 203 I->second.Initialize(MB.first, MB.second); 204 205 return I->second; 206} 207 208/// InsertText - Insert the specified string at the specified location in the 209/// original buffer. 210bool Rewriter::InsertText(SourceLocation Loc, 211 const char *StrData, unsigned StrLen) { 212 if (!isRewritable(Loc)) return true; 213 unsigned FileID; 214 unsigned StartOffs = getLocationOffsetAndFileID(Loc, FileID); 215 getEditBuffer(FileID).InsertText(StartOffs, StrData, StrLen); 216 return false; 217} 218 219/// RemoveText - Remove the specified text region. 220bool Rewriter::RemoveText(SourceLocation Start, unsigned Length) { 221 if (!isRewritable(Start)) return true; 222 unsigned FileID; 223 unsigned StartOffs = getLocationOffsetAndFileID(Start, FileID); 224 getEditBuffer(FileID).RemoveText(StartOffs, Length); 225 return false; 226} 227 228/// ReplaceText - This method replaces a range of characters in the input 229/// buffer with a new string. This is effectively a combined "remove/insert" 230/// operation. 231bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength, 232 const char *NewStr, unsigned NewLength) { 233 if (!isRewritable(Start)) return true; 234 unsigned StartFileID; 235 unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID); 236 237 getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, 238 NewStr, NewLength); 239 return false; 240} 241 242/// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty 243/// printer to generate the replacement code. This returns true if the input 244/// could not be rewritten, or false if successful. 245bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) { 246 // Measaure the old text. 247 int Size = getRangeSize(From->getSourceRange()); 248 if (Size == -1) 249 return true; 250 251 // Get the new text. 252 std::ostringstream S; 253 To->printPretty(S); 254 const std::string &Str = S.str(); 255 256 ReplaceText(From->getLocStart(), Size, &Str[0], Str.size()); 257 return false; 258} 259 260 261