RustAssistant:使用 LLM 修复 Rust 代码编译错误

第47届国际软件工程会议 (ICSE) | 2025年4月 下载 BibTex

Rust 编程语言凭借其安全性保证,已经成为一种可行的底层系统编程语言选择,可以替代传统的、不安全的 C/C++ 等语言。 这些保证来自于强大的基于所有权的类型系统,以及对闭包、模式匹配等特性的原始支持,这使得代码更简洁且易于推理。 这些独特的 Rust 特性也为程序员带来了陡峭的学习曲线。

本文介绍了一个名为 RustAssistant 的工具,该工具利用大型语言模型 (LLMs) 的新兴能力,自动为 Rust 编译错误提供修复建议。 RustAssistant 巧妙地结合了提示技术以及 LLMRust 编译器之间的迭代,以实现高精度的修复。 在流行的开源 Rust 仓库中的真实编译错误上,RustAssistant 能够实现大约 74% 的惊人峰值准确率。 我们还贡献了一个 Rust 编译错误数据集,以支持进一步的研究。

Publication

研究小组

研究领域

研究实验室

使用 LLM 进行安全的底层编程 | Microsoft Research Forum

Aseem RastogiPantazis Deligiannis 在 Microsoft Research Forum 第 5 集中呈现

Microsoft Research FoSSE (未来可扩展软件工程) 的首席研究员 Aseem Rastogi 和首席研究工程师 Pantazis Deligiannis 讨论了 ICSE’2025 中关于使用大型语言模型 (LLMs) 进行安全底层编程的技术成果。 这些结果展示了 LLMs 如何在遗留 C 代码中推断出机器可检查的内存安全不变量,以及 LLMs 如何协助修复 Rust 代码库中的编译错误。

注册该系列 其他第 5 集的演讲 所有之前的演讲

闪电演讲记录

用于安全底层编程的 LLM

Aseem Rastogi, Microsoft Research FoSSE (未来可扩展软件工程) 首席研究员

Pantazis Deligiannis, Microsoft Research FoSSE (未来可扩展软件工程) 首席研究工程师

本次演讲涵盖了 ICSE 2025 上关于使用大型语言模型 (LLMs) 进行安全底层编程的两项技术成果。 这些结果展示了 LLMs 如何在遗留 C 代码中推断出机器可检查的内存安全不变量,以及 LLMs 如何协助修复 Rust 代码库中的编译错误。

Microsoft Research Forum,2025 年 2 月 25 日

FRIEDERIKE NIEDTNER, Microsoft Research AI Frontiers 首席技术研究项目经理:接下来的演讲结合了两个项目,这两个项目都利用 LLM 的能力来理解和生成代码。 两者都旨在帮助开发人员解决安全底层编程的难题。 一个是确保遗留 C 代码中的内存安全;另一个是介绍 RustAssistant,这是一款用于帮助开发人员自动修复 Rust 编译错误的工具。

ASEEM RASTOGI:大家好,我是 Aseem Rastogi,我是 Microsoft Research 未来可扩展软件工程组织的一名研究员。 我将向大家介绍我们的论文“LLM 辅助内存安全”。 本文将于今年晚些时候在 5 月份举行的第 47 届国际软件工程会议上发表。

底层语言(如 CC++)中缺乏内存安全性是导致软件安全漏洞的主要原因之一。 例如,Microsoft 的一项研究估计,Microsoft 每年修复并分配 CVE 的安全漏洞中,有 70% 是由内存安全问题引起的。 研究人员已经提出了 C 的安全方言,例如 Checked C,它在额外的源代码级注解的帮助下,提供了低性能开销的内存安全保证。 然而,添加这些注解以及实现它们所需的代码重构的成本,成为采用这些工具的瓶颈。 通常,将形式验证应用于真实软件也面临着同样的挑战。

在我们的论文中,我们探索了使用预训练的大型语言模型来帮助进行代码重构和推断采用 Checked C 所需的源代码注解的任务。 让我们考虑一个以整数数组作为输入并对前 n 个元素求和的示例。 为了推理这个函数的内存安全性,Checked C 需要对 p 进行注解。 其中一种注解如下所示。 这告诉编译器 p 是一个至少有 n 个元素的数组,这足以确保此函数中内存访问的安全性。 它还有助于对该函数的调用者施加明确的义务,即他们必须将适当大小的数组传递给它。

我们的目标是在 LLM 的帮助下推断出此类注解。 对于这个问题,LLM 似乎是一个完美的匹配。 很难在符号工具中编码关于真实世界代码和复杂代码模式的推理。 另一方面,LLMs 已经展示了与程序员类似的、惊人的代码理解和推理能力,即使对于真实世界的代码也是如此。 其次,LLM 的幻觉可能会导致不正确的注解,但它们不会损害内存安全性。 一旦将注解添加到代码中,即使注解不正确,Checked C 编译器也能保证内存安全性。 这样,我们就获得了两全其美的结果!

然而,在大型代码库中为整个程序转换使用 LLM 提出了另一个挑战。 我们需要将任务分解为可以放入 LLM 提示中的较小子任务,同时向每个提示添加相关的符号上下文。 换句话说,为了让 LLMs 能够像程序员一样进行推理,我们需要向它们提供程序员会考虑的上下文。 我们的论文提出了一个框架,通过程序依赖图与 LLMs 协同工作来实现这一点。 我们在名为 MSA 的工具中实现了我们的想法,并在高达 20,000 行代码的真实代码库上对其进行评估。 我们观察到,MSA 可以推断出最先进的符号工具无法推断出的 86% 的注解。 虽然我们的论文侧重于内存安全,但我们的方法更通用,可以有效地利用 LLMs 来扩展形式验证在真实软件中的使用——最重要的是,在不损害可靠性保证的情况下这样做。 我们对这个研究方向感到非常兴奋。

接下来,我的同事 Pantazis 将向您介绍我们如何利用 LLMs 让程序员更容易采用 Rust。 谢谢。

PANTAZIS DELIGIANNIS:大家好。 我是 Pantazis,今天我将介绍我们关于利用大型语言模型的力量进行安全底层编程的工作。 具体来说,我将重点介绍我们最近关于 RustAssistant 的论文,该工具使用 LLMs 自动修复用 Rust 编写的代码中的编译错误。 这项工作是与屏幕上列出的其他人共同完成的,并将于今年春季晚些时候在国际软件工程会议上发表。

好的,让我们深入了解一下! 为什么我们关心使用 Rust 进行安全底层编程? 因此,Rust 编程语言凭借其内存和并发安全保证,已经成为构建底层软件系统的可行选择,可以替代传统的、不安全的替代方案,如 CC++。 这些保证来自强大的基于所有权的类型系统,该系统在编译时强制执行内存和并发安全性。 然而,Rust 对开发人员来说提出了陡峭的学习曲线,尤其是当他们遇到与高级语言特性(如所有权、生命周期或特征)相关的编译错误时。 与此同时,Rust 每年都变得越来越受欢迎,因此随着越来越多的开发人员采用 Rust 来编写关键软件系统,解决使用 Rust 编写代码的难题至关重要。

Microsoft Research,我们创建了一个名为 RustAssistant 的工具,该工具利用最先进的 LLMs 的力量,通过自动为 Rust 编译错误提供修复建议来帮助开发人员。 我们的工具巧妙地结合了提示技术以及大型语言模型和 Rust 编译器之间的迭代,以提供高精度的修复。 在 GitHub 上流行的开源 Rust 仓库中的真实编译错误上,RustAssistant 能够实现大约 74% 的惊人峰值准确率。

好的,现在让我们逐步了解 RustAssistant 的工作原理。 让我们从第一步开始:构建代码并解析构建错误。 此类错误可能从简单的语法错误到涉及特征、生命周期或 Rust 代码中跨多个文件传播的所有权规则的非常复杂的问题。 因此,当开发人员编写无法编译的 Rust 代码时,Rust 编译器会生成详细的错误消息,其中包括错误代码、错误位置以及与此错误代码相关的文档和示例。

为了说明这个过程,让我们看一下屏幕上这个非常简单的例子。 在本例中,开发人员尝试使用大于等于运算符比较其代码中的自定义 VerbosityLevel 枚举。 然而,Rust 编译器抛出一个错误,声明此二元运算不能应用于 VerbosityLevel。 编译器建议此错误背后的原因是 VerbosityLevel 没有实现 Rust 中执行此类比较所需的特征。 这个详细的错误消息正是 RustAssistant 在此步骤中捕获的内容,为其进行下一个处理阶段做准备。

在下一步中,RustAssistant 会获取在上一步中生成的这个详细的错误信息,并专注于提取与此错误直接相关的特定代码部分。 查看屏幕上的示例,我们的工具会自动提取与枚举相关的代码片段及其在 log_error 函数中的使用。 这不仅包括有问题的代码行,还包括其他代码片段,这些代码片段提供了理解和解决错误所需的必要上下文。 该工具还会捕获错误详细信息,例如错误代码和随附的编译器关于缺少用于执行比较的特征的建议。 然后将这些提取的代码片段和错误详细信息打包成 LLM 的提示。 这确保了 LLM 仅接收建议准确修复所需的必要信息,而不会被代码库中不相关的部分淹没。 这种仔细的定位步骤对于效率和准确性都至关重要,尤其是在处理非常大的代码库时。

现在让我们进入最后一步。 在这里,RustAssistant 将仔细定位的提示(包括错误详细信息和相关代码片段)发送到大型语言模型 APILLM 生成一个建议的修复,格式化为代码差异——换句话说,不包括整个代码片段以提高效率,而只包括新的、编辑过的或删除的代码行。 例如,在我们的构建错误的情况下,LLM 建议向枚举添加缺少的特征,如屏幕上所示。 此修复确保使用大于等于运算符的比较现在可以按预期工作。 接下来,RustAssistant 解析此建议的修复并将更改应用于代码库中的相应文件。 一旦应用了修复,我们的工具就会再次运行 Rust 编译器,以验证构建错误是否已解决。 如果代码编译通过,那就太好了! 现在流程完成,我们可以进行进一步的验证,例如运行任何单元测试。

然而,如果出现新错误或修复未能完全解决问题,RustAssistant 会将更新的上下文发送回 LLM,迭代直到代码编译无误。 这种迭代过程使我们的工具能够处理复杂的、多步骤的修复,同时确保正确性并与开发人员的意图保持一致。 当然,我在此处展示的示例非常简单,但您可以想象该工具能够修复更复杂的构建错误。

总而言之,我快速介绍了如何使用 RustAssistant 帮助开发人员自动修复其 Rust 代码库中的构建错误。 在我们的论文中,我们评估了 GitHub 上前 100 个 Rust 仓库中的 RustAssistant,并表明它可以在真实编译错误上实现大约 74% 的惊人峰值准确率。 我们邀请您阅读我们的 ICSE 论文,因为它不仅详细讨论了评估结果,而且还深入探讨了有趣的技术细节,例如我们如何设计我们的提示以及我们为在不损失准确性的情况下在非常大的代码库上扩展 RustAssistant 而开发的各种技术。

感谢您的收听。