本文转载自InfoQ中文站,作者:Johan Janssen,译者:BO,策划:丁晓昀,审校:马可薇

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 的信息可参见装置引导。