xref: /llvm-project/clang/include/clang/Rewrite/Core/Rewriter.h (revision 0d150db214e2aa13a825b563c7238e1243d61db1)
1 //===- Rewriter.h - Code rewriting interface --------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //  This file defines the Rewriter class, which is used for code
10 //  transformations.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_CLANG_REWRITE_CORE_REWRITER_H
15 #define LLVM_CLANG_REWRITE_CORE_REWRITER_H
16 
17 #include "clang/Basic/LLVM.h"
18 #include "clang/Basic/SourceLocation.h"
19 #include "llvm/ADT/RewriteBuffer.h"
20 #include "llvm/ADT/StringRef.h"
21 #include <map>
22 #include <string>
23 
24 namespace clang {
25 
26 class LangOptions;
27 class SourceManager;
28 
29 /// Rewriter - This is the main interface to the rewrite buffers.  Its primary
30 /// job is to dispatch high-level requests to the low-level RewriteBuffers that
31 /// are involved.
32 class Rewriter {
33   SourceManager *SourceMgr = nullptr;
34   const LangOptions *LangOpts = nullptr;
35   std::map<FileID, llvm::RewriteBuffer> RewriteBuffers;
36 
37 public:
38   struct RewriteOptions {
39     /// Given a source range, true to include previous inserts at the
40     /// beginning of the range as part of the range itself (true by default).
41     bool IncludeInsertsAtBeginOfRange = true;
42 
43     /// Given a source range, true to include previous inserts at the
44     /// end of the range as part of the range itself (true by default).
45     bool IncludeInsertsAtEndOfRange = true;
46 
47     /// If true and removing some text leaves a blank line
48     /// also remove the empty line (false by default).
49     ///
50     /// FIXME: This sometimes corrupts the file's rewrite buffer due to
51     /// incorrect indexing in the implementation (see the FIXME in
52     /// llvm::RewriteBuffer::RemoveText).  Moreover, it's inefficient because
53     /// it must scan the buffer from the beginning to find the start of the
54     /// line.  When feasible, it's better for the caller to check for a blank
55     /// line and then, if found, expand the removal range to include it.
56     /// Checking for a blank line is easy if, for example, the caller can
57     /// guarantee this is the first edit of a line.  In that case, it can just
58     /// scan before and after the removal range until the next newline or
59     /// begin/end of the input.
60     bool RemoveLineIfEmpty = false;
61 
62     RewriteOptions() {}
63   };
64 
65   using buffer_iterator = std::map<FileID, llvm::RewriteBuffer>::iterator;
66   using const_buffer_iterator =
67       std::map<FileID, llvm::RewriteBuffer>::const_iterator;
68 
69   explicit Rewriter() = default;
70   explicit Rewriter(SourceManager &SM, const LangOptions &LO)
71       : SourceMgr(&SM), LangOpts(&LO) {}
72 
73   void setSourceMgr(SourceManager &SM, const LangOptions &LO) {
74     SourceMgr = &SM;
75     LangOpts = &LO;
76   }
77 
78   SourceManager &getSourceMgr() const { return *SourceMgr; }
79   const LangOptions &getLangOpts() const { return *LangOpts; }
80 
81   /// isRewritable - Return true if this location is a raw file location, which
82   /// is rewritable.  Locations from macros, etc are not rewritable.
83   static bool isRewritable(SourceLocation Loc) {
84     return Loc.isFileID();
85   }
86 
87   /// getRangeSize - Return the size in bytes of the specified range if they
88   /// are in the same file.  If not, this returns -1.
89   int getRangeSize(SourceRange Range,
90                    RewriteOptions opts = RewriteOptions()) const;
91   int getRangeSize(const CharSourceRange &Range,
92                    RewriteOptions opts = RewriteOptions()) const;
93 
94   /// getRewrittenText - Return the rewritten form of the text in the specified
95   /// range.  If the start or end of the range was unrewritable or if they are
96   /// in different buffers, this returns an empty string.
97   ///
98   /// Note that this method is not particularly efficient.
99   std::string getRewrittenText(CharSourceRange Range) const;
100 
101   /// getRewrittenText - Return the rewritten form of the text in the specified
102   /// range.  If the start or end of the range was unrewritable or if they are
103   /// in different buffers, this returns an empty string.
104   ///
105   /// Note that this method is not particularly efficient.
106   std::string getRewrittenText(SourceRange Range) const {
107     return getRewrittenText(CharSourceRange::getTokenRange(Range));
108   }
109 
110   /// InsertText - Insert the specified string at the specified location in the
111   /// original buffer.  This method returns true (and does nothing) if the input
112   /// location was not rewritable, false otherwise.
113   ///
114   /// \param indentNewLines if true new lines in the string are indented
115   /// using the indentation of the source line in position \p Loc.
116   bool InsertText(SourceLocation Loc, StringRef Str,
117                   bool InsertAfter = true, bool indentNewLines = false);
118 
119   /// InsertTextAfter - Insert the specified string at the specified location in
120   ///  the original buffer.  This method returns true (and does nothing) if
121   ///  the input location was not rewritable, false otherwise.  Text is
122   ///  inserted after any other text that has been previously inserted
123   ///  at the some point (the default behavior for InsertText).
124   bool InsertTextAfter(SourceLocation Loc, StringRef Str) {
125     return InsertText(Loc, Str);
126   }
127 
128   /// Insert the specified string after the token in the
129   /// specified location.
130   bool InsertTextAfterToken(SourceLocation Loc, StringRef Str);
131 
132   /// InsertText - Insert the specified string at the specified location in the
133   /// original buffer.  This method returns true (and does nothing) if the input
134   /// location was not rewritable, false otherwise.  Text is
135   /// inserted before any other text that has been previously inserted
136   /// at the some point.
137   bool InsertTextBefore(SourceLocation Loc, StringRef Str) {
138     return InsertText(Loc, Str, false);
139   }
140 
141   /// RemoveText - Remove the specified text region.
142   bool RemoveText(SourceLocation Start, unsigned Length,
143                   RewriteOptions opts = RewriteOptions());
144 
145   /// Remove the specified text region.
146   bool RemoveText(CharSourceRange range,
147                   RewriteOptions opts = RewriteOptions()) {
148     return RemoveText(range.getBegin(), getRangeSize(range, opts), opts);
149   }
150 
151   /// Remove the specified text region.
152   bool RemoveText(SourceRange range, RewriteOptions opts = RewriteOptions()) {
153     return RemoveText(range.getBegin(), getRangeSize(range, opts), opts);
154   }
155 
156   /// ReplaceText - This method replaces a range of characters in the input
157   /// buffer with a new string.  This is effectively a combined "remove/insert"
158   /// operation.
159   bool ReplaceText(SourceLocation Start, unsigned OrigLength,
160                    StringRef NewStr);
161 
162   /// ReplaceText - This method replaces a range of characters in the input
163   /// buffer with a new string.  This is effectively a combined "remove/insert"
164   /// operation.
165   bool ReplaceText(CharSourceRange range, StringRef NewStr) {
166     return ReplaceText(range.getBegin(), getRangeSize(range), NewStr);
167   }
168 
169   /// ReplaceText - This method replaces a range of characters in the input
170   /// buffer with a new string.  This is effectively a combined "remove/insert"
171   /// operation.
172   bool ReplaceText(SourceRange range, StringRef NewStr) {
173     return ReplaceText(range.getBegin(), getRangeSize(range), NewStr);
174   }
175 
176   /// ReplaceText - This method replaces a range of characters in the input
177   /// buffer with a new string.  This is effectively a combined "remove/insert"
178   /// operation.
179   bool ReplaceText(SourceRange range, SourceRange replacementRange);
180 
181   /// Increase indentation for the lines between the given source range.
182   /// To determine what the indentation should be, 'parentIndent' is used
183   /// that should be at a source location with an indentation one degree
184   /// lower than the given range.
185   bool IncreaseIndentation(CharSourceRange range, SourceLocation parentIndent);
186   bool IncreaseIndentation(SourceRange range, SourceLocation parentIndent) {
187     return IncreaseIndentation(CharSourceRange::getTokenRange(range),
188                                parentIndent);
189   }
190 
191   /// getEditBuffer - This is like getRewriteBufferFor, but always returns a
192   /// buffer, and allows you to write on it directly.  This is useful if you
193   /// want efficient low-level access to apis for scribbling on one specific
194   /// FileID's buffer.
195   llvm::RewriteBuffer &getEditBuffer(FileID FID);
196 
197   /// getRewriteBufferFor - Return the rewrite buffer for the specified FileID.
198   /// If no modification has been made to it, return null.
199   const llvm::RewriteBuffer *getRewriteBufferFor(FileID FID) const {
200     std::map<FileID, llvm::RewriteBuffer>::const_iterator I =
201         RewriteBuffers.find(FID);
202     return I == RewriteBuffers.end() ? nullptr : &I->second;
203   }
204 
205   // Iterators over rewrite buffers.
206   buffer_iterator buffer_begin() { return RewriteBuffers.begin(); }
207   buffer_iterator buffer_end() { return RewriteBuffers.end(); }
208   const_buffer_iterator buffer_begin() const { return RewriteBuffers.begin(); }
209   const_buffer_iterator buffer_end() const { return RewriteBuffers.end(); }
210 
211   /// overwriteChangedFiles - Save all changed files to disk.
212   ///
213   /// Returns true if any files were not saved successfully.
214   /// Outputs diagnostics via the source manager's diagnostic engine
215   /// in case of an error.
216   bool overwriteChangedFiles();
217 
218 private:
219   unsigned getLocationOffsetAndFileID(SourceLocation Loc, FileID &FID) const;
220 };
221 
222 } // namespace clang
223 
224 #endif // LLVM_CLANG_REWRITE_CORE_REWRITER_H
225