1 //===--- Replacement.h - Framework for clang refactoring tools --*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
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.
17 //===----------------------------------------------------------------------===//
19 #ifndef LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H
20 #define LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H
22 #include "clang/Basic/FileManager.h"
23 #include "clang/Basic/LangOptions.h"
24 #include "clang/Basic/SourceLocation.h"
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"
32 #include <system_error>
41 /// \brief A source range independent of the \c SourceManager.
44 Range() : Offset(0), Length(0) {}
45 Range(unsigned Offset, unsigned Length) : Offset(Offset), Length(Length) {}
49 unsigned getOffset() const { return Offset; }
50 unsigned getLength() const { return Length; }
53 /// \name Range Predicates
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;
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);
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();
77 /// \brief A text replacement.
79 /// Represents a SourceManager independent replacement of a range of text in a
83 /// \brief Creates an invalid (not applicable) replacement.
86 /// \brief Creates a replacement of the range [Offset, Offset+Length) in
87 /// FilePath with ReplacementText.
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);
95 /// \brief Creates a Replacement of the range [Start, Start+Length) with
97 Replacement(const SourceManager &Sources, SourceLocation Start,
98 unsigned Length, StringRef ReplacementText);
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());
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());
111 /// \brief Returns whether this replacement can be applied to a file.
113 /// Only replacements that are in a valid file can be applied.
114 bool isApplicable() const;
116 /// \brief Accessors.
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; }
124 /// \brief Applies the replacement on the Rewriter.
125 bool apply(Rewriter &Rewrite) const;
127 /// \brief Returns a human readable string representation.
128 std::string toString() const;
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);
138 std::string FilePath;
139 Range ReplacementRange;
140 std::string ReplacementText;
143 enum class replacement_error {
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> {
154 ReplacementError(replacement_error Err) : Err(Err) {}
156 /// \brief Constructs an error related to an existing replacement.
157 ReplacementError(replacement_error Err, Replacement Existing)
158 : Err(Err), ExistingReplacement(std::move(Existing)) {}
160 /// \brief Constructs an error related to a new replacement and an existing
161 /// replacement in a set of replacements.
162 ReplacementError(replacement_error Err, Replacement New, Replacement Existing)
163 : Err(Err), NewReplacement(std::move(New)),
164 ExistingReplacement(std::move(Existing)) {}
166 std::string message() const override;
168 void log(raw_ostream &OS) const override { OS << message(); }
170 replacement_error get() const { return Err; }
174 const llvm::Optional<Replacement> &getNewReplacement() const {
175 return NewReplacement;
178 const llvm::Optional<Replacement> &getExistingReplacement() const {
179 return ExistingReplacement;
183 // Users are not expected to use error_code.
184 std::error_code convertToErrorCode() const override {
185 return llvm::inconvertibleErrorCode();
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;
196 /// \brief Less-than operator between two Replacements.
197 bool operator<(const Replacement &LHS, const Replacement &RHS);
199 /// \brief Equal-to operator between two Replacements.
200 bool operator==(const Replacement &LHS, const Replacement &RHS);
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).
207 typedef std::set<Replacement> ReplacementsImpl;
210 typedef ReplacementsImpl::const_iterator const_iterator;
211 typedef ReplacementsImpl::const_reverse_iterator const_reverse_iterator;
213 Replacements() = default;
215 explicit Replacements(const Replacement &R) { Replaces.insert(R); }
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
237 /// - are identical replacements, i.e. applying the same replacement twice
238 /// is equivalent to applying it once.
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,
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);
256 /// \brief Merges \p Replaces into the current replacements. \p Replaces
257 /// refers to code after applying the current replacements.
258 Replacements merge(const Replacements &Replaces) const;
260 // Returns the affected ranges in the changed code.
261 std::vector<Range> getAffectedRanges() const;
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;
268 unsigned size() const { return Replaces.size(); }
270 void clear() { Replaces.clear(); }
272 bool empty() const { return Replaces.empty(); }
274 const_iterator begin() const { return Replaces.begin(); }
276 const_iterator end() const { return Replaces.end(); }
278 const_reverse_iterator rbegin() const { return Replaces.rbegin(); }
280 const_reverse_iterator rend() const { return Replaces.rend(); }
282 bool operator==(const Replacements &RHS) const {
283 return Replaces == RHS.Replaces;
288 Replacements(const_iterator Begin, const_iterator End)
289 : Replaces(Begin, End) {}
291 // Returns `R` with new range that refers to code after `Replaces` being
293 Replacement getReplacementInChangedCode(const Replacement &R) const;
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
299 Replacements getCanonicalReplacements() const;
301 // If `R` and all existing replacements are order-indepedent, then merge it
302 // with `Replaces` and returns the merged replacements; otherwise, returns an
304 llvm::Expected<Replacements>
305 mergeIfOrderIndependent(const Replacement &R) const;
307 ReplacementsImpl Replaces;
310 /// \brief Apply all replacements in \p Replaces to the Rewriter \p Rewrite.
312 /// Replacement applications happen independently of the success of
313 /// other applications.
315 /// \returns true if all replacements apply. false otherwise.
316 bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite);
318 /// \brief Applies all replacements in \p Replaces to \p Code.
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).
325 llvm::Expected<std::string> applyAllReplacements(StringRef Code,
326 const Replacements &Replaces);
328 /// \brief Collection of Replacements generated from a single translation unit.
329 struct TranslationUnitReplacements {
330 /// Name of the main source for the translation unit.
331 std::string MainSourceFile;
332 std::vector<Replacement> Replacements;
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
339 /// \pre Replacements must be for the same file.
341 /// \return The new ranges after \p Replaces are applied. The new ranges will be
342 /// sorted and non-overlapping.
344 calculateRangesAfterReplacements(const Replacements &Replaces,
345 const std::vector<Range> &Ranges);
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);
354 template <typename Node>
355 Replacement::Replacement(const SourceManager &Sources,
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);
363 } // end namespace tooling
364 } // end namespace clang
366 #endif // LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H