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/AST/Decl.h" 18 #include "clang/Lex/Lexer.h" 19 #include "clang/Basic/SourceManager.h" 20 #include "llvm/Support/raw_ostream.h" 21 using namespace clang; 22 23 void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size) { 24 // Nothing to remove, exit early. 25 if (Size == 0) return; 26 27 unsigned RealOffset = getMappedOffset(OrigOffset, true); 28 assert(RealOffset+Size < Buffer.size() && "Invalid location"); 29 30 // Remove the dead characters. 31 Buffer.erase(RealOffset, Size); 32 33 // Add a delta so that future changes are offset correctly. 34 AddReplaceDelta(OrigOffset, -Size); 35 } 36 37 void RewriteBuffer::InsertText(unsigned OrigOffset, 38 const char *StrData, unsigned StrLen, 39 bool InsertAfter) { 40 41 // Nothing to insert, exit early. 42 if (StrLen == 0) return; 43 44 unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter); 45 Buffer.insert(RealOffset, StrData, StrData+StrLen); 46 47 // Add a delta so that future changes are offset correctly. 48 AddInsertDelta(OrigOffset, StrLen); 49 } 50 51 /// ReplaceText - This method replaces a range of characters in the input 52 /// buffer with a new string. This is effectively a combined "remove+insert" 53 /// operation. 54 void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, 55 const char *NewStr, unsigned NewLength) { 56 unsigned RealOffset = getMappedOffset(OrigOffset, true); 57 Buffer.erase(RealOffset, OrigLength); 58 Buffer.insert(RealOffset, NewStr, NewStr+NewLength); 59 if (OrigLength != NewLength) 60 AddReplaceDelta(OrigOffset, NewLength-OrigLength); 61 } 62 63 64 //===----------------------------------------------------------------------===// 65 // Rewriter class 66 //===----------------------------------------------------------------------===// 67 68 /// getRangeSize - Return the size in bytes of the specified range if they 69 /// are in the same file. If not, this returns -1. 70 int Rewriter::getRangeSize(SourceRange Range) const { 71 if (!isRewritable(Range.getBegin()) || 72 !isRewritable(Range.getEnd())) return -1; 73 74 FileID StartFileID, EndFileID; 75 unsigned StartOff, EndOff; 76 77 StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); 78 EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); 79 80 if (StartFileID != EndFileID) 81 return -1; 82 83 // If edits have been made to this buffer, the delta between the range may 84 // have changed. 85 std::map<FileID, RewriteBuffer>::const_iterator I = 86 RewriteBuffers.find(StartFileID); 87 if (I != RewriteBuffers.end()) { 88 const RewriteBuffer &RB = I->second; 89 EndOff = RB.getMappedOffset(EndOff, true); 90 StartOff = RB.getMappedOffset(StartOff); 91 } 92 93 94 // Adjust the end offset to the end of the last token, instead of being the 95 // start of the last token. 96 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); 97 98 return EndOff-StartOff; 99 } 100 101 /// getRewritenText - Return the rewritten form of the text in the specified 102 /// range. If the start or end of the range was unrewritable or if they are 103 /// in different buffers, this returns an empty string. 104 /// 105 /// Note that this method is not particularly efficient. 106 /// 107 std::string Rewriter::getRewritenText(SourceRange Range) const { 108 if (!isRewritable(Range.getBegin()) || 109 !isRewritable(Range.getEnd())) 110 return ""; 111 112 FileID StartFileID, EndFileID; 113 unsigned StartOff, EndOff; 114 StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); 115 EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); 116 117 if (StartFileID != EndFileID) 118 return ""; // Start and end in different buffers. 119 120 // If edits have been made to this buffer, the delta between the range may 121 // have changed. 122 std::map<FileID, RewriteBuffer>::const_iterator I = 123 RewriteBuffers.find(StartFileID); 124 if (I == RewriteBuffers.end()) { 125 // If the buffer hasn't been rewritten, just return the text from the input. 126 const char *Ptr = SourceMgr->getCharacterData(Range.getBegin()); 127 128 // Adjust the end offset to the end of the last token, instead of being the 129 // start of the last token. 130 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); 131 return std::string(Ptr, Ptr+EndOff-StartOff); 132 } 133 134 const RewriteBuffer &RB = I->second; 135 EndOff = RB.getMappedOffset(EndOff, true); 136 StartOff = RB.getMappedOffset(StartOff); 137 138 // Adjust the end offset to the end of the last token, instead of being the 139 // start of the last token. 140 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); 141 142 // Advance the iterators to the right spot, yay for linear time algorithms. 143 RewriteBuffer::iterator Start = RB.begin(); 144 std::advance(Start, StartOff); 145 RewriteBuffer::iterator End = Start; 146 std::advance(End, EndOff-StartOff); 147 148 return std::string(Start, End); 149 } 150 151 unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc, 152 FileID &FID) const { 153 assert(Loc.isValid() && "Invalid location"); 154 std::pair<FileID,unsigned> V = SourceMgr->getDecomposedLoc(Loc); 155 FID = V.first; 156 return V.second; 157 } 158 159 160 /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID. 161 /// 162 RewriteBuffer &Rewriter::getEditBuffer(FileID FID) { 163 std::map<FileID, RewriteBuffer>::iterator I = 164 RewriteBuffers.lower_bound(FID); 165 if (I != RewriteBuffers.end() && I->first == FID) 166 return I->second; 167 I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer())); 168 169 std::pair<const char*, const char*> MB = SourceMgr->getBufferData(FID); 170 I->second.Initialize(MB.first, MB.second); 171 172 return I->second; 173 } 174 175 /// InsertText - Insert the specified string at the specified location in the 176 /// original buffer. 177 bool Rewriter::InsertText(SourceLocation Loc, const char *StrData, 178 unsigned StrLen, bool InsertAfter) { 179 if (!isRewritable(Loc)) return true; 180 FileID FID; 181 unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); 182 getEditBuffer(FID).InsertText(StartOffs, StrData, StrLen, InsertAfter); 183 return false; 184 } 185 186 /// RemoveText - Remove the specified text region. 187 bool Rewriter::RemoveText(SourceLocation Start, unsigned Length) { 188 if (!isRewritable(Start)) return true; 189 FileID FID; 190 unsigned StartOffs = getLocationOffsetAndFileID(Start, FID); 191 getEditBuffer(FID).RemoveText(StartOffs, Length); 192 return false; 193 } 194 195 /// ReplaceText - This method replaces a range of characters in the input 196 /// buffer with a new string. This is effectively a combined "remove/insert" 197 /// operation. 198 bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength, 199 const char *NewStr, unsigned NewLength) { 200 if (!isRewritable(Start)) return true; 201 FileID StartFileID; 202 unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID); 203 204 getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, 205 NewStr, NewLength); 206 return false; 207 } 208 209 /// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty 210 /// printer to generate the replacement code. This returns true if the input 211 /// could not be rewritten, or false if successful. 212 bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) { 213 // Measaure the old text. 214 int Size = getRangeSize(From->getSourceRange()); 215 if (Size == -1) 216 return true; 217 218 // Get the new text. 219 std::string SStr; 220 llvm::raw_string_ostream S(SStr); 221 To->printPretty(S); 222 const std::string &Str = S.str(); 223 224 ReplaceText(From->getLocStart(), Size, &Str[0], Str.size()); 225 return false; 226 } 227 228 229