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