防锈分析器是一个新的IDE后端,用于防锈编程语言。支持Open Collective上的防锈分析器。
在过去的几个月里,我一直忙于战壕中的生锈分析仪工作。今天,我不由自主地决定退一步,想想更长远的生锈分析仪路线图。
以下是我(@matklad)对此事的个人想法,它们不一定反映ide或编译团队的共识观点:-)。
对我来说,Ruust分析器最令我惊讶的一个方面是它已经很有用了。今天,我编写了Ruust代码,享受着快速的代码完成、大部分正确的转到定义和过多的帮助。甚至宏内的语法突出显示也起作用了!
我对锈蚀分析器的最初计划是编写一到两年的快速技巧,以演示概念验证IDE支持,这是值得努力而不是最终产品的东西。显然,我们已经大大超过了这个目标:今天人们依赖锈蚀分析器进行高效的锈蚀编程。这创造了它自己的机会和危险,这就给这份规划文档提供了信息。
今天人们编写了大量的锈蚀代码,他们至少应该得到基线级别的IDE支持。我认为我们当前的目标是使锈蚀分析器在当前状态下更易于使用,从而有效地实现RFC2912。
锈检仪端的编程工作量相对较小:我们需要修复各种协议一致性问题,清理各种缺省值以减少实验性,编写不需要太多热情就能理解的文档等等。组织人员的数量要大得多,我们需要用Rustup打包锈检分析器,合并 - 和锈检分析器VS代码扩展,弄清楚存储库结构等。
另外,我想确保防锈分析仪可以在大型非货运单库中使用。我们已经对此提供了一些初步支持,但还有一堆细节需要解决。
我看到的主要危险是锈谱仪在目前的状态下可能会僵化。这将是不好的,因为虽然目前的锈谱仪架构大体上是正确的,但许多重要的和难以改变的细节是错误的。在我们将锈谱仪推向公众之后,我们应该把重点放在枯燥的实施和设计工作上,相对较少的闪亮的GIF和大量的基础工作在未来十年内。
生锈分析仪使用粉笔作为特性解算器已经有很长一段时间了,如果能完成这项工作,并将其集成到生锈分析中,给人们带来他们的GATS,那就太好了。
我们应该在rustc和ruust分析器之间共享解析器。从IDE的观点来看,解析是编译器最有趣的部分之一。通过将rustc转换为无损语法,我们将跨越最重要的障碍,从那时起它将是一条下坡路。我认为这里的设计空间很容易理解,但要做的工作很多。在某个时候,我应该暂时不再积极研究rustc,而是专注于共享解析器。
Rust-Analyzer中最基本的数据结构,甚至比语法树更基本的数据结构是VFS,即虚拟文件系统。它有两个目标:
这种抽象是Ruust分析器的纯功能世界和外部世界的混乱之间的边界。它需要将不区分大小写的文件系统、符号链接和循环连接到一个更简单的树模型,其中包含我们想要的UTF8路径。此外,它还应该与非路径文件一起使用:在某些用例中,我们想要分析RuST代码,而这些代码并不真正驻留在文件系统中。
我正在努力解决的一个具体方面是动态性。一方面,似乎一个好的设计是要求预先将VFS中的文件集指定为一个全局集。这一点很重要,因为要正确注册文件监视器而不丢失更新,您需要急于抓取文件系统。然而,预先指定一组全局会使稍后更改该集变得混乱。
我很想知道这方面的现有解决方案。我有一个具体的问题是:";看守人如何处理项目的动态添加/删除?";。如果您有任何经验要分享,请在防锈分析仪中评论VFS问题。理想情况下,我们将VFS变成一个板条箱。IO板条箱,因为它似乎通常很有用,并且可以封装相当多的复杂性。
当前vfs为…。不太好,我不喜欢在上面建生锈分析仪。
目前,proc-宏是作为动态库实现的,可以加载到编译器进程中。这对编译器来说已经足够了,但对于IDE来说却相当不合适:
目前,我们通过在单独的进程中运行proc-宏来掩盖这一点,并且从不使proc-宏缓存无效,但这感觉像是一次黑客攻击,而且实现复杂性很高。如果proc-宏是确定性的,并且根据定义是可控的,那会更好,而WASM可以给我们提供这一点。
我有点担心这会受到那些希望在编译时通过TCP连接到数据库的人的反对:)从长远来看,我认为保证确定性编译是非常重要的,不管IDE的情况如何。
IDE可以利用一个非常重要的语言属性来大幅提高性能:
如果可以在不查看其他函数体的情况下对函数体进行类型检查,则可以通过大幅减少需要执行的工作量来加快IDE的运行速度。
如果我们想要快速而正确的IDE支持,我们应该通过版本机制将其从语言中逐步淘汰。
请注意,impl特征的自动特征泄漏问题不大,因为如果调用函数,您只需要检查函数的主体。当然,作为IDE作者,我希望要求指定自动特征,但作为语言用户,我更喜欢当前的设计。
铁锈分析器采用了一种新颖的、相当高科技的基于查询的体系结构进行增量计算。今天,很明显,这种通用的方法非常适合IDE用例。但是,我对具体的细节有很多疑问。我觉得今天的铁锈分析器缺乏机械上的同情心,留下了大量的性能。很多内部数据结构都是堆分配的弧滴,我们过度使用散列和未充分使用索引,我们甚至没有内部标识符!
要了解编译器前端的速度有多快,我强烈推荐查看用于Ruby的类型检查器Sorbet。您可以从以下两个链接开始:
这项工作给了我很大的启发,但也让我感到尴尬的是,生锈分析仪与那种原始的性能和简单性相去甚远。
我认为部分原因是本质上的复杂性,HashMap的名称解析和宏扩展都很困难,但我也想知道我们是否可以将SALSA更改为使用基于 - 的arenas,而不是HashMap中的Arc。
目前锈检分析器的一个特点是不将缓存持久化到磁盘上,在锈检分析器中打开项目意味着我们在处理标准库和依赖项时需要等待十几秒。
我认为这一限制实际上是一项非常有价值的资产!它迫使我们保持非增量代码路径相当快。
我认为我们实际上根本不需要持久缓存是合理的。RUST分析器基本上是文本处理,输入的大小是几十兆字节(无论如何我们都忽略了其中的大部分)。如果我们不在这里和那里降低性能,并将工作放在所有的内核上,我们应该能够在合理的时间预算内从头开始加载项目。
这里的第一步将是建立持续基准和性能调优的文化。
我们已经成功地使用了锈蚀分析器来锻造一个可以在IDE中工作的体系结构。现在是时候试验一下像所有锈蚀代码一样快速工作的体系结构了:-)
在我看来,决定项目长期成功的两个重要特征是:
构建用于测试的项目的发布版本需要多长时间?
我对铁锈分析器的测试速度非常满意。我在IntelliJ中的一个错误是添加了许多使用Rust标准库的测试,因此速度很慢。在铁锈分析器中,只有三个超级集成的测试需要真正的libstd,所有其他的测试都是从内存中只包含相关STD位的设备进行工作的。
但构建时间还有很多需要改进的地方。这一点非常重要, - 您构建代码的速度越快,做其他任何事情的速度就越快。请注意,即使是为了缩短构建时间,您也需要更快的构建时间!最近,我试图在Ruust-Analyzer中进行一些编译时优化,并测量“现在编译是否更快?”需要很长时间,所以只需尝试较少的不同优化!
这里最大的问题是,作为一种语言,Rust很难快速编译。我经常遇到的一个具体问题是,更改深度依赖项会重新编译世界。这与C/C++相反,在C/C++中,如果不接触任何.h文件,更改依赖项只需要重新链接。理论上,通过自动从cratts派生公共头文件,我们可以在Rust中拥有类似的东西。虽然我担心,如果没有显式的、物理的“这是ABI”边界,它在保持编译方面的效率会低得多。
顺便说一句,如果Rust坚持使用.crate文件,实现IDE支持会容易得多:-)。
尽管如此,生锈分析仪要比生锈容易造得多,我相信我们也可以为生锈做很多事情。
关于这一点,我已经在IRO上写了很长时间。要点是,我认为我们可以将rustc拆分成前端的文本处理部分,以及后端的LLVM、链接器和现实部分。那么,从理论上讲,前端部分可能是一个沼泽标准的Rust项目,它完全不依赖IO、外部工具或C++代码。
这里的一个问题是,目前rustc测试套件主要由UI和Run-pass测试集成组成,它们通过构建整个编译器来工作。这样的测试套件非常适合测试一致性和捕获回归,但不太适合快速TDD。我认为我们应该努力构建一个单元测试套件,例如,它可以在不构建类型检查器的情况下轻松地测试名称解析,并且不需要stdlib。
最后,这里的所有变化都是对现有软件体系的深度削减。推动如此雄心勃勃的项目完成需要人们,他们可以投入大量的时间和精力。直截了当地说,我们需要更多全职从事IDE工具的有偿工作的人员。我感谢我在铁业系统公司的同事们,他们投入了大量的精力使这成为现实。
如果您觉得铁锈分析仪很有用,并且是专业使用的,请考虑请贵公司通过我们的公开征集赞助铁锈分析仪。个人海绵也可以接受(非常感谢!)