150
|
1 ==========================
|
|
2 Clang's refactoring engine
|
|
3 ==========================
|
|
4
|
|
5 This document describes the design of Clang's refactoring engine and provides
|
|
6 a couple of examples that show how various primitives in the refactoring API
|
|
7 can be used to implement different refactoring actions. The :doc:`LibTooling`
|
|
8 library provides several other APIs that are used when developing a
|
|
9 refactoring action.
|
|
10
|
|
11 Refactoring engine can be used to implement local refactorings that are
|
|
12 initiated using a selection in an editor or an IDE. You can combine
|
|
13 :doc:`AST matchers<LibASTMatchers>` and the refactoring engine to implement
|
|
14 refactorings that don't lend themselves well to source selection and/or have to
|
|
15 query ASTs for some particular nodes.
|
|
16
|
|
17 We assume basic knowledge about the Clang AST. See the :doc:`Introduction
|
|
18 to the Clang AST <IntroductionToTheClangAST>` if you want to learn more
|
|
19 about how the AST is structured.
|
|
20
|
|
21 .. FIXME: create new refactoring action tutorial and link to the tutorial
|
|
22
|
|
23 Introduction
|
|
24 ------------
|
|
25
|
|
26 Clang's refactoring engine defines a set refactoring actions that implement
|
|
27 a number of different source transformations. The ``clang-refactor``
|
|
28 command-line tool can be used to perform these refactorings. Certain
|
|
29 refactorings are also available in other clients like text editors and IDEs.
|
|
30
|
|
31 A refactoring action is a class that defines a list of related refactoring
|
|
32 operations (rules). These rules are grouped under a common umbrella - a single
|
|
33 ``clang-refactor`` command. In addition to rules, the refactoring action
|
|
34 provides the action's command name and description to ``clang-refactor``.
|
|
35 Each action must implement the ``RefactoringAction`` interface. Here's an
|
|
36 outline of a ``local-rename`` action:
|
|
37
|
|
38 .. code-block:: c++
|
|
39
|
|
40 class LocalRename final : public RefactoringAction {
|
|
41 public:
|
|
42 StringRef getCommand() const override { return "local-rename"; }
|
|
43
|
|
44 StringRef getDescription() const override {
|
|
45 return "Finds and renames symbols in code with no indexer support";
|
|
46 }
|
|
47
|
|
48 RefactoringActionRules createActionRules() const override {
|
|
49 ...
|
|
50 }
|
|
51 };
|
|
52
|
|
53 Refactoring Action Rules
|
|
54 ------------------------
|
|
55
|
|
56 An individual refactoring action is responsible for creating the set of
|
|
57 grouped refactoring action rules that represent one refactoring operation.
|
|
58 Although the rules in one action may have a number of different implementations,
|
|
59 they should strive to produce a similar result. It should be easy for users to
|
|
60 identify which refactoring action produced the result regardless of which
|
|
61 refactoring action rule was used.
|
|
62
|
|
63 The distinction between actions and rules enables the creation of actions
|
|
64 that define a set of different rules that produce similar results. For example,
|
|
65 the "add missing switch cases" refactoring operation typically adds missing
|
|
66 cases to one switch at a time. However, it could be useful to have a
|
|
67 refactoring that works on all switches that operate on a particular enum, as
|
|
68 one could then automatically update all of them after adding a new enum
|
|
69 constant. To achieve that, we can create two different rules that will use one
|
|
70 ``clang-refactor`` subcommand. The first rule will describe a local operation
|
|
71 that's initiated when the user selects a single switch. The second rule will
|
|
72 describe a global operation that works across translation units and is initiated
|
|
73 when the user provides the name of the enum to clang-refactor (or the user could
|
|
74 select the enum declaration instead). The clang-refactor tool will then analyze
|
|
75 the selection and other options passed to the refactoring action, and will pick
|
|
76 the most appropriate rule for the given selection and other options.
|
|
77
|
|
78 Rule Types
|
|
79 ^^^^^^^^^^
|
|
80
|
|
81 Clang's refactoring engine supports several different refactoring rules:
|
|
82
|
|
83 - ``SourceChangeRefactoringRule`` produces source replacements that are applied
|
|
84 to the source files. Subclasses that choose to implement this rule have to
|
|
85 implement the ``createSourceReplacements`` member function. This type of
|
|
86 rule is typically used to implement local refactorings that transform the
|
|
87 source in one translation unit only.
|
|
88
|
|
89 - ``FindSymbolOccurrencesRefactoringRule`` produces a "partial" refactoring
|
|
90 result: a set of occurrences that refer to a particular symbol. This type
|
|
91 of rule is typically used to implement an interactive renaming action that
|
|
92 allows users to specify which occurrences should be renamed during the
|
|
93 refactoring. Subclasses that choose to implement this rule have to implement
|
|
94 the ``findSymbolOccurrences`` member function.
|
|
95
|
|
96 The following set of quick checks might help if you are unsure about the type
|
|
97 of rule you should use:
|
|
98
|
|
99 #. If you would like to transform the source in one translation unit and if
|
|
100 you don't need any cross-TU information, then the
|
|
101 ``SourceChangeRefactoringRule`` should work for you.
|
|
102
|
|
103 #. If you would like to implement a rename-like operation with potential
|
|
104 interactive components, then ``FindSymbolOccurrencesRefactoringRule`` might
|
|
105 work for you.
|
|
106
|
|
107 How to Create a Rule
|
|
108 ^^^^^^^^^^^^^^^^^^^^
|
|
109
|
|
110 Once you determine which type of rule is suitable for your needs you can
|
|
111 implement the refactoring by subclassing the rule and implementing its
|
|
112 interface. The subclass should have a constructor that takes the inputs that
|
|
113 are needed to perform the refactoring. For example, if you want to implement a
|
|
114 rule that simply deletes a selection, you should create a subclass of
|
|
115 ``SourceChangeRefactoringRule`` with a constructor that accepts the selection
|
|
116 range:
|
|
117
|
|
118 .. code-block:: c++
|
|
119
|
|
120 class DeleteSelectedRange final : public SourceChangeRefactoringRule {
|
|
121 public:
|
|
122 DeleteSelection(SourceRange Selection) : Selection(Selection) {}
|
|
123
|
|
124 Expected<AtomicChanges>
|
|
125 createSourceReplacements(RefactoringRuleContext &Context) override {
|
|
126 AtomicChange Replacement(Context.getSources(), Selection.getBegin());
|
|
127 Replacement.replace(Context.getSource,
|
|
128 CharSourceRange::getCharRange(Selection), "");
|
|
129 return { Replacement };
|
|
130 }
|
|
131 private:
|
|
132 SourceRange Selection;
|
|
133 };
|
|
134
|
|
135 The rule's subclass can then be added to the list of refactoring action's
|
|
136 rules for a particular action using the ``createRefactoringActionRule``
|
|
137 function. For example, the class that's shown above can be added to the
|
|
138 list of action rules using the following code:
|
|
139
|
|
140 .. code-block:: c++
|
|
141
|
|
142 RefactoringActionRules Rules;
|
|
143 Rules.push_back(
|
|
144 createRefactoringActionRule<DeleteSelectedRange>(
|
|
145 SourceRangeSelectionRequirement())
|
|
146 );
|
|
147
|
|
148 The ``createRefactoringActionRule`` function takes in a list of refactoring
|
|
149 action rule requirement values. These values describe the initiation
|
|
150 requirements that have to be satisfied by the refactoring engine before the
|
|
151 provided action rule can be constructed and invoked. The next section
|
|
152 describes how these requirements are evaluated and lists all the possible
|
|
153 requirements that can be used to construct a refactoring action rule.
|
|
154
|
|
155 Refactoring Action Rule Requirements
|
|
156 ------------------------------------
|
|
157
|
|
158 A refactoring action rule requirement is a value whose type derives from the
|
|
159 ``RefactoringActionRuleRequirement`` class. The type must define an
|
|
160 ``evaluate`` member function that returns a value of type ``Expected<...>``.
|
|
161 When a requirement value is used as an argument to
|
|
162 ``createRefactoringActionRule``, that value is evaluated during the initiation
|
|
163 of the action rule. The evaluated result is then passed to the rule's
|
|
164 constructor unless the evaluation produced an error. For example, the
|
|
165 ``DeleteSelectedRange`` sample rule that's defined in the previous section
|
|
166 will be evaluated using the following steps:
|
|
167
|
|
168 #. ``SourceRangeSelectionRequirement``'s ``evaluate`` member function will be
|
|
169 called first. It will return an ``Expected<SourceRange>``.
|
|
170
|
|
171 #. If the return value is an error the initiation will fail and the error
|
|
172 will be reported to the client. Note that the client may not report the
|
|
173 error to the user.
|
|
174
|
|
175 #. Otherwise the source range return value will be used to construct the
|
|
176 ``DeleteSelectedRange`` rule. The rule will then be invoked as the initiation
|
|
177 succeeded (all requirements were evaluated successfully).
|
|
178
|
|
179 The same series of steps applies to any refactoring rule. Firstly, the engine
|
|
180 will evaluate all of the requirements. Then it will check if these requirements
|
|
181 are satisfied (they should not produce an error). Then it will construct the
|
|
182 rule and invoke it.
|
|
183
|
|
184 The separation of requirements, their evaluation and the invocation of the
|
|
185 refactoring action rule allows the refactoring clients to:
|
|
186
|
|
187 - Disable refactoring action rules whose requirements are not supported.
|
|
188
|
|
189 - Gather the set of options and define a command-line / visual interface
|
|
190 that allows users to input these options without ever invoking the
|
|
191 action.
|
|
192
|
|
193 Selection Requirements
|
|
194 ^^^^^^^^^^^^^^^^^^^^^^
|
|
195
|
|
196 The refactoring rule requirements that require some form of source selection
|
|
197 are listed below:
|
|
198
|
|
199 - ``SourceRangeSelectionRequirement`` evaluates to a source range when the
|
|
200 action is invoked with some sort of selection. This requirement should be
|
|
201 satisfied when a refactoring is initiated in an editor, even when the user
|
|
202 has not selected anything (the range will contain the cursor's location in
|
|
203 that case).
|
|
204
|
|
205 .. FIXME: Future selection requirements
|
|
206
|
|
207 .. FIXME: Maybe mention custom selection requirements?
|
|
208
|
|
209 Other Requirements
|
|
210 ^^^^^^^^^^^^^^^^^^
|
|
211
|
|
212 There are several other requirements types that can be used when creating
|
|
213 a refactoring rule:
|
|
214
|
|
215 - The ``RefactoringOptionsRequirement`` requirement is an abstract class that
|
|
216 should be subclassed by requirements working with options. The more
|
|
217 concrete ``OptionRequirement`` requirement is a simple implementation of the
|
|
218 aforementioned class that returns the value of the specified option when
|
|
219 it's evaluated. The next section talks more about refactoring options and
|
|
220 how they can be used when creating a rule.
|
|
221
|
|
222 Refactoring Options
|
|
223 -------------------
|
|
224
|
|
225 Refactoring options are values that affect a refactoring operation and are
|
|
226 specified either using command-line options or another client-specific
|
|
227 mechanism. Options should be created using a class that derives either from
|
|
228 the ``OptionalRequiredOption`` or ``RequiredRefactoringOption``. The following
|
|
229 example shows how one can created a required string option that corresponds to
|
|
230 the ``-new-name`` command-line option in clang-refactor:
|
|
231
|
|
232 .. code-block:: c++
|
|
233
|
|
234 class NewNameOption : public RequiredRefactoringOption<std::string> {
|
|
235 public:
|
|
236 StringRef getName() const override { return "new-name"; }
|
|
237 StringRef getDescription() const override {
|
|
238 return "The new name to change the symbol to";
|
|
239 }
|
|
240 };
|
|
241
|
|
242 The option that's shown in the example above can then be used to create
|
|
243 a requirement for a refactoring rule using a requirement like
|
|
244 ``OptionRequirement``:
|
|
245
|
|
246 .. code-block:: c++
|
|
247
|
|
248 createRefactoringActionRule<RenameOccurrences>(
|
|
249 ...,
|
|
250 OptionRequirement<NewNameOption>())
|
|
251 );
|
|
252
|
|
253 .. FIXME: Editor Bindings section
|