clang API Documentation

Rewriter.cpp
Go to the documentation of this file.
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 }