
开发者用 Rust 构建兼容 Checkstyle 的快速 Java 代码检查工具
来源标题:Show HN: I built a fast Java linter in Rust
开发者发布用 Rust 编写的 Java linter javaid,可直接读取现有 checkstyle.xml 并输出与 Checkstyle 14.4.0 一致的结果,速度提升 4-6 倍、内存占用约为其十分之一。在 Guava 和 Spring 上测试结果零差异,项目已在 Show HN 展示。
以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。
一个用 Rust 写的、兼容 Checkstyle 的 Java linter。把它指向你现有的 checkstyle.xml,就能得到 Checkstyle 14.4.0 输出的检查结果,而耗时只有后者的一小部分。哪些内容没有覆盖,见「限制」一节。
安装 javaid 后,在本仓库的克隆目录里运行下面的命令。docs/demo/ 下有一个 14 行的文件,里面有几个问题。先下载 Google 的配置(即 Checkstyle 14.4.0 自带的 google_checks.xml),然后运行:
$ cd docs/demo $ curl -LO https://raw.githubusercontent.com/checkstyle/checkstyle/checkstyle-14.4.0/src/main/resources/google_checks.xml $ javaid --config google_checks.xml src javaid: google_checks.xml: running 106 module(s), skipped 0 src/demo/Demo.java:3:17: AvoidStarImport: Using the '.' form of import should be avoided - java.util.. src/demo/Demo.java:5:1: MissingJavadocType: Missing a Javadoc comment for 'Demo'. src/demo/Demo.java:6:15: GoogleNonConstantFieldName: Non-constant field name 'Count' must start lowercase, be at least 2 chars, avoid single lowercase letter followed by uppercase, contain only letters, digits or underscores, with underscores allowed only between adjacent digits. src/demo/Demo.java:8:3: MissingJavadocMethod: Missing a Javadoc comment for 'do_work'. src/demo/Demo.java:8:15: GoogleMethodName: Method name 'do_work' has invalid underscore usage, underscores only allowed between adjacent digits. src/demo/Demo.java:9:5: NeedBraces: 'if' construct must use '{}'s. src/demo/Demo.java:11:9: Indentation: 'for' child has incorrect indentation level 8, expected level should be 6. $ echo $? 1
Checkstyle 14.4.0 会以它自己的输出格式打印出同样的七条结果。第一行输出到 stderr,说明你的配置里有多少个模块运行了。只要有结果被报告,退出状态就是 1,因此可以直接用在 CI 和 hook 里。
以下是完整内置配置的实测耗时(wall-clock 秒数;javaid 取 3 次运行的中位数,Checkstyle 取 2 次),测试机器为 i7-10700 台式机(8 核 16 线程)。Guava 有 3,277 个文件,Spring Framework 有 9,293 个。
配置、语料 | Checkstyle 14.4.0 | javaid,1 线程 | javaid,16 线程 Google、Guava | 67.9 s | 18.2 s (3.7x) | 2.88 s (24x) Google、Spring | 153.6 s | 26.8 s (5.7x) | 4.06 s (38x) Sun、Guava | 43.3 s | 12.0 s (3.6x) | 1.94 s (22x) Sun、Spring | 72.4 s | 17.1 s (4.2x) | 2.67 s (27x) 峰值内存 | 3.0 到 3.9 GB | 37 到 300 MB | 190 到 400 MB 单文件(Joiner.java),从进程启动到退出 | 1.0 到 1.3 s | 9 到 15 ms | 不适用
Checkstyle 是单线程运行的,所以 1 线程那一列才是同类对比。单文件那一行统计的是从进程启动到退出的时间:javaid 取 100 次运行的平均值,Checkstyle 取 10 次运行的中位数。测试机器是一台普通台式机,不是隔离的基准测试机。测试方法、原始数据和脚本都在 bench/results/full-configs.md。
可以从 crates.io 安装,需要 Rust 工具链(构建和测试用的是 Rust 1.96;这个 crate 使用 edition 2024)。目前还没有预编译的二进制文件。
cargo install javaid # 安装到 ~/.cargo/bin
或者从克隆的仓库构建:
git clone https://github.com/rsmb7z/javaid && cd javaid cargo build --release # 二进制文件在 target/release/javaid
用法:
javaid --config checkstyle.xml src/ # 你的 Checkstyle 配置 javaid src/ # 所有模块,使用 Checkstyle 的默认值 javaid --rules NeedBraces,MethodName src/ javaid --list-rules # 128 个模块,也列在 docs/modules.md 中 javaid --grammar upstream src/ # 使用上游的 tree-sitter-java 语法(见「限制」) javaid -j 4 --stats src/ # 四个线程,计时输出到 stderr
输出为每行一条结果,格式是 path:line:col: Module: message。符号链接目录会被跟随,与 Checkstyle 的行为一致(--no-follow-symlinks 可以关掉这一行为)。
使用你自己的配置时:javaid 未实现的模块,或它无法满足的属性,会在 stderr 中连同原因一起列出,并且该模块不会运行(它无法应用的自定义 <message> 模板只会被记录一条提示,javaid 会打印自己的措辞)。例如:
javaid: custom.xml: running 17 module(s), skipped 1 skipped Foo: not implemented
如何验证
Checkstyle 14.4.0 是判定基准。一个模块只有在以下方面与它零差异时才会被接受:
对比覆盖文件、行、列、规则和消息。对比不包含 Checkstyle 自己输出的「Javadoc comment ... has parse error」这类行(Google 配置下有 36 条,Sun 配置下有 4 条),javaid 有意不模仿这些行。仅仅与文档一致还不够,因为 Checkstyle 有一些怪癖,而 javaid 会复现它们:Javadoc 只会附着在声明第一个 token 之前,中缀运算符之前的注释永远不算「尾随注释」,还有一条消息打印的是 1,152,而另一条打印的是 1152。
要复现对比和计时,见 bench/README.md。最初启动这个项目的实验及其注意事项在 FINDINGS.md 中。
限制
并非 Checkstyle 的全部功能。实现了 128 个模块,即 Google 和 Sun 配置用到的那些,包含过滤器。Checkstyle 14.4.0 的 jar 里大约有 250 个 check 类。欢迎提 issue 要求增加模块。
只做语法层面,和 Checkstyle 一样:不做类型解析。javaid 默认使用持续维护的 tree-sitter-java-orchard 语法,因为上游的 tree-sitter-java 无法解析带注解的可变参数(Object @Nullable ... args),而 Spring 和 Guava 用到了这种写法。
只支持纯文本输出。没有 XML、SARIF 或其他格式,没有 --fix,没有缓存。
某些属性,以及受支持子集之外的 XPath 构造,会按名称被拒绝,例如 ModifierOrder 的 modifiersOrder 和 ArrayTypeStyle 的 javaStyle=false。
重复模块。对于一组列出的模块(LeftCurly、OperatorWrap、SeparatorWrap、TypeName、RegexpSingleline、TodoComment 等,完整列表在 docs/compatibility.md 中),同一模块的多个实例会全部运行;其他模块的第二个实例会被跳过,并在 stderr 中列出。
Checkstyle 对奇怪 Javadoc 打印的解析错误行不会被模仿;少数针对奇怪构造的已知差异列在兼容性说明中。
只读取 .java 文件。没有 fileExtensions 时,Checkstyle 会把路径下的每个文件都交给文件级模块;javaid 只读取 .java 文件,并打印一条说明。一个 Checker 级别的
(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。