When a BugChecker throws from its constructor, a CompilationTestHelper test fails under the en_US locale with ErrorProne suffered an internal crash. Under de_DE the same test passes: the checker never runs, and doTest() returns normally. ja_JP and zh_CN behave like de_DE.
Reproducer
src/test/java/repro/ThrowingCheckerTest.java:
package repro;
import com.google.errorprone.BugPattern;
import com.google.errorprone.CompilationTestHelper;
import com.google.errorprone.bugpatterns.BugChecker;
import java.util.Locale;
import org.junit.Test;
public class ThrowingCheckerTest {
@BugPattern(summary = "Fails to initialize", severity = BugPattern.SeverityLevel.ERROR)
public static class ThrowingChecker extends BugChecker {
public ThrowingChecker() {
throw new IllegalStateException("checker failed to initialize");
}
}
@Test
public void checkerThatCannotBeConstructedFailsTheTest() {
System.out.println("Locale.getDefault() = " + Locale.getDefault());
CompilationTestHelper.newInstance(ThrowingChecker.class, getClass())
.addSourceLines("Test.java", "class Test {}")
.doTest();
}
}
build.gradle (plus a settings.gradle with rootProject.name = 'ep-locale'):
plugins { id 'java' }
repositories { mavenCentral() }
dependencies {
testImplementation 'com.google.errorprone:error_prone_check_api:2.50.0'
testImplementation 'com.google.errorprone:error_prone_test_helpers:2.50.0'
testImplementation 'junit:junit:4.13.2'
}
test {
jvmArgs(['api', 'code', 'comp', 'file', 'main', 'model', 'parser', 'processing', 'tree', 'util']
.collect { "--add-exports=jdk.compiler/com.sun.tools.javac.$it=ALL-UNNAMED" })
jvmArgs "-Duser.language=${findProperty('lang')}", "-Duser.country=${findProperty('country')}"
testLogging { events 'passed', 'failed'; showStandardStreams = true }
outputs.upToDateWhen { false }
}
gradle test -Plang=en -Pcountry=US
gradle test -Plang=de -Pcountry=DE
Under en_US the test fails, as expected:
ThrowingCheckerTest > checkerThatCannotBeConstructedFailsTheTest STANDARD_OUT
Locale.getDefault() = en_US
ThrowingCheckerTest > checkerThatCannotBeConstructedFailsTheTest FAILED
com.google.common.truth.AssertionErrorWithFacts at ThrowingCheckerTest.java:23
1 test completed, 1 failed
com.google.common.truth.AssertionErrorWithFacts: ErrorProne suffered an internal crash: An exception has occurred in the compiler (21.0.9). Please file a bug against the Java compiler via the Java bug reporting page (https://bugreport.java.com) after checking the Bug Database (https://bugs.java.com) for duplicates. Include your program, the following diagnostic, and the parameters passed to the Java compiler in your report. Thank you.
java.lang.AssertionError: com.google.errorprone.scanner.ErrorProneInjector$ProvisionException: Failed to initialize repro.ThrowingCheckerTest.ThrowingChecker
at com.google.errorprone.ErrorProneAnalyzer.lambda$scansPlugins$2(ErrorProneAnalyzer.java:163)
Under de_DE it passes:
ThrowingCheckerTest > checkerThatCannotBeConstructedFailsTheTest STANDARD_OUT
Locale.getDefault() = de_DE
ThrowingCheckerTest > checkerThatCannotBeConstructedFailsTheTest PASSED
BUILD SUCCESSFUL in 1s
ja_JP and zh_CN pass the same way. fr_FR fails like en_US.
Which tests are affected
Under de_DE, the crash goes unnoticed only where nothing else checks the diagnostics or the compilation result:
- A source with no
// BUG: Diagnostic ... markers, and no expectNoDiagnostics() or expectResult(...), passes. This is the reproducer above.
- A test that expects
doTest() to throw because the checker cannot initialize, written as assertThrows(AssertionError.class, helper::doTest) with JUnit 4.13.2, fails with expected java.lang.AssertionError to be thrown, but nothing was thrown.
expectNoDiagnostics() still fails, because the result check catches it: Expected compilation result to be OK, but was ERROR. No diagnostics were emitted.
- A source with a
// BUG: Diagnostic contains: x marker still fails, because the expected diagnostic is missing: Did not see an error on line 3 matching x. There were no errors.
Expected behavior
doTest() throws AssertionError with ErrorProne suffered an internal crash: ... whatever the JVM default locale is, as it does under en_US. Commit 5abc8e5 (from #4112) added that check for this case, a checker that fails in its constructor.
Cause (my reading of the code; the report above stands without it)
A checker constructor that throws is not reported as an error.prone.crash diagnostic: the checker is instantiated from ErrorProneAnalyzer.finished (see the stack trace above), and the exception propagates into javac, which prints its msg.bug banner and returns a failure from JavacTask.call(). doTest() recognizes that banner by its English text (CompilationTestHelper.java#L342-L344 on master):
assertWithMessage("ErrorProne suffered an internal crash: %s", stringifiedOutput)
.that(stringifiedOutput)
.doesNotContain("An exception has occurred in the compiler");
javac localizes msg.bug, and JDK 21.0.9 prints it in German, Japanese, and Simplified Chinese. This is the first line of the compiler output for the reproducer under de_DE, ja_JP, and zh_CN, read from the helper's private outputStream field after doTest() returned:
Im Compiler (21.0.9) ist eine Ausnahme aufgetreten. Erstellen Sie auf der Java-Seite zum Melden von Bugs (https://bugreport.java.com) einen Bugbericht, nachdem Sie die Bugdatenbank (https://bugs.java.com) auf Duplikate geprüft haben. Geben Sie in Ihrem Bericht Ihr Programm, die folgende Diagnose und die Parameter an, die Sie dem Java-Compiler übergeben haben. Vielen Dank.
コンパイラで例外が発生しました(21.0.9)。バグ・データベース(https://bugs.java.com)で重複がないかをご確認のうえ、Javaのバグ・レポート・ページ(https://bugreport.java.com)から、Javaコンパイラに対するバグの登録をお願いいたします。レポートには、該当のプログラム、次の診断内容、およびJavaコンパイラに渡されたパラメータをご入力ください。ご協力ありがとうございます。
编译器 (21.0.9) 中出现异常错误。如果在 Bug Database (https://bugs.java.com) 中没有找到有关该错误的 Java 编译器 Bug,请通过 Java Bug 报告页 (https://bugreport.java.com) 提交 Java 编译器 Bug。请在报告中附上您的程序、以下诊断信息以及传递到 Java 编译器的参数。谢谢。
A possible direction
In this case JavacTask.call() returns false and the diagnostic collector holds no diagnostics. I checked that by calling BaseErrorProneJavaCompiler.getTask(...).call() directly under de_DE: call()=false diagnostics=0. A result of ERROR with no error diagnostic would be a locale-independent signal. I have not checked whether a legitimate compilation failure can also produce it.
Setting the task locale does not help: with task.setLocale(Locale.ENGLISH) before call(), the banner under de_DE is still German.
Versions
- Error Prone 2.50.0 (
error_prone_check_api, error_prone_test_helpers). The string check is unchanged on master at f9edd2e.
- JDK: Amazon Corretto 21.0.9+10-LTS
- Gradle 9.8.0, JUnit 4.13.2
- macOS 26.5.1, aarch64
I searched the issues and pull requests for locale, "An exception has occurred in the compiler", CompilationTestHelper, user.language, and "suffered an internal crash" and found no report of this. The nearest is #4112, which added the check.
NullAway found this when it added a locale axis to its CI test matrix (uber/NullAway#1781): four tests in ErrorProneCLIFlagsConfigTest that expect NullAway to fail initialization stopped failing under de_DE.
When a
BugCheckerthrows from its constructor, aCompilationTestHelpertest fails under theen_USlocale withErrorProne suffered an internal crash. Underde_DEthe same test passes: the checker never runs, anddoTest()returns normally.ja_JPandzh_CNbehave likede_DE.Reproducer
src/test/java/repro/ThrowingCheckerTest.java:build.gradle(plus asettings.gradlewithrootProject.name = 'ep-locale'):plugins { id 'java' } repositories { mavenCentral() } dependencies { testImplementation 'com.google.errorprone:error_prone_check_api:2.50.0' testImplementation 'com.google.errorprone:error_prone_test_helpers:2.50.0' testImplementation 'junit:junit:4.13.2' } test { jvmArgs(['api', 'code', 'comp', 'file', 'main', 'model', 'parser', 'processing', 'tree', 'util'] .collect { "--add-exports=jdk.compiler/com.sun.tools.javac.$it=ALL-UNNAMED" }) jvmArgs "-Duser.language=${findProperty('lang')}", "-Duser.country=${findProperty('country')}" testLogging { events 'passed', 'failed'; showStandardStreams = true } outputs.upToDateWhen { false } }Under
en_USthe test fails, as expected:Under
de_DEit passes:ja_JPandzh_CNpass the same way.fr_FRfails likeen_US.Which tests are affected
Under
de_DE, the crash goes unnoticed only where nothing else checks the diagnostics or the compilation result:// BUG: Diagnostic ...markers, and noexpectNoDiagnostics()orexpectResult(...), passes. This is the reproducer above.doTest()to throw because the checker cannot initialize, written asassertThrows(AssertionError.class, helper::doTest)with JUnit 4.13.2, fails withexpected java.lang.AssertionError to be thrown, but nothing was thrown.expectNoDiagnostics()still fails, because the result check catches it:Expected compilation result to be OK, but was ERROR. No diagnostics were emitted.// BUG: Diagnostic contains: xmarker still fails, because the expected diagnostic is missing:Did not see an error on line 3 matching x. There were no errors.Expected behavior
doTest()throwsAssertionErrorwithErrorProne suffered an internal crash: ...whatever the JVM default locale is, as it does underen_US. Commit 5abc8e5 (from #4112) added that check for this case, a checker that fails in its constructor.Cause (my reading of the code; the report above stands without it)
A checker constructor that throws is not reported as an
error.prone.crashdiagnostic: the checker is instantiated fromErrorProneAnalyzer.finished(see the stack trace above), and the exception propagates into javac, which prints itsmsg.bugbanner and returns a failure fromJavacTask.call().doTest()recognizes that banner by its English text (CompilationTestHelper.java#L342-L344on master):javac localizes
msg.bug, and JDK 21.0.9 prints it in German, Japanese, and Simplified Chinese. This is the first line of the compiler output for the reproducer underde_DE,ja_JP, andzh_CN, read from the helper's privateoutputStreamfield afterdoTest()returned:A possible direction
In this case
JavacTask.call()returnsfalseand the diagnostic collector holds no diagnostics. I checked that by callingBaseErrorProneJavaCompiler.getTask(...).call()directly underde_DE:call()=false diagnostics=0. A result ofERRORwith no error diagnostic would be a locale-independent signal. I have not checked whether a legitimate compilation failure can also produce it.Setting the task locale does not help: with
task.setLocale(Locale.ENGLISH)beforecall(), the banner underde_DEis still German.Versions
error_prone_check_api,error_prone_test_helpers). The string check is unchanged on master at f9edd2e.I searched the issues and pull requests for
locale,"An exception has occurred in the compiler",CompilationTestHelper,user.language, and"suffered an internal crash"and found no report of this. The nearest is #4112, which added the check.NullAway found this when it added a locale axis to its CI test matrix (uber/NullAway#1781): four tests in
ErrorProneCLIFlagsConfigTestthat expect NullAway to fail initialization stopped failing underde_DE.