• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- Replacement.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 //  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.
16 //
17 //===----------------------------------------------------------------------===//
18 
19 #ifndef LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H
20 #define LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H
21 
22 #include "clang/Basic/LangOptions.h"
23 #include "clang/Basic/SourceLocation.h"
24 #include "llvm/ADT/StringRef.h"
25 #include "llvm/Support/Error.h"
26 #include <map>
27 #include <set>
28 #include <string>
29 #include <vector>
30 
31 namespace clang {
32 
33 class Rewriter;
34 
35 namespace tooling {
36 
37 /// \brief A source range independent of the \c SourceManager.
38 class Range {
39 public:
Range()40   Range() : Offset(0), Length(0) {}
Range(unsigned Offset,unsigned Length)41   Range(unsigned Offset, unsigned Length) : Offset(Offset), Length(Length) {}
42 
43   /// \brief Accessors.
44   /// @{
getOffset()45   unsigned getOffset() const { return Offset; }
getLength()46   unsigned getLength() const { return Length; }
47   /// @}
48 
49   /// \name Range Predicates
50   /// @{
51   /// \brief Whether this range overlaps with \p RHS or not.
overlapsWith(Range RHS)52   bool overlapsWith(Range RHS) const {
53     return Offset + Length > RHS.Offset && Offset < RHS.Offset + RHS.Length;
54   }
55 
56   /// \brief Whether this range contains \p RHS or not.
contains(Range RHS)57   bool contains(Range RHS) const {
58     return RHS.Offset >= Offset &&
59            (RHS.Offset + RHS.Length) <= (Offset + Length);
60   }
61 
62   /// \brief Whether this range equals to \p RHS or not.
63   bool operator==(const Range &RHS) const {
64     return Offset == RHS.getOffset() && Length == RHS.getLength();
65   }
66   /// @}
67 
68 private:
69   unsigned Offset;
70   unsigned Length;
71 };
72 
73 /// \brief A text replacement.
74 ///
75 /// Represents a SourceManager independent replacement of a range of text in a
76 /// specific file.
77 class Replacement {
78 public:
79   /// \brief Creates an invalid (not applicable) replacement.
80   Replacement();
81 
82   /// \brief Creates a replacement of the range [Offset, Offset+Length) in
83   /// FilePath with ReplacementText.
84   ///
85   /// \param FilePath A source file accessible via a SourceManager.
86   /// \param Offset The byte offset of the start of the range in the file.
87   /// \param Length The length of the range in bytes.
88   Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
89               StringRef ReplacementText);
90 
91   /// \brief Creates a Replacement of the range [Start, Start+Length) with
92   /// ReplacementText.
93   Replacement(const SourceManager &Sources, SourceLocation Start,
94               unsigned Length, StringRef ReplacementText);
95 
96   /// \brief Creates a Replacement of the given range with ReplacementText.
97   Replacement(const SourceManager &Sources, const CharSourceRange &Range,
98               StringRef ReplacementText,
99               const LangOptions &LangOpts = LangOptions());
100 
101   /// \brief Creates a Replacement of the node with ReplacementText.
102   template <typename Node>
103   Replacement(const SourceManager &Sources, const Node &NodeToReplace,
104               StringRef ReplacementText,
105               const LangOptions &LangOpts = LangOptions());
106 
107   /// \brief Returns whether this replacement can be applied to a file.
108   ///
109   /// Only replacements that are in a valid file can be applied.
110   bool isApplicable() const;
111 
112   /// \brief Accessors.
113   /// @{
getFilePath()114   StringRef getFilePath() const { return FilePath; }
getOffset()115   unsigned getOffset() const { return ReplacementRange.getOffset(); }
getLength()116   unsigned getLength() const { return ReplacementRange.getLength(); }
getReplacementText()117   StringRef getReplacementText() const { return ReplacementText; }
118   /// @}
119 
120   /// \brief Applies the replacement on the Rewriter.
121   bool apply(Rewriter &Rewrite) const;
122 
123   /// \brief Returns a human readable string representation.
124   std::string toString() const;
125 
126  private:
127    void setFromSourceLocation(const SourceManager &Sources,
128                               SourceLocation Start, unsigned Length,
129                               StringRef ReplacementText);
130    void setFromSourceRange(const SourceManager &Sources,
131                            const CharSourceRange &Range,
132                            StringRef ReplacementText,
133                            const LangOptions &LangOpts);
134 
135   std::string FilePath;
136   Range ReplacementRange;
137   std::string ReplacementText;
138 };
139 
140 /// \brief Less-than operator between two Replacements.
141 bool operator<(const Replacement &LHS, const Replacement &RHS);
142 
143 /// \brief Equal-to operator between two Replacements.
144 bool operator==(const Replacement &LHS, const Replacement &RHS);
145 
146 /// \brief A set of Replacements.
147 /// FIXME: Change to a vector and deduplicate in the RefactoringTool.
148 typedef std::set<Replacement> Replacements;
149 
150 /// \brief Apply all replacements in \p Replaces to the Rewriter \p Rewrite.
151 ///
152 /// Replacement applications happen independently of the success of
153 /// other applications.
154 ///
155 /// \returns true if all replacements apply. false otherwise.
156 bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite);
157 
158 /// \brief Apply all replacements in \p Replaces to the Rewriter \p Rewrite.
159 ///
160 /// Replacement applications happen independently of the success of
161 /// other applications.
162 ///
163 /// \returns true if all replacements apply. false otherwise.
164 bool applyAllReplacements(const std::vector<Replacement> &Replaces,
165                           Rewriter &Rewrite);
166 
167 /// \brief Applies all replacements in \p Replaces to \p Code.
168 ///
169 /// This completely ignores the path stored in each replacement. If all
170 /// replacements are applied successfully, this returns the code with
171 /// replacements applied; otherwise, an llvm::Error carrying llvm::StringError
172 /// is returned (the Error message can be converted to string using
173 /// `llvm::toString()` and 'std::error_code` in the `Error` should be ignored).
174 llvm::Expected<std::string> applyAllReplacements(StringRef Code,
175                                                  const Replacements &Replaces);
176 
177 /// \brief Calculates how a code \p Position is shifted when \p Replaces are
178 /// applied.
179 unsigned shiftedCodePosition(const Replacements& Replaces, unsigned Position);
180 
181 /// \brief Calculates how a code \p Position is shifted when \p Replaces are
182 /// applied.
183 ///
184 /// \pre Replaces[i].getOffset() <= Replaces[i+1].getOffset().
185 unsigned shiftedCodePosition(const std::vector<Replacement> &Replaces,
186                              unsigned Position);
187 
188 /// \brief Removes duplicate Replacements and reports if Replacements conflict
189 /// with one another. All Replacements are assumed to be in the same file.
190 ///
191 /// \post Replaces[i].getOffset() <= Replaces[i+1].getOffset().
192 ///
193 /// This function sorts \p Replaces so that conflicts can be reported simply by
194 /// offset into \p Replaces and number of elements in the conflict.
195 void deduplicate(std::vector<Replacement> &Replaces,
196                  std::vector<Range> &Conflicts);
197 
198 /// \brief Collection of Replacements generated from a single translation unit.
199 struct TranslationUnitReplacements {
200   /// Name of the main source for the translation unit.
201   std::string MainSourceFile;
202 
203   /// A freeform chunk of text to describe the context of the replacements.
204   /// Will be printed, for example, when detecting conflicts during replacement
205   /// deduplication.
206   std::string Context;
207 
208   std::vector<Replacement> Replacements;
209 };
210 
211 /// \brief Calculates the ranges in a single file that are affected by the
212 /// Replacements. Overlapping ranges will be merged.
213 ///
214 /// \pre Replacements must be for the same file.
215 ///
216 /// \returns a non-overlapping and sorted ranges.
217 std::vector<Range> calculateChangedRanges(const Replacements &Replaces);
218 
219 /// \brief Calculates the new ranges after \p Replaces are applied. These
220 /// include both the original \p Ranges and the affected ranges of \p Replaces
221 /// in the new code.
222 ///
223 /// \pre Replacements must be for the same file.
224 ///
225 /// \return The new ranges after \p Replaces are applied. The new ranges will be
226 /// sorted and non-overlapping.
227 std::vector<Range>
228 calculateRangesAfterReplacements(const Replacements &Replaces,
229                                  const std::vector<Range> &Ranges);
230 
231 /// \brief Groups a random set of replacements by file path. Replacements
232 /// related to the same file entry are put into the same vector.
233 std::map<std::string, Replacements>
234 groupReplacementsByFile(const Replacements &Replaces);
235 
236 /// \brief Merges two sets of replacements with the second set referring to the
237 /// code after applying the first set. Within both 'First' and 'Second',
238 /// replacements must not overlap.
239 Replacements mergeReplacements(const Replacements &First,
240                                const Replacements &Second);
241 
242 template <typename Node>
Replacement(const SourceManager & Sources,const Node & NodeToReplace,StringRef ReplacementText,const LangOptions & LangOpts)243 Replacement::Replacement(const SourceManager &Sources,
244                          const Node &NodeToReplace, StringRef ReplacementText,
245                          const LangOptions &LangOpts) {
246   const CharSourceRange Range =
247       CharSourceRange::getTokenRange(NodeToReplace->getSourceRange());
248   setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
249 }
250 
251 } // end namespace tooling
252 } // end namespace clang
253 
254 #endif // LLVM_CLANG_TOOLING_CORE_REPLACEMENT_H
255