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/Core/Rewriter.h" 00016 #include "clang/Basic/Diagnostic.h" 00017 #include "clang/Basic/DiagnosticIDs.h" 00018 #include "clang/Basic/FileManager.h" 00019 #include "clang/Basic/SourceManager.h" 00020 #include "clang/Lex/Lexer.h" 00021 #include "llvm/ADT/SmallString.h" 00022 #include "llvm/Config/llvm-config.h" 00023 #include "llvm/Support/FileSystem.h" 00024 #include "llvm/Support/raw_ostream.h" 00025 using namespace clang; 00026 00027 raw_ostream &RewriteBuffer::write(raw_ostream &os) const { 00028 // Walk RewriteRope chunks efficiently using MoveToNextPiece() instead of the 00029 // character iterator. 00030 for (RopePieceBTreeIterator I = begin(), E = end(); I != E; 00031 I.MoveToNextPiece()) 00032 os << I.piece(); 00033 return os; 00034 } 00035 00036 /// \brief Return true if this character is non-new-line whitespace: 00037 /// ' ', '\\t', '\\f', '\\v', '\\r'. 00038 static inline bool isWhitespace(unsigned char c) { 00039 switch (c) { 00040 case ' ': 00041 case '\t': 00042 case '\f': 00043 case '\v': 00044 case '\r': 00045 return true; 00046 default: 00047 return false; 00048 } 00049 } 00050 00051 void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size, 00052 bool removeLineIfEmpty) { 00053 // Nothing to remove, exit early. 00054 if (Size == 0) return; 00055 00056 unsigned RealOffset = getMappedOffset(OrigOffset, true); 00057 assert(RealOffset+Size < Buffer.size() && "Invalid location"); 00058 00059 // Remove the dead characters. 00060 Buffer.erase(RealOffset, Size); 00061 00062 // Add a delta so that future changes are offset correctly. 00063 AddReplaceDelta(OrigOffset, -Size); 00064 00065 if (removeLineIfEmpty) { 00066 // Find the line that the remove occurred and if it is completely empty 00067 // remove the line as well. 00068 00069 iterator curLineStart = begin(); 00070 unsigned curLineStartOffs = 0; 00071 iterator posI = begin(); 00072 for (unsigned i = 0; i != RealOffset; ++i) { 00073 if (*posI == '\n') { 00074 curLineStart = posI; 00075 ++curLineStart; 00076 curLineStartOffs = i + 1; 00077 } 00078 ++posI; 00079 } 00080 00081 unsigned lineSize = 0; 00082 posI = curLineStart; 00083 while (posI != end() && isWhitespace(*posI)) { 00084 ++posI; 00085 ++lineSize; 00086 } 00087 if (posI != end() && *posI == '\n') { 00088 Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/); 00089 AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/)); 00090 } 00091 } 00092 } 00093 00094 void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str, 00095 bool InsertAfter) { 00096 00097 // Nothing to insert, exit early. 00098 if (Str.empty()) return; 00099 00100 unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter); 00101 Buffer.insert(RealOffset, Str.begin(), Str.end()); 00102 00103 // Add a delta so that future changes are offset correctly. 00104 AddInsertDelta(OrigOffset, Str.size()); 00105 } 00106 00107 /// ReplaceText - This method replaces a range of characters in the input 00108 /// buffer with a new string. This is effectively a combined "remove+insert" 00109 /// operation. 00110 void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength, 00111 StringRef NewStr) { 00112 unsigned RealOffset = getMappedOffset(OrigOffset, true); 00113 Buffer.erase(RealOffset, OrigLength); 00114 Buffer.insert(RealOffset, NewStr.begin(), NewStr.end()); 00115 if (OrigLength != NewStr.size()) 00116 AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength); 00117 } 00118 00119 00120 //===----------------------------------------------------------------------===// 00121 // Rewriter class 00122 //===----------------------------------------------------------------------===// 00123 00124 /// getRangeSize - Return the size in bytes of the specified range if they 00125 /// are in the same file. If not, this returns -1. 00126 int Rewriter::getRangeSize(const CharSourceRange &Range, 00127 RewriteOptions opts) const { 00128 if (!isRewritable(Range.getBegin()) || 00129 !isRewritable(Range.getEnd())) return -1; 00130 00131 FileID StartFileID, EndFileID; 00132 unsigned StartOff, EndOff; 00133 00134 StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); 00135 EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); 00136 00137 if (StartFileID != EndFileID) 00138 return -1; 00139 00140 // If edits have been made to this buffer, the delta between the range may 00141 // have changed. 00142 std::map<FileID, RewriteBuffer>::const_iterator I = 00143 RewriteBuffers.find(StartFileID); 00144 if (I != RewriteBuffers.end()) { 00145 const RewriteBuffer &RB = I->second; 00146 EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange); 00147 StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange); 00148 } 00149 00150 00151 // Adjust the end offset to the end of the last token, instead of being the 00152 // start of the last token if this is a token range. 00153 if (Range.isTokenRange()) 00154 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); 00155 00156 return EndOff-StartOff; 00157 } 00158 00159 int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const { 00160 return getRangeSize(CharSourceRange::getTokenRange(Range), opts); 00161 } 00162 00163 00164 /// getRewrittenText - Return the rewritten form of the text in the specified 00165 /// range. If the start or end of the range was unrewritable or if they are 00166 /// in different buffers, this returns an empty string. 00167 /// 00168 /// Note that this method is not particularly efficient. 00169 /// 00170 std::string Rewriter::getRewrittenText(SourceRange Range) const { 00171 if (!isRewritable(Range.getBegin()) || 00172 !isRewritable(Range.getEnd())) 00173 return ""; 00174 00175 FileID StartFileID, EndFileID; 00176 unsigned StartOff, EndOff; 00177 StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); 00178 EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); 00179 00180 if (StartFileID != EndFileID) 00181 return ""; // Start and end in different buffers. 00182 00183 // If edits have been made to this buffer, the delta between the range may 00184 // have changed. 00185 std::map<FileID, RewriteBuffer>::const_iterator I = 00186 RewriteBuffers.find(StartFileID); 00187 if (I == RewriteBuffers.end()) { 00188 // If the buffer hasn't been rewritten, just return the text from the input. 00189 const char *Ptr = SourceMgr->getCharacterData(Range.getBegin()); 00190 00191 // Adjust the end offset to the end of the last token, instead of being the 00192 // start of the last token. 00193 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); 00194 return std::string(Ptr, Ptr+EndOff-StartOff); 00195 } 00196 00197 const RewriteBuffer &RB = I->second; 00198 EndOff = RB.getMappedOffset(EndOff, true); 00199 StartOff = RB.getMappedOffset(StartOff); 00200 00201 // Adjust the end offset to the end of the last token, instead of being the 00202 // start of the last token. 00203 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); 00204 00205 // Advance the iterators to the right spot, yay for linear time algorithms. 00206 RewriteBuffer::iterator Start = RB.begin(); 00207 std::advance(Start, StartOff); 00208 RewriteBuffer::iterator End = Start; 00209 std::advance(End, EndOff-StartOff); 00210 00211 return std::string(Start, End); 00212 } 00213 00214 unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc, 00215 FileID &FID) const { 00216 assert(Loc.isValid() && "Invalid location"); 00217 std::pair<FileID,unsigned> V = SourceMgr->getDecomposedLoc(Loc); 00218 FID = V.first; 00219 return V.second; 00220 } 00221 00222 00223 /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID. 00224 /// 00225 RewriteBuffer &Rewriter::getEditBuffer(FileID FID) { 00226 std::map<FileID, RewriteBuffer>::iterator I = 00227 RewriteBuffers.lower_bound(FID); 00228 if (I != RewriteBuffers.end() && I->first == FID) 00229 return I->second; 00230 I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer())); 00231 00232 StringRef MB = SourceMgr->getBufferData(FID); 00233 I->second.Initialize(MB.begin(), MB.end()); 00234 00235 return I->second; 00236 } 00237 00238 /// InsertText - Insert the specified string at the specified location in the 00239 /// original buffer. 00240 bool Rewriter::InsertText(SourceLocation Loc, StringRef Str, 00241 bool InsertAfter, bool indentNewLines) { 00242 if (!isRewritable(Loc)) return true; 00243 FileID FID; 00244 unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); 00245 00246 SmallString<128> indentedStr; 00247 if (indentNewLines && Str.find('\n') != StringRef::npos) { 00248 StringRef MB = SourceMgr->getBufferData(FID); 00249 00250 unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1; 00251 const SrcMgr::ContentCache * 00252 Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); 00253 unsigned lineOffs = Content->SourceLineCache[lineNo]; 00254 00255 // Find the whitespace at the start of the line. 00256 StringRef indentSpace; 00257 { 00258 unsigned i = lineOffs; 00259 while (isWhitespace(MB[i])) 00260 ++i; 00261 indentSpace = MB.substr(lineOffs, i-lineOffs); 00262 } 00263 00264 SmallVector<StringRef, 4> lines; 00265 Str.split(lines, "\n"); 00266 00267 for (unsigned i = 0, e = lines.size(); i != e; ++i) { 00268 indentedStr += lines[i]; 00269 if (i < e-1) { 00270 indentedStr += '\n'; 00271 indentedStr += indentSpace; 00272 } 00273 } 00274 Str = indentedStr.str(); 00275 } 00276 00277 getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter); 00278 return false; 00279 } 00280 00281 bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) { 00282 if (!isRewritable(Loc)) return true; 00283 FileID FID; 00284 unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); 00285 RewriteOptions rangeOpts; 00286 rangeOpts.IncludeInsertsAtBeginOfRange = false; 00287 StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts); 00288 getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true); 00289 return false; 00290 } 00291 00292 /// RemoveText - Remove the specified text region. 00293 bool Rewriter::RemoveText(SourceLocation Start, unsigned Length, 00294 RewriteOptions opts) { 00295 if (!isRewritable(Start)) return true; 00296 FileID FID; 00297 unsigned StartOffs = getLocationOffsetAndFileID(Start, FID); 00298 getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty); 00299 return false; 00300 } 00301 00302 /// ReplaceText - This method replaces a range of characters in the input 00303 /// buffer with a new string. This is effectively a combined "remove/insert" 00304 /// operation. 00305 bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength, 00306 StringRef NewStr) { 00307 if (!isRewritable(Start)) return true; 00308 FileID StartFileID; 00309 unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID); 00310 00311 getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr); 00312 return false; 00313 } 00314 00315 bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) { 00316 if (!isRewritable(range.getBegin())) return true; 00317 if (!isRewritable(range.getEnd())) return true; 00318 if (replacementRange.isInvalid()) return true; 00319 SourceLocation start = range.getBegin(); 00320 unsigned origLength = getRangeSize(range); 00321 unsigned newLength = getRangeSize(replacementRange); 00322 FileID FID; 00323 unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(), 00324 FID); 00325 StringRef MB = SourceMgr->getBufferData(FID); 00326 return ReplaceText(start, origLength, MB.substr(newOffs, newLength)); 00327 } 00328 00329 bool Rewriter::IncreaseIndentation(CharSourceRange range, 00330 SourceLocation parentIndent) { 00331 if (range.isInvalid()) return true; 00332 if (!isRewritable(range.getBegin())) return true; 00333 if (!isRewritable(range.getEnd())) return true; 00334 if (!isRewritable(parentIndent)) return true; 00335 00336 FileID StartFileID, EndFileID, parentFileID; 00337 unsigned StartOff, EndOff, parentOff; 00338 00339 StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID); 00340 EndOff = getLocationOffsetAndFileID(range.getEnd(), EndFileID); 00341 parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID); 00342 00343 if (StartFileID != EndFileID || StartFileID != parentFileID) 00344 return true; 00345 if (StartOff > EndOff) 00346 return true; 00347 00348 FileID FID = StartFileID; 00349 StringRef MB = SourceMgr->getBufferData(FID); 00350 00351 unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1; 00352 unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1; 00353 unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1; 00354 00355 const SrcMgr::ContentCache * 00356 Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); 00357 00358 // Find where the lines start. 00359 unsigned parentLineOffs = Content->SourceLineCache[parentLineNo]; 00360 unsigned startLineOffs = Content->SourceLineCache[startLineNo]; 00361 00362 // Find the whitespace at the start of each line. 00363 StringRef parentSpace, startSpace; 00364 { 00365 unsigned i = parentLineOffs; 00366 while (isWhitespace(MB[i])) 00367 ++i; 00368 parentSpace = MB.substr(parentLineOffs, i-parentLineOffs); 00369 00370 i = startLineOffs; 00371 while (isWhitespace(MB[i])) 00372 ++i; 00373 startSpace = MB.substr(startLineOffs, i-startLineOffs); 00374 } 00375 if (parentSpace.size() >= startSpace.size()) 00376 return true; 00377 if (!startSpace.startswith(parentSpace)) 00378 return true; 00379 00380 StringRef indent = startSpace.substr(parentSpace.size()); 00381 00382 // Indent the lines between start/end offsets. 00383 RewriteBuffer &RB = getEditBuffer(FID); 00384 for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) { 00385 unsigned offs = Content->SourceLineCache[lineNo]; 00386 unsigned i = offs; 00387 while (isWhitespace(MB[i])) 00388 ++i; 00389 StringRef origIndent = MB.substr(offs, i-offs); 00390 if (origIndent.startswith(startSpace)) 00391 RB.InsertText(offs, indent, /*InsertAfter=*/false); 00392 } 00393 00394 return false; 00395 } 00396 00397 namespace { 00398 // A wrapper for a file stream that atomically overwrites the target. 00399 // 00400 // Creates a file output stream for a temporary file in the constructor, 00401 // which is later accessible via getStream() if ok() return true. 00402 // Flushes the stream and moves the temporary file to the target location 00403 // in the destructor. 00404 class AtomicallyMovedFile { 00405 public: 00406 AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename, 00407 bool &AllWritten) 00408 : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) { 00409 TempFilename = Filename; 00410 TempFilename += "-%%%%%%%%"; 00411 int FD; 00412 if (llvm::sys::fs::createUniqueFile(TempFilename.str(), FD, TempFilename)) { 00413 AllWritten = false; 00414 Diagnostics.Report(clang::diag::err_unable_to_make_temp) 00415 << TempFilename; 00416 } else { 00417 FileStream.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true)); 00418 } 00419 } 00420 00421 ~AtomicallyMovedFile() { 00422 if (!ok()) return; 00423 00424 FileStream->flush(); 00425 #ifdef LLVM_ON_WIN32 00426 // Win32 does not allow rename/removing opened files. 00427 FileStream.reset(); 00428 #endif 00429 if (std::error_code ec = 00430 llvm::sys::fs::rename(TempFilename.str(), Filename)) { 00431 AllWritten = false; 00432 Diagnostics.Report(clang::diag::err_unable_to_rename_temp) 00433 << TempFilename << Filename << ec.message(); 00434 // If the remove fails, there's not a lot we can do - this is already an 00435 // error. 00436 llvm::sys::fs::remove(TempFilename.str()); 00437 } 00438 } 00439 00440 bool ok() { return (bool)FileStream; } 00441 raw_ostream &getStream() { return *FileStream; } 00442 00443 private: 00444 DiagnosticsEngine &Diagnostics; 00445 StringRef Filename; 00446 SmallString<128> TempFilename; 00447 std::unique_ptr<llvm::raw_fd_ostream> FileStream; 00448 bool &AllWritten; 00449 }; 00450 } // end anonymous namespace 00451 00452 bool Rewriter::overwriteChangedFiles() { 00453 bool AllWritten = true; 00454 for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { 00455 const FileEntry *Entry = 00456 getSourceMgr().getFileEntryForID(I->first); 00457 AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(), 00458 AllWritten); 00459 if (File.ok()) { 00460 I->second.write(File.getStream()); 00461 } 00462 } 00463 return !AllWritten; 00464 }