1 //===--- Rewriter.cpp - Code rewriting interface --------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines the Rewriter class, which is used for code
13 //===----------------------------------------------------------------------===//
15 #include "clang/Rewrite/Rewriter.h"
16 #include "clang/AST/Stmt.h"
17 #include "clang/AST/Decl.h"
18 #include "clang/Lex/Lexer.h"
19 #include "clang/Basic/SourceManager.h"
20 #include "llvm/Support/raw_ostream.h"
21 using namespace clang;
23 llvm::raw_ostream &RewriteBuffer::write(llvm::raw_ostream &os) const {
24 // FIXME: eliminate the copy by writing out each chunk at a time
25 os << std::string(begin(), end());
29 void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size) {
30 // Nothing to remove, exit early.
31 if (Size == 0) return;
33 unsigned RealOffset = getMappedOffset(OrigOffset, true);
34 assert(RealOffset+Size < Buffer.size() && "Invalid location");
36 // Remove the dead characters.
37 Buffer.erase(RealOffset, Size);
39 // Add a delta so that future changes are offset correctly.
40 AddReplaceDelta(OrigOffset, -Size);
43 void RewriteBuffer::InsertText(unsigned OrigOffset, llvm::StringRef Str,
46 // Nothing to insert, exit early.
47 if (Str.empty()) return;
49 unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter);
50 Buffer.insert(RealOffset, Str.begin(), Str.end());
52 // Add a delta so that future changes are offset correctly.
53 AddInsertDelta(OrigOffset, Str.size());
56 /// ReplaceText - This method replaces a range of characters in the input
57 /// buffer with a new string. This is effectively a combined "remove+insert"
59 void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
60 llvm::StringRef NewStr) {
61 unsigned RealOffset = getMappedOffset(OrigOffset, true);
62 Buffer.erase(RealOffset, OrigLength);
63 Buffer.insert(RealOffset, NewStr.begin(), NewStr.end());
64 if (OrigLength != NewStr.size())
65 AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength);
69 //===----------------------------------------------------------------------===//
71 //===----------------------------------------------------------------------===//
73 /// getRangeSize - Return the size in bytes of the specified range if they
74 /// are in the same file. If not, this returns -1.
75 int Rewriter::getRangeSize(const CharSourceRange &Range) const {
76 if (!isRewritable(Range.getBegin()) ||
77 !isRewritable(Range.getEnd())) return -1;
79 FileID StartFileID, EndFileID;
80 unsigned StartOff, EndOff;
82 StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
83 EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
85 if (StartFileID != EndFileID)
88 // If edits have been made to this buffer, the delta between the range may
90 std::map<FileID, RewriteBuffer>::const_iterator I =
91 RewriteBuffers.find(StartFileID);
92 if (I != RewriteBuffers.end()) {
93 const RewriteBuffer &RB = I->second;
94 EndOff = RB.getMappedOffset(EndOff, true);
95 StartOff = RB.getMappedOffset(StartOff);
99 // Adjust the end offset to the end of the last token, instead of being the
100 // start of the last token if this is a token range.
101 if (Range.isTokenRange())
102 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
104 return EndOff-StartOff;
107 int Rewriter::getRangeSize(SourceRange Range) const {
108 return getRangeSize(CharSourceRange::getTokenRange(Range));
112 /// getRewrittenText - Return the rewritten form of the text in the specified
113 /// range. If the start or end of the range was unrewritable or if they are
114 /// in different buffers, this returns an empty string.
116 /// Note that this method is not particularly efficient.
118 std::string Rewriter::getRewrittenText(SourceRange Range) const {
119 if (!isRewritable(Range.getBegin()) ||
120 !isRewritable(Range.getEnd()))
123 FileID StartFileID, EndFileID;
124 unsigned StartOff, EndOff;
125 StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
126 EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
128 if (StartFileID != EndFileID)
129 return ""; // Start and end in different buffers.
131 // If edits have been made to this buffer, the delta between the range may
133 std::map<FileID, RewriteBuffer>::const_iterator I =
134 RewriteBuffers.find(StartFileID);
135 if (I == RewriteBuffers.end()) {
136 // If the buffer hasn't been rewritten, just return the text from the input.
137 const char *Ptr = SourceMgr->getCharacterData(Range.getBegin());
139 // Adjust the end offset to the end of the last token, instead of being the
140 // start of the last token.
141 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
142 return std::string(Ptr, Ptr+EndOff-StartOff);
145 const RewriteBuffer &RB = I->second;
146 EndOff = RB.getMappedOffset(EndOff, true);
147 StartOff = RB.getMappedOffset(StartOff);
149 // Adjust the end offset to the end of the last token, instead of being the
150 // start of the last token.
151 EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
153 // Advance the iterators to the right spot, yay for linear time algorithms.
154 RewriteBuffer::iterator Start = RB.begin();
155 std::advance(Start, StartOff);
156 RewriteBuffer::iterator End = Start;
157 std::advance(End, EndOff-StartOff);
159 return std::string(Start, End);
162 unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
164 assert(Loc.isValid() && "Invalid location");
165 std::pair<FileID,unsigned> V = SourceMgr->getDecomposedLoc(Loc);
171 /// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
173 RewriteBuffer &Rewriter::getEditBuffer(FileID FID) {
174 std::map<FileID, RewriteBuffer>::iterator I =
175 RewriteBuffers.lower_bound(FID);
176 if (I != RewriteBuffers.end() && I->first == FID)
178 I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));
180 llvm::StringRef MB = SourceMgr->getBufferData(FID);
181 I->second.Initialize(MB.begin(), MB.end());
186 /// InsertText - Insert the specified string at the specified location in the
188 bool Rewriter::InsertText(SourceLocation Loc, llvm::StringRef Str,
190 if (!isRewritable(Loc)) return true;
192 unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
193 getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter);
197 /// RemoveText - Remove the specified text region.
198 bool Rewriter::RemoveText(SourceLocation Start, unsigned Length) {
199 if (!isRewritable(Start)) return true;
201 unsigned StartOffs = getLocationOffsetAndFileID(Start, FID);
202 getEditBuffer(FID).RemoveText(StartOffs, Length);
206 /// ReplaceText - This method replaces a range of characters in the input
207 /// buffer with a new string. This is effectively a combined "remove/insert"
209 bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
210 llvm::StringRef NewStr) {
211 if (!isRewritable(Start)) return true;
213 unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
215 getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr);
219 /// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty
220 /// printer to generate the replacement code. This returns true if the input
221 /// could not be rewritten, or false if successful.
222 bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) {
223 // Measaure the old text.
224 int Size = getRangeSize(From->getSourceRange());
230 llvm::raw_string_ostream S(SStr);
231 To->printPretty(S, 0, PrintingPolicy(*LangOpts));
232 const std::string &Str = S.str();
234 ReplaceText(From->getLocStart(), Size, Str);