Home | History | Annotate | Download | only in Core
      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/Core/Rewriter.h"
     16 #include "clang/AST/Decl.h"
     17 #include "clang/AST/PrettyPrinter.h"
     18 #include "clang/AST/Stmt.h"
     19 #include "clang/Basic/DiagnosticIDs.h"
     20 #include "clang/Basic/FileManager.h"
     21 #include "clang/Basic/SourceManager.h"
     22 #include "clang/Lex/Lexer.h"
     23 #include "llvm/ADT/SmallString.h"
     24 #include "llvm/Support/FileSystem.h"
     25 #include "llvm/Support/raw_ostream.h"
     26 using namespace clang;
     27 
     28 raw_ostream &RewriteBuffer::write(raw_ostream &os) const {
     29   // FIXME: eliminate the copy by writing out each chunk at a time
     30   os << std::string(begin(), end());
     31   return os;
     32 }
     33 
     34 /// \brief Return true if this character is non-new-line whitespace:
     35 /// ' ', '\\t', '\\f', '\\v', '\\r'.
     36 static inline bool isWhitespace(unsigned char c) {
     37   switch (c) {
     38   case ' ':
     39   case '\t':
     40   case '\f':
     41   case '\v':
     42   case '\r':
     43     return true;
     44   default:
     45     return false;
     46   }
     47 }
     48 
     49 void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size,
     50                                bool removeLineIfEmpty) {
     51   // Nothing to remove, exit early.
     52   if (Size == 0) return;
     53 
     54   unsigned RealOffset = getMappedOffset(OrigOffset, true);
     55   assert(RealOffset+Size < Buffer.size() && "Invalid location");
     56 
     57   // Remove the dead characters.
     58   Buffer.erase(RealOffset, Size);
     59 
     60   // Add a delta so that future changes are offset correctly.
     61   AddReplaceDelta(OrigOffset, -Size);
     62 
     63   if (removeLineIfEmpty) {
     64     // Find the line that the remove occurred and if it is completely empty
     65     // remove the line as well.
     66 
     67     iterator curLineStart = begin();
     68     unsigned curLineStartOffs = 0;
     69     iterator posI = begin();
     70     for (unsigned i = 0; i != RealOffset; ++i) {
     71       if (*posI == '\n') {
     72         curLineStart = posI;
     73         ++curLineStart;
     74         curLineStartOffs = i + 1;
     75       }
     76       ++posI;
     77     }
     78 
     79     unsigned lineSize = 0;
     80     posI = curLineStart;
     81     while (posI != end() && isWhitespace(*posI)) {
     82       ++posI;
     83       ++lineSize;
     84     }
     85     if (posI != end() && *posI == '\n') {
     86       Buffer.erase(curLineStartOffs, lineSize + 1/* + '\n'*/);
     87       AddReplaceDelta(curLineStartOffs, -(lineSize + 1/* + '\n'*/));
     88     }
     89   }
     90 }
     91 
     92 void RewriteBuffer::InsertText(unsigned OrigOffset, StringRef Str,
     93                                bool InsertAfter) {
     94 
     95   // Nothing to insert, exit early.
     96   if (Str.empty()) return;
     97 
     98   unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter);
     99   Buffer.insert(RealOffset, Str.begin(), Str.end());
    100 
    101   // Add a delta so that future changes are offset correctly.
    102   AddInsertDelta(OrigOffset, Str.size());
    103 }
    104 
    105 /// ReplaceText - This method replaces a range of characters in the input
    106 /// buffer with a new string.  This is effectively a combined "remove+insert"
    107 /// operation.
    108 void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
    109                                 StringRef NewStr) {
    110   unsigned RealOffset = getMappedOffset(OrigOffset, true);
    111   Buffer.erase(RealOffset, OrigLength);
    112   Buffer.insert(RealOffset, NewStr.begin(), NewStr.end());
    113   if (OrigLength != NewStr.size())
    114     AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength);
    115 }
    116 
    117 
    118 //===----------------------------------------------------------------------===//
    119 // Rewriter class
    120 //===----------------------------------------------------------------------===//
    121 
    122 /// getRangeSize - Return the size in bytes of the specified range if they
    123 /// are in the same file.  If not, this returns -1.
    124 int Rewriter::getRangeSize(const CharSourceRange &Range,
    125                            RewriteOptions opts) const {
    126   if (!isRewritable(Range.getBegin()) ||
    127       !isRewritable(Range.getEnd())) return -1;
    128 
    129   FileID StartFileID, EndFileID;
    130   unsigned StartOff, EndOff;
    131 
    132   StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
    133   EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
    134 
    135   if (StartFileID != EndFileID)
    136     return -1;
    137 
    138   // If edits have been made to this buffer, the delta between the range may
    139   // have changed.
    140   std::map<FileID, RewriteBuffer>::const_iterator I =
    141     RewriteBuffers.find(StartFileID);
    142   if (I != RewriteBuffers.end()) {
    143     const RewriteBuffer &RB = I->second;
    144     EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange);
    145     StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange);
    146   }
    147 
    148 
    149   // Adjust the end offset to the end of the last token, instead of being the
    150   // start of the last token if this is a token range.
    151   if (Range.isTokenRange())
    152     EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
    153 
    154   return EndOff-StartOff;
    155 }
    156 
    157 int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const {
    158   return getRangeSize(CharSourceRange::getTokenRange(Range), opts);
    159 }
    160 
    161 
    162 /// getRewrittenText - Return the rewritten form of the text in the specified
    163 /// range.  If the start or end of the range was unrewritable or if they are
    164 /// in different buffers, this returns an empty string.
    165 ///
    166 /// Note that this method is not particularly efficient.
    167 ///
    168 std::string Rewriter::getRewrittenText(SourceRange Range) const {
    169   if (!isRewritable(Range.getBegin()) ||
    170       !isRewritable(Range.getEnd()))
    171     return "";
    172 
    173   FileID StartFileID, EndFileID;
    174   unsigned StartOff, EndOff;
    175   StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
    176   EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
    177 
    178   if (StartFileID != EndFileID)
    179     return ""; // Start and end in different buffers.
    180 
    181   // If edits have been made to this buffer, the delta between the range may
    182   // have changed.
    183   std::map<FileID, RewriteBuffer>::const_iterator I =
    184     RewriteBuffers.find(StartFileID);
    185   if (I == RewriteBuffers.end()) {
    186     // If the buffer hasn't been rewritten, just return the text from the input.
    187     const char *Ptr = SourceMgr->getCharacterData(Range.getBegin());
    188 
    189     // Adjust the end offset to the end of the last token, instead of being the
    190     // start of the last token.
    191     EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
    192     return std::string(Ptr, Ptr+EndOff-StartOff);
    193   }
    194 
    195   const RewriteBuffer &RB = I->second;
    196   EndOff = RB.getMappedOffset(EndOff, true);
    197   StartOff = RB.getMappedOffset(StartOff);
    198 
    199   // Adjust the end offset to the end of the last token, instead of being the
    200   // start of the last token.
    201   EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
    202 
    203   // Advance the iterators to the right spot, yay for linear time algorithms.
    204   RewriteBuffer::iterator Start = RB.begin();
    205   std::advance(Start, StartOff);
    206   RewriteBuffer::iterator End = Start;
    207   std::advance(End, EndOff-StartOff);
    208 
    209   return std::string(Start, End);
    210 }
    211 
    212 unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
    213                                               FileID &FID) const {
    214   assert(Loc.isValid() && "Invalid location");
    215   std::pair<FileID,unsigned> V = SourceMgr->getDecomposedLoc(Loc);
    216   FID = V.first;
    217   return V.second;
    218 }
    219 
    220 
    221 /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
    222 ///
    223 RewriteBuffer &Rewriter::getEditBuffer(FileID FID) {
    224   std::map<FileID, RewriteBuffer>::iterator I =
    225     RewriteBuffers.lower_bound(FID);
    226   if (I != RewriteBuffers.end() && I->first == FID)
    227     return I->second;
    228   I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));
    229 
    230   StringRef MB = SourceMgr->getBufferData(FID);
    231   I->second.Initialize(MB.begin(), MB.end());
    232 
    233   return I->second;
    234 }
    235 
    236 /// InsertText - Insert the specified string at the specified location in the
    237 /// original buffer.
    238 bool Rewriter::InsertText(SourceLocation Loc, StringRef Str,
    239                           bool InsertAfter, bool indentNewLines) {
    240   if (!isRewritable(Loc)) return true;
    241   FileID FID;
    242   unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
    243 
    244   SmallString<128> indentedStr;
    245   if (indentNewLines && Str.find('\n') != StringRef::npos) {
    246     StringRef MB = SourceMgr->getBufferData(FID);
    247 
    248     unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1;
    249     const SrcMgr::ContentCache *
    250         Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache();
    251     unsigned lineOffs = Content->SourceLineCache[lineNo];
    252 
    253     // Find the whitespace at the start of the line.
    254     StringRef indentSpace;
    255     {
    256       unsigned i = lineOffs;
    257       while (isWhitespace(MB[i]))
    258         ++i;
    259       indentSpace = MB.substr(lineOffs, i-lineOffs);
    260     }
    261 
    262     SmallVector<StringRef, 4> lines;
    263     Str.split(lines, "\n");
    264 
    265     for (unsigned i = 0, e = lines.size(); i != e; ++i) {
    266       indentedStr += lines[i];
    267       if (i < e-1) {
    268         indentedStr += '\n';
    269         indentedStr += indentSpace;
    270       }
    271     }
    272     Str = indentedStr.str();
    273   }
    274 
    275   getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter);
    276   return false;
    277 }
    278 
    279 bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) {
    280   if (!isRewritable(Loc)) return true;
    281   FileID FID;
    282   unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
    283   RewriteOptions rangeOpts;
    284   rangeOpts.IncludeInsertsAtBeginOfRange = false;
    285   StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts);
    286   getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true);
    287   return false;
    288 }
    289 
    290 /// RemoveText - Remove the specified text region.
    291 bool Rewriter::RemoveText(SourceLocation Start, unsigned Length,
    292                           RewriteOptions opts) {
    293   if (!isRewritable(Start)) return true;
    294   FileID FID;
    295   unsigned StartOffs = getLocationOffsetAndFileID(Start, FID);
    296   getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty);
    297   return false;
    298 }
    299 
    300 /// ReplaceText - This method replaces a range of characters in the input
    301 /// buffer with a new string.  This is effectively a combined "remove/insert"
    302 /// operation.
    303 bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
    304                            StringRef NewStr) {
    305   if (!isRewritable(Start)) return true;
    306   FileID StartFileID;
    307   unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
    308 
    309   getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr);
    310   return false;
    311 }
    312 
    313 bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) {
    314   if (!isRewritable(range.getBegin())) return true;
    315   if (!isRewritable(range.getEnd())) return true;
    316   if (replacementRange.isInvalid()) return true;
    317   SourceLocation start = range.getBegin();
    318   unsigned origLength = getRangeSize(range);
    319   unsigned newLength = getRangeSize(replacementRange);
    320   FileID FID;
    321   unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(),
    322                                                 FID);
    323   StringRef MB = SourceMgr->getBufferData(FID);
    324   return ReplaceText(start, origLength, MB.substr(newOffs, newLength));
    325 }
    326 
    327 /// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty
    328 /// printer to generate the replacement code.  This returns true if the input
    329 /// could not be rewritten, or false if successful.
    330 bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) {
    331   // Measaure the old text.
    332   int Size = getRangeSize(From->getSourceRange());
    333   if (Size == -1)
    334     return true;
    335 
    336   // Get the new text.
    337   std::string SStr;
    338   llvm::raw_string_ostream S(SStr);
    339   To->printPretty(S, 0, PrintingPolicy(*LangOpts));
    340   const std::string &Str = S.str();
    341 
    342   ReplaceText(From->getLocStart(), Size, Str);
    343   return false;
    344 }
    345 
    346 std::string Rewriter::ConvertToString(Stmt *From) {
    347   std::string SStr;
    348   llvm::raw_string_ostream S(SStr);
    349   From->printPretty(S, 0, PrintingPolicy(*LangOpts));
    350   return S.str();
    351 }
    352 
    353 bool Rewriter::IncreaseIndentation(CharSourceRange range,
    354                                    SourceLocation parentIndent) {
    355   if (range.isInvalid()) return true;
    356   if (!isRewritable(range.getBegin())) return true;
    357   if (!isRewritable(range.getEnd())) return true;
    358   if (!isRewritable(parentIndent)) return true;
    359 
    360   FileID StartFileID, EndFileID, parentFileID;
    361   unsigned StartOff, EndOff, parentOff;
    362 
    363   StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID);
    364   EndOff   = getLocationOffsetAndFileID(range.getEnd(), EndFileID);
    365   parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID);
    366 
    367   if (StartFileID != EndFileID || StartFileID != parentFileID)
    368     return true;
    369   if (StartOff > EndOff)
    370     return true;
    371 
    372   FileID FID = StartFileID;
    373   StringRef MB = SourceMgr->getBufferData(FID);
    374 
    375   unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1;
    376   unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1;
    377   unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1;
    378 
    379   const SrcMgr::ContentCache *
    380       Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache();
    381 
    382   // Find where the lines start.
    383   unsigned parentLineOffs = Content->SourceLineCache[parentLineNo];
    384   unsigned startLineOffs = Content->SourceLineCache[startLineNo];
    385 
    386   // Find the whitespace at the start of each line.
    387   StringRef parentSpace, startSpace;
    388   {
    389     unsigned i = parentLineOffs;
    390     while (isWhitespace(MB[i]))
    391       ++i;
    392     parentSpace = MB.substr(parentLineOffs, i-parentLineOffs);
    393 
    394     i = startLineOffs;
    395     while (isWhitespace(MB[i]))
    396       ++i;
    397     startSpace = MB.substr(startLineOffs, i-startLineOffs);
    398   }
    399   if (parentSpace.size() >= startSpace.size())
    400     return true;
    401   if (!startSpace.startswith(parentSpace))
    402     return true;
    403 
    404   StringRef indent = startSpace.substr(parentSpace.size());
    405 
    406   // Indent the lines between start/end offsets.
    407   RewriteBuffer &RB = getEditBuffer(FID);
    408   for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) {
    409     unsigned offs = Content->SourceLineCache[lineNo];
    410     unsigned i = offs;
    411     while (isWhitespace(MB[i]))
    412       ++i;
    413     StringRef origIndent = MB.substr(offs, i-offs);
    414     if (origIndent.startswith(startSpace))
    415       RB.InsertText(offs, indent, /*InsertAfter=*/false);
    416   }
    417 
    418   return false;
    419 }
    420 
    421 namespace {
    422 // A wrapper for a file stream that atomically overwrites the target.
    423 //
    424 // Creates a file output stream for a temporary file in the constructor,
    425 // which is later accessible via getStream() if ok() return true.
    426 // Flushes the stream and moves the temporary file to the target location
    427 // in the destructor.
    428 class AtomicallyMovedFile {
    429 public:
    430   AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename,
    431                       bool &AllWritten)
    432     : Diagnostics(Diagnostics), Filename(Filename), AllWritten(AllWritten) {
    433     TempFilename = Filename;
    434     TempFilename += "-%%%%%%%%";
    435     int FD;
    436     if (llvm::sys::fs::unique_file(TempFilename.str(), FD, TempFilename,
    437                                     /*makeAbsolute=*/true, 0664)) {
    438       AllWritten = false;
    439       Diagnostics.Report(clang::diag::err_unable_to_make_temp)
    440         << TempFilename;
    441     } else {
    442       FileStream.reset(new llvm::raw_fd_ostream(FD, /*shouldClose=*/true));
    443     }
    444   }
    445 
    446   ~AtomicallyMovedFile() {
    447     if (!ok()) return;
    448 
    449     FileStream->flush();
    450 #ifdef _WIN32
    451     // Win32 does not allow rename/removing opened files.
    452     FileStream.reset();
    453 #endif
    454     if (llvm::error_code ec =
    455           llvm::sys::fs::rename(TempFilename.str(), Filename)) {
    456       AllWritten = false;
    457       Diagnostics.Report(clang::diag::err_unable_to_rename_temp)
    458         << TempFilename << Filename << ec.message();
    459       bool existed;
    460       // If the remove fails, there's not a lot we can do - this is already an
    461       // error.
    462       llvm::sys::fs::remove(TempFilename.str(), existed);
    463     }
    464   }
    465 
    466   bool ok() { return FileStream; }
    467   raw_ostream &getStream() { return *FileStream; }
    468 
    469 private:
    470   DiagnosticsEngine &Diagnostics;
    471   StringRef Filename;
    472   SmallString<128> TempFilename;
    473   OwningPtr<llvm::raw_fd_ostream> FileStream;
    474   bool &AllWritten;
    475 };
    476 } // end anonymous namespace
    477 
    478 bool Rewriter::overwriteChangedFiles() {
    479   bool AllWritten = true;
    480   for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
    481     const FileEntry *Entry =
    482         getSourceMgr().getFileEntryForID(I->first);
    483     AtomicallyMovedFile File(getSourceMgr().getDiagnostics(), Entry->getName(),
    484                              AllWritten);
    485     if (File.ok()) {
    486       I->second.write(File.getStream());
    487     }
    488   }
    489   return !AllWritten;
    490 }
    491