clang API Documentation
00001 //===--- Rewriter.cpp - Code rewriting interface --------------------------===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines the Rewriter class, which is used for code 00011 // transformations. 00012 // 00013 //===----------------------------------------------------------------------===// 00014 00015 #include "clang/Rewrite/Rewriter.h" 00016 #include "clang/AST/Stmt.h" 00017 #include "clang/AST/Decl.h" 00018 #include "clang/Lex/Lexer.h" 00019 #include "clang/Basic/SourceManager.h" 00020 #include "llvm/ADT/SmallString.h" 00021 using namespace clang; 00022 00023 raw_ostream &RewriteBuffer::write(raw_ostream &os) const { 00024 // FIXME: eliminate the copy by writing out each chunk at a time 00025 os << std::string(begin(), end()); 00026 return os; 00027 } 00028 00029 /// \brief Return true if this character is non-new-line whitespace: 00030 /// ' ', '\t', '\f', '\v', '\r'. 00031 static inline bool isWhitespace(unsigned char c) { 00032 switch (c) { 00033 case ' ': 00034 case '\t': 00035 case '\f': 00036 case '\v': 00037 case '\r': 00038 return true; 00039 default: 00040 return false; 00041 } 00042 } 00043 00044 void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size, 00045 bool removeLineIfEmpty) { 00046 // Nothing to remove, exit early. 00047 if (Size == 0) return; 00048 00049 unsigned RealOffset = getMappedOffset(OrigOffset, true); 00050 assert(RealOffset+Size < Buffer.size() && "Invalid location"); 00051 00052 // Remove the dead characters. 00053 Buffer.erase(RealOffset, Size); 00054 00055 // Add a delta so that future changes are offset correctly. 00056 AddReplaceDelta(OrigOffset, -Size); 00057 00058 if (removeLineIfEmpty) { 00059 // Find the line that the remove occurred and if it is completely empty 00060 // remove the line as well. 00061 00062 iterator curLineStart = begin(); 00063 unsigned curLineStartOffs = 0; 00064 iterator posI = begin(); 00065 for (unsigned i = 0; i != RealOffset; ++i) { 00066 if (*posI == '\n') { 00067 curLineStart = posI; 00068 ++curLineStart; 00069 curLineStartOffs = i + 1; 00070 } 00071 ++posI; 00072 } 00073 00074 unsigned lineSize = 0; 00075 posI = curLineStart; 00076 while (posI != end() && isWhitespace(*posI)) { 00077 ++posI; 00078 ++lineSize; 00079 } 00080 if (posI != end() && *posI == '\n') { 00081 Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/); 00082 AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/)); 00083 } 00084 } 00085 } 00086 00087 void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str, 00088 bool InsertAfter) { 00089 00090 // Nothing to insert, exit early. 00091 if (Str.empty()) return; 00092 00093 unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter); 00094 Buffer.insert(RealOffset, Str.begin(), Str.end()); 00095 00096 // Add a delta so that future changes are offset correctly. 00097 AddInsertDelta(OrigOffset, Str.size()); 00098 } 00099 00100 /// ReplaceText - This method replaces a range of characters in the input 00101 /// buffer with a new string. This is effectively a combined "remove+insert" 00102 /// operation. 00103 void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, 00104 StringRef NewStr) { 00105 unsigned RealOffset = getMappedOffset(OrigOffset, true); 00106 Buffer.erase(RealOffset, OrigLength); 00107 Buffer.insert(RealOffset, NewStr.begin(), NewStr.end()); 00108 if (OrigLength != NewStr.size()) 00109 AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength); 00110 } 00111 00112 00113 //===----------------------------------------------------------------------===// 00114 // Rewriter class 00115 //===----------------------------------------------------------------------===// 00116 00117 /// getRangeSize - Return the size in bytes of the specified range if they 00118 /// are in the same file. If not, this returns -1. 00119 int Rewriter::getRangeSize(const CharSourceRange &Range, 00120 RewriteOptions opts) const { 00121 if (!isRewritable(Range.getBegin()) || 00122 !isRewritable(Range.getEnd())) return -1; 00123 00124 FileID StartFileID, EndFileID; 00125 unsigned StartOff, EndOff; 00126 00127 StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); 00128 EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); 00129 00130 if (StartFileID != EndFileID) 00131 return -1; 00132 00133 // If edits have been made to this buffer, the delta between the range may 00134 // have changed. 00135 std::map<FileID, RewriteBuffer>::const_iterator I = 00136 RewriteBuffers.find(StartFileID); 00137 if (I != RewriteBuffers.end()) { 00138 const RewriteBuffer &RB = I->second; 00139 EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange); 00140 StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange); 00141 } 00142 00143 00144 // Adjust the end offset to the end of the last token, instead of being the 00145 // start of the last token if this is a token range. 00146 if (Range.isTokenRange()) 00147 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); 00148 00149 return EndOff-StartOff; 00150 } 00151 00152 int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const { 00153 return getRangeSize(CharSourceRange::getTokenRange(Range), opts); 00154 } 00155 00156 00157 /// getRewrittenText - Return the rewritten form of the text in the specified 00158 /// range. If the start or end of the range was unrewritable or if they are 00159 /// in different buffers, this returns an empty string. 00160 /// 00161 /// Note that this method is not particularly efficient. 00162 /// 00163 std::string Rewriter::getRewrittenText(SourceRange Range) const { 00164 if (!isRewritable(Range.getBegin()) || 00165 !isRewritable(Range.getEnd())) 00166 return ""; 00167 00168 FileID StartFileID, EndFileID; 00169 unsigned StartOff, EndOff; 00170 StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); 00171 EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); 00172 00173 if (StartFileID != EndFileID) 00174 return ""; // Start and end in different buffers. 00175 00176 // If edits have been made to this buffer, the delta between the range may 00177 // have changed. 00178 std::map<FileID, RewriteBuffer>::const_iterator I = 00179 RewriteBuffers.find(StartFileID); 00180 if (I == RewriteBuffers.end()) { 00181 // If the buffer hasn't been rewritten, just return the text from the input. 00182 const char *Ptr = SourceMgr->getCharacterData(Range.getBegin()); 00183 00184 // Adjust the end offset to the end of the last token, instead of being the 00185 // start of the last token. 00186 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); 00187 return std::string(Ptr, Ptr+EndOff-StartOff); 00188 } 00189 00190 const RewriteBuffer &RB = I->second; 00191 EndOff = RB.getMappedOffset(EndOff, true); 00192 StartOff = RB.getMappedOffset(StartOff); 00193 00194 // Adjust the end offset to the end of the last token, instead of being the 00195 // start of the last token. 00196 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); 00197 00198 // Advance the iterators to the right spot, yay for linear time algorithms. 00199 RewriteBuffer::iterator Start = RB.begin(); 00200 std::advance(Start, StartOff); 00201 RewriteBuffer::iterator End = Start; 00202 std::advance(End, EndOff-StartOff); 00203 00204 return std::string(Start, End); 00205 } 00206 00207 unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc, 00208 FileID &FID) const { 00209 assert(Loc.isValid() && "Invalid location"); 00210 std::pair<FileID,unsigned> V = SourceMgr->getDecomposedLoc(Loc); 00211 FID = V.first; 00212 return V.second; 00213 } 00214 00215 00216 /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID. 00217 /// 00218 RewriteBuffer &Rewriter::getEditBuffer(FileID FID) { 00219 std::map<FileID, RewriteBuffer>::iterator I = 00220 RewriteBuffers.lower_bound(FID); 00221 if (I != RewriteBuffers.end() && I->first == FID) 00222 return I->second; 00223 I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer())); 00224 00225 StringRef MB = SourceMgr->getBufferData(FID); 00226 I->second.Initialize(MB.begin(), MB.end()); 00227 00228 return I->second; 00229 } 00230 00231 /// InsertText - Insert the specified string at the specified location in the 00232 /// original buffer. 00233 bool Rewriter::InsertText(SourceLocation Loc, StringRef Str, 00234 bool InsertAfter, bool indentNewLines) { 00235 if (!isRewritable(Loc)) return true; 00236 FileID FID; 00237 unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); 00238 00239 SmallString<128> indentedStr; 00240 if (indentNewLines && Str.find('\n') != StringRef::npos) { 00241 StringRef MB = SourceMgr->getBufferData(FID); 00242 00243 unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1; 00244 const SrcMgr::ContentCache * 00245 Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); 00246 unsigned lineOffs = Content->SourceLineCache[lineNo]; 00247 00248 // Find the whitespace at the start of the line. 00249 StringRef indentSpace; 00250 { 00251 unsigned i = lineOffs; 00252 while (isWhitespace(MB[i])) 00253 ++i; 00254 indentSpace = MB.substr(lineOffs, i-lineOffs); 00255 } 00256 00257 SmallVector<StringRef, 4> lines; 00258 Str.split(lines, "\n"); 00259 00260 for (unsigned i = 0, e = lines.size(); i != e; ++i) { 00261 indentedStr += lines[i]; 00262 if (i < e-1) { 00263 indentedStr += '\n'; 00264 indentedStr += indentSpace; 00265 } 00266 } 00267 Str = indentedStr.str(); 00268 } 00269 00270 getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter); 00271 return false; 00272 } 00273 00274 bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) { 00275 if (!isRewritable(Loc)) return true; 00276 FileID FID; 00277 unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); 00278 RewriteOptions rangeOpts; 00279 rangeOpts.IncludeInsertsAtBeginOfRange = false; 00280 StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts); 00281 getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true); 00282 return false; 00283 } 00284 00285 /// RemoveText - Remove the specified text region. 00286 bool Rewriter::RemoveText(SourceLocation Start, unsigned Length, 00287 RewriteOptions opts) { 00288 if (!isRewritable(Start)) return true; 00289 FileID FID; 00290 unsigned StartOffs = getLocationOffsetAndFileID(Start, FID); 00291 getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty); 00292 return false; 00293 } 00294 00295 /// ReplaceText - This method replaces a range of characters in the input 00296 /// buffer with a new string. This is effectively a combined "remove/insert" 00297 /// operation. 00298 bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength, 00299 StringRef NewStr) { 00300 if (!isRewritable(Start)) return true; 00301 FileID StartFileID; 00302 unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID); 00303 00304 getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr); 00305 return false; 00306 } 00307 00308 bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) { 00309 if (!isRewritable(range.getBegin())) return true; 00310 if (!isRewritable(range.getEnd())) return true; 00311 if (replacementRange.isInvalid()) return true; 00312 SourceLocation start = range.getBegin(); 00313 unsigned origLength = getRangeSize(range); 00314 unsigned newLength = getRangeSize(replacementRange); 00315 FileID FID; 00316 unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(), 00317 FID); 00318 StringRef MB = SourceMgr->getBufferData(FID); 00319 return ReplaceText(start, origLength, MB.substr(newOffs, newLength)); 00320 } 00321 00322 /// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty 00323 /// printer to generate the replacement code. This returns true if the input 00324 /// could not be rewritten, or false if successful. 00325 bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) { 00326 // Measaure the old text. 00327 int Size = getRangeSize(From->getSourceRange()); 00328 if (Size == -1) 00329 return true; 00330 00331 // Get the new text. 00332 std::string SStr; 00333 llvm::raw_string_ostream S(SStr); 00334 To->printPretty(S, 0, PrintingPolicy(*LangOpts)); 00335 const std::string &Str = S.str(); 00336 00337 ReplaceText(From->getLocStart(), Size, Str); 00338 return false; 00339 } 00340 00341 std::string Rewriter::ConvertToString(Stmt *From) { 00342 std::string SStr; 00343 llvm::raw_string_ostream S(SStr); 00344 From->printPretty(S, 0, PrintingPolicy(*LangOpts)); 00345 return S.str(); 00346 } 00347 00348 bool Rewriter::IncreaseIndentation(CharSourceRange range, 00349 SourceLocation parentIndent) { 00350 if (range.isInvalid()) return true; 00351 if (!isRewritable(range.getBegin())) return true; 00352 if (!isRewritable(range.getEnd())) return true; 00353 if (!isRewritable(parentIndent)) return true; 00354 00355 FileID StartFileID, EndFileID, parentFileID; 00356 unsigned StartOff, EndOff, parentOff; 00357 00358 StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID); 00359 EndOff = getLocationOffsetAndFileID(range.getEnd(), EndFileID); 00360 parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID); 00361 00362 if (StartFileID != EndFileID || StartFileID != parentFileID) 00363 return true; 00364 if (StartOff > EndOff) 00365 return true; 00366 00367 FileID FID = StartFileID; 00368 StringRef MB = SourceMgr->getBufferData(FID); 00369 00370 unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1; 00371 unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1; 00372 unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1; 00373 00374 const SrcMgr::ContentCache * 00375 Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); 00376 00377 // Find where the lines start. 00378 unsigned parentLineOffs = Content->SourceLineCache[parentLineNo]; 00379 unsigned startLineOffs = Content->SourceLineCache[startLineNo]; 00380 00381 // Find the whitespace at the start of each line. 00382 StringRef parentSpace, startSpace; 00383 { 00384 unsigned i = parentLineOffs; 00385 while (isWhitespace(MB[i])) 00386 ++i; 00387 parentSpace = MB.substr(parentLineOffs, i-parentLineOffs); 00388 00389 i = startLineOffs; 00390 while (isWhitespace(MB[i])) 00391 ++i; 00392 startSpace = MB.substr(startLineOffs, i-startLineOffs); 00393 } 00394 if (parentSpace.size() >= startSpace.size()) 00395 return true; 00396 if (!startSpace.startswith(parentSpace)) 00397 return true; 00398 00399 StringRef indent = startSpace.substr(parentSpace.size()); 00400 00401 // Indent the lines between start/end offsets. 00402 RewriteBuffer &RB = getEditBuffer(FID); 00403 for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) { 00404 unsigned offs = Content->SourceLineCache[lineNo]; 00405 unsigned i = offs; 00406 while (isWhitespace(MB[i])) 00407 ++i; 00408 StringRef origIndent = MB.substr(offs, i-offs); 00409 if (origIndent.startswith(startSpace)) 00410 RB.InsertText(offs, indent, /*InsertAfter=*/false); 00411 } 00412 00413 return false; 00414 }