探索叮当工具第1部分:扩展叮当工具

这篇文章是一系列常规文章的一部分,其中C++产品团队和其他来宾回答了我们从客户那里收到的问题。这些问题可以是任何与C++相关的:MSVC工具集、标准语言和库、C++标准委员会、ISOCPP.ORG、CppCon等。

null

今天的帖子由客座作者Stephen Kelly撰写,他是Havok的开发人员,Qt和CMake的贡献者 博主 . 这篇文章是一个系列文章的一部分,他在这个系列文章中分享了他在当前团队中使用叮当工具的经验。

这篇文章是使用CLAN AST匹配器机械重构C++代码的三部分系列的一部分。在 最后一篇文章 ,我们确保代码至少可以用叮当声构建。虽然这一部分只对那些还没有用Clang构建代码的人感兴趣,但这一系列的其余部分吸引了一般C++用户。

互联网上还有许多其他资源,包括使用现有的叮当整洁检查和他们的动机,如我的 前雇主 . 在使用clangtidy为源代码到源代码的转换创建自定义扩展时,关于开发人员工作流程和工具的信息很少。本系列博客旨在填补这一空白。

大规模重构

让我们从意图和动机开始,让每个人都站在同一个立场上。

想象一下,我们希望重命名 时间() 上的方法 QDateTime编辑 类到 值() . 我们可以从使用一个简单的find-replace来转换文本的所有实例开始 时间 在我们的代码中 价值 . 这显然会改变太多–变量或名为 时间 本地函数将更改为 价值 例如。

我们可以尝试改进我们的模式,以找到类似于调用 时间() 只把它们转换成 值() ,但这就留下了一个问题,其他类使用一个名为 时间() 会受到影响,尽管我们不想这样。发生 时间() 字符串和注释也会受到影响,尽管我们可能不希望这样。我们只想更改一个类及其子类以及所有相关调用方的方法。

其中一个策略是在 QDateTime编辑 类头并尝试生成代码,修复编译器报告为生成错误的任何方法调用。任何尝试过这种方法的人都知道它无法扩展,所以我们尝试将其自动化。如果要移植的方法的名称足够唯一,则使用naï使用find-replace脚本的ve方法可以工作。即使使用具有通用名称的方法,现代开发人员ide也为一些语义移植提供了特性,因此这个示例有点做作。但是,我们可以给出一些示例,这些示例并不是通用的,而是内置到ide中的。

铿锵工具的可能性远远超出了我们使用正则表达式和文本处理工具(如 塞德 . 工具的早期演示,例如 LLVM会议 C++现在 强调几个目标:

  • 结构的上下文移植拼写相同
  • 同名不同类方法的区分
  • 与交互式/人工干预相对的批量操作模式
  • 使创建“一次性工具解决难题”的障碍非常低
  • C++中基于隐式构造和规则的重构代码
  • C++中基于隐式构造和规则的重构代码

作为一个C++程序员,维护一个代码库,你可以立即看到一些你可以使用的东西,比如:

  • 重命名特定的方法、变量或类(包括移植调用者)
  • 用新类型替换类型(包括函数内签名和API用户)
  • 使构造函数显式(包括在调用代码中添加显式构造)

这是令人信服的,因为创建这样的工具比直接尝试在源代码中进行更改需要更少的程序员时间。编码实践会随着时间的推移而改变,倡导它们的人也会改变!即使是维护良好的代码库,如果它足够老的话,也会有值得更改的结构。

潜入

让我们遵循C++开发人员创建新的步骤 叮当声 工具。我们将遵循“潜入”的精神,看看我们能在多大程度上掌握基本概念,根据需要扩展我们的知识。这意味着我们将掩盖一些代码、api、命令行选项和概念,它们阻碍了我们关注的内容。

可以基于clang创建独立的工具,但是在本例中,扩展 叮当声 它本身提供了一些优势,比如测试基础设施和现有的构建系统。但是,因为 叮当声 不支持外部插件 ,我们目前需要 从源代码构建llvm、clang和clang tidy .

一种相对快速的方法是:

cd ${SRC_ROOT}
git clone https://git.llvm.org/git/llvm.git
cd llvm/tools
git clone https://git.llvm.org/git/clang.git
cd clang/tools
git clone https://git.llvm.org/git/clang-tools-extra.git extra
cd ../../..
mkdir build
cd build
cmake .. -G "Visual Studio 15 2017" ^
    -DCMAKE_GENERATOR_PLATFORM=x64 ^
    -Thost=x64 ^
    -DLLVM_INCLUDE_TESTS=OFF ^
    -DLLVM_BUILD_TOOLS=OFF ^
    -DLLVM_INCLUDE_UTILS=OFF ^
    -DLLVM_TARGETS_TO_BUILD="" ^
    -DCLANG_ENABLE_STATIC_ANALYZER=OFF ^
    -DCLANG_ENABLE_ARCMT=OFF
cmake --build . --target clang-tidy --config RelWithDebInfo
cmake --build . --target clang-query --config RelWithDebInfo

确保将clangtoolsextra克隆到名为 额外的 ,因为构建系统依赖于此。

叮当声 既可以用于检查代码问题,也可以实际实现源代码到源代码的转换。它的扩展称为 检查 在文档和扩展API中 检查 不仅仅负责“检查”。

创建clangtidy扩展的开发循环如下图所示。

图片[1]-探索叮当工具第1部分:扩展叮当工具-yiteyi-C++库

创建新支票

我们从创建一个新的 叮当声 检查。然后我们检查 抽象语法树 要确定它与源代码的关系,请创建一个Matcher原型来处理AST并使用Clang 修复 系统替换源代码中的模式。这个过程被迭代,直到工具移植了所有相关的模式。我们将涵盖这个过程的所有部分,因为我们通过博客系列进展。

一旦LLVM/Clang构建完成,我们就可以运行 新建u check.py 中的脚本 叮当声 源代码为我们的移植工具生成代码。

cd ${SRC_ROOT}llvm	oolsclang	oolsextraclang-tidy
python add_new_check.py misc my-first-check

这将在 叮当声 来源。检查来源 ./杂项/MyFirstCheck.cpp ,我们可以看到它添加了前缀 太棒了_ 到那些还没有它的函数。检查 ./misc/MiscTidyModule.cpp文件 ,我们可以看到我们的新支票以名称注册在工具中 杂项我的第一张支票 .

重新生成clang tidy并在一些测试代码上试用:

void foo()
{  
}

void awesome_bar()
{  
}

叮当声 从命令行和我们的新检查:

clang-tidy.exe -checks=-*,misc-my-first-check testfile.cpp --

这个 -支票 选项接受逗号分隔的迷你语言,该语言用于启用和禁用对指定源文件运行的检查。 叮当声 有几个默认启用的检查。这个 -* 零件禁用任何默认检查,并且 杂项我的第一张支票 零件仅启用我们的新支票。有关启用和禁用检查的详细信息,请参阅 叮当声 文档 .

命令后面的两个破折号用于消除有关缺少编译数据库的警告。如果需要,可以在破折号之后指定其他编译选项,例如include目录和预处理器定义。

输出显示clang找到了我们的函数并建议添加 太棒了_ 前缀:

1 warning generated.
testfile.cpp:2:6: warning: function 'foo' is insufficiently awesome [misc-my-first-check]
void foo()
     ^~~
     awesome_

添加 -修复 命令行参数实际导致 叮当声 要重写源代码以修改函数:

clang-tidy.exe -checks=-*,misc-my-first-check -fix testfile.cpp

1 warning generated.
testfile.cpp:2:6: warning: function 'foo' is insufficiently awesome [misc-my-first-check]
void foo()
     ^~~
     awesome_
testfile.cpp:2:6: note: FIX-IT applied suggested code changes
clang-tidy applied 1 of 1 suggested fixes.

所以, 叮当声 报告它更改了我们的源文件,如果我们检查,我们将看到 测试文件.cpp 现在已经更新了内容!

验证

看起来我们有一个半通用的重命名工具,所以让我们尝试一些更复杂的东西。让我们还原“修复”并调用 foo() 棒极了吧() :

 
void foo()
{
}

void awesome_bar()
{
    foo();
}

如果我们逃跑 叮当声 又一次和 -修复 选项,我们将看到 void foo() 函数定义已移植,但调用 foo() 未移植,结果未生成。

这对一些读者来说可能很明显——函数的声明与函数的使用或调用不同。程序自动生成的代码 新建u check.py 脚本还不够复杂,无法完全生成代码 令人惊叹的 .

下一篇博客文章将探讨Clang如何表示C++源代码。然后,我们将能够扩展这个新工具来同时移植函数调用。

您打算在代码库中实现什么类型的机械源代码转换?请在下面的评论中告诉我们,或通过电子邮件直接联系作者 stkelly@microsoft.com ,或在Twitter上 @斯蒂维尔 .

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享