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