Com*_* 10 5 c++ refactoring automated-refactoring
我正在重构大量代码(主要是C++),以删除一些已经永久设置为给定值的临时配置检查.例如,我将拥有以下代码:
#include <value1.h>
#include <value2.h>
#include <value3.h>
...
if ( value1() )
{
// do something
}
bool b = value2();
if ( b && anotherCondition )
{
// do more stuff
}
if ( value3() < 10 )
{
// more stuff again
}
Run Code Online (Sandbox Code Playgroud)
对值的调用返回bool或int.因为我知道这些调用总是返回的值,所以我做了一些正则表达式替换以将调用扩展为它们的正常值:
// where:
// value1() == true
// value2() == false
// value3() == 4
// TODO: Remove expanded config (value1)
if ( true )
{
// do something
}
// TODO: Remove expanded config (value2)
bool b = false;
if ( b && anotherCondition )
{
// do more stuff
}
// TODO: Remove expanded config (value3)
if ( 4 < 10 )
{
// more stuff again
}
Run Code Online (Sandbox Code Playgroud)
请注意,尽管这些值是固定的,但它们不是在编译时设置的,而是从共享内存中读取的,因此编译器当前不会在后台优化任何内容.
虽然结果代码看起来有点傻,但这种正则表达式方法实现了我想要的很多东西,因为它很容易应用并消除了对调用的依赖,同时不改变代码的行为,编译器也可能优化了知道一个块永远不会被调用或者检查总是返回true,所以很多.它也使得它相当容易(特别是在对版本控制进行区分时)以查看已更改的内容并采取最后一步清理它,因此上面代码的代码最终如下所示:
// do something
// DONT do more stuff (b being false always prevented this)
// more stuff again
Run Code Online (Sandbox Code Playgroud)
麻烦的是,我有数百(可能是数千)的更改来从第二个,正确但愚蠢的阶段获得最终清理代码.
我想知道是否有人知道可以处理这个或我可以应用的任何技术的重构工具.主要问题是C++语法使得完全扩展或消除非常难以实现,并且上面的代码有许多排列.我觉得我几乎需要一个编译器来处理我需要涵盖的语法变化.
我知道有类似的问题,但我找不到任何相似的要求,也想知道是否有任何工具或程序出现,因为他们被问到?
听起来你有我称之为"僵尸代码"......在实践中死了,但就编译器而言仍然存在.对于大多数有组织的运行时配置变量系统而言,这是一个非常常见的问题:最终一些配置变量到达永久固定状态,但在运行时重复进行重新评估.
正如您所注意到的,固化不是正则表达式,因为正则表达式不能可靠地解析C++代码.您需要的是程序转换系统.这是一个真正解析源代码的工具,可以将一组代码到代码的重写规则应用于解析树,并可以从更改的树中重新生成源文本.
我知道Clang在这里有一些能力; 它可以解析C++并构建一个树,但它没有源到源的转换功能.您可以通过编写AST-to-AST转换来模拟该功能,但这样会更加不方便恕我直言.我相信它可以重新生成C++代码,但我不知道它是否会保留注释或预处理器指令.
我们的DMS软件再造工具包及其C++(11)前端可以(并且已经习惯)对C++源代码进行大规模转换,并具有源到源转换.AFAIK,它是唯一可以做到这一点的生产工具.您需要的是一组转换,表示您对所关注的配置变量的最终状态的了解,以及一些简单的代码简化规则.以下DMS规则接近您可能需要的规则:
rule fix_value1():expression->expression
"value1()" -> "true";
rule fix_value2():expression->expression
"value2()" -> "false";
rule fix_value3():expression->expression
"value3()" -> "4";
rule simplify_boolean_and_true(r:relation):condition->condition
"r && true" -> "r".
rule simplify_boolean_or_ture(r:relation):condition->condition
"r || true" -> "true".
rule simplify_boolean_and_false(r:relation):condition->condition
"r && false" -> "false".
...
rule simplify_boolean_not_true(r:relation):condition->condition
"!true" -> "false".
...
rule simplify_if_then_false(s:statement): statement->statement
" if (false) \s" -> ";";
rule simplify_if_then_true(s:statement): statement->statement
" if (true) \s" -> "\s";
rule simplify_if_then_else_false(s1:statement, s2:statement): statement->statement
" if (false) \s1 else \s2" -> "\s2";
rule simplify_if_then_else_true(s1:statement, s2: statement): statement->statement
" if (true) \s1 else \s2" -> "\s2";
Run Code Online (Sandbox Code Playgroud)
您还需要规则来简化("折叠")涉及算术的常量表达式,以及处理现在常量表达式的切换的规则.要查看整数常量折叠的DMS规则,请将代数视为DMS域.
与正则表达式不同,DMS重写规则不能"不匹配"代码; 它们代表相应的AST,它是匹配的AST.因为它是AST匹配,所以它们没有空格,换行符或注释的问题.您可能认为他们可能会遇到操作数的顺序(如果遇到"假的&& x",会发生什么?'); 他们没有,因为&&和||的语法规则 在DMS C++解析器中标记为关联和可交换,匹配过程自动将其考虑在内.
这些规则本身不能做的就是跨任务的价值(在你的情况下,不变)传播.为此,您需要流量分析,以便您可以跟踪此类分配("到达定义").显然,如果你没有这样的任务或很少,你可以手动修补那些.如果你这样做,你需要流量分析; 唉,DMS的C++前端并不完全存在,但我们正在研究它; 我们有控制流程分析.(DMS的C前端具有全流量分析).
(编辑2015年2月:现在完整的C++ 14;功能/方法中的流程分析).
实际上,我们在十年前将此技术应用于来自IBM Tivoli的混合C和C++代码的1.5M SLOC应用,取得了极大的成功; 我们不需要流量分析: - }