Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:37:10

0001 //===- Replacement.h - Framework for clang refactoring tools ----*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 //  Classes supporting refactorings that span multiple translation units.
0010 //  While single translation unit refactorings are supported via the Rewriter,
0011 //  when refactoring multiple translation units changes must be stored in a
0012 //  SourceManager independent form, duplicate changes need to be removed, and
0013 //  all changes must be applied at once at the end of the refactoring so that
0014 //  the code is always parseable.
0015 //
0016 //===----------------------------------------------------------------------===//
0017 
0018 #ifndef LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H
0019 #define LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H
0020 
0021 #include "clang/Basic/LangOptions.h"
0022 #include "clang/Basic/SourceLocation.h"
0023 #include "llvm/ADT/StringRef.h"
0024 #include "llvm/Support/Compiler.h"
0025 #include "llvm/Support/Error.h"
0026 #include "llvm/Support/raw_ostream.h"
0027 #include <map>
0028 #include <optional>
0029 #include <set>
0030 #include <string>
0031 #include <system_error>
0032 #include <utility>
0033 #include <vector>
0034 
0035 namespace clang {
0036 
0037 class FileManager;
0038 class Rewriter;
0039 class SourceManager;
0040 
0041 namespace tooling {
0042 
0043 /// A source range independent of the \c SourceManager.
0044 class Range {
0045 public:
0046   Range() = default;
0047   Range(unsigned Offset, unsigned Length) : Offset(Offset), Length(Length) {}
0048 
0049   /// Accessors.
0050   /// @{
0051   unsigned getOffset() const { return Offset; }
0052   unsigned getLength() const { return Length; }
0053   /// @}
0054 
0055   /// \name Range Predicates
0056   /// @{
0057   /// Whether this range overlaps with \p RHS or not.
0058   bool overlapsWith(Range RHS) const {
0059     return Offset + Length > RHS.Offset && Offset < RHS.Offset + RHS.Length;
0060   }
0061 
0062   /// Whether this range contains \p RHS or not.
0063   bool contains(Range RHS) const {
0064     return RHS.Offset >= Offset &&
0065            (RHS.Offset + RHS.Length) <= (Offset + Length);
0066   }
0067 
0068   /// Whether this range equals to \p RHS or not.
0069   bool operator==(const Range &RHS) const {
0070     return Offset == RHS.getOffset() && Length == RHS.getLength();
0071   }
0072   /// @}
0073 
0074 private:
0075   unsigned Offset = 0;
0076   unsigned Length = 0;
0077 };
0078 
0079 /// A text replacement.
0080 ///
0081 /// Represents a SourceManager independent replacement of a range of text in a
0082 /// specific file.
0083 class Replacement {
0084 public:
0085   /// Creates an invalid (not applicable) replacement.
0086   Replacement();
0087 
0088   /// Creates a replacement of the range [Offset, Offset+Length) in
0089   /// FilePath with ReplacementText.
0090   ///
0091   /// \param FilePath A source file accessible via a SourceManager.
0092   /// \param Offset The byte offset of the start of the range in the file.
0093   /// \param Length The length of the range in bytes.
0094   Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
0095               StringRef ReplacementText);
0096 
0097   /// Creates a Replacement of the range [Start, Start+Length) with
0098   /// ReplacementText.
0099   Replacement(const SourceManager &Sources, SourceLocation Start,
0100               unsigned Length, StringRef ReplacementText);
0101 
0102   /// Creates a Replacement of the given range with ReplacementText.
0103   Replacement(const SourceManager &Sources, const CharSourceRange &Range,
0104               StringRef ReplacementText,
0105               const LangOptions &LangOpts = LangOptions());
0106 
0107   /// Creates a Replacement of the node with ReplacementText.
0108   template <typename Node>
0109   Replacement(const SourceManager &Sources, const Node &NodeToReplace,
0110               StringRef ReplacementText,
0111               const LangOptions &LangOpts = LangOptions());
0112 
0113   /// Returns whether this replacement can be applied to a file.
0114   ///
0115   /// Only replacements that are in a valid file can be applied.
0116   bool isApplicable() const;
0117 
0118   /// Accessors.
0119   /// @{
0120   StringRef getFilePath() const { return FilePath; }
0121   unsigned getOffset() const { return ReplacementRange.getOffset(); }
0122   unsigned getLength() const { return ReplacementRange.getLength(); }
0123   StringRef getReplacementText() const { return ReplacementText; }
0124   /// @}
0125 
0126   /// Applies the replacement on the Rewriter.
0127   bool apply(Rewriter &Rewrite) const;
0128 
0129   /// Returns a human readable string representation.
0130   std::string toString() const;
0131 
0132 private:
0133   void setFromSourceLocation(const SourceManager &Sources, SourceLocation Start,
0134                              unsigned Length, StringRef ReplacementText);
0135   void setFromSourceRange(const SourceManager &Sources,
0136                           const CharSourceRange &Range,
0137                           StringRef ReplacementText,
0138                           const LangOptions &LangOpts);
0139 
0140   std::string FilePath;
0141   Range ReplacementRange;
0142   std::string ReplacementText;
0143 };
0144 
0145 enum class replacement_error {
0146   fail_to_apply = 0,
0147   wrong_file_path,
0148   overlap_conflict,
0149   insert_conflict,
0150 };
0151 
0152 /// Carries extra error information in replacement-related llvm::Error,
0153 /// e.g. fail applying replacements and replacements conflict.
0154 class ReplacementError : public llvm::ErrorInfo<ReplacementError> {
0155 public:
0156   ReplacementError(replacement_error Err) : Err(Err) {}
0157 
0158   /// Constructs an error related to an existing replacement.
0159   ReplacementError(replacement_error Err, Replacement Existing)
0160       : Err(Err), ExistingReplacement(std::move(Existing)) {}
0161 
0162   /// Constructs an error related to a new replacement and an existing
0163   /// replacement in a set of replacements.
0164   ReplacementError(replacement_error Err, Replacement New, Replacement Existing)
0165       : Err(Err), NewReplacement(std::move(New)),
0166         ExistingReplacement(std::move(Existing)) {}
0167 
0168   std::string message() const override;
0169 
0170   void log(raw_ostream &OS) const override { OS << message(); }
0171 
0172   replacement_error get() const { return Err; }
0173 
0174   static char ID;
0175 
0176   const std::optional<Replacement> &getNewReplacement() const {
0177     return NewReplacement;
0178   }
0179 
0180   const std::optional<Replacement> &getExistingReplacement() const {
0181     return ExistingReplacement;
0182   }
0183 
0184 private:
0185   // Users are not expected to use error_code.
0186   std::error_code convertToErrorCode() const override {
0187     return llvm::inconvertibleErrorCode();
0188   }
0189 
0190   replacement_error Err;
0191 
0192   // A new replacement, which is to expected be added into a set of
0193   // replacements, that is causing problem.
0194   std::optional<Replacement> NewReplacement;
0195 
0196   // An existing replacement in a replacements set that is causing problem.
0197   std::optional<Replacement> ExistingReplacement;
0198 };
0199 
0200 /// Less-than operator between two Replacements.
0201 bool operator<(const Replacement &LHS, const Replacement &RHS);
0202 
0203 /// Equal-to operator between two Replacements.
0204 bool operator==(const Replacement &LHS, const Replacement &RHS);
0205 inline bool operator!=(const Replacement &LHS, const Replacement &RHS) {
0206   return !(LHS == RHS);
0207 }
0208 
0209 /// Maintains a set of replacements that are conflict-free.
0210 /// Two replacements are considered conflicts if they overlap or have the same
0211 /// offset (i.e. order-dependent).
0212 class Replacements {
0213 private:
0214   using ReplacementsImpl = std::set<Replacement>;
0215 
0216 public:
0217   using const_iterator = ReplacementsImpl::const_iterator;
0218   using const_reverse_iterator = ReplacementsImpl::const_reverse_iterator;
0219 
0220   Replacements() = default;
0221 
0222   explicit Replacements(const Replacement &R) { Replaces.insert(R); }
0223 
0224   /// Adds a new replacement \p R to the current set of replacements.
0225   /// \p R must have the same file path as all existing replacements.
0226   /// Returns `success` if the replacement is successfully inserted; otherwise,
0227   /// it returns an llvm::Error, i.e. there is a conflict between R and the
0228   /// existing replacements (i.e. they are order-dependent) or R's file path is
0229   /// different from the filepath of existing replacements. Callers must
0230   /// explicitly check the Error returned, and the returned error can be
0231   /// converted to a string message with `llvm::toString()`. This prevents users
0232   /// from adding order-dependent replacements. To control the order in which
0233   /// order-dependent replacements are applied, use merge({R}) with R referring
0234   /// to the changed code after applying all existing replacements.
0235   /// Two replacements A and B are considered order-independent if applying them
0236   /// in either order produces the same result. Note that the range of the
0237   /// replacement that is applied later still refers to the original code.
0238   /// These include (but not restricted to) replacements that:
0239   ///   - don't overlap (being directly adjacent is fine) and
0240   ///   - are overlapping deletions.
0241   ///   - are insertions at the same offset and applying them in either order
0242   ///     has the same effect, i.e. X + Y = Y + X when inserting X and Y
0243   ///     respectively.
0244   ///   - are identical replacements, i.e. applying the same replacement twice
0245   ///     is equivalent to applying it once.
0246   /// Examples:
0247   /// 1. Replacement A(0, 0, "a") and B(0, 0, "aa") are order-independent since
0248   ///    applying them in either order gives replacement (0, 0, "aaa").
0249   ///    However, A(0, 0, "a") and B(0, 0, "b") are order-dependent since
0250   ///    applying A first gives (0, 0, "ab") while applying B first gives (B, A,
0251   ///    "ba").
0252   /// 2. Replacement A(0, 2, "123") and B(0, 2, "123") are order-independent
0253   ///    since applying them in either order gives (0, 2, "123").
0254   /// 3. Replacement A(0, 3, "123") and B(2, 3, "321") are order-independent
0255   ///    since either order gives (0, 5, "12321").
0256   /// 4. Replacement A(0, 3, "ab") and B(0, 3, "ab") are order-independent since
0257   ///    applying the same replacement twice is equivalent to applying it once.
0258   /// Replacements with offset UINT_MAX are special - we do not detect conflicts
0259   /// for such replacements since users may add them intentionally as a special
0260   /// category of replacements.
0261   llvm::Error add(const Replacement &R);
0262 
0263   /// Merges \p Replaces into the current replacements. \p Replaces
0264   /// refers to code after applying the current replacements.
0265   [[nodiscard]] Replacements merge(const Replacements &Replaces) const;
0266 
0267   // Returns the affected ranges in the changed code.
0268   std::vector<Range> getAffectedRanges() const;
0269 
0270   // Returns the new offset in the code after replacements being applied.
0271   // Note that if there is an insertion at Offset in the current replacements,
0272   // \p Offset will be shifted to Offset + Length in inserted text.
0273   unsigned getShiftedCodePosition(unsigned Position) const;
0274 
0275   unsigned size() const { return Replaces.size(); }
0276 
0277   void clear() { Replaces.clear(); }
0278 
0279   bool empty() const { return Replaces.empty(); }
0280 
0281   const_iterator begin() const { return Replaces.begin(); }
0282 
0283   const_iterator end() const { return Replaces.end(); }
0284 
0285   const_reverse_iterator rbegin() const  { return Replaces.rbegin(); }
0286 
0287   const_reverse_iterator rend() const { return Replaces.rend(); }
0288 
0289   bool operator==(const Replacements &RHS) const {
0290     return Replaces == RHS.Replaces;
0291   }
0292 
0293 private:
0294   Replacements(const_iterator Begin, const_iterator End)
0295       : Replaces(Begin, End) {}
0296 
0297   // Returns `R` with new range that refers to code after `Replaces` being
0298   // applied.
0299   Replacement getReplacementInChangedCode(const Replacement &R) const;
0300 
0301   // Returns a set of replacements that is equivalent to the current
0302   // replacements by merging all adjacent replacements. Two sets of replacements
0303   // are considered equivalent if they have the same effect when they are
0304   // applied.
0305   Replacements getCanonicalReplacements() const;
0306 
0307   // If `R` and all existing replacements are order-independent, then merge it
0308   // with `Replaces` and returns the merged replacements; otherwise, returns an
0309   // error.
0310   llvm::Expected<Replacements>
0311   mergeIfOrderIndependent(const Replacement &R) const;
0312 
0313   ReplacementsImpl Replaces;
0314 };
0315 
0316 /// Apply all replacements in \p Replaces to the Rewriter \p Rewrite.
0317 ///
0318 /// Replacement applications happen independently of the success of
0319 /// other applications.
0320 ///
0321 /// \returns true if all replacements apply. false otherwise.
0322 bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite);
0323 
0324 /// Applies all replacements in \p Replaces to \p Code.
0325 ///
0326 /// This completely ignores the path stored in each replacement. If all
0327 /// replacements are applied successfully, this returns the code with
0328 /// replacements applied; otherwise, an llvm::Error carrying llvm::StringError
0329 /// is returned (the Error message can be converted to string using
0330 /// `llvm::toString()` and 'std::error_code` in the `Error` should be ignored).
0331 llvm::Expected<std::string> applyAllReplacements(StringRef Code,
0332                                                  const Replacements &Replaces);
0333 
0334 /// Collection of Replacements generated from a single translation unit.
0335 struct TranslationUnitReplacements {
0336   /// Name of the main source for the translation unit.
0337   std::string MainSourceFile;
0338 
0339   std::vector<Replacement> Replacements;
0340 };
0341 
0342 /// Calculates the new ranges after \p Replaces are applied. These
0343 /// include both the original \p Ranges and the affected ranges of \p Replaces
0344 /// in the new code.
0345 ///
0346 /// \pre Replacements must be for the same file.
0347 ///
0348 /// \return The new ranges after \p Replaces are applied. The new ranges will be
0349 /// sorted and non-overlapping.
0350 std::vector<Range>
0351 calculateRangesAfterReplacements(const Replacements &Replaces,
0352                                  const std::vector<Range> &Ranges);
0353 
0354 /// If there are multiple <File, Replacements> pairs with the same file
0355 /// entry, we only keep one pair and discard the rest.
0356 /// If a file does not exist, its corresponding replacements will be ignored.
0357 std::map<std::string, Replacements> groupReplacementsByFile(
0358     FileManager &FileMgr,
0359     const std::map<std::string, Replacements> &FileToReplaces);
0360 
0361 template <typename Node>
0362 Replacement::Replacement(const SourceManager &Sources,
0363                          const Node &NodeToReplace, StringRef ReplacementText,
0364                          const LangOptions &LangOpts) {
0365   const CharSourceRange Range =
0366       CharSourceRange::getTokenRange(NodeToReplace->getSourceRange());
0367   setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
0368 }
0369 
0370 } // namespace tooling
0371 
0372 } // namespace clang
0373 
0374 #endif // LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H