annotate clang/docs/RefactoringEngine.rst @ 221:79ff65ed7e25

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