是否有基于 libclang 的任何 C/C++ 重构工具?(即使是最简单的“玩具示例”)

53

正如我在这里所指出的,看起来clang的libclang非常适合实现C/C++代码分析和修改这一艰巨的任务(请看视频演示和幻灯片)。

你知道基于libclang的任何C/C++重构工具吗?

"任何"包括状态仅处于alpha测试的简单项目,支持一种重构技术即可。它可以没有预处理器支持。作为我所讲述的功能的例子:更改方法名称,无论它是否支持多个文件或仅支持一次一个文件。你可能会想知道询问甚至小型的工作示例的目的是什么。我的想法是,创建一个包含所有代码示例和小工具的列表将提供更好的学习资源,以了解如何使用libclang实现重构。我相信从简单的项目中可以发展成更大的项目--以适当的开源方式:)


4
即使有很棒的基础设施,做起来也比看起来更困难。作为第一步,你对预处理器了解多少? - Ira Baxter
好的,我同意这并不是一项微不足道的任务。我之前写“轻松”可能过于夸张了,让我改用另一个形容词。我的观点是使用libclang可以使这个过程更加容易,但我同意,仍然不容易。 - Grzegorz Wierzowiecki
1
可以说,Clang确实是一个简单的重构项目,并且它似乎正在不断发展壮大。 - Ira Baxter
1
你的问题更关注Clang作为C/C++重构工具,还是更感兴趣于C/C++重构工具,其中Clang可能是一个有趣的选择? - Ira Baxter
我对使用libclang制作的C/C++重构工具更感兴趣。我很想深入了解它们的源代码并做出贡献。 - Grzegorz Wierzowiecki
1
在软件推荐SE上有一个相似的问题,链接为:[http://softwarerecs.stackexchange.com/q/2705/11844],显然是基于这个问题的。 - Kyle Strand
8个回答

24

Clang 包含一个名为 "CIndex" 的库,我认为它是为了在集成开发环境中实现代码补全而开发的。它也可以用于解析 C++ 和遍历 AST,但在重构方面不具备任何功能。参见 Eli Bendersky 的文章 这里.

最近我开始了这样一个项目:cmonster。它是基于 Python 构建的 API,可用于解析 C++(使用 libclang),分析 AST,并具有“重写”接口(即插入/删除/修改源范围)。目前还没有好的方法来进行修改函数名称并将其转换为源代码修改,但这并不难。

我还没有发布包含此功能的版本(尽管它在 github 存储库中),因为我正在等待 llvm/clang 3.0 发布。

此外,我应该指出一些事情:

  • 代码非常粗糙,在称之为 alpha 版本可能有点慷慨。
  • 我并不是这个主题的专家(与 Ira Baxter 博士相比)。

请适当调整期望值。

更新:cmonster 0.2 已发布,其中包括上述功能。请在 Github 上查看。


15

Google一直在为Clang开发工具库。自3.2版本以来,它包括一个ASTMatchers库,因此您可以构建查询而不必遍历AST。

有一篇很棒的视频讲座介绍了这个主题,演示了一个简单的重命名示例。(这是与上面发布的MapReduce讲座同一人所做,但更新且更关注于一个简单的实际实现,而不是Google内部设计和企业规模的内容)。

该重命名方法的示例源代码可在工具分支中获取。它可能在主干中的某个地方,但我找不到它。还要将getDeclAs函数重命名为getNodesAs,因为另一个函数显然已弃用。还有一个更高级的示例可以删除重复的c_str调用(在主干中,有人在上面发布了)。
这里是LibASTMatchersLibTooling的文档。

编辑:ASTMatcher的一些更好的文档。 在这里在这里

编辑:Google现在正在开发一种名为Clangd的东西,旨在成为用于重构的某种Clang服务器。


链接已失效。请查看github上的RemoveCStrCalls.cpp - undefined

8
Google使用基于Clang的重构工具对其C++代码库进行了优化,并计划发布该工具。我不知道该项目的当前状态,但是您可以在2011年LLVM开发者会议上看到此演示:https://www.youtube.com/watch?v=mVbDzTM21BQ
此外,XCode(4+)内置的自动完成和重构功能也是基于libclang实现的。

6
这可能有点"元编程",但这里有一个clang工具示例,它是用clang编写的(尽管不仅限于此)。 RemoveCStrCalls.cpp
//  This file implements a tool that prints replacements that remove redundant
//  calls of c_str() on strings.
//
//  Usage:
//  remove-cstr-calls <cmake-output-dir> <file1> <file2> ...
//
//  Where <cmake-output-dir> is a CMake build directory in which a file named
//  compile_commands.json exists (enable -DCMAKE_EXPORT_COMPILE_COMMANDS in
//  CMake to get this output).
//
//  <file1> ... specify the paths of files in the CMake source tree. This path
//  is looked up in the compile command database. If the path of a file is
//  absolute, it needs to point into CMake's source tree. If the path is
//  relative, the current working directory needs to be in the CMake source
//  tree and the file must be in a subdirectory of the current working
//  directory. "./" prefixes in the relative files will be automatically
//  removed, but the rest of a relative path must be a suffix of a path in
//  the compile command line database.
//
//  For example, to use remove-cstr-calls on all files in a subtree of the
//  source tree, use:
//
//    /path/in/subtree $ find . -name '*.cpp'|
//        xargs remove-cstr-calls /path/to/source

这可以被视为一种重构。当然,对于对重构感兴趣的人来说,这可能是有用的代码。所以加1 :) - Grzegorz Wierzowiecki
链接已失效。请查看github上的RemoveCStrCalls.cpp - undefined

6
不是开源的,但已经被用来进行非常复杂的自动化重构 C++ 程序的工作:我们的 DMS 软件重构工具包。DMS 是一个“库”(我们称之为“工具包”),可以组合使用其中的各种功能实现分析和/或自动翻译。
与 C++ 相关的,DMS 目前提供以下功能:
- 完整的 C++11 解析器,构建 AST 并能够准确地重构源代码(包括注释),具有完整的预处理器。 - 具有 C++ 名称和类型解析的完整 C++ 解析器(ANSI、GNU、MS Visual C++)。 - C++ 的控制流分析。 - 源到源的转换。 - 部分完成的“重命名”机制(请参见下面的讨论)。
从经验上说,我可以说 C++ 是一种非常难以转换的语言。
我们正在继续努力,并正在完成一个可靠的重命名工具。即使如此,这也很难;一个关键问题是名称遮蔽问题。您有一个局部变量X和该作用域内的对Y的引用;您尝试将Y重命名为X并发现局部变量“捕获”了访问。令人惊讶的是,在C++中您需要关注多少个命名空间和捕获类型。而这是许多其他重构的基础。
2014年2月编辑:完整的C++14解析器,控制流分析,局部数据流分析。

我不知道为什么我是第一个点赞这个答案的人。如果提问者想要限制为开源,他们应该说出来。当你说C++是一种很难通过解析器/机器“理解”的语言时,我相信你:这对于Eclipse的CDT重命名功能有何影响?我从未用过Eclipse进行C++编程,但我用过Java,以及它丰富的重构工具——当然,简单得多。 - kevinarpe
2
公平地说,OP要求Clang相关的答案,而我的答案并不涉及Clang。我加上它是因为暗示似乎没有任何东西可以在C++上进行重构,这显然是不正确的。 - Ira Baxter

5

https://github.com/lukhnos/refactorial基于clang,并提供以下变换:

Accessor:为指定的成员变量合成getter和setter

MethodMove:将内联的成员函数体移动到实现文件中

ExtractParameter:将一个函数变量提升为该函数的参数

TypeRename:重命名类型,包括标记类型(枚举,结构体,联合体,类),模板类,Objective-C类型(类和协议),typedefs甚至是内置类型(例如unsigned重命名为uint32_t)

RecordFieldRename:重命名记录(结构,联合)字段,包括C++成员变量

FunctionRename:重命名函数,包括C++成员函数

通过YAML配置文件进行规范化。我还没有试过它(但值得一试)。


3
另一个可能性是开发自己的GCC插件,或者开发GCC MELT扩展来完成任务。但是扩展GCC(或Clang)需要了解这些编译器的内部表示(GCC的Gimple和Tree),这需要一些工作。MELT是一个高级领域特定语言,用于扩展GCC。

1
还有一些C++重构的开源基础,其中包括Eclipse CDT。 - Ira Baxter
@IraBaxter - 你提到Eclipse CDT的观点很好。我遇到的问题是,我没有看到提供这种功能的命令行工具或库。因此,我想Eclipse CDT不能独立工作,而只能作为“可点击的GUI工具”,这限制了应用程序的使用,特别是在考虑重构自动化方面。 - Grzegorz Wierzowiecki
@GrzegorzWierzowiecki:如果Eclipse可以通过GUI点击来实现函数,那么显然内部有机制执行这些函数,只要付出一些(?)努力,就可以将它们公开为命令行工具。你的目标是什么?从你提出问题的方式来看并不清楚。如果是这样,我希望你能欣赏我的回答。 - Ira Baxter
@IraBaxter 我的意思是,如果有人知道Eclipse CDT作为库或命令行工具公开的信息,请告诉我们:)。 - Grzegorz Wierzowiecki
@IraBaxter 我不知道现在的情况如何,但我曾经尝试过在2009-2010年使用Eclipse CDT。我记得它感觉不适合用作批处理任务的“重构组件”和/或编辑器不可知的组件,适合为vim、emacs和其他人可能喜欢的插件准备。这就是为什么我主要询问libclang的原因 - 我发现它的API很有趣。 - Grzegorz Wierzowiecki
我认为,GCC在重构方面是无法使用的,因为它的解析器在生成AST期间进行代码转换。 - Tim Seguine

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接