1 //===- Replacement.h - Framework for clang refactoring tools ----*- 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 // Classes supporting refactorings that span multiple translation units.
11 // While single translation unit refactorings are supported via the Rewriter,
12 // when refactoring multiple translation units changes must be stored in a
13 // SourceManager independent form, duplicate changes need to be removed, and
14 // all changes must be applied at once at the end of the refactoring so that
15 // the code is always parseable.
16 //
17 //===----------------------------------------------------------------------===//
18
19 #ifndef LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H
20 #define LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H
21
22 #include "clang/Basic/LangOptions.h"
23 #include "clang/Basic/SourceLocation.h"
24 #include "llvm/ADT/Optional.h"
25 #include "llvm/ADT/StringRef.h"
26 #include "llvm/Support/Compiler.h"
27 #include "llvm/Support/Error.h"
28 #include "llvm/Support/raw_ostream.h"
29 #include <map>
30 #include <set>
31 #include <string>
32 #include <system_error>
33 #include <utility>
34 #include <vector>
35
36 namespace clang {
37
38 class FileManager;
39 class Rewriter;
40 class SourceManager;
41
42 namespace tooling {
43
44 /// A source range independent of the \c SourceManager.
45 class Range {
46 public:
47 Range() = default;
Range(unsigned Offset,unsigned Length)48 Range(unsigned Offset, unsigned Length) : Offset(Offset), Length(Length) {}
49
50 /// Accessors.
51 /// @{
getOffset()52 unsigned getOffset() const { return Offset; }
getLength()53 unsigned getLength() const { return Length; }
54 /// @}
55
56 /// \name Range Predicates
57 /// @{
58 /// Whether this range overlaps with \p RHS or not.
overlapsWith(Range RHS)59 bool overlapsWith(Range RHS) const {
60 return Offset + Length > RHS.Offset && Offset < RHS.Offset + RHS.Length;
61 }
62
63 /// Whether this range contains \p RHS or not.
contains(Range RHS)64 bool contains(Range RHS) const {
65 return RHS.Offset >= Offset &&
66 (RHS.Offset + RHS.Length) <= (Offset + Length);
67 }
68
69 /// Whether this range equals to \p RHS or not.
70 bool operator==(const Range &RHS) const {
71 return Offset == RHS.getOffset() && Length == RHS.getLength();
72 }
73 /// @}
74
75 private:
76 unsigned Offset = 0;
77 unsigned Length = 0;
78 };
79
80 /// A text replacement.
81 ///
82 /// Represents a SourceManager independent replacement of a range of text in a
83 /// specific file.
84 class Replacement {
85 public:
86 /// Creates an invalid (not applicable) replacement.
87 Replacement();
88
89 /// Creates a replacement of the range [Offset, Offset+Length) in
90 /// FilePath with ReplacementText.
91 ///
92 /// \param FilePath A source file accessible via a SourceManager.
93 /// \param Offset The byte offset of the start of the range in the file.
94 /// \param Length The length of the range in bytes.
95 Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
96 StringRef ReplacementText);
97
98 /// Creates a Replacement of the range [Start, Start+Length) with
99 /// ReplacementText.
100 Replacement(const SourceManager &Sources, SourceLocation Start,
101 unsigned Length, StringRef ReplacementText);
102
103 /// Creates a Replacement of the given range with ReplacementText.
104 Replacement(const SourceManager &Sources, const CharSourceRange &Range,
105 StringRef ReplacementText,
106 const LangOptions &LangOpts = LangOptions());
107
108 /// Creates a Replacement of the node with ReplacementText.
109 template <typename Node>
110 Replacement(const SourceManager &Sources, const Node &NodeToReplace,
111 StringRef ReplacementText,
112 const LangOptions &LangOpts = LangOptions());
113
114 /// Returns whether this replacement can be applied to a file.
115 ///
116 /// Only replacements that are in a valid file can be applied.
117 bool isApplicable() const;
118
119 /// Accessors.
120 /// @{
getFilePath()121 StringRef getFilePath() const { return FilePath; }
getOffset()122 unsigned getOffset() const { return ReplacementRange.getOffset(); }
getLength()123 unsigned getLength() const { return ReplacementRange.getLength(); }
getReplacementText()124 StringRef getReplacementText() const { return ReplacementText; }
125 /// @}
126
127 /// Applies the replacement on the Rewriter.
128 bool apply(Rewriter &Rewrite) const;
129
130 /// Returns a human readable string representation.
131 std::string toString() const;
132
133 private:
134 void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start,
135 unsigned Length, StringRef ReplacementText);
136 void setFromSourceRange(const SourceManager &Sources,
137 const CharSourceRange &Range,
138 StringRef ReplacementText,
139 const LangOptions &LangOpts);
140
141 std::string FilePath;
142 Range ReplacementRange;
143 std::string ReplacementText;
144 };
145
146 enum class replacement_error {
147 fail_to_apply = 0,
148 wrong_file_path,
149 overlap_conflict,
150 insert_conflict,
151 };
152
153 /// Carries extra error information in replacement-related llvm::Error,
154 /// e.g. fail applying replacements and replacements conflict.
155 class ReplacementError : public llvm::ErrorInfo<ReplacementError> {
156 public:
ReplacementError(replacement_error Err)157 ReplacementError(replacement_error Err) : Err(Err) {}
158
159 /// Constructs an error related to an existing replacement.
ReplacementError(replacement_error Err,Replacement Existing)160 ReplacementError(replacement_error Err, Replacement Existing)
161 : Err(Err), ExistingReplacement(std::move(Existing)) {}
162
163 /// Constructs an error related to a new replacement and an existing
164 /// replacement in a set of replacements.
ReplacementError(replacement_error Err,Replacement New,Replacement Existing)165 ReplacementError(replacement_error Err, Replacement New, Replacement Existing)
166 : Err(Err), NewReplacement(std::move(New)),
167 ExistingReplacement(std::move(Existing)) {}
168
169 std::string message() const override;
170
log(raw_ostream & OS)171 void log(raw_ostream &OS) const override { OS << message(); }
172
get()173 replacement_error get() const { return Err; }
174
175 static char ID;
176
getNewReplacement()177 const llvm::Optional<Replacement> &getNewReplacement() const {
178 return NewReplacement;
179 }
180
getExistingReplacement()181 const llvm::Optional<Replacement> &getExistingReplacement() const {
182 return ExistingReplacement;
183 }
184
185 private:
186 // Users are not expected to use error_code.
convertToErrorCode()187 std::error_code convertToErrorCode() const override {
188 return llvm::inconvertibleErrorCode();
189 }
190
191 replacement_error Err;
192
193 // A new replacement, which is to expected be added into a set of
194 // replacements, that is causing problem.
195 llvm::Optional<Replacement> NewReplacement;
196
197 // An existing replacement in a replacements set that is causing problem.
198 llvm::Optional<Replacement> ExistingReplacement;
199 };
200
201 /// Less-than operator between two Replacements.
202 bool operator<(const Replacement &LHS, const Replacement &RHS);
203
204 /// Equal-to operator between two Replacements.
205 bool operator==(const Replacement &LHS, const Replacement &RHS);
206
207 /// Maintains a set of replacements that are conflict-free.
208 /// Two replacements are considered conflicts if they overlap or have the same
209 /// offset (i.e. order-dependent).
210 class Replacements {
211 private:
212 using ReplacementsImpl = std::set<Replacement>;
213
214 public:
215 using const_iterator = ReplacementsImpl::const_iterator;
216 using const_reverse_iterator = ReplacementsImpl::const_reverse_iterator;
217
218 Replacements() = default;
219
Replacements(const Replacement & R)220 explicit Replacements(const Replacement &R) { Replaces.insert(R); }
221
222 /// Adds a new replacement \p R to the current set of replacements.
223 /// \p R must have the same file path as all existing replacements.
224 /// Returns `success` if the replacement is successfully inserted; otherwise,
225 /// it returns an llvm::Error, i.e. there is a conflict between R and the
226 /// existing replacements (i.e. they are order-dependent) or R's file path is
227 /// different from the filepath of existing replacements. Callers must
228 /// explicitly check the Error returned, and the returned error can be
229 /// converted to a string message with `llvm::toString()`. This prevents users
230 /// from adding order-dependent replacements. To control the order in which
231 /// order-dependent replacements are applied, use merge({R}) with R referring
232 /// to the changed code after applying all existing replacements.
233 /// Two replacements A and B are considered order-independent if applying them
234 /// in either order produces the same result. Note that the range of the
235 /// replacement that is applied later still refers to the original code.
236 /// These include (but not restricted to) replacements that:
237 /// - don't overlap (being directly adjacent is fine) and
238 /// - are overlapping deletions.
239 /// - are insertions at the same offset and applying them in either order
240 /// has the same effect, i.e. X + Y = Y + X when inserting X and Y
241 /// respectively.
242 /// - are identical replacements, i.e. applying the same replacement twice
243 /// is equivalent to applying it once.
244 /// Examples:
245 /// 1. Replacement A(0, 0, "a") and B(0, 0, "aa") are order-independent since
246 /// applying them in either order gives replacement (0, 0, "aaa").
247 /// However, A(0, 0, "a") and B(0, 0, "b") are order-dependent since
248 /// applying A first gives (0, 0, "ab") while applying B first gives (B, A,
249 /// "ba").
250 /// 2. Replacement A(0, 2, "123") and B(0, 2, "123") are order-independent
251 /// since applying them in either order gives (0, 2, "123").
252 /// 3. Replacement A(0, 3, "123") and B(2, 3, "321") are order-independent
253 /// since either order gives (0, 5, "12321").
254 /// 4. Replacement A(0, 3, "ab") and B(0, 3, "ab") are order-independent since
255 /// applying the same replacement twice is equivalent to applying it once.
256 /// Replacements with offset UINT_MAX are special - we do not detect conflicts
257 /// for such replacements since users may add them intentionally as a special
258 /// category of replacements.
259 llvm::Error add(const Replacement &R);
260
261 /// Merges \p Replaces into the current replacements. \p Replaces
262 /// refers to code after applying the current replacements.
263 LLVM_NODISCARD Replacements merge(const Replacements &Replaces) const;
264
265 // Returns the affected ranges in the changed code.
266 std::vector<Range> getAffectedRanges() const;
267
268 // Returns the new offset in the code after replacements being applied.
269 // Note that if there is an insertion at Offset in the current replacements,
270 // \p Offset will be shifted to Offset + Length in inserted text.
271 unsigned getShiftedCodePosition(unsigned Position) const;
272
size()273 unsigned size() const { return Replaces.size(); }
274
clear()275 void clear() { Replaces.clear(); }
276
empty()277 bool empty() const { return Replaces.empty(); }
278
begin()279 const_iterator begin() const { return Replaces.begin(); }
280
end()281 const_iterator end() const { return Replaces.end(); }
282
rbegin()283 const_reverse_iterator rbegin() const { return Replaces.rbegin(); }
284
rend()285 const_reverse_iterator rend() const { return Replaces.rend(); }
286
287 bool operator==(const Replacements &RHS) const {
288 return Replaces == RHS.Replaces;
289 }
290
291 private:
Replacements(const_iterator Begin,const_iterator End)292 Replacements(const_iterator Begin, const_iterator End)
293 : Replaces(Begin, End) {}
294
295 // Returns `R` with new range that refers to code after `Replaces` being
296 // applied.
297 Replacement getReplacementInChangedCode(const Replacement &R) const;
298
299 // Returns a set of replacements that is equivalent to the current
300 // replacements by merging all adjacent replacements. Two sets of replacements
301 // are considered equivalent if they have the same effect when they are
302 // applied.
303 Replacements getCanonicalReplacements() const;
304
305 // If `R` and all existing replacements are order-indepedent, then merge it
306 // with `Replaces` and returns the merged replacements; otherwise, returns an
307 // error.
308 llvm::Expected<Replacements>
309 mergeIfOrderIndependent(const Replacement &R) const;
310
311 ReplacementsImpl Replaces;
312 };
313
314 /// Apply all replacements in \p Replaces to the Rewriter \p Rewrite.
315 ///
316 /// Replacement applications happen independently of the success of
317 /// other applications.
318 ///
319 /// \returns true if all replacements apply. false otherwise.
320 bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite);
321
322 /// Applies all replacements in \p Replaces to \p Code.
323 ///
324 /// This completely ignores the path stored in each replacement. If all
325 /// replacements are applied successfully, this returns the code with
326 /// replacements applied; otherwise, an llvm::Error carrying llvm::StringError
327 /// is returned (the Error message can be converted to string using
328 /// `llvm::toString()` and 'std::error_code` in the `Error` should be ignored).
329 llvm::Expected<std::string> applyAllReplacements(StringRef Code,
330 const Replacements &Replaces);
331
332 /// Collection of Replacements generated from a single translation unit.
333 struct TranslationUnitReplacements {
334 /// Name of the main source for the translation unit.
335 std::string MainSourceFile;
336
337 std::vector<Replacement> Replacements;
338 };
339
340 /// Calculates the new ranges after \p Replaces are applied. These
341 /// include both the original \p Ranges and the affected ranges of \p Replaces
342 /// in the new code.
343 ///
344 /// \pre Replacements must be for the same file.
345 ///
346 /// \return The new ranges after \p Replaces are applied. The new ranges will be
347 /// sorted and non-overlapping.
348 std::vector<Range>
349 calculateRangesAfterReplacements(const Replacements &Replaces,
350 const std::vector<Range> &Ranges);
351
352 /// If there are multiple <File, Replacements> pairs with the same file
353 /// entry, we only keep one pair and discard the rest.
354 /// If a file does not exist, its corresponding replacements will be ignored.
355 std::map<std::string, Replacements> groupReplacementsByFile(
356 FileManager &FileMgr,
357 const std::map<std::string, Replacements> &FileToReplaces);
358
359 template <typename Node>
Replacement(const SourceManager & Sources,const Node & NodeToReplace,StringRef ReplacementText,const LangOptions & LangOpts)360 Replacement::Replacement(const SourceManager &Sources,
361 const Node &NodeToReplace, StringRef ReplacementText,
362 const LangOptions &LangOpts) {
363 const CharSourceRange Range =
364 CharSourceRange::getTokenRange(NodeToReplace->getSourceRange());
365 setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
366 }
367
368 } // namespace tooling
369
370 } // namespace clang
371
372 #endif // LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H
373