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 |
254 | |