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 return Offset + Length > RHS.Offset && Offset < RHS.Offset + RHS.Length; 52 } 53 54 /// \brief Whether this range contains \p RHS or not. 55 bool contains(Range RHS) const { 56 return RHS.Offset >= Offset && 57 (RHS.Offset + RHS.Length) <= (Offset + Length); 58 } 59 /// @} 60 61 private: 62 unsigned Offset; 63 unsigned Length; 64 }; 65 66 /// \brief A text replacement. 67 /// 68 /// Represents a SourceManager independent replacement of a range of text in a 69 /// specific file. 70 class Replacement { 71 public: 72 /// \brief Creates an invalid (not applicable) replacement. 73 Replacement(); 74 75 /// \brief Creates a replacement of the range [Offset, Offset+Length) in 76 /// FilePath with ReplacementText. 77 /// 78 /// \param FilePath A source file accessible via a SourceManager. 79 /// \param Offset The byte offset of the start of the range in the file. 80 /// \param Length The length of the range in bytes. 81 Replacement(StringRef FilePath, unsigned Offset, 82 unsigned Length, StringRef ReplacementText); 83 84 /// \brief Creates a Replacement of the range [Start, Start+Length) with 85 /// ReplacementText. 86 Replacement(SourceManager &Sources, SourceLocation Start, unsigned Length, 87 StringRef ReplacementText); 88 89 /// \brief Creates a Replacement of the given range with ReplacementText. 90 Replacement(SourceManager &Sources, const CharSourceRange &Range, 91 StringRef ReplacementText); 92 93 /// \brief Creates a Replacement of the node with ReplacementText. 94 template <typename Node> 95 Replacement(SourceManager &Sources, const Node &NodeToReplace, 96 StringRef ReplacementText); 97 98 /// \brief Returns whether this replacement can be applied to a file. 99 /// 100 /// Only replacements that are in a valid file can be applied. 101 bool isApplicable() const; 102 103 /// \brief Accessors. 104 /// @{ 105 StringRef getFilePath() const { return FilePath; } 106 unsigned getOffset() const { return ReplacementRange.getOffset(); } 107 unsigned getLength() const { return ReplacementRange.getLength(); } 108 StringRef getReplacementText() const { return ReplacementText; } 109 /// @} 110 111 /// \brief Applies the replacement on the Rewriter. 112 bool apply(Rewriter &Rewrite) const; 113 114 /// \brief Returns a human readable string representation. 115 std::string toString() const; 116 117 private: 118 void setFromSourceLocation(SourceManager &Sources, SourceLocation Start, 119 unsigned Length, StringRef ReplacementText); 120 void setFromSourceRange(SourceManager &Sources, const CharSourceRange &Range, 121 StringRef ReplacementText); 122 123 std::string FilePath; 124 Range ReplacementRange; 125 std::string ReplacementText; 126 }; 127 128 /// \brief Less-than operator between two Replacements. 129 bool operator<(const Replacement &LHS, const Replacement &RHS); 130 131 /// \brief Equal-to operator between two Replacements. 132 bool operator==(const Replacement &LHS, const Replacement &RHS); 133 134 /// \brief A set of Replacements. 135 /// FIXME: Change to a vector and deduplicate in the RefactoringTool. 136 typedef std::set<Replacement> Replacements; 137 138 /// \brief Apply all replacements in \p Replaces to the Rewriter \p Rewrite. 139 /// 140 /// Replacement applications happen independently of the success of 141 /// other applications. 142 /// 143 /// \returns true if all replacements apply. false otherwise. 144 bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite); 145 146 /// \brief Apply all replacements in \p Replaces to the Rewriter \p Rewrite. 147 /// 148 /// Replacement applications happen independently of the success of 149 /// other applications. 150 /// 151 /// \returns true if all replacements apply. false otherwise. 152 bool applyAllReplacements(const std::vector<Replacement> &Replaces, 153 Rewriter &Rewrite); 154 155 /// \brief Applies all replacements in \p Replaces to \p Code. 156 /// 157 /// This completely ignores the path stored in each replacement. If one or more 158 /// replacements cannot be applied, this returns an empty \c string. 159 std::string applyAllReplacements(StringRef Code, const Replacements &Replaces); 160 161 /// \brief Calculates how a code \p Position is shifted when \p Replaces are 162 /// applied. 163 unsigned shiftedCodePosition(const Replacements& Replaces, unsigned Position); 164 165 /// \brief Calculates how a code \p Position is shifted when \p Replaces are 166 /// applied. 167 /// 168 /// \pre Replaces[i].getOffset() <= Replaces[i+1].getOffset(). 169 unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces, 170 unsigned Position); 171 172 /// \brief Removes duplicate Replacements and reports if Replacements conflict 173 /// with one another. 174 /// 175 /// \post Replaces[i].getOffset() <= Replaces[i+1].getOffset(). 176 /// 177 /// This function sorts \p Replaces so that conflicts can be reported simply by 178 /// offset into \p Replaces and number of elements in the conflict. 179 void deduplicate(std::vector<Replacement> &Replaces, 180 std::vector<Range> &Conflicts); 181 182 /// \brief Collection of Replacements generated from a single translation unit. 183 struct TranslationUnitReplacements { 184 /// Name of the main source for the translation unit. 185 std::string MainSourceFile; 186 187 /// A freeform chunk of text to describe the context of the replacements. 188 /// Will be printed, for example, when detecting conflicts during replacement 189 /// deduplication. 190 std::string Context; 191 192 std::vector<Replacement> Replacements; 193 }; 194 195 /// \brief A tool to run refactorings. 196 /// 197 /// This is a refactoring specific version of \see ClangTool. FrontendActions 198 /// passed to run() and runAndSave() should add replacements to 199 /// getReplacements(). 200 class RefactoringTool : public ClangTool { 201 public: 202 /// \see ClangTool::ClangTool. 203 RefactoringTool(const CompilationDatabase &Compilations, 204 ArrayRef<std::string> SourcePaths); 205 206 /// \brief Returns the set of replacements to which replacements should 207 /// be added during the run of the tool. 208 Replacements &getReplacements(); 209 210 /// \brief Call run(), apply all generated replacements, and immediately save 211 /// the results to disk. 212 /// 213 /// \returns 0 upon success. Non-zero upon failure. 214 int runAndSave(FrontendActionFactory *ActionFactory); 215 216 /// \brief Apply all stored replacements to the given Rewriter. 217 /// 218 /// Replacement applications happen independently of the success of other 219 /// applications. 220 /// 221 /// \returns true if all replacements apply. false otherwise. 222 bool applyAllReplacements(Rewriter &Rewrite); 223 224 private: 225 /// \brief Write all refactored files to disk. 226 int saveRewrittenFiles(Rewriter &Rewrite); 227 228 private: 229 Replacements Replace; 230 }; 231 232 template <typename Node> 233 Replacement::Replacement(SourceManager &Sources, const Node &NodeToReplace, 234 StringRef ReplacementText) { 235 const CharSourceRange Range = 236 CharSourceRange::getTokenRange(NodeToReplace->getSourceRange()); 237 setFromSourceRange(Sources, Range, ReplacementText); 238 } 239 240 } // end namespace tooling 241 } // end namespace clang 242 243 #endif // end namespace LLVM_CLANG_TOOLING_REFACTORING_H 244