Clang
的重构引擎展示如何使用重构API
中的各种原语
来实现不同的重构.
LibTooling
库提供了几个在开发重构
操作时,使用的其他API
.
可用重构引擎
来实现,用编辑器
或IDE
中的选择启动
的本地重构
.可结合AST
匹配器和重构引擎
,以实现不适合源选择
和/或必须查询
某些指定节点
的AST
的重构.
假定基本了解了ClangAST
.更多见ClangAST
简介.
Clang
的重构引擎定义了一组重构操作
,来实现许多不同的源转换
.clang-refactor
命令行工具可用来重构
.
其他如文本
编辑器和IDE
也用这些重构.
重构操作
是一个定义了相关重构操作
(规则)列表
的类.在共同的单个clang-refactor
命令下,分组
这些规则.
除了规则
外,重构操作
还向clang-refactor
提供操作的命令名和描述
.每个操作
都必须实现RefactoringAction
接口.
下面是本地
重命名操作的大概
:
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 {
...
}
};
单个重构操作
负责创建一组表示重构操作
的分组重构操作规则
.尽管一个操作中的规则
可能有多个不同实现,但它们应努力产生类似结果
.
无论使用哪种重构操作规则
,用户都应很容易识别是哪个重构操作
产生了结果.
通过区分操作和规则
,可创建定义一组产生类似结果
的不同规则
的操作.如,"添加缺失
的开关(switch)
"重构操作,一般一次
添加一个缺失
到switch
.
但是,在指定枚举
上运行所有开关
上都适用的重构
可能很有用,因为在添加新的枚举常量
后,可自动更新
所有开关.为此,可创建两个使用clang-refactor
子命令的不同规则
.
第一条规则
描述在用户选择单个开关
时启动的本地操作
.第二条规则
描述跨翻译单元
工作的全局操作
,并在用户向clang-refactor
提供枚举名
时启动(或用户可改为选择枚举声明
).
然后,clang-refactor
工具分析传递给重构操作
的选择和其他选项
,并为给定选择
和其他选项
选择最合适的规则
.
Clang
的重构引擎支持几个不同的重构规则
:
1,SourceChangeRefactoringRule
生成应用至源文件
的源替换
.选择实现此规则
的子类
必须实现createSourceReplacements
成员函数.
该类型
规则一般用来实现仅在翻译单元
内转换源的本地重构
.
2,FindSymbolOccurrencesRefactoringRule
生成"部分"
重构结果:引用
指定符号的一组实例
.该类型的规则
一般用来实现,重构时,允许用户指定
应重命名
哪些匹配项的交互式
重命名操作.
选择实现此规则
的子类必须实现findSymbolOccurrences
成员函数.
如果不确定应使用的规则类型
,以下快速检查
也许有用:
如果想在一个翻译单元
中转换源,且不需要跨TU
信息,则SourceChangeRefactoringRule
应该适合你.
如果要使用潜在的交互式组件
实现类似重命名
的操作,则FindSymbolOccurrencesRefactoringRule
可能适合你.
确定适合规则
后,可通过继承
规则并实现其接口
来重构.子类应该有个取期望重构
的输入的构造器
.
如,如果要实现仅删除所选内容
规则,则应使用接受选择区间
的构造器,来创建SourceChangeRefactoringRule
的子类:
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;
};
然后,可用createRefactoringActionRule
函数添加规则子类
到特定操作
的重构操作规则列表
中.如,可用以下代码
,把上面显示
的类添加
到操作规则列表
中:
RefactoringActionRules Rules;
Rules.push_back(
createRefactoringActionRule<DeleteSelectedRange>( SourceRangeSelectionRequirement()));
createRefactoringActionRule
函数取重构操作规则
要求值的列表
.这些值描述重构引擎
必须满足的启动要求
,然后才能构造
和调用
提供的操作规则
.
后面介绍如何求值
这些要求,并列举
可用来构造重构操作规则
的所有可能要求
.
重构操作规则要求
是一个从RefactoringActionRuleRequirement
继承的类型的值.该类型
必须定义一个返回Expected<...>
的evaluate
成员函数.
按createRefactoringActionRule
的参数用要求值
时,在启动
操作规则时求值
该值.然后,除非求值产生错误,把求值结果
传递给规则的构造器
.
如,以下步骤求值
上一部分中定义的DeleteSelectedRange
示例规则:
首先调用SourceRangeSelectionRequirement
的返回Expected
的evaluate
成员函数.
如果返回值
为错误,则启动
失败,并给客户报告错误
.注意,客户可能不会向用户
报告错误.
否则,用源区间返回值
来构造DeleteSelectedRange
规则.然后,启动
成功(已成功求值所有要求
)时,调用
该规则.
相同的一系列步骤适合所有重构规则
.首先,引擎
求值所有要求
.然后,检查是否满足
这些要求(它们不应
产生错误).然后,构造
规则并调用
它.
需求,求值和调用重构操作规则
的分离允许重构客户
:
1,禁止
不支持的要求的重构操作规则
.
2,收集选项集
并定义一个允许用户不调用操作
时,输入这些选项
的命令行/可视化
接口.
下面列举了需要选择源
的重构规则要求
:
1,在使用某种选择调用操作时,按源区间计算SourceRangeSelectionRequirement
.在编辑器
中启动重构
时,即使用户
未选择内容(此时,区间
包含光标的位置),也应满足
此要求.
创建重构规则
时,可用其他几种要求类型
:
1,RefactoringOptionsRequirement
要求是一个应由使用选项
的需求继承
的抽象类
.更具体的OptionRequirement
要求是上述类
在计算特定选项
时,返回该选项值
的简单实现
.
重构选项
是影响重构操作
的值,它用命令行选项
或其他客户相关
机制指定.应使用从OptionalRequiredOption
或RequiredRefactoringOption
继承的类
创建选项
.
以下示例显示了如何创建与clang-refactor
中的-new-name
命令行选项对应的必需串选项
:
class NewNameOption : public RequiredRefactoringOption<std::string> {
public:
StringRef getName() const override { return "new-name"; }
StringRef getDescription() const override {
return "目标的新名";
}
};
然后,可用上面示例
中显示的选项
,通过类似OptionRequirement
的要求为重构规则
创建要求:
createRefactoringActionRule<RenameOccurrences>(
...,
OptionRequirement<NewNameOption>())
);