Home | History | Annotate | Download | only in Tooling
      1 //===--- Refactoring.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 //  Interfaces 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_REFACTORING_H
     20 #define LLVM_CLANG_TOOLING_REFACTORING_H
     21 
     22 #include "clang/Basic/SourceLocation.h"
     23 #include "clang/Tooling/Tooling.h"
     24 #include "llvm/ADT/StringRef.h"
     25 #include <set>
     26 #include <string>
     27 
     28 namespace clang {
     29 
     30 class Rewriter;
     31 class SourceLocation;
     32 
     33 namespace tooling {
     34 
     35 /// \brief A source range independent of the \c SourceManager.
     36 class Range {
     37 public:
     38   Range() : Offset(0), Length(0) {}
     39   Range(unsigned Offset, unsigned Length) : Offset(Offset), Length(Length) {}
     40 
     41   /// \brief Accessors.
     42   /// @{
     43   unsigned getOffset() const { return Offset; }
     44   unsigned getLength() const { return Length; }
     45   /// @}
     46 
     47   /// \name Range Predicates
     48   /// @{
     49   /// \brief Whether this range overlaps with \p RHS or not.
     50   bool overlapsWith(Range RHS) const {
     51     if ((Offset + Length) <= RHS.Offset || Offset >= (RHS.Offset + RHS.Length))
     52       return false;
     53     return true;
     54   }
     55 
     56   /// \brief Whether this range contains \p RHS or not.
     57   bool contains(Range RHS) const {
     58     return RHS.Offset >= Offset &&
     59            (RHS.Offset + RHS.Length) <= (Offset + Length);
     60   }
     61   /// @}
     62 
     63 private:
     64   unsigned Offset;
     65   unsigned Length;
     66 };
     67 
     68 /// \brief A text replacement.
     69 ///
     70 /// Represents a SourceManager independent replacement of a range of text in a
     71 /// specific file.
     72 class Replacement {
     73 public:
     74   /// \brief Creates an invalid (not applicable) replacement.
     75   Replacement();
     76 
     77   /// \brief Creates a replacement of the range [Offset, Offset+Length) in
     78   /// FilePath with ReplacementText.
     79   ///
     80   /// \param FilePath A source file accessible via a SourceManager.
     81   /// \param Offset The byte offset of the start of the range in the file.
     82   /// \param Length The length of the range in bytes.
     83   Replacement(StringRef FilePath, unsigned Offset,
     84               unsigned Length, StringRef ReplacementText);
     85 
     86   /// \brief Creates a Replacement of the range [Start, Start+Length) with
     87   /// ReplacementText.
     88   Replacement(SourceManager &Sources, SourceLocation Start, unsigned Length,
     89               StringRef ReplacementText);
     90 
     91   /// \brief Creates a Replacement of the given range with ReplacementText.
     92   Replacement(SourceManager &Sources, const CharSourceRange &Range,
     93               StringRef ReplacementText);
     94 
     95   /// \brief Creates a Replacement of the node with ReplacementText.
     96   template <typename Node>
     97   Replacement(SourceManager &Sources, const Node &NodeToReplace,
     98               StringRef ReplacementText);
     99 
    100   /// \brief Returns whether this replacement can be applied to a file.
    101   ///
    102   /// Only replacements that are in a valid file can be applied.
    103   bool isApplicable() const;
    104 
    105   /// \brief Accessors.
    106   /// @{
    107   StringRef getFilePath() const { return FilePath; }
    108   unsigned getOffset() const { return ReplacementRange.getOffset(); }
    109   unsigned getLength() const { return ReplacementRange.getLength(); }
    110   StringRef getReplacementText() const { return ReplacementText; }
    111   /// @}
    112 
    113   /// \brief Applies the replacement on the Rewriter.
    114   bool apply(Rewriter &Rewrite) const;
    115 
    116   /// \brief Returns a human readable string representation.
    117   std::string toString() const;
    118 
    119   /// \brief Comparator to be able to use Replacement in std::set for uniquing.
    120   class Less {
    121   public:
    122     bool operator()(const Replacement &R1, const Replacement &R2) const;
    123   };
    124 
    125  private:
    126   void setFromSourceLocation(SourceManager &Sources, SourceLocation Start,
    127                              unsigned Length, StringRef ReplacementText);
    128   void setFromSourceRange(SourceManager &Sources, const CharSourceRange &Range,
    129                           StringRef ReplacementText);
    130 
    131   std::string FilePath;
    132   Range ReplacementRange;
    133   std::string ReplacementText;
    134 };
    135 
    136 /// \brief A set of Replacements.
    137 /// FIXME: Change to a vector and deduplicate in the RefactoringTool.
    138 typedef std::set<Replacement, Replacement::Less> Replacements;
    139 
    140 /// \brief Apply all replacements in \p Replaces to the Rewriter \p Rewrite.
    141 ///
    142 /// Replacement applications happen independently of the success of
    143 /// other applications.
    144 ///
    145 /// \returns true if all replacements apply. false otherwise.
    146 bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite);
    147 
    148 /// \brief Applies all replacements in \p Replaces to \p Code.
    149 ///
    150 /// This completely ignores the path stored in each replacement. If one or more
    151 /// replacements cannot be applied, this returns an empty \c string.
    152 std::string applyAllReplacements(StringRef Code, const Replacements &Replaces);
    153 
    154 /// \brief Calculates how a code \p Position is shifted when \p Replaces are
    155 /// applied.
    156 unsigned shiftedCodePosition(const Replacements& Replaces, unsigned Position);
    157 
    158 /// \brief A tool to run refactorings.
    159 ///
    160 /// This is a refactoring specific version of \see ClangTool. FrontendActions
    161 /// passed to run() and runAndSave() should add replacements to
    162 /// getReplacements().
    163 class RefactoringTool : public ClangTool {
    164 public:
    165   /// \see ClangTool::ClangTool.
    166   RefactoringTool(const CompilationDatabase &Compilations,
    167                   ArrayRef<std::string> SourcePaths);
    168 
    169   /// \brief Returns the set of replacements to which replacements should
    170   /// be added during the run of the tool.
    171   Replacements &getReplacements();
    172 
    173   /// \brief Call run(), apply all generated replacements, and immediately save
    174   /// the results to disk.
    175   ///
    176   /// \returns 0 upon success. Non-zero upon failure.
    177   int runAndSave(FrontendActionFactory *ActionFactory);
    178 
    179   /// \brief Apply all stored replacements to the given Rewriter.
    180   ///
    181   /// Replacement applications happen independently of the success of other
    182   /// applications.
    183   ///
    184   /// \returns true if all replacements apply. false otherwise.
    185   bool applyAllReplacements(Rewriter &Rewrite);
    186 
    187 private:
    188   /// \brief Write all refactored files to disk.
    189   int saveRewrittenFiles(Rewriter &Rewrite);
    190 
    191 private:
    192   Replacements Replace;
    193 };
    194 
    195 template <typename Node>
    196 Replacement::Replacement(SourceManager &Sources, const Node &NodeToReplace,
    197                          StringRef ReplacementText) {
    198   const CharSourceRange Range =
    199       CharSourceRange::getTokenRange(NodeToReplace->getSourceRange());
    200   setFromSourceRange(Sources, Range, ReplacementText);
    201 }
    202 
    203 } // end namespace tooling
    204 } // end namespace clang
    205 
    206 #endif // end namespace LLVM_CLANG_TOOLING_REFACTORING_H
    207