clang  8.0.0svn
Replacement.h
Go to the documentation of this file.
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 
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;
48  Range(unsigned Offset, unsigned Length) : Offset(Offset), Length(Length) {}
49 
50  /// Accessors.
51  /// @{
52  unsigned getOffset() const { return Offset; }
53  unsigned getLength() const { return Length; }
54  /// @}
55 
56  /// \name Range Predicates
57  /// @{
58  /// Whether this range overlaps with \p RHS or not.
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.
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  /// @{
121  StringRef getFilePath() const { return FilePath; }
122  unsigned getOffset() const { return ReplacementRange.getOffset(); }
123  unsigned getLength() const { return ReplacementRange.getLength(); }
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,
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:
158 
159  /// Constructs an error related to an existing replacement.
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.
166  : Err(Err), NewReplacement(std::move(New)),
167  ExistingReplacement(std::move(Existing)) {}
168 
169  std::string message() const override;
170 
171  void log(raw_ostream &OS) const override { OS << message(); }
172 
173  replacement_error get() const { return Err; }
174 
175  static char ID;
176 
178  return NewReplacement;
179  }
180 
182  return ExistingReplacement;
183  }
184 
185 private:
186  // Users are not expected to use error_code.
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).
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 
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 
273  unsigned size() const { return Replaces.size(); }
274 
275  void clear() { Replaces.clear(); }
276 
277  bool empty() const { return Replaces.empty(); }
278 
279  const_iterator begin() const { return Replaces.begin(); }
280 
281  const_iterator end() const { return Replaces.end(); }
282 
283  const_reverse_iterator rbegin() const { return Replaces.rbegin(); }
284 
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:
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.
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).
330  const Replacements &Replaces);
331 
332 /// Collection of Replacements generated from a single translation unit.
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>
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>
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
const llvm::Optional< Replacement > & getExistingReplacement() const
Definition: Replacement.h:181
bool operator==(const Replacements &RHS) const
Definition: Replacement.h:287
StringRef getFilePath() const
Accessors.
Definition: Replacement.h:121
bool overlapsWith(Range RHS) const
Definition: Replacement.h:59
const llvm::Optional< Replacement > & getNewReplacement() const
Definition: Replacement.h:177
Implements support for file system lookup, file system caching, and directory search management...
Definition: FileManager.h:116
bool contains(Range RHS) const
Whether this range contains RHS or not.
Definition: Replacement.h:64
static CharSourceRange getTokenRange(SourceRange R)
Replacements(const Replacement &R)
Definition: Replacement.h:220
Maintains a set of replacements that are conflict-free.
Definition: Replacement.h:210
ReplacementError(replacement_error Err)
Definition: Replacement.h:157
bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite)
Apply all replacements in Replaces to the Rewriter Rewrite.
const_reverse_iterator rend() const
Definition: Replacement.h:285
Definition: Format.h:2031
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:50
Collection of Replacements generated from a single translation unit.
Definition: Replacement.h:333
uint32_t Offset
Definition: CacheTokens.cpp:43
Replacement()
Creates an invalid (not applicable) replacement.
Definition: Replacement.cpp:46
const_iterator end() const
Definition: Replacement.h:281
A text replacement.
Definition: Replacement.h:84
unsigned getLength() const
Definition: Replacement.h:53
A source range independent of the SourceManager.
Definition: Replacement.h:45
bool operator<(const Replacement &LHS, const Replacement &RHS)
Less-than operator between two Replacements.
Definition: Replacement.cpp:99
bool operator==(const Range &RHS) const
Whether this range equals to RHS or not.
Definition: Replacement.h:70
void log(raw_ostream &OS) const override
Definition: Replacement.h:171
Carries extra error information in replacement-related llvm::Error, e.g.
Definition: Replacement.h:155
Defines the clang::LangOptions interface.
SourceLocation End
Represents a character-granular source range.
SourceLocation Begin
unsigned getOffset() const
Accessors.
Definition: Replacement.h:52
const_iterator begin() const
Definition: Replacement.h:279
const_reverse_iterator rbegin() const
Definition: Replacement.h:283
Encodes a location in the source.
std::string MainSourceFile
Name of the main source for the translation unit.
Definition: Replacement.h:335
unsigned getLength() const
Definition: Replacement.h:123
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:165
ast_type_traits::DynTypedNode Node
Dataflow Directional Tag Classes.
std::vector< Replacement > Replacements
Definition: Replacement.h:337
unsigned getOffset() const
Definition: Replacement.h:122
std::string toString(const til::SExpr *E)
Rewriter - This is the main interface to the rewrite buffers.
Definition: Rewriter.h:33
Defines the clang::SourceLocation class and associated facilities.
Range(unsigned Offset, unsigned Length)
Definition: Replacement.h:48
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 ...
ReplacementsImpl::const_reverse_iterator const_reverse_iterator
Definition: Replacement.h:216
ReplacementError(replacement_error Err, Replacement Existing)
Constructs an error related to an existing replacement.
Definition: Replacement.h:160
ReplacementsImpl::const_iterator const_iterator
Definition: Replacement.h:215
std::vector< Range > calculateRangesAfterReplacements(const Replacements &Replaces, const std::vector< Range > &Ranges)
Calculates the new ranges after Replaces are applied.
StringRef getReplacementText() const
Definition: Replacement.h:124
This class handles loading and caching of source files into memory.