写点什么

Error Prone 通过检测常见错误帮助改善 Java 代码

  • 2022-10-26
    北京
  • 本文字数:3718 字

    阅读完需:约 12 分钟

Error Prone 通过检测常见错误帮助改善Java代码

Error Prone是谷歌开源的一个 Java 编译插件,可以在编译时进行静态分析、bug 检测,或者对可能的优化提出建议。插件中包括了超过 500 个预定义的bug检查,并且允许第三方和自定义插件。检查到问题之后,Error Prone 能够将问题通过 warning 显示出来或者用预定义的解决方案自动修改代码。Error Prone 支持 Java 8、11,以及 17,可以被用来修复 bug 或者大规模重构。文档中提供了使用 Maven、Bazel、Ant 以及 Grandle 的安装和配置教程。需要将 Error Prone 在编译器中配置为 annotation processor(注解处理器),下面是通过 Maven 创建测试工程的示例:

<plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-compiler-plugin</artifactId>    <version>3.10.1</version>    <configuration>        <release>17</release>        <encoding>UTF-8</encoding>        <compilerArgs>            <arg>-XDcompilePolicy=simple</arg>            <arg>-Xplugin:ErrorProne</arg>        </compilerArgs>        <annotationProcessorPaths>            <path>                <groupId>com.google.errorprone</groupId>                <artifactId>error_prone_core</artifactId>                <version>2.15.0</version>            </path>        </annotationProcessorPaths>    </configuration></plugin>
复制代码


接下来可以创建一个示例类。下面的方法使用了 equals 方法来对比两个数组,更准确地说,此处所比较的是对象本身而不是数组的内容。


public boolean compare(String firstList[], String secondList[]) {    return firstList.equals(secondList);}
复制代码


执行 mvn clean verify 触发 Error Prone 分析,下面是运行结果中的错误信息中:


[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.10.1:        compile (default-compile) on project ErrorProne: Compilation failure[ERROR] …/ErrorProne/src/main/java/org/example/Main.java:[5,28]     [ArrayEquals] Reference equality used to compare arrays[ERROR]   (see https://errorprone.info/bugpattern/ArrayEquals)[ERROR]   Did you mean 'return Arrays.equals(firstList, secondList);'?
复制代码


报出了ArrayEquals错误,Error Prone 的建议是修改实现方式,以比较数组的内容而不是比较对象。


return Arrays.equals(firstList, secondList);
复制代码


报错不仅可以帮助改善代码,也可以让 Error Prone 自动应用解决方案。 -XepPatchChecks 参数的应用应该包含由逗号分隔开的 bug 模式列表,在上面的情况中,只有 ArrayEquals 解决方案用于这段代码。 -XepPatchLocation 参数用于具体定位解决方案文件位置,在当前情境中是修改了源文件:


<compilerArgs>    <arg>-XDcompilePolicy=simple</arg>    <arg>-Xplugin:ErrorProne -XepPatchChecks:ArrayEquals            -XepPatchLocation:IN_PLACE</arg></compilerArgs>
复制代码


现在,在执行 mvn clean verify 之后,类文件被自动修改为:


public boolean compare(String firstList[], String secondList[]) {    return Arrays.equals(firstList, secondList);}
复制代码


文档里提供了更多关于命令行标识的信息。除了内置的 bug 模式,也可以使用例如SLF4J等第三方发布的插件,或创建自定义插件。内置规则的源码提供了多种可用于定义插件的不同示例模板。例如,自定义一个能够用新的 JUnit 5 @BeforeEach 注解器代替旧版 @Before JUnit 注解器的 Error Prone 插件。


和前文例子不同,自定义的 Error Prone 插件应该被放置于 Maven 模块。Error Prone 通过服务加载器机制来加载 bug 检测。这类之际通常一定的配置,然而谷歌的AutoService项目借助 @AutoService 注解简化了配置工作。@BugPattern 注解用于定义 bug 的名称、简介以及严重性。在下面的例子中,如果没有找到 @Before 注解器会返回Description.NO_MATCH ,否则SuggestedFix会用 @BeforeEach 注解替代 @Before 注解。


@AutoService(BugChecker.class)@BugPattern(    name = "BeforeCheck",    summary = "JUnit 4's @Before is replaced by JUnit 5's @BeforeEach",    severity = BugPattern.SeverityLevel.SUGGESTION)public class BeforeCheck extends BugChecker implements BugChecker.AnnotationTreeMatcher {    private static final Matcher<AnnotationTree> matcher =            isType("org.junit.Before");
@Override public Description matchAnnotation(AnnotationTree annotationTree, VisitorState visitorState) { if (!matcher.matches(annotationTree, visitorState)) { return Description.NO_MATCH; } return describeMatch(annotationTree, SuggestedFix.replace(annotationTree, "@BeforeEach")); }}
复制代码


构建自定义 Error Prone 插件的时候都是需要 Error Prone 和 AutoService 依赖的。


<dependency>  <groupId>com.google.errorprone</groupId>  <artifactId>error_prone_annotations</artifactId>  <version>2.15.0</version></dependency><dependency>  <groupId>com.google.errorprone</groupId>  <artifactId>error_prone_check_api</artifactId>  <version>2.15.0</version></dependency><dependency>  <groupId>com.google.auto.service</groupId>  <artifactId>auto-service-annotations</artifactId>  <version>1.0.1</version></dependency>
复制代码


AutoService 应该被配置为一个注解处理器。


<annotationProcessorPaths>    <path>        <groupId>com.google.auto.service</groupId>        <artifactId>auto-service</artifactId>        <version>1.0.1</version>    </path></annotationProcessorPaths>
复制代码


现在,自定义的 Error Prone 插件可以通过 mvn install 命令,安装在本地的 Maven 仓库。执行命令后,示例工程应该会被配置为使用新的自定义插件作为注解处理器。


<annotationProcessorPaths>    <path>        <groupId>org.example.custom.plugin</groupId>        <artifactId>ErrorProneBeforeCheck</artifactId>        <version>1.0-SNAPSHOT</version>    </path></annotationProcessorPaths>
复制代码


新的 BeforeCheck 应该被加入到了 Error Prone 分析中。


<compilerArgs>  <arg>-XDcompilePolicy=simple</arg>  <arg>-Xplugin:ErrorProne -XepPatchChecks:BeforeCheck            -XepPatchLocation:IN_PLACE</arg></compilerArgs>
复制代码


添加一个示例测试类,其中包含@Before@BeforeEach的两个注解。


public class ErrorProneTest {  @Before  void before() {  }  @BeforeEach  void beforeEach() {  }}
复制代码


运行 mvn verify 时,新的自定义 Error Prone 插件将用@BeforeEach注解替换@Before注解。


public class ErrorProneTest {  @BeforeEach  void before() {  }  @BeforeEach  void beforeEach() {  }}
复制代码


Error Prone 所使用的 Java internal 目前处于隐藏状态,可能会导致如下错误:


java.lang.IllegalAccessError: class com.google.errorprone.BaseErrorProneJavaCompiler (in unnamed module @0x1a6cf771) cannot access class com.sun.tools.javac.api.BasicJavacTask (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.api to unnamed module @0x1a6cf771
复制代码


Maven 的解决办法是通过在项目根目录下创建.mvn 目录来暴露 Java internal,在目录中创建一个 jvm.config 文件,其中配置如下:


--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED
复制代码


或者可以将--add-exports--add-opens 参数配置添加到 Maven 编译器插件的 pom 文件中:


<plugin>    <groupId>org.apache.maven.plugins</groupId>    <artifactId>maven-compiler-plugin</artifactId>    <version>3.10.1</version>    <configuration>        <compilerArgs>            <arg>--add-exports</arg>            <arg>jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
复制代码


更多在 Bazel、Ant 和 Gradle 中使用 Error Prone 的信息可参见安装引导


相关阅读:

Java 近期新闻:顺序集合、Spring 6.0-RC1、Tomcat、Reactor 2022.0-RC1

Spring Boot 3 将于 2022 年 11 月发布,延迟了对 Java 模块系统的支持


2022-10-26 08:009194

评论

发布
暂无评论
发现更多内容

情感语音识别的前世今生

数据堂

Python MySQL 数据库查询:选择数据、使用筛选条件、防止 SQL 注入

小万哥

Python 程序员 软件 后端 开发

项目开发老板的预算低,华为云这款轻量应用服务便宜又好用

平平无奇爱好科技

开发人员的私人助手:亚马逊CodeWhisperer

阿呆

Amazon CodeWhisperer

百度智能云千帆大模型平台再升级,SDK版本开源发布!

herosunly

让程序猿轻松告别996,华为云这款轻量应用服务器火了

平平无奇爱好科技

当我们在选国产工业软件时,到底在选什么?

ToB行业头条

华为云耀云服务器L实例在中小企业里爆“火”,掌握使用技巧效率翻倍

YG科技

外贸新手如何做好网站?华为云耀云服务器L实例轻松“避雷”

平平无奇爱好科技

新手站长如何选择云服务器?华为云耀云服务器L实例值得拥有

轶天下事

mac电脑端Git客户端 Fork 激活最新版

胖墩儿不胖y

git Mac软件 Git客户端

评估 RAG 的神器来啦!TruLens + Milvus=?

Zilliz

Zilliz rag trulens

批量网站建设成本太高?华为云“神器”轻量应用服务器破解困局

YG科技

“轻”而不“弱”,华为云耀云服务器L实例引领轻量应用新时代

轶天下事

Go,14周年

Tony Bai

Go golang 编程 编程语言 go语言

10款好用的项目管理工具推荐,项目经理必备的高效办公神器!

彭宏豪95

项目管理 项目经理 在线白板 项目管理软件 办公软件

另辟蹊径者 PoseiSwap:背靠潜力叙事,构建 DeFi 理想国

股市老人

“断崖式”客户预算和客户要求,华为云耀云服务器L实例填平鸿沟!

轶天下事

文心耀乌镇,“大模型之光”展现了什么?

脑极体

AI

CodeWhisperer 史上最强大的 AI 编程助手!!

亚马逊云科技 (Amazon Web Services)

Java Python 人工智能 云上探索实验室 Amazon CodeWhisperer

GPT最佳实践:五分钟打造你自己的GPT

caiyongji

openai GPT ChatGPT

【亚马逊云科技产品测评】活动征文|10分钟拥有一台AWS Linux系统

青花锁

Linux AWS EC2

把“上云”变成一件简单事情,华为云这款轻量应用服务器大有乾坤

YG科技

如何降低开发测试成本?华为云这个宝藏工具值得一试!

YG科技

百度智能云正式上线Python SDK版本并全面开源!

爱编程的喵喵

Linux软件包(源码包和二进制包)

芯动大师

大厂都在用的运营_秘诀_,华为云这款产品让小程序开发价值脱颖而出!

YG科技

Aws EC2系统上搭建Echarts大屏展示项目

青花锁

AWS EC2

临时项目人员空缺,华为云耀云服务器L实例江湖救急

平平无奇爱好科技

甲方“爸爸”又加开发需求,华为云这款轻量应用服务器解燃眉之急

轶天下事

Error Prone 通过检测常见错误帮助改善Java代码_编程语言_Johan Janssen_InfoQ精选文章