123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- ==========================
- 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
|