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