| ========================== |
| 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: |
| |
| .. code-block:: 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: |
| |
| #. 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. |
| |
| #. 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: |
| |
| .. code-block:: 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: |
| |
| .. code-block:: 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: |
| |
| #. ``SourceRangeSelectionRequirement``'s ``evaluate`` member function will be |
| called first. It will return an ``Expected<SourceRange>``. |
| |
| #. 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. |
| |
| #. 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: |
| |
| .. code-block:: 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``: |
| |
| .. code-block:: c++ |
| |
| createRefactoringActionRule<RenameOccurrences>( |
| ..., |
| OptionRequirement<NewNameOption>()) |
| ); |
| |
| .. FIXME: Editor Bindings section |