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