1 //===--- Rewriter.h - Code rewriting interface ------------------*- C++ -*-===//
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 #ifndef LLVM_CLANG_REWRITE_CORE_REWRITER_H
16 #define LLVM_CLANG_REWRITE_CORE_REWRITER_H
17 
18 #include "clang/Basic/SourceLocation.h"
19 #include "clang/Rewrite/Core/DeltaTree.h"
20 #include "clang/Rewrite/Core/RewriteRope.h"
21 #include "llvm/ADT/StringRef.h"
22 #include <cstring>
23 #include <map>
24 #include <string>
25 
26 namespace clang {
27   class LangOptions;
28   class Rewriter;
29   class SourceManager;
30 
31 /// RewriteBuffer - As code is rewritten, SourceBuffer's from the original
32 /// input with modifications get a new RewriteBuffer associated with them.  The
33 /// RewriteBuffer captures the modified text itself as well as information used
34 /// to map between SourceLocation's in the original input and offsets in the
35 /// RewriteBuffer.  For example, if text is inserted into the buffer, any
36 /// locations after the insertion point have to be mapped.
37 class RewriteBuffer {
38   friend class Rewriter;
39   /// Deltas - Keep track of all the deltas in the source code due to insertions
40   /// and deletions.
41   DeltaTree Deltas;
42   RewriteRope Buffer;
43 public:
44   typedef RewriteRope::const_iterator iterator;
begin()45   iterator begin() const { return Buffer.begin(); }
end()46   iterator end() const { return Buffer.end(); }
size()47   unsigned size() const { return Buffer.size(); }
48 
49   /// \brief Write to \p Stream the result of applying all changes to the
50   /// original buffer.
51   /// Note that it isn't safe to use this function to overwrite memory mapped
52   /// files in-place (PR17960). Consider using a higher-level utility such as
53   /// Rewriter::overwriteChangedFiles() instead.
54   ///
55   /// The original buffer is not actually changed.
56   raw_ostream &write(raw_ostream &Stream) const;
57 
58   /// RemoveText - Remove the specified text.
59   void RemoveText(unsigned OrigOffset, unsigned Size,
60                   bool removeLineIfEmpty = false);
61 
62   /// InsertText - Insert some text at the specified point, where the offset in
63   /// the buffer is specified relative to the original SourceBuffer.  The
64   /// text is inserted after the specified location.
65   ///
66   void InsertText(unsigned OrigOffset, StringRef Str,
67                   bool InsertAfter = true);
68 
69 
70   /// InsertTextBefore - Insert some text before the specified point, where the
71   /// offset in the buffer is specified relative to the original
72   /// SourceBuffer. The text is inserted before the specified location.  This is
73   /// method is the same as InsertText with "InsertAfter == false".
InsertTextBefore(unsigned OrigOffset,StringRef Str)74   void InsertTextBefore(unsigned OrigOffset, StringRef Str) {
75     InsertText(OrigOffset, Str, false);
76   }
77 
78   /// InsertTextAfter - Insert some text at the specified point, where the
79   /// offset in the buffer is specified relative to the original SourceBuffer.
80   /// The text is inserted after the specified location.
InsertTextAfter(unsigned OrigOffset,StringRef Str)81   void InsertTextAfter(unsigned OrigOffset, StringRef Str) {
82     InsertText(OrigOffset, Str);
83   }
84 
85   /// ReplaceText - This method replaces a range of characters in the input
86   /// buffer with a new string.  This is effectively a combined "remove/insert"
87   /// operation.
88   void ReplaceText(unsigned OrigOffset, unsigned OrigLength,
89                    StringRef NewStr);
90 
91 private:  // Methods only usable by Rewriter.
92 
93   /// Initialize - Start this rewrite buffer out with a copy of the unmodified
94   /// input buffer.
Initialize(const char * BufStart,const char * BufEnd)95   void Initialize(const char *BufStart, const char *BufEnd) {
96     Buffer.assign(BufStart, BufEnd);
97   }
98 
99   /// getMappedOffset - Given an offset into the original SourceBuffer that this
100   /// RewriteBuffer is based on, map it into the offset space of the
101   /// RewriteBuffer.  If AfterInserts is true and if the OrigOffset indicates a
102   /// position where text is inserted, the location returned will be after any
103   /// inserted text at the position.
104   unsigned getMappedOffset(unsigned OrigOffset,
105                            bool AfterInserts = false) const{
106     return Deltas.getDeltaAt(2*OrigOffset+AfterInserts)+OrigOffset;
107   }
108 
109   /// AddInsertDelta - When an insertion is made at a position, this
110   /// method is used to record that information.
AddInsertDelta(unsigned OrigOffset,int Change)111   void AddInsertDelta(unsigned OrigOffset, int Change) {
112     return Deltas.AddDelta(2*OrigOffset, Change);
113   }
114 
115   /// AddReplaceDelta - When a replacement/deletion is made at a position, this
116   /// method is used to record that information.
AddReplaceDelta(unsigned OrigOffset,int Change)117   void AddReplaceDelta(unsigned OrigOffset, int Change) {
118     return Deltas.AddDelta(2*OrigOffset+1, Change);
119   }
120 };
121 
122 
123 /// Rewriter - This is the main interface to the rewrite buffers.  Its primary
124 /// job is to dispatch high-level requests to the low-level RewriteBuffers that
125 /// are involved.
126 class Rewriter {
127   SourceManager *SourceMgr;
128   const LangOptions *LangOpts;
129   std::map<FileID, RewriteBuffer> RewriteBuffers;
130 public:
131   struct RewriteOptions {
132     /// \brief Given a source range, true to include previous inserts at the
133     /// beginning of the range as part of the range itself (true by default).
134     bool IncludeInsertsAtBeginOfRange;
135     /// \brief Given a source range, true to include previous inserts at the
136     /// end of the range as part of the range itself (true by default).
137     bool IncludeInsertsAtEndOfRange;
138     /// \brief If true and removing some text leaves a blank line
139     /// also remove the empty line (false by default).
140     bool RemoveLineIfEmpty;
141 
RewriteOptionsRewriteOptions142     RewriteOptions()
143       : IncludeInsertsAtBeginOfRange(true),
144         IncludeInsertsAtEndOfRange(true),
145         RemoveLineIfEmpty(false) { }
146   };
147 
148   typedef std::map<FileID, RewriteBuffer>::iterator buffer_iterator;
149   typedef std::map<FileID, RewriteBuffer>::const_iterator const_buffer_iterator;
150 
Rewriter(SourceManager & SM,const LangOptions & LO)151   explicit Rewriter(SourceManager &SM, const LangOptions &LO)
152     : SourceMgr(&SM), LangOpts(&LO) {}
Rewriter()153   explicit Rewriter() : SourceMgr(nullptr), LangOpts(nullptr) {}
154 
setSourceMgr(SourceManager & SM,const LangOptions & LO)155   void setSourceMgr(SourceManager &SM, const LangOptions &LO) {
156     SourceMgr = &SM;
157     LangOpts = &LO;
158   }
getSourceMgr()159   SourceManager &getSourceMgr() const { return *SourceMgr; }
getLangOpts()160   const LangOptions &getLangOpts() const { return *LangOpts; }
161 
162   /// isRewritable - Return true if this location is a raw file location, which
163   /// is rewritable.  Locations from macros, etc are not rewritable.
isRewritable(SourceLocation Loc)164   static bool isRewritable(SourceLocation Loc) {
165     return Loc.isFileID();
166   }
167 
168   /// getRangeSize - Return the size in bytes of the specified range if they
169   /// are in the same file.  If not, this returns -1.
170   int getRangeSize(SourceRange Range,
171                    RewriteOptions opts = RewriteOptions()) const;
172   int getRangeSize(const CharSourceRange &Range,
173                    RewriteOptions opts = RewriteOptions()) const;
174 
175   /// getRewrittenText - Return the rewritten form of the text in the specified
176   /// range.  If the start or end of the range was unrewritable or if they are
177   /// in different buffers, this returns an empty string.
178   ///
179   /// Note that this method is not particularly efficient.
180   ///
181   std::string getRewrittenText(SourceRange Range) const;
182 
183   /// InsertText - Insert the specified string at the specified location in the
184   /// original buffer.  This method returns true (and does nothing) if the input
185   /// location was not rewritable, false otherwise.
186   ///
187   /// \param indentNewLines if true new lines in the string are indented
188   /// using the indentation of the source line in position \p Loc.
189   bool InsertText(SourceLocation Loc, StringRef Str,
190                   bool InsertAfter = true, bool indentNewLines = false);
191 
192   /// InsertTextAfter - Insert the specified string at the specified location in
193   ///  the original buffer.  This method returns true (and does nothing) if
194   ///  the input location was not rewritable, false otherwise.  Text is
195   ///  inserted after any other text that has been previously inserted
196   ///  at the some point (the default behavior for InsertText).
InsertTextAfter(SourceLocation Loc,StringRef Str)197   bool InsertTextAfter(SourceLocation Loc, StringRef Str) {
198     return InsertText(Loc, Str);
199   }
200 
201   /// \brief Insert the specified string after the token in the
202   /// specified location.
203   bool InsertTextAfterToken(SourceLocation Loc, StringRef Str);
204 
205   /// InsertText - Insert the specified string at the specified location in the
206   /// original buffer.  This method returns true (and does nothing) if the input
207   /// location was not rewritable, false otherwise.  Text is
208   /// inserted before any other text that has been previously inserted
209   /// at the some point.
InsertTextBefore(SourceLocation Loc,StringRef Str)210   bool InsertTextBefore(SourceLocation Loc, StringRef Str) {
211     return InsertText(Loc, Str, false);
212   }
213 
214   /// RemoveText - Remove the specified text region.
215   bool RemoveText(SourceLocation Start, unsigned Length,
216                   RewriteOptions opts = RewriteOptions());
217 
218   /// \brief Remove the specified text region.
219   bool RemoveText(CharSourceRange range,
220                   RewriteOptions opts = RewriteOptions()) {
221     return RemoveText(range.getBegin(), getRangeSize(range, opts), opts);
222   }
223 
224   /// \brief Remove the specified text region.
225   bool RemoveText(SourceRange range, RewriteOptions opts = RewriteOptions()) {
226     return RemoveText(range.getBegin(), getRangeSize(range, opts), opts);
227   }
228 
229   /// ReplaceText - This method replaces a range of characters in the input
230   /// buffer with a new string.  This is effectively a combined "remove/insert"
231   /// operation.
232   bool ReplaceText(SourceLocation Start, unsigned OrigLength,
233                    StringRef NewStr);
234 
235   /// ReplaceText - This method replaces a range of characters in the input
236   /// buffer with a new string.  This is effectively a combined "remove/insert"
237   /// operation.
ReplaceText(SourceRange range,StringRef NewStr)238   bool ReplaceText(SourceRange range, StringRef NewStr) {
239     return ReplaceText(range.getBegin(), getRangeSize(range), NewStr);
240   }
241 
242   /// ReplaceText - This method replaces a range of characters in the input
243   /// buffer with a new string.  This is effectively a combined "remove/insert"
244   /// operation.
245   bool ReplaceText(SourceRange range, SourceRange replacementRange);
246 
247   /// \brief Increase indentation for the lines between the given source range.
248   /// To determine what the indentation should be, 'parentIndent' is used
249   /// that should be at a source location with an indentation one degree
250   /// lower than the given range.
251   bool IncreaseIndentation(CharSourceRange range, SourceLocation parentIndent);
IncreaseIndentation(SourceRange range,SourceLocation parentIndent)252   bool IncreaseIndentation(SourceRange range, SourceLocation parentIndent) {
253     return IncreaseIndentation(CharSourceRange::getTokenRange(range),
254                                parentIndent);
255   }
256 
257   /// getEditBuffer - This is like getRewriteBufferFor, but always returns a
258   /// buffer, and allows you to write on it directly.  This is useful if you
259   /// want efficient low-level access to apis for scribbling on one specific
260   /// FileID's buffer.
261   RewriteBuffer &getEditBuffer(FileID FID);
262 
263   /// getRewriteBufferFor - Return the rewrite buffer for the specified FileID.
264   /// If no modification has been made to it, return null.
getRewriteBufferFor(FileID FID)265   const RewriteBuffer *getRewriteBufferFor(FileID FID) const {
266     std::map<FileID, RewriteBuffer>::const_iterator I =
267       RewriteBuffers.find(FID);
268     return I == RewriteBuffers.end() ? nullptr : &I->second;
269   }
270 
271   // Iterators over rewrite buffers.
buffer_begin()272   buffer_iterator buffer_begin() { return RewriteBuffers.begin(); }
buffer_end()273   buffer_iterator buffer_end() { return RewriteBuffers.end(); }
buffer_begin()274   const_buffer_iterator buffer_begin() const { return RewriteBuffers.begin(); }
buffer_end()275   const_buffer_iterator buffer_end() const { return RewriteBuffers.end(); }
276 
277   /// overwriteChangedFiles - Save all changed files to disk.
278   ///
279   /// Returns true if any files were not saved successfully.
280   /// Outputs diagnostics via the source manager's diagnostic engine
281   /// in case of an error.
282   bool overwriteChangedFiles();
283 
284 private:
285   unsigned getLocationOffsetAndFileID(SourceLocation Loc, FileID &FID) const;
286 };
287 
288 } // end namespace clang
289 
290 #endif
291