Alex Lorenz | 80275fe | 2017-09-29 12:21:38 +0000 | [diff] [blame] | 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 | |
Haojian Wu | eac034b | 2017-10-10 09:00:56 +0000 | [diff] [blame] | 44 | StringRef getDescription() const override { |
Alex Lorenz | 80275fe | 2017-09-29 12:21:38 +0000 | [diff] [blame] | 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()) |
Haojian Wu | eac034b | 2017-10-10 09:00:56 +0000 | [diff] [blame] | 146 | ); |
Alex Lorenz | 80275fe | 2017-09-29 12:21:38 +0000 | [diff] [blame] | 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 |