| # Clang's refactoring engine | |
| This document describes the design of Clang's refactoring engine and provides | |
| a couple of examples that show how various primitives in the refactoring API | |
| can be used to implement different refactoring actions. The {doc}`LibTooling` | |
| library provides several other APIs that are used when developing a | |
| refactoring action. | |
| Refactoring engine can be used to implement local refactorings that are | |
| initiated using a selection in an editor or an IDE. You can combine | |
| {doc}`AST matchers<LibASTMatchers>` and the refactoring engine to implement | |
| refactorings that don't lend themselves well to source selection and/or have to | |
| query ASTs for some particular nodes. | |
| We assume basic knowledge about the Clang AST. See the {doc}`Introduction | |
| to the Clang AST <IntroductionToTheClangAST>` if you want to learn more | |
| about how the AST is structured. | |
| % FIXME: create new refactoring action tutorial and link to the tutorial | |
| ## Introduction | |
| Clang's refactoring engine defines a set refactoring actions that implement | |
| a number of different source transformations. The `clang-refactor` | |
| command-line tool can be used to perform these refactorings. Certain | |
| refactorings are also available in other clients like text editors and IDEs. | |
| A refactoring action is a class that defines a list of related refactoring | |
| operations (rules). These rules are grouped under a common umbrella - a single | |
| `clang-refactor` command. In addition to rules, the refactoring action | |
| provides the action's command name and description to `clang-refactor`. | |
| Each action must implement the `RefactoringAction` interface. Here's an | |
| outline of a `local-rename` action: | |
| ```c++ | |
| class LocalRename final : public RefactoringAction { | |
| public: | |
| StringRef getCommand() const override { return "local-rename"; } | |
| StringRef getDescription() const override { | |
| return "Finds and renames symbols in code with no indexer support"; | |
| } | |
| RefactoringActionRules createActionRules() const override { | |
| ... | |
| } | |
| }; | |
| ``` | |
| ## Refactoring Action Rules | |
| An individual refactoring action is responsible for creating the set of | |
| grouped refactoring action rules that represent one refactoring operation. | |
| Although the rules in one action may have a number of different implementations, | |
| they should strive to produce a similar result. It should be easy for users to | |
| identify which refactoring action produced the result regardless of which | |
| refactoring action rule was used. | |
| The distinction between actions and rules enables the creation of actions | |
| that define a set of different rules that produce similar results. For example, | |
| the "add missing switch cases" refactoring operation typically adds missing | |
| cases to one switch at a time. However, it could be useful to have a | |
| refactoring that works on all switches that operate on a particular enum, as | |
| one could then automatically update all of them after adding a new enum | |
| constant. To achieve that, we can create two different rules that will use one | |
| `clang-refactor` subcommand. The first rule will describe a local operation | |
| that's initiated when the user selects a single switch. The second rule will | |
| describe a global operation that works across translation units and is initiated | |
| when the user provides the name of the enum to clang-refactor (or the user could | |
| select the enum declaration instead). The clang-refactor tool will then analyze | |
| the selection and other options passed to the refactoring action, and will pick | |
| the most appropriate rule for the given selection and other options. | |
| ### Rule Types | |
| Clang's refactoring engine supports several different refactoring rules: | |
| - `SourceChangeRefactoringRule` produces source replacements that are applied | |
| to the source files. Subclasses that choose to implement this rule have to | |
| implement the `createSourceReplacements` member function. This type of | |
| rule is typically used to implement local refactorings that transform the | |
| source in one translation unit only. | |
| - `FindSymbolOccurrencesRefactoringRule` produces a "partial" refactoring | |
| result: a set of occurrences that refer to a particular symbol. This type | |
| of rule is typically used to implement an interactive renaming action that | |
| allows users to specify which occurrences should be renamed during the | |
| refactoring. Subclasses that choose to implement this rule have to implement | |
| the `findSymbolOccurrences` member function. | |
| The following set of quick checks might help if you are unsure about the type | |
| of rule you should use: | |
| 1. If you would like to transform the source in one translation unit and if | |
| you don't need any cross-TU information, then the | |
| `SourceChangeRefactoringRule` should work for you. | |
| 2. If you would like to implement a rename-like operation with potential | |
| interactive components, then `FindSymbolOccurrencesRefactoringRule` might | |
| work for you. | |
| ### How to Create a Rule | |
| Once you determine which type of rule is suitable for your needs you can | |
| implement the refactoring by subclassing the rule and implementing its | |
| interface. The subclass should have a constructor that takes the inputs that | |
| are needed to perform the refactoring. For example, if you want to implement a | |
| rule that simply deletes a selection, you should create a subclass of | |
| `SourceChangeRefactoringRule` with a constructor that accepts the selection | |
| range: | |
| ```c++ | |
| class DeleteSelectedRange final : public SourceChangeRefactoringRule { | |
| public: | |
| DeleteSelection(SourceRange Selection) : Selection(Selection) {} | |
| Expected<AtomicChanges> | |
| createSourceReplacements(RefactoringRuleContext &Context) override { | |
| AtomicChange Replacement(Context.getSources(), Selection.getBegin()); | |
| Replacement.replace(Context.getSource, | |
| CharSourceRange::getCharRange(Selection), ""); | |
| return { Replacement }; | |
| } | |
| private: | |
| SourceRange Selection; | |
| }; | |
| ``` | |
| The rule's subclass can then be added to the list of refactoring action's | |
| rules for a particular action using the `createRefactoringActionRule` | |
| function. For example, the class that's shown above can be added to the | |
| list of action rules using the following code: | |
| ```c++ | |
| RefactoringActionRules Rules; | |
| Rules.push_back( | |
| createRefactoringActionRule<DeleteSelectedRange>( | |
| SourceRangeSelectionRequirement()) | |
| ); | |
| ``` | |
| The `createRefactoringActionRule` function takes in a list of refactoring | |
| action rule requirement values. These values describe the initiation | |
| requirements that have to be satisfied by the refactoring engine before the | |
| provided action rule can be constructed and invoked. The next section | |
| describes how these requirements are evaluated and lists all the possible | |
| requirements that can be used to construct a refactoring action rule. | |
| ## Refactoring Action Rule Requirements | |
| A refactoring action rule requirement is a value whose type derives from the | |
| `RefactoringActionRuleRequirement` class. The type must define an | |
| `evaluate` member function that returns a value of type `Expected<...>`. | |
| When a requirement value is used as an argument to | |
| `createRefactoringActionRule`, that value is evaluated during the initiation | |
| of the action rule. The evaluated result is then passed to the rule's | |
| constructor unless the evaluation produced an error. For example, the | |
| `DeleteSelectedRange` sample rule that's defined in the previous section | |
| will be evaluated using the following steps: | |
| 1. `SourceRangeSelectionRequirement`'s `evaluate` member function will be | |
| called first. It will return an `Expected<SourceRange>`. | |
| 2. If the return value is an error the initiation will fail and the error | |
| will be reported to the client. Note that the client may not report the | |
| error to the user. | |
| 3. Otherwise the source range return value will be used to construct the | |
| `DeleteSelectedRange` rule. The rule will then be invoked as the initiation | |
| succeeded (all requirements were evaluated successfully). | |
| The same series of steps applies to any refactoring rule. Firstly, the engine | |
| will evaluate all of the requirements. Then it will check if these requirements | |
| are satisfied (they should not produce an error). Then it will construct the | |
| rule and invoke it. | |
| The separation of requirements, their evaluation and the invocation of the | |
| refactoring action rule allows the refactoring clients to: | |
| - Disable refactoring action rules whose requirements are not supported. | |
| - Gather the set of options and define a command-line / visual interface | |
| that allows users to input these options without ever invoking the | |
| action. | |
| ### Selection Requirements | |
| The refactoring rule requirements that require some form of source selection | |
| are listed below: | |
| - `SourceRangeSelectionRequirement` evaluates to a source range when the | |
| action is invoked with some sort of selection. This requirement should be | |
| satisfied when a refactoring is initiated in an editor, even when the user | |
| has not selected anything (the range will contain the cursor's location in | |
| that case). | |
| % FIXME: Future selection requirements | |
| % FIXME: Maybe mention custom selection requirements? | |
| ### Other Requirements | |
| There are several other requirements types that can be used when creating | |
| a refactoring rule: | |
| - The `RefactoringOptionsRequirement` requirement is an abstract class that | |
| should be subclassed by requirements working with options. The more | |
| concrete `OptionRequirement` requirement is a simple implementation of the | |
| aforementioned class that returns the value of the specified option when | |
| it's evaluated. The next section talks more about refactoring options and | |
| how they can be used when creating a rule. | |
| ## Refactoring Options | |
| Refactoring options are values that affect a refactoring operation and are | |
| specified either using command-line options or another client-specific | |
| mechanism. Options should be created using a class that derives either from | |
| the `OptionalRequiredOption` or `RequiredRefactoringOption`. The following | |
| example shows how one can created a required string option that corresponds to | |
| the `-new-name` command-line option in clang-refactor: | |
| ```c++ | |
| class NewNameOption : public RequiredRefactoringOption<std::string> { | |
| public: | |
| StringRef getName() const override { return "new-name"; } | |
| StringRef getDescription() const override { | |
| return "The new name to change the symbol to"; | |
| } | |
| }; | |
| ``` | |
| The option that's shown in the example above can then be used to create | |
| a requirement for a refactoring rule using a requirement like | |
| `OptionRequirement`: | |
| ```c++ | |
| createRefactoringActionRule<RenameOccurrences>( | |
| ..., | |
| OptionRequirement<NewNameOption>()) | |
| ); | |
| ``` | |
| % FIXME: Editor Bindings section |