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