r314509 - [docs][refactor] Add refactoring engine design documentation

Alex Lorenz via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 29 05:21:38 PDT 2017

Author: arphaman
Date: Fri Sep 29 05:21:38 2017
New Revision: 314509

URL: http://llvm.org/viewvc/llvm-project?rev=314509&view=rev
[docs][refactor] Add refactoring engine design documentation

This commit adds a refactoring engine design document that talks about the
design and provides several example of how the engine can be used.

Differential Revision: https://reviews.llvm.org/D37976


Added: cfe/trunk/docs/RefactoringEngine.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/RefactoringEngine.rst?rev=314509&view=auto
--- cfe/trunk/docs/RefactoringEngine.rst (added)
+++ cfe/trunk/docs/RefactoringEngine.rst Fri Sep 29 05:21:38 2017
@@ -0,0 +1,253 @@
+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
+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
+.. 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
+.. code-block:: c++
+  createRefactoringActionRule<RenameOccurrences>(
+    ...,
+    OptionRequirement<NewNameOption>())
+  );
+..  FIXME: Editor Bindings section

Modified: cfe/trunk/docs/index.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/index.rst?rev=314509&r1=314508&r2=314509&view=diff
--- cfe/trunk/docs/index.rst (original)
+++ cfe/trunk/docs/index.rst Fri Sep 29 05:21:38 2017
@@ -60,6 +60,7 @@ Using Clang as a Library
+   RefactoringEngine
 Using Clang Tools

More information about the cfe-commits mailing list