Rewriter.cpp revision f6ac97b101c8840efa92bf29166077ce4049e293
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/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"
21using namespace clang;
22
23void RewriteBuffer::RemoveText(unsigned OrigOffset, unsigned Size) {
24  // Nothing to remove, exit early.
25  if (Size == 0) return;
26
27  unsigned RealOffset = getMappedOffset(OrigOffset, true);
28  assert(RealOffset+Size < Buffer.size() && "Invalid location");
29
30  // Remove the dead characters.
31  Buffer.erase(RealOffset, Size);
32
33  // Add a delta so that future changes are offset correctly.
34  AddReplaceDelta(OrigOffset, -Size);
35}
36
37void RewriteBuffer::InsertText(unsigned OrigOffset, const llvm::StringRef &Str,
38                               bool InsertAfter) {
39
40  // Nothing to insert, exit early.
41  if (Str.empty()) return;
42
43  unsigned RealOffset = getMappedOffset(OrigOffset, InsertAfter);
44  Buffer.insert(RealOffset, Str.begin(), Str.end());
45
46  // Add a delta so that future changes are offset correctly.
47  AddInsertDelta(OrigOffset, Str.size());
48}
49
50/// ReplaceText - This method replaces a range of characters in the input
51/// buffer with a new string.  This is effectively a combined "remove+insert"
52/// operation.
53void RewriteBuffer::ReplaceText(unsigned OrigOffset, unsigned OrigLength,
54                                const llvm::StringRef &NewStr) {
55  unsigned RealOffset = getMappedOffset(OrigOffset, true);
56  Buffer.erase(RealOffset, OrigLength);
57  Buffer.insert(RealOffset, NewStr.begin(), NewStr.end());
58  if (OrigLength != NewStr.size())
59    AddReplaceDelta(OrigOffset, NewStr.size() - OrigLength);
60}
61
62
63//===----------------------------------------------------------------------===//
64// Rewriter class
65//===----------------------------------------------------------------------===//
66
67/// getRangeSize - Return the size in bytes of the specified range if they
68/// are in the same file.  If not, this returns -1.
69int Rewriter::getRangeSize(SourceRange Range) const {
70  if (!isRewritable(Range.getBegin()) ||
71      !isRewritable(Range.getEnd())) return -1;
72
73  FileID StartFileID, EndFileID;
74  unsigned StartOff, EndOff;
75
76  StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
77  EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
78
79  if (StartFileID != EndFileID)
80    return -1;
81
82  // If edits have been made to this buffer, the delta between the range may
83  // have changed.
84  std::map<FileID, RewriteBuffer>::const_iterator I =
85    RewriteBuffers.find(StartFileID);
86  if (I != RewriteBuffers.end()) {
87    const RewriteBuffer &RB = I->second;
88    EndOff = RB.getMappedOffset(EndOff, true);
89    StartOff = RB.getMappedOffset(StartOff);
90  }
91
92
93  // Adjust the end offset to the end of the last token, instead of being the
94  // start of the last token.
95  EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
96
97  return EndOff-StartOff;
98}
99
100/// getRewrittenText - Return the rewritten form of the text in the specified
101/// range.  If the start or end of the range was unrewritable or if they are
102/// in different buffers, this returns an empty string.
103///
104/// Note that this method is not particularly efficient.
105///
106std::string Rewriter::getRewrittenText(SourceRange Range) const {
107  if (!isRewritable(Range.getBegin()) ||
108      !isRewritable(Range.getEnd()))
109    return "";
110
111  FileID StartFileID, EndFileID;
112  unsigned StartOff, EndOff;
113  StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);
114  EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);
115
116  if (StartFileID != EndFileID)
117    return ""; // Start and end in different buffers.
118
119  // If edits have been made to this buffer, the delta between the range may
120  // have changed.
121  std::map<FileID, RewriteBuffer>::const_iterator I =
122    RewriteBuffers.find(StartFileID);
123  if (I == RewriteBuffers.end()) {
124    // If the buffer hasn't been rewritten, just return the text from the input.
125    const char *Ptr = SourceMgr->getCharacterData(Range.getBegin());
126
127    // Adjust the end offset to the end of the last token, instead of being the
128    // start of the last token.
129    EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
130    return std::string(Ptr, Ptr+EndOff-StartOff);
131  }
132
133  const RewriteBuffer &RB = I->second;
134  EndOff = RB.getMappedOffset(EndOff, true);
135  StartOff = RB.getMappedOffset(StartOff);
136
137  // Adjust the end offset to the end of the last token, instead of being the
138  // start of the last token.
139  EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);
140
141  // Advance the iterators to the right spot, yay for linear time algorithms.
142  RewriteBuffer::iterator Start = RB.begin();
143  std::advance(Start, StartOff);
144  RewriteBuffer::iterator End = Start;
145  std::advance(End, EndOff-StartOff);
146
147  return std::string(Start, End);
148}
149
150unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,
151                                              FileID &FID) const {
152  assert(Loc.isValid() && "Invalid location");
153  std::pair<FileID,unsigned> V = SourceMgr->getDecomposedLoc(Loc);
154  FID = V.first;
155  return V.second;
156}
157
158
159/// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.
160///
161RewriteBuffer &Rewriter::getEditBuffer(FileID FID) {
162  std::map<FileID, RewriteBuffer>::iterator I =
163    RewriteBuffers.lower_bound(FID);
164  if (I != RewriteBuffers.end() && I->first == FID)
165    return I->second;
166  I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));
167
168  llvm::StringRef MB = SourceMgr->getBufferData(FID);
169  I->second.Initialize(MB.begin(), MB.end());
170
171  return I->second;
172}
173
174/// InsertText - Insert the specified string at the specified location in the
175/// original buffer.
176bool Rewriter::InsertText(SourceLocation Loc, const llvm::StringRef &Str,
177                          bool InsertAfter) {
178  if (!isRewritable(Loc)) return true;
179  FileID FID;
180  unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);
181  getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter);
182  return false;
183}
184
185/// RemoveText - Remove the specified text region.
186bool Rewriter::RemoveText(SourceLocation Start, unsigned Length) {
187  if (!isRewritable(Start)) return true;
188  FileID FID;
189  unsigned StartOffs = getLocationOffsetAndFileID(Start, FID);
190  getEditBuffer(FID).RemoveText(StartOffs, Length);
191  return false;
192}
193
194/// ReplaceText - This method replaces a range of characters in the input
195/// buffer with a new string.  This is effectively a combined "remove/insert"
196/// operation.
197bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,
198                           const llvm::StringRef &NewStr) {
199  if (!isRewritable(Start)) return true;
200  FileID StartFileID;
201  unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);
202
203  getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr);
204  return false;
205}
206
207/// ReplaceStmt - This replaces a Stmt/Expr with another, using the pretty
208/// printer to generate the replacement code.  This returns true if the input
209/// could not be rewritten, or false if successful.
210bool Rewriter::ReplaceStmt(Stmt *From, Stmt *To) {
211  // Measaure the old text.
212  int Size = getRangeSize(From->getSourceRange());
213  if (Size == -1)
214    return true;
215
216  // Get the new text.
217  std::string SStr;
218  llvm::raw_string_ostream S(SStr);
219  To->printPretty(S, 0, PrintingPolicy(*LangOpts));
220  const std::string &Str = S.str();
221
222  ReplaceText(From->getLocStart(), Size, Str);
223  return false;
224}
225
226
227