diff --git a/gradle-suppressible-error-prone/src/test/groovy/com/palantir/gradle/suppressibleerrorprone/SuppressibleErrorPronePluginIntegrationTest.groovy b/gradle-suppressible-error-prone/src/test/groovy/com/palantir/gradle/suppressibleerrorprone/SuppressibleErrorPronePluginIntegrationTest.groovy deleted file mode 100644 index db22798e..00000000 --- a/gradle-suppressible-error-prone/src/test/groovy/com/palantir/gradle/suppressibleerrorprone/SuppressibleErrorPronePluginIntegrationTest.groovy +++ /dev/null @@ -1,2083 +0,0 @@ -/* - * (c) Copyright 2024 Palantir Technologies Inc. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.palantir.gradle.suppressibleerrorprone - - -import com.palantir.gradle.plugintesting.ConfigurationCacheSpec -import com.palantir.javaformat.java.JavaFormatterOptions -import org.apache.commons.io.FileUtils -import org.gradle.testkit.runner.BuildResult -import spock.lang.Unroll -import com.palantir.javaformat.java.Formatter - - -class SuppressibleErrorPronePluginIntegrationTest extends ConfigurationCacheSpec { - static Formatter formatter = Formatter.createFormatter(JavaFormatterOptions.builder().style(JavaFormatterOptions.Style.PALANTIR).build()) - - // This makes debugging the errorprone check code running inside the compiler (including the bytecode - // edited modifications we have made) "just work" from inside these tests. - // Change the variable below to true to enable it, after setting up the standalone debugger: - // 1. Make a new run configuration in IntelliJ of type JVM Debug - // 2. Change it to "Listen" rather than "Attach" - // 3. Select Auto-restart. - // 4. Run the debugger - // 5. Run the tests as well - // If the variable below is true the tests will fail as the compilation process will try to - // attach to a non-existent debugger. Set it to false before you push any code. - boolean debuggingErrorPrones = false - - def setup() { - // language=Gradle - buildFile << ''' - buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'com.palantir.gradle.consistentversions:gradle-consistent-versions:3.1.0' - } - } - // Consistent versions checks we don't resolve configurations at configuration time and - // also interacts in many ways with dependencies - apply plugin: 'com.palantir.consistent-versions' - - apply plugin: 'com.palantir.suppressible-error-prone' - apply plugin: 'java' - - repositories { - mavenCentral() - // Needed so that suppressible-error-prone and suppressible-error-prone-annotations can be added - // as jars to the various configurations. We make sure to publish these to maven local before the - // test task runs. - mavenLocal() - } - - sourceSets { - other - } - - dependencies { - errorprone 'com.google.errorprone:error_prone_core:2.31.0' - // Mimick the way SuppressibleErrorPronePlugin adds the dependency on suppressible-error-prone - // This should guarantee that we're using the same version, both of which should be in maven local - // and be the current version - errorprone 'com.palantir.suppressible-error-prone:test-error-prone-checks:' + project.findProperty("suppressibleErrorProneVersion") - } - - suppressibleErrorProne { - configureEachErrorProneOptions { - // These interfere with some tests, so disable them - // TODO(callumr): Rewrite the tests to use custom testing error-prones rather than built in checks - // to make upgrading error-prone easier. - disable('Varifier', 'ReturnValueIgnored', 'UnusedVariable', 'IdentifierName', 'UnusedMethod') - ignoreUnknownCheckNames = true - } - } - '''.stripIndent(true) - - if (debuggingErrorPrones) { - // language=Gradle - buildFile << ''' - tasks.withType(JavaCompile).configureEach { - it.options.forkOptions.jvmArgumentProviders.add(new CommandLineArgumentProvider() { - @Override - public Iterable asArguments() { - return List.of("-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:5005") - } - }) - } - '''.stripIndent(true) - } - - file('gradle.properties') << ''' - __TESTING=true - __TESTING_CACHE_BUST_ERRORPRONE_TRANSFORM=true - '''.stripIndent(true) - - file('versions.lock') - } - - def 'reports a failing error prone'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - def stderr = runTasksWithFailure('compileAllErrorProne').output - - then: - stderr.contains('[ArrayToString]') - } - - def 'can suppress an error prone with for-rollout prefix'() { - // This test is explicitly checking we suppress the for-rollout prefix as that is what exists - // in people's codebases - - when: - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - @SuppressWarnings("for-rollout:ArrayToString") - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - then: - runTasksSuccessfully('compileAllErrorProne') - } - - def 'ensure error prone checks are disabled in generated code'() { - // language=Java - def erroringCode = ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - - when: - def sourceDir1 = new File(projectDir, 'src/generated') - def sourceDir2 = new File(projectDir, 'build/generated') - - writeJavaSourceFile(erroringCode, sourceDir1) - writeJavaSourceFile(erroringCode.replace('App', 'App2'), sourceDir2) - - buildFile << """ - sourceSets.main.java.srcDirs('${projectDir.relativePath(sourceDir1)}', '${projectDir.relativePath(sourceDir2)}') - """.stripIndent(true) - - then: - runTasksSuccessfully('compileAllErrorProne') - } - - def 'can apply patches for a check if added to the patchChecks list'() { - // language=Gradle - buildFile << ''' - suppressibleErrorProne { - patchChecks.add('ArrayToString') - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') - - then: - runTasksSuccessfully('compileAllErrorProne') - - javaSourceContains('Arrays.toString(new int[3])') - } - - def 'does not apply patches for a check if not added to the patchChecks list'() { - // language=Gradle - buildFile << ''' - suppressibleErrorProne { - // To make sure set is not empty - patchChecks = ['SomeCheck'] - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') - - then: - javaSourceContains('new int[3].toString()') - } - - def 'does not apply patches if there is nothing in patchChecks set'() { - // language=Gradle - buildFile << ''' - suppressibleErrorProne { - patchChecks.empty() - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - // Doesn't actually do any patching as the set is empty. It just does a normal compile that fails. - def stderr = runTasksWithFailure('compileAllErrorProne', '-PerrorProneApply').output - - then: - stderr.contains('[ArrayToString]') - javaSourceContains('new int[3].toString()') - } - - def 'does not apply patches for check that was explicitly disabled'() { - // language=Gradle - buildFile << ''' - suppressibleErrorProne { - patchChecks.add('ArrayToString') - } - - tasks.withType(JavaCompile).configureEach { - options.errorprone.disable 'ArrayToString' - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') - - then: - javaSourceContains('new int[3].toString()') - } - - def 'can patch specific checks using -PerrorProneApply'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - new int[2].equals(new int[1]); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply=ArrayToString,ArrayEquals') - - then: - javaSourceContains('Arrays.toString(new int[3])') - javaSourceContains('Arrays.equals(new int[2], new int[1])') - } - - def 'can suppress a failing check (even if not in patchChecks set)'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: - javaSourceContains('@SuppressWarnings(\"for-rollout:ArrayToString\")') - - - runTasksSuccessfully('compileAllErrorProne') - } - - def 'does not apply SuppressWarnings to implicit lambda parameters'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - import java.util.stream.Stream; - - public class App { - void test() { - Stream.of(new Object()).forEach(o -> o.toString()); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: - // Suppression should be on the method, not the lambda parameter - // language=Java - javaSourceIsSyntacticallyEqualTo """ - package app; - - import java.util.stream.Stream; - - public class App { - @SuppressWarnings("for-rollout:TestCheckNoSingleLetterVariable") - void test() { - Stream.of(new Object()).forEach(o -> o.toString()); - } - } - """.stripIndent(true) - - // Verify the code still compiles after the suppression has been applied, as previous versions - // were adding the annotation to the lambda implicit parameter which is not valid java - runTasksSuccessfully('compileAllErrorProne') - } - - def 'does not apply SuppressWarnings to explicit lambda parameters'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - import java.util.stream.Stream; - - public class App { - void test() { - Stream.of(new Object()).forEach((Object o) -> o.toString()); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: - // Suppression should be on the method, not the lambda parameter - // language=Java - javaSourceIsSyntacticallyEqualTo(""" - package app; - - import java.util.stream.Stream; - - public class App { - @SuppressWarnings("for-rollout:TestCheckNoSingleLetterVariable") - void test() { - Stream.of(new Object()).forEach((Object o) -> o.toString()); - } - } - """.stripIndent(true)) - - // Verify the code still compiles after the suppression has been applied, as previous versions - // were adding the annotation to the lambda implicit parameter which is not valid java - runTasksSuccessfully('compileAllErrorProne') - } - - def 'does not apply SuppressWarnings to anonymous classes'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - import java.util.stream.Stream; - - public class App { - void test() { - new Object() { - { - Stream.of(new Object()).forEach(o -> o.toString()); - } - }; - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: - // Suppression should be on the method, not the anonymous class - // language=Java - javaSourceIsSyntacticallyEqualTo(""" - package app; - - import java.util.stream.Stream; - - public class App { - @SuppressWarnings("for-rollout:TestCheckNoSingleLetterVariable") - void test() { - new Object() { - { - Stream.of(new Object()).forEach(o -> o.toString()); - } - }; - } - } - """.stripIndent(true)) - - // Verify the code still compiles after the suppression has been applied, as previous versions - // were adding the annotation to the anonymous class which is not valid java - runTasksSuccessfully('compileAllErrorProne') - } - - def 'demonstrate suppressions on different source elements'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public final String field = new int[3].toString(); - - public App() { - new int[3].toString(); - } - - public void method() { - new int[3].toString(); - } - - public void variables() { - String variable = new int[3].toString(); - } - - public static class SomeClass { - static { - new int[3].toString(); - } - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - @SuppressWarnings("for-rollout:ArrayToString") - public final String field = new int[3].toString(); - - @SuppressWarnings("for-rollout:ArrayToString") - public App() { - new int[3].toString(); - } - - @SuppressWarnings("for-rollout:ArrayToString") - public void method() { - new int[3].toString(); - } - - public void variables() { - @SuppressWarnings("for-rollout:ArrayToString") - String variable = new int[3].toString(); - } - - @SuppressWarnings("for-rollout:ArrayToString") - public static class SomeClass { - static { - new int[3].toString(); - } - } - } - '''.stripIndent(true) - - runTasksSuccessfully('compileAllErrorProne') - } - - def 'supports errorprone checks that match on a larger element than they report errors on'() { - // The UnusedVariable check implements CompilationUnitTreeMatcher, so will start with a whole - // CompilationUnitTree and then narrows down to the specific variable declaration that is unused. - // This trips up the "naive" suppression logic, which looks at where the visitor has got to rather - // than where the diagnostic description was produced. - - // language=Gradle - buildFile << ''' - suppressibleErrorProne { - configureEachErrorProneOptions { - enable('UnusedVariable') - } - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public void variables() { - String variable; - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - public void variables() { - @SuppressWarnings("for-rollout:UnusedVariable") - String variable; - } - } - '''.stripIndent(true) - - runTasksSuccessfully('compileAllErrorProne') - } - - def 'supports suppressing errorprone checks on classes, interfaces, records, enums, etc'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - static class exports {} - interface opens {} - record provides(int cat) {} - enum to {;} - @interface module {} - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - @SuppressWarnings("for-rollout:NamedLikeContextualKeyword") - static class exports {} - - @SuppressWarnings("for-rollout:NamedLikeContextualKeyword") - interface opens {} - - @SuppressWarnings("for-rollout:NamedLikeContextualKeyword") - record provides(int cat) {} - - @SuppressWarnings("for-rollout:NamedLikeContextualKeyword") - enum to { - ; - } - - @SuppressWarnings("for-rollout:NamedLikeContextualKeyword") - @interface module {} - } - '''.stripIndent(true) - - - runTasksSuccessfully('compileAllErrorProne') - } - - def 'does not place suppress warnings annotation in the middle of a Type.Builder variables reference'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - @SuppressWarnings("UnusedVariable") - public static void main(String[] args) { - App.Builder builder = new App.Builder(new int[3].toString()); - } - - static class Builder { - Builder(Object object) {} - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - @SuppressWarnings("UnusedVariable") - public static void main(String[] args) { - @SuppressWarnings("for-rollout:ArrayToString") - App.Builder builder = new App.Builder(new int[3].toString()); - } - - static class Builder { - Builder(Object object) {} - } - } - '''.stripIndent(true) - - - runTasksSuccessfully('compileAllErrorProne') - } - - def 'can run apply and suppress at the same time - it uses the suggested fix if a patch check, suppresses otherwise'() { - // language=Gradle - buildFile << ''' - suppressibleErrorProne { - patchChecks.add('ArrayToString') - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - new int[3].equals(new int[3]); - } - - // Does not remove existing suppressions - @SuppressWarnings("checkstyle:LineLength") - public static void helper() { - new int[3].equals(new int[3]); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply', '-PerrorProneSuppress') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - import java.util.Arrays; - - public final class App { - @SuppressWarnings("for-rollout:ArrayEquals") - public static void main(String[] args) { - Arrays.toString(new int[3]); - new int[3].equals(new int[3]); - } - - // Does not remove existing suppressions - @SuppressWarnings({"checkstyle:LineLength", "for-rollout:ArrayEquals"}) - public static void helper() { - new int[3].equals(new int[3]); - } - } - '''.stripIndent(true) - - runTasksSuccessfully('compileAllErrorProne') - } - - def 'can run apply and suppress at the same time with IfModuleIsUsed without exploding'() { - // language=Gradle - buildFile << ''' - import com.palantir.gradle.suppressibleerrorprone.ConditionalPatchCheck - import com.palantir.gradle.suppressibleerrorprone.IfModuleIsUsed - - suppressibleErrorProne { - conditionalPatchChecks.add(new ConditionalPatchCheck( - new IfModuleIsUsed('com.fasterxml.jackson.core', 'jackson-core'), 'ArrayToString')) - } - - dependencies { - implementation 'com.fasterxml.jackson.core:jackson-core:2.17.1' - otherImplementation 'com.fasterxml.jackson.core:jackson-core:2.17.1' - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - new int[3].equals(new int[3]); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply', '-PerrorProneSuppress') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - import java.util.Arrays; - - public final class App { - @SuppressWarnings("for-rollout:ArrayEquals") - public static void main(String[] args) { - Arrays.toString(new int[3]); - new int[3].equals(new int[3]); - } - } - '''.stripIndent(true) - - runTasksSuccessfully('compileAllErrorProne') - } - - def 'can disable errorprone using property'() { - when: 'there is java code some that will fail an errorprone during compilation' - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - then: 'compilation succeeds when errorprone is disabled' - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneDisable') - runTasksSuccessfully('compileAllErrorProne', '-Pcom.palantir.baseline-error-prone.disable') - runTasksSuccessfully('compileAllErrorProne', '-Pcom.palantir.baseline-error-prone.disable=true') - - then: 'compilation fails the legacy baseline errorprone disable property is set to false' - runTasksWithFailure('compileAllErrorProne', '-Pcom.palantir.baseline-error-prone.disable=false') - } - - def 'should be able to refactor near usages of deprecated methods'() { - // If a deprecated method usage appears in a compilation unit that is being refactored, the compiler will - // raise a warning about the deprecated method usage. If -Werror is also enabled, compilation will fail - // rather than succeed, even when patching checks. The code should make sure to disable the -Werror - // behaviour so patching always succeeds. - - // language=Gradle - buildFile << ''' - tasks.withType(JavaCompile) { - options.compilerArgs += ['-Werror', '-Xlint:deprecation'] - doFirst { - println "COMPILER ARGS: ${options.compilerArgs}" - } - } - - suppressibleErrorProne { - patchChecks.add('ArrayToString') - } - - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - Character.isJavaLetter('c'); // deprecated method - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') - - then: - javaSourceContains('Arrays.toString(new int[3])') - } - - def 'can conditionally add patch checks'() { - // language=Gradle - buildFile << ''' - import com.palantir.gradle.suppressibleerrorprone.ConditionalPatchCheck - - suppressibleErrorProne { - patchChecks.add('Something') - conditionalPatchChecks.add(new ConditionalPatchCheck({ true }, 'ArrayToString')) - conditionalPatchChecks.add(new ConditionalPatchCheck({ false }, Set.of('ArrayEquals'))) - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - new int[2].equals(new int[1]); - } - } - '''.stripIndent(true) - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') - - - then: - javaSourceContains('Arrays.toString(new int[3])') - javaSourceContains('new int[2].equals(new int[1])') - } - - def 'IfModuleIsUsed works properly'() { - // language=Gradle - buildFile << ''' - import com.palantir.gradle.suppressibleerrorprone.ConditionalPatchCheck - import com.palantir.gradle.suppressibleerrorprone.IfModuleIsUsed - - suppressibleErrorProne { - conditionalPatchChecks.add(new ConditionalPatchCheck(new IfModuleIsUsed('com.fasterxml.jackson.core', 'jackson-core'), 'ArrayToString')) - conditionalPatchChecks.add(new ConditionalPatchCheck(new IfModuleIsUsed('doesnt', 'exist'), 'ArrayEquals')) - } - - dependencies { - // Depends on jackson-core - implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' - otherImplementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - new int[2].equals(new int[1]); - } - } - '''.stripIndent(true) - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') - - then: - javaSourceContains('Arrays.toString(new int[3])') - javaSourceContains('new int[2].equals(new int[1])') - } - - def 'compileAllErrorProne only depends on compile tasks with errorprone enabled'() { - // language=Gradle - buildFile << ''' - tasks.named('compileTestJava').configure { - options.errorprone.enabled = false - } - '''.stripIndent(true) - - when: - def stdout = runTasksSuccessfully('compileAllErrorProne', '--dry-run').output - - then: - stdout.contains(':compileJava SKIPPED') - !stdout.contains(':compileTestJava SKIPPED') - stdout.contains(':compileOtherJava SKIPPED') - } - - def 'SUGGESTION level checks are not suppressed'() { - def originalBuildFile = buildFile.text - - // The code below should hit the FieldCanBeFinal suggestion level check - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - private int field; - public App() { - this.field = 3; - } - public int getField() { - return field; - } - } - '''.stripIndent(true) - - when: 'a suggestion check is made error level' - // language=Gradle - buildFile << ''' - tasks.withType(JavaCompile).configureEach { - options.errorprone.error('FieldCanBeFinal') - } - '''.stripIndent(true) - - then: 'it causes the test code to fail compilation, confirming the check is being run on the code' - def stderr = runTasksWithFailure('compileAllErrorProne').output - stderr.contains('[FieldCanBeFinal]') - - when: 'the check is run at the default SUGGESTION level, and then automated suppressions are not applied' - buildFile.text = originalBuildFile - // language=Gradle - buildFile << ''' - tasks.withType(JavaCompile).configureEach { - // This is disabled by default in error-prone, so enable it - // https://github.com/google/error-prone/blob/04f05c24882152d3c84f4caf9345efd15859b928/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java#L1191 - options.errorprone.enable('FieldCanBeFinal') - } - '''.stripIndent(true) - - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: 'it is not suppressed' - javaSourceDoesNotContain("SuppressWarnings") - } - - def 'WARNING level checks are suppressed'() { - when: 'the check is at warning level' - // language=Gradle - buildFile << ''' - tasks.withType(JavaCompile).configureEach { - options.errorprone.warn('ArrayToString') - } - '''.stripIndent(true) - - // The code below should hit the LongDoubleConversion warning level check - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String... args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - then: 'compilation does not fail' - runTasksSuccessfully('compileAllErrorProne') - - when: 'the check is run at the default WARNING level, and then automated suppressions are applied' - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: 'it is suppressed' - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - @SuppressWarnings("for-rollout:ArrayToString") - public static void main(String... args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - } - - def 'makes no changes when there is an error on an import'() { - when: 'theres an illegal import' - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - public class A { - public static class Inner {} - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - public class B extends A {} - '''.stripIndent(true) - - // This below hits the NonCanonicalStaticImport as it should refer to app.A.Inner, not app.B.Inner - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - import static app.B.Inner; - public final class App {} - '''.stripIndent(true) - - then: 'compilation fails' - def stderr = runTasksWithFailure('compileAllErrorProne').output - stderr.contains('[NonCanonicalStaticImport]') - - when: 'we try to suppress it' - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: 'nothing has changed as we cant put SuppressWarnings on an import' - def stderr2 = runTasksWithFailure('compileAllErrorProne').output - stderr2.contains('[NonCanonicalStaticImport]') - - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - import static app.B.Inner; - - public final class App {} - '''.stripIndent(true) - } - - def 'timings are outputted'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String... args) {} - } - '''.stripIndent(true) - - when: 'a compilation happens but -PerrorProneTimings is not applied' - runTasksSuccessfully('compileAllErrorProne') - - then: 'timings are not outputted' - !new File(projectDir, 'build/errorprone-timings/compileJava').exists() - !new File(projectDir, 'build/errorprone-timings/compileOtherJava').exists() - - when: 'a compilation happens and -PerrorProneTimings is applied' - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneTimings') - - then: 'timings are outputted' - new File(projectDir, 'build/errorprone-timings/compileJava').exists() - new File(projectDir, 'build/errorprone-timings/compileOtherJava').exists() - } - - @Unroll - def 'compile tasks are never up-to-date when applying changes under #mode'() { - // language=Gradle - buildFile << ''' - suppressibleErrorProne { - patchChecks.add('ArrayToString') - } - '''.stripIndent(true) - - // language=Java - def originalSource = ''' - package app; - - public final class App { - public static void main(String... args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - writeJavaSourceFileToSourceSets originalSource - - when: 'a compilation with code changes happens' - runTasksSuccessfully('compileAllErrorProne', *mode) - - then: 'the source code is reset back to the original state' - writeJavaSourceFileToSourceSets originalSource - - when: 'compilation with changes runs again' - runTasksSuccessfully('compileAllErrorProne', *mode) - - then: 'changes are actually made, it was not up-to-date' - javaSourceIsSyntacticallyNotEqualTo originalSource - - where: - mode << [ - ['-PerrorProneApply'], - ['-PerrorProneSuppress'], - ['-PerrorProneApply', '-PerrorProneSuppress'] - ] - } - - def 'throws exception when -PerrorProneDisable is combined with -PerrorProneApply or -PerrorProneSuppress'() { - when: - def applyOutput = runTasksWithFailure('compileAllErrorProne', '-PerrorProneDisable', '-PerrorProneApply').output - - then: - applyOutput.contains '-PerrorProneDisable cannot be used' - - when: - def suppressOutput = runTasksWithFailure('compileAllErrorProne', '-PerrorProneDisable', '-PerrorProneSuppress').output - - then: - suppressOutput.contains '-PerrorProneDisable cannot be used' - } - - // This test also verifies we're properly passing the arguments to the errorprone plugin - def 'supports removing specific error prone suppressions'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - @SuppressWarnings("for-rollout:Test") - public final class App {} - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=Test') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App {} - '''.stripIndent(true) - } - - // This test also verifies we're properly passing the arguments to the errorprone plugin - def 'supports removing all error prone suppressions'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - // We can remove entire lines - @SuppressWarnings("for-rollout:Test") - public final class App { - // We keep non-rollout suppressions untouched - @SuppressWarnings({"for-rollout:Test", "Test"}) - void nested() {} - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - // We can remove entire lines - public final class App { - // We keep non-rollout suppressions untouched - @SuppressWarnings("Test") - void nested() {} - } - '''.stripIndent(true) - } - - // This test also verifies we're properly passing the arguments to the errorprone plugin - def 'does not remove suppressions other than requested'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - @SuppressWarnings("for-rollout:Test") - public final class App {} - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=Other') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - @SuppressWarnings("for-rollout:Test") - public final class App {} - '''.stripIndent(true) - } - - def 'does not suppress RemoveRolloutSuppressions'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - @SuppressWarnings("for-rollout:Test") - public final class App {} - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - @SuppressWarnings("for-rollout:Test") - public final class App {} - '''.stripIndent(true) - } - - def 'RemoveRolloutSuppressions can remove itself'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - @SuppressWarnings("for-rollout:RemoveRolloutSuppressions") - public final class App {} - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=RemoveRolloutSuppressions') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App {} - '''.stripIndent(true) - } - - def 'can patch checks while using -PerrorProneRemoveRollout, even if suppressed for rollout'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - @SuppressWarnings("for-rollout:ArrayToString") - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ArrayToString', '-PerrorProneApply=ArrayToString') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - import java.util.Arrays; - - public final class App { - public static void main(String[] args) { - Arrays.toString(new int[3]); - } - } - '''.stripIndent(true) - } - - def 'can patch checks while using -PerrorProneRemoveRollout, which also add annotations as fixes'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - @SuppressWarnings("for-rollout:ShouldBeNullable") - private Object fixme() { - return null; - } - - @SuppressWarnings("for-rollout:ShouldBeNullable") - private Object fixme(Object andMySuppressionHasWhiteSpaceAfterIt) { - return null; - } - - @SuppressWarnings({"for-rollout:ShouldBePrivate", "for-rollout:ShouldBeNullable"}) - Integer fixme(Integer i) { - return null; - } - } - '''.stripIndent(true) - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ShouldBeNullable,ShouldBePrivate', '-PerrorProneApply=ShouldBeNullable,ShouldBePrivate') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - import javax.annotation.Nullable; - - public final class App { - @Nullable - private Object fixme() { - return null; - } - - @Nullable - private Object fixme(Object andMySuppressionHasWhiteSpaceAfterIt) { - return null; - } - - @Nullable - private Integer fixme(Integer i) { - return null; - } - } - '''.stripIndent(true) - } - - def '-PerrorProneSuppress then -PerrorProneRemoveRollout does not add newlines'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - private Object fixme() { - return null; - } - } - '''.stripIndent(true) - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress=ShouldBeNullable') - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ShouldBeNullable') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - private Object fixme() { - return null; - } - } - '''.stripIndent(true) - } - - - def 'does not patch checks while using -PerrorProneRemoveRollout, if suppressed normally'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - @SuppressWarnings("ArrayToString") - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ArrayToString', '-PerrorProneApply=ArrayToString') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - @SuppressWarnings("ArrayToString") - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - } - - def 'can patch checks while using -PerrorProneRemoveRollout, if not suppressed'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ArrayToString', '-PerrorProneApply=ArrayToString') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - import java.util.Arrays; - - public final class App { - public static void main(String[] args) { - Arrays.toString(new int[3]); - } - } - '''.stripIndent(true) - } - - def 'errorProneRemoveRollout does not patch unrelated checks'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=NullAway', '-PerrorProneApply=NullAway') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - } - - def 'errorProneRemoveRollout does not patch by itself'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - @SuppressWarnings("for-rollout:ArrayToString") - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ArrayToString') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - } - - def 'errorProneRemoveRollout does not patch if specific check is not selected'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - @SuppressWarnings("for-rollout:Test") - public final class App { - @SuppressWarnings("for-rollout:ArrayToString") - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ArrayToString,Test', '-PerrorProneApply=Test') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - } - - def 'can patch specific checks even if errorProneRemoveRollout argument is empty'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - @SuppressWarnings("for-rollout:ArrayToString") - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout', '-PerrorProneApply=ArrayToString') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - import java.util.Arrays; - - public final class App { - public static void main(String[] args) { - Arrays.toString(new int[3]); - } - } - '''.stripIndent(true) - } - - def 'can patch configured checks even if errorProneRemoveRollout argument is empty'() { - // language=Gradle - buildFile << ''' - suppressibleErrorProne { - patchChecks.add('ArrayToString') - } - '''.stripIndent(true) - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - @SuppressWarnings("for-rollout:ArrayToString") - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout', '-PerrorProneApply') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - import java.util.Arrays; - - public final class App { - public static void main(String[] args) { - Arrays.toString(new int[3]); - } - } - '''.stripIndent(true) - } - - def 'RemoveRolloutSuppressions does not appear as a Note: [RemoveRolloutSuppressions] in unrelated errors'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - public final class App { - @SuppressWarnings("for-rollout:NullAway") - public static void method() { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - def output = runTasksWithFailure('compileAllErrorProne').output - - then: - !output.contains('[RemoveRolloutSuppressions]') - } - - def 'errorProneRemoveUnused removes only unused error-prone suppressions, and leaves unknown suppressions untouched'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - @SuppressWarnings({"ArrayToString", "UnnecessaryFinal", "InlineTrivialConstant", "NotAnErrorProne", "checkstyle:Bla",}) - public final class App { - private static final String EMPTY_STRING = ""; - - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused=ArrayToString') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - @SuppressWarnings({"ArrayToString", "InlineTrivialConstant", "NotAnErrorProne", "checkstyle:Bla"}) - public final class App { - private static final String EMPTY_STRING = ""; - - public static void main(String[] args) { - new int[3].toString(); - } - } - '''.stripIndent(true) - } - - def 'errorProneRemoveUnused does not apply fixes'() { - given: - // language=Java - def initialSource = ''' - package app; - - public final class App { - class Inner { - class InnerInner {} - } - } - '''.stripIndent(true) - - writeJavaSourceFileToSourceSets initialSource - - when: 'errorProneRemoveUnused is run by itself' - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused') - - then: 'No checks are applied' - javaSourceIsSyntacticallyEqualTo initialSource - - when: 'ClassCanBeStatic is applied alongside errorProneRemoveUnused' - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused', '-PerrorProneApply=ClassCanBeStatic') - - then: 'ClassCanBeStatic is applied' - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - static class Inner { - static class InnerInner {} - } - } - '''.stripIndent(true) - } - - def 'errorProneRemoveUnused only keeps the closest suppression to a violation'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - @SuppressWarnings("InlineTrivialConstant") - public final class App { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY_STRING = ""; - - @SuppressWarnings("InlineTrivialConstant") - class Inner { - @SuppressWarnings("InlineTrivialConstant") - class InnerInner { - @SuppressWarnings("InlineTrivialConstant") - class InnerInnerInner { - private static final String EMPTY = ""; - } - } - } - } - '''.stripIndent(true) - - when: - def result = runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused') - - then: - - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY_STRING = ""; - - class Inner { - class InnerInner { - @SuppressWarnings("InlineTrivialConstant") - class InnerInnerInner { - private static final String EMPTY = ""; - } - } - } - } - '''.stripIndent(true) - } - - def 'errorProneRemoveUnused handles multiple suppressions on different tree types gracefully'() { - // Here we test the three types of trees you can suppress — ClassTree, MethodTree, VariableTree - - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) - public final class App { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY_STRING = ""; - - // Doesn't move an already existing suppression, even if it could be closer to the violation - @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) - static class Inner { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY = ""; - boolean truism = new int[3].equals(new int[3]); - - @SuppressWarnings("InlineTrivialConstant") - static class InnerInner { - @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) - void method() { - new int[3].equals(new int[3]); - } - } - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused') - - then: - - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY_STRING = ""; - - // Doesn't move an already existing suppression, even if it could be closer to the violation - @SuppressWarnings("ArrayEquals") - static class Inner { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY = ""; - - boolean truism = new int[3].equals(new int[3]); - - static class InnerInner { - @SuppressWarnings("ArrayEquals") - void method() { - new int[3].equals(new int[3]); - } - } - } - } - '''.stripIndent(true) - } - - def 'errorProneRemoveUnused removes entire SuppressWarnings annotation when all suppressions are unused'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - @SuppressWarnings({"UnusedVariable", "ArrayToString"}) - public final class App { - public static void main(String[] args) { - System.out.println("No violations here"); - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused') - - then: - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - public static void main(String[] args) { - System.out.println("No violations here"); - } - } - '''.stripIndent(true) - } - - def 'errorProneRemoveUnused and errorProneSuppress uses existing suppressions if possible'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - @SuppressWarnings("InlineTrivialConstant") - public final class App { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY_STRING = ""; - - @SuppressWarnings("InlineTrivialConstant") - static class Inner { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY = ""; - boolean truism = new int[3].equals(new int[3]); - - @SuppressWarnings("InlineTrivialConstant") - static class InnerInner { - @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) - void method() { - new int[3].equals(new int[3]); - } - } - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused', '-PerrorProneSuppress') - - then: - - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - public final class App { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY_STRING = ""; - - static class Inner { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY = ""; - - @SuppressWarnings("for-rollout:ArrayEquals") - boolean truism = new int[3].equals(new int[3]); - - static class InnerInner { - @SuppressWarnings("ArrayEquals") - void method() { - new int[3].equals(new int[3]); - } - } - } - } - '''.stripIndent(true) - } - - def 'errorProneRemoveUnused and errorProneApply applies fixes on previously suppressed elements'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) - public final class App { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY_STRING = ""; - - @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) - static class Inner { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY = ""; - boolean truism = new int[3].equals(new int[3]); - - @SuppressWarnings("InlineTrivialConstant") - static class InnerInner { - @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) - void method() { - new int[3].equals(new int[3]); - } - } - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused', '-PerrorProneApply=ArrayEquals') - - then: - - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - import java.util.Arrays; - - public final class App { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY_STRING = ""; - - static class Inner { - @SuppressWarnings("InlineTrivialConstant") - private static final String EMPTY = ""; - - boolean truism = Arrays.equals(new int[3], new int[3]); - - static class InnerInner { - void method() { - Arrays.equals(new int[3], new int[3]); - } - } - } - } - '''.stripIndent(true) - } - - def 'errorProneRemoveUnused + errorProneApply + errorProneSuppress applies fixes and suppressions on previously suppressed elements'() { - // language=Java - writeJavaSourceFileToSourceSets ''' - package app; - - @SuppressWarnings("ArrayEquals") - public final class App { - private static final String EMPTY_STRING = ""; - - // Although InlineTrivialConstant can be placed lower in the AST hierarchy, - // we preserve existing suppressions whenever possible rather than move suppressions around. - // Also, note that we don't add for-rollout here. - @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) - static class Inner { - private static final String EMPTY = ""; - boolean truism = new int[3].equals(new int[3]); - - @SuppressWarnings("InlineTrivialConstant") - static class InnerInner { - @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) - void method() { - new int[3].equals(new int[3]); - } - } - } - } - '''.stripIndent(true) - - when: - runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused', '-PerrorProneSuppress', '-PerrorProneApply=ArrayEquals') - - then: - - // language=Java - javaSourceIsSyntacticallyEqualTo ''' - package app; - - import java.util.Arrays; - - public final class App { - @SuppressWarnings("for-rollout:InlineTrivialConstant") - private static final String EMPTY_STRING = ""; - - // Although InlineTrivialConstant can be placed lower in the AST hierarchy, - // we preserve existing suppressions whenever possible rather than move suppressions around. - // Also, note that we don't add for-rollout here. - @SuppressWarnings("InlineTrivialConstant") - static class Inner { - private static final String EMPTY = ""; - boolean truism = Arrays.equals(new int[3], new int[3]); - - static class InnerInner { - void method() { - Arrays.equals(new int[3], new int[3]); - } - } - } - } - '''.stripIndent(true) - } - - def 'error-prone dependencies have versions bound together by a virtual platform'() { - setup: 'when an error-prone dependency is forced to certain version' - // language=Gradle - buildFile << ''' - configurations.named('annotationProcessor') { - resolutionStrategy { - force 'com.google.errorprone:error_prone_annotation:2.3.4' - } - } - - tasks.register('printErrorProneVersions') { - inputs.files(configurations.named('annotationProcessor')) - doLast { - inputs.files.files.each { - println("ERROR-PRONE: ${it.name}") - } - } - } - '''.stripIndent(true) - - when: - def output = runTasksSuccessfully('printErrorProneVersions').output - - then: 'every single error-prone dependency has the same version' - output.contains('ERROR-PRONE: error_prone_annotation-2.3.4.jar') - output.contains('ERROR-PRONE: error_prone_core-2.3.4.jar') - } - - // Running CC with debuggingErrorPrones (see setup method above) causes this issue: - // ERROR: transport error 202: connect failed: Connection refused - // ERROR: JDWP Transport dt_socket failed to initialize, TRANSPORT_INIT(510) - // JDWP exit error AGENT_ERROR_TRANSPORT_INIT(197): No transports initialized [src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c:700] - BuildResult runTasksSuccessfully(String... tasks) { - def projectVersion = Optional.ofNullable(System.getProperty('projectVersion')).orElseThrow() - String[] strings = tasks + ["-PsuppressibleErrorProneVersion=${projectVersion}".toString()] - if (debuggingErrorPrones) { - return super.runTasks(strings) - } else { - return super.runTasksWithConfigurationCache(strings) - } - } - - BuildResult runTasksWithFailure(String... tasks) { - def projectVersion = Optional.ofNullable(System.getProperty('projectVersion')).orElseThrow() - String[] strings = tasks + ["-PsuppressibleErrorProneVersion=${projectVersion}".toString()] - if (debuggingErrorPrones) { - return super.runTasksAndFail(strings) - } else { - return super.runTasksAndFailWithConfigurationCache(strings) - } - } - - void writeJavaSourceFileToSourceSets(String source) { - super.writeJavaSourceFile(source, 'src/main/java') - super.writeJavaSourceFile(source, 'src/other/java') - } - - void javaSourceContains(String substring) { - assert file('src/main/java/app/App.java').text.contains(substring) - assert file('src/other/java/app/App.java').text.contains(substring) - } - - void javaSourceDoesNotContain(String substring) { - assert !file('src/main/java/app/App.java').text.contains(substring) - assert !file('src/other/java/app/App.java').text.contains(substring) - } - - // Normalizes Java source by trimming whitespace and applying consistent formatting. - // Preserves newlines since the formatter allows them within methods, and we need - // to test that error-prone doesn't introduce unwanted line breaks. - private static String normalizeSource(String content) { - String stripped = content.readLines() - .collect { it.trim() } // Remove leading/trailing whitespace - .join('\n') - - return formatter.formatSource(stripped) - } - - void javaSourceIsSyntacticallyEqualTo(String source) { - def output = normalizeSource(file('src/main/java/app/App.java').text) - def expected = normalizeSource(source) - - // Ensure test fixtures are properly formatted - assert "\n" + expected == source, "Please update your text fixtures to be in palantir-java-format" - assert output == expected - - def outputOther = normalizeSource(file('src/other/java/app/App.java').text) - def expectedOther = normalizeSource(source) - assert outputOther == expectedOther - } - - void javaSourceIsSyntacticallyNotEqualTo(String source) { - def output = normalizeSource(file('src/main/java/app/App.java').text) - def expected = normalizeSource(source) - - // Ensure test fixtures are properly formatted - assert "\n" + expected == source, "Please update your text fixtures to be in palantir-java-format" - assert output != expected - - def outputOther = normalizeSource(file('src/other/java/app/App.java').text) - def expectedOther = normalizeSource(source) - assert outputOther != expectedOther - } -} diff --git a/gradle-suppressible-error-prone/src/test/java/com/palantir/gradle/suppressibleerrorprone/SuppressibleErrorPronePluginIntegrationTest.java b/gradle-suppressible-error-prone/src/test/java/com/palantir/gradle/suppressibleerrorprone/SuppressibleErrorPronePluginIntegrationTest.java new file mode 100644 index 00000000..3c0cc10e --- /dev/null +++ b/gradle-suppressible-error-prone/src/test/java/com/palantir/gradle/suppressibleerrorprone/SuppressibleErrorPronePluginIntegrationTest.java @@ -0,0 +1,399 @@ +/* + * (c) Copyright 2024 Palantir Technologies Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.palantir.gradle.suppressibleerrorprone; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.palantir.gradle.testing.execution.GradleInvoker; +import com.palantir.gradle.testing.execution.InvocationResult; +import com.palantir.gradle.testing.junit.DisabledConfigurationCache; +import com.palantir.gradle.testing.junit.GradlePluginTests; +import com.palantir.gradle.testing.project.RootProject; +import com.palantir.javaformat.java.Formatter; +import com.palantir.javaformat.java.FormatterException; +import com.palantir.javaformat.java.JavaFormatterOptions; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@GradlePluginTests +@DisabledConfigurationCache("Tests manipulate files directly which is not compatible with configuration cache") +final class SuppressibleErrorPronePluginIntegrationTest { + private static final Formatter FORMATTER = Formatter.createFormatter(JavaFormatterOptions.builder() + .style(JavaFormatterOptions.Style.PALANTIR) + .build()); + + // This makes debugging the errorprone check code running inside the compiler (including the bytecode + // edited modifications we have made) "just work" from inside these tests. + // Change the variable below to true to enable it, after setting up the standalone debugger: + // 1. Make a new run configuration in IntelliJ of type JVM Debug + // 2. Change it to "Listen" rather than "Attach" + // 3. Select Auto-restart. + // 4. Run the debugger + // 5. Run the tests as well + // If the variable below is true the tests will fail as the compilation process will try to + // attach to a non-existent debugger. Set it to false before you push any code. + private static final boolean DEBUGGING_ERROR_PRONES = false; + + private String projectVersion; + + @BeforeEach + @SuppressWarnings("GradleTestPluginsBlock") // buildscript and apply plugin need special handling + void setup(RootProject rootProject) { + projectVersion = Optional.ofNullable(System.getProperty("projectVersion")) + .orElseThrow(() -> new IllegalStateException("projectVersion system property must be set")); + + // Note: We use append() with buildscript block and apply plugin because these need special handling + // that can't use the plugins() API + rootProject.buildGradle().append(""" + buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.palantir.gradle.consistentversions:gradle-consistent-versions:3.1.0' + } + } + // Consistent versions checks we don't resolve configurations at configuration time and + // also interacts in many ways with dependencies + apply plugin: 'com.palantir.consistent-versions' + + apply plugin: 'com.palantir.suppressible-error-prone' + """); + + rootProject.buildGradle().plugins().add("java"); + + rootProject.buildGradle().append(""" + repositories { + mavenCentral() + // Needed so that suppressible-error-prone and suppressible-error-prone-annotations can be added + // as jars to the various configurations. We make sure to publish these to maven local before the + // test task runs. + mavenLocal() + } + + sourceSets { + other + } + + dependencies { + errorprone 'com.google.errorprone:error_prone_core:2.31.0' + // Mimick the way SuppressibleErrorPronePlugin adds the dependency on suppressible-error-prone + // This should guarantee that we're using the same version, both of which should be in maven local + // and be the current version + errorprone 'com.palantir.suppressible-error-prone:test-error-prone-checks:' + project.findProperty("suppressibleErrorProneVersion") + } + + suppressibleErrorProne { + configureEachErrorProneOptions { + // These interfere with some tests, so disable them + // TODO(callumr): Rewrite the tests to use custom testing error-prones rather than built in checks + // to make upgrading error-prone easier. + disable('Varifier', 'ReturnValueIgnored', 'UnusedVariable', 'IdentifierName', 'UnusedMethod') + ignoreUnknownCheckNames = true + } + } + """); + + if (DEBUGGING_ERROR_PRONES) { + rootProject.buildGradle().append(""" + tasks.withType(JavaCompile).configureEach { + it.options.forkOptions.jvmArgumentProviders.add(new CommandLineArgumentProvider() { + @Override + public Iterable asArguments() { + return List.of("-agentlib:jdwp=transport=dt_socket,server=n,address=localhost:5005") + } + }) + } + """); + } + + rootProject + .gradlePropertiesFile() + .appendProperty("__TESTING", "true") + .appendProperty("__TESTING_CACHE_BUST_ERRORPRONE_TRANSFORM", "true"); + + rootProject.file("versions.lock").createEmpty(); + } + + @Test + void reports_a_failing_error_prone(GradleInvoker gradle, RootProject rootProject) { + writeJavaSourceFileToSourceSets(rootProject, """ + package app; + + public final class App { + public static void main(String[] args) { + new int[3].toString(); + } + } + """); + + InvocationResult result = runTasksWithFailure(gradle, "compileAllErrorProne"); + + assertThat(result.output()).contains("[ArrayToString]"); + } + + @Test + void can_suppress_an_error_prone_with_for_rollout_prefix(GradleInvoker gradle, RootProject rootProject) { + // This test is explicitly checking we suppress the for-rollout prefix as that is what exists + // in people's codebases + + writeJavaSourceFileToSourceSets(rootProject, """ + package app; + + public final class App { + @SuppressWarnings("for-rollout:ArrayToString") + public static void main(String[] args) { + new int[3].toString(); + } + } + """); + + runTasksSuccessfully(gradle, "compileAllErrorProne"); + } + + @Test + void ensure_error_prone_checks_are_disabled_in_generated_code(GradleInvoker gradle, RootProject rootProject) + throws IOException { + String erroringCode = """ + package app; + + public final class App { + public static void main(String[] args) { + new int[3].toString(); + } + } + """; + + Path sourceDir1 = rootProject.path().resolve("src/generated"); + Path sourceDir2 = rootProject.path().resolve("build/generated"); + + Files.createDirectories(sourceDir1); + Files.createDirectories(sourceDir2); + + writeJavaSourceFile(rootProject, erroringCode, sourceDir1); + writeJavaSourceFile(rootProject, erroringCode.replace("App", "App2"), sourceDir2); + + rootProject + .buildGradle() + .append( + "sourceSets.main.java.srcDirs('%s', '%s')", + rootProject.path().relativize(sourceDir1), + rootProject.path().relativize(sourceDir2)); + + runTasksSuccessfully(gradle, "compileAllErrorProne"); + } + + @Test + void can_apply_patches_for_a_check_if_added_to_the_patchChecks_list(GradleInvoker gradle, RootProject rootProject) { + rootProject.buildGradle().append(""" + suppressibleErrorProne { + patchChecks.add('ArrayToString') + } + """); + + writeJavaSourceFileToSourceSets(rootProject, """ + package app; + + public final class App { + public static void main(String[] args) { + new int[3].toString(); + } + } + """); + + runTasksSuccessfully(gradle, "compileAllErrorProne", "-PerrorProneApply"); + + runTasksSuccessfully(gradle, "compileAllErrorProne"); + + javaSourceContains(rootProject, "Arrays.toString(new int[3])"); + } + + @Test + void does_not_apply_patches_for_a_check_if_not_added_to_the_patchChecks_list( + GradleInvoker gradle, RootProject rootProject) { + rootProject.buildGradle().append(""" + suppressibleErrorProne { + // To make sure set is not empty + patchChecks = ['SomeCheck'] + } + """); + + writeJavaSourceFileToSourceSets(rootProject, """ + package app; + + public final class App { + public static void main(String[] args) { + new int[3].toString(); + } + } + """); + + runTasksSuccessfully(gradle, "compileAllErrorProne", "-PerrorProneApply"); + + javaSourceContains(rootProject, "new int[3].toString()"); + } + + // Helper methods + + private InvocationResult runTasksSuccessfully(GradleInvoker gradle, String... tasks) { + String[] allTasks = new String[tasks.length + 1]; + System.arraycopy(tasks, 0, allTasks, 0, tasks.length); + allTasks[tasks.length] = "-PsuppressibleErrorProneVersion=" + projectVersion; + + if (DEBUGGING_ERROR_PRONES) { + return gradle.withArgs(allTasks).buildsSuccessfully(); + } else { + // Note: Configuration cache is disabled at class level due to file manipulation + return gradle.withArgs(allTasks).buildsSuccessfully(); + } + } + + private InvocationResult runTasksWithFailure(GradleInvoker gradle, String... tasks) { + String[] allTasks = new String[tasks.length + 1]; + System.arraycopy(tasks, 0, allTasks, 0, tasks.length); + allTasks[tasks.length] = "-PsuppressibleErrorProneVersion=" + projectVersion; + + if (DEBUGGING_ERROR_PRONES) { + return gradle.withArgs(allTasks).buildsWithFailure(); + } else { + // Note: Configuration cache is disabled at class level due to file manipulation + return gradle.withArgs(allTasks).buildsWithFailure(); + } + } + + private void writeJavaSourceFileToSourceSets(RootProject rootProject, String source) { + rootProject.sourceSet("main").java().writeClass(source.stripIndent()); + rootProject.sourceSet("other").java().writeClass(source.stripIndent()); + } + + private void writeJavaSourceFile(RootProject rootProject, String source, Path sourceDir) throws IOException { + // Extract package and class name to create proper file structure + String packageName = "app"; + String className = "App"; + if (source.contains("App2")) { + className = "App2"; + } + + Path packageDir = sourceDir.resolve(packageName.replace('.', '/')); + Files.createDirectories(packageDir); + Path javaFile = packageDir.resolve(className + ".java"); + Files.writeString(javaFile, source.stripIndent()); + } + + private void javaSourceContains(RootProject rootProject, String substring) { + Path mainJava = rootProject.path().resolve("src/main/java/app/App.java"); + Path otherJava = rootProject.path().resolve("src/other/java/app/App.java"); + + try { + String mainContent = Files.readString(mainJava); + String otherContent = Files.readString(otherJava); + assertThat(mainContent).contains(substring); + assertThat(otherContent).contains(substring); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read Java source files", e); + } + } + + @SuppressWarnings("UnusedMethod") + private void javaSourceDoesNotContain(RootProject rootProject, String substring) { + Path mainJava = rootProject.path().resolve("src/main/java/app/App.java"); + Path otherJava = rootProject.path().resolve("src/other/java/app/App.java"); + + try { + String mainContent = Files.readString(mainJava); + String otherContent = Files.readString(otherJava); + assertThat(mainContent).doesNotContain(substring); + assertThat(otherContent).doesNotContain(substring); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read Java source files", e); + } + } + + // Normalizes Java source by trimming whitespace and applying consistent formatting. + // Preserves newlines since the formatter allows them within methods, and we need + // to test that error-prone doesn't introduce unwanted line breaks. + @SuppressWarnings("StringSplitter") + private static String normalizeSource(String content) { + String[] lines = content.split("\n"); + StringBuilder stripped = new StringBuilder(); + for (String line : lines) { + stripped.append(line.trim()).append('\n'); + } + + try { + return FORMATTER.formatSource(stripped.toString()); + } catch (FormatterException e) { + throw new RuntimeException("Failed to format source", e); + } + } + + private void javaSourceIsSyntacticallyEqualTo(RootProject rootProject, String source) { + Path mainJava = rootProject.path().resolve("src/main/java/app/App.java"); + Path otherJava = rootProject.path().resolve("src/other/java/app/App.java"); + + try { + String mainContent = Files.readString(mainJava); + String otherContent = Files.readString(otherJava); + + String output = normalizeSource(mainContent); + String expected = normalizeSource(source); + + // Ensure test fixtures are properly formatted + assertThat("\n" + expected) + .as("Please update your text fixtures to be in palantir-java-format") + .isEqualTo(source); + assertThat(output).isEqualTo(expected); + + String outputOther = normalizeSource(otherContent); + String expectedOther = normalizeSource(source); + assertThat(outputOther).isEqualTo(expectedOther); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read Java source files", e); + } + } + + @SuppressWarnings("UnusedMethod") + private void javaSourceIsSyntacticallyNotEqualTo(RootProject rootProject, String source) { + Path mainJava = rootProject.path().resolve("src/main/java/app/App.java"); + Path otherJava = rootProject.path().resolve("src/other/java/app/App.java"); + + try { + String mainContent = Files.readString(mainJava); + String otherContent = Files.readString(otherJava); + + String output = normalizeSource(mainContent); + String expected = normalizeSource(source); + + // Ensure test fixtures are properly formatted + assertThat("\n" + expected) + .as("Please update your text fixtures to be in palantir-java-format") + .isEqualTo(source); + assertThat(output).isNotEqualTo(expected); + + String outputOther = normalizeSource(otherContent); + String expectedOther = normalizeSource(source); + assertThat(outputOther).isNotEqualTo(expectedOther); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read Java source files", e); + } + } +} diff --git a/test-migration-notes/SuppressibleErrorPronePluginIntegrationTest.html b/test-migration-notes/SuppressibleErrorPronePluginIntegrationTest.html new file mode 100644 index 00000000..f5582d50 --- /dev/null +++ b/test-migration-notes/SuppressibleErrorPronePluginIntegrationTest.html @@ -0,0 +1,42652 @@ + + + + + Diff to HTML by rtfpessoa + + + + + + + + + + + + + +

Diff to HTML by rtfpessoa

+ +
+
+
+
+ + gradle-suppressible-error-prone/src/test/{groovy/com/palantir/gradle/suppressibleerrorprone/SuppressibleErrorPronePluginIntegrationTest.groovy → java/com/palantir/gradle/suppressibleerrorprone/SuppressibleErrorPronePluginIntegrationTest.java} + RENAMED + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
@@ -14,20 +14,33 @@
+
+ 14 + +
+   + * limitations under the License. +
+
+ 15 + +
+   + */ +
+
+ 16 + +
+   +
+
+
+ 17 + +
+ - + package com.palantir.gradle.suppressibleerrorprone +
+
+ 18 + +
+ - +
+
+
+ 19 + +
+ - +
+
+
+ 20 + +
+ - + import com.palantir.gradle.plugintesting.ConfigurationCacheSpec +
+
+ 21 + +
+ - + import com.palantir.javaformat.java.JavaFormatterOptions +
+
+ 22 + +
+ - + import org.apache.commons.io.FileUtils +
+
+ 23 + +
+ - + import org.gradle.testkit.runner.BuildResult +
+
+ 24 + +
+ - + import spock.lang.Unroll +
+
+ 25 + +
+ - + import com.palantir.javaformat.java.Formatter +
+
+ 26 + +
+ - +
+
+
+ 27 + +
+ - +
+
+
+ 28 + +
+ - + class SuppressibleErrorPronePluginIntegrationTest extends ConfigurationCacheSpec { +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 29 + +
+   + // ***DELINEATOR FOR REVIEW: formatter +
+
+ 30 + +
+ - + static Formatter formatter = Formatter.createFormatter(JavaFormatterOptions.builder().style(JavaFormatterOptions.Style.PALANTIR).build()) +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 31 + +
+   +
+
+
+ 32 + +
+   + // This makes debugging the errorprone check code running inside the compiler (including the bytecode +
+
+ 33 + +
+   + // edited modifications we have made) "just work" from inside these tests. +
+
+
@@ -40,12 +53,20 @@ class SuppressibleErrorPronePluginIntegrationTest extends ConfigurationCacheSpec
+
+ 40 + +
+   + // If the variable below is true the tests will fail as the compilation process will try to +
+
+ 41 + +
+   + // attach to a non-existent debugger. Set it to false before you push any code. +
+
+ 42 + +
+   + // ***DELINEATOR FOR REVIEW: debuggingErrorPrones +
+
+ 43 + +
+ - + boolean debuggingErrorPrones = false +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 44 + +
+   +
+
+
+ 45 + +
+   + // ***DELINEATOR FOR REVIEW: setup +
+
+ 46 + +
+ - + def setup() { +
+
+ 47 + +
+ - + // language=Gradle +
+
+ 48 + +
+ - + buildFile << ''' +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 49 + +
+   + buildscript { +
+
+ 50 + +
+   + repositories { +
+
+ 51 + +
+   + mavenCentral() +
+
+
@@ -59,20 +80,23 @@ class SuppressibleErrorPronePluginIntegrationTest extends ConfigurationCacheSpec
+
+ 59 + +
+   + apply plugin: 'com.palantir.consistent-versions' +
+
+ 60 + +
+   +
+
+
+ 61 + +
+   + apply plugin: 'com.palantir.suppressible-error-prone' +
+
+ 62 + +
+ - + apply plugin: 'java' +
+
+ 63 + +
+ - + +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 64 + +
+   + repositories { +
+
+ 65 + +
+   + mavenCentral() +
+
+ 66 + +
+   + // Needed so that suppressible-error-prone and suppressible-error-prone-annotations can be added +
+
+ 67 + +
+   + // as jars to the various configurations. We make sure to publish these to maven local before the +
+
+ 68 + +
+ - + // test task runs. +
+
+ 69 + +
+   + mavenLocal() +
+
+ 70 + +
+   + } +
+
+ 71 + +
+ - + +
+
+ 72 + +
+   + sourceSets { +
+
+ 73 + +
+   + other +
+
+ 74 + +
+   + } +
+
+ 75 + +
+ - + +
+
+ 76 + +
+   + dependencies { +
+
+ 77 + +
+   + errorprone 'com.google.errorprone:error_prone_core:2.31.0' +
+
+ 78 + +
+   + // Mimick the way SuppressibleErrorPronePlugin adds the dependency on suppressible-error-prone +
+
+
@@ -80,7 +104,7 @@ class SuppressibleErrorPronePluginIntegrationTest extends ConfigurationCacheSpec
+
+ 80 + +
+   + // and be the current version +
+
+ 81 + +
+   + errorprone 'com.palantir.suppressible-error-prone:test-error-prone-checks:' + project.findProperty("suppressibleErrorProneVersion") +
+
+ 82 + +
+   + } +
+
+ 83 + +
+ - + +
+
+ 84 + +
+   + suppressibleErrorProne { +
+
+ 85 + +
+   + configureEachErrorProneOptions { +
+
+ 86 + +
+   + // These interfere with some tests, so disable them +
+
+
@@ -90,11 +114,10 @@ class SuppressibleErrorPronePluginIntegrationTest extends ConfigurationCacheSpec
+
+ 90 + +
+   + ignoreUnknownCheckNames = true +
+
+ 91 + +
+   + } +
+
+ 92 + +
+   + } +
+
+ 93 + +
+ - + '''.stripIndent(true) +
+
+ 94 + +
+   +
+
+
+ 95 + +
+ - + if (debuggingErrorPrones) { +
+
+ 96 + +
+ - + // language=Gradle +
+
+ 97 + +
+ - + buildFile << ''' +
+
+ 98 + +
+   + tasks.withType(JavaCompile).configureEach { +
+
+ 99 + +
+   + it.options.forkOptions.jvmArgumentProviders.add(new CommandLineArgumentProvider() { +
+
+ 100 + +
+   + @Override +
+
+
@@ -103,1985 +126,291 @@ class SuppressibleErrorPronePluginIntegrationTest extends ConfigurationCacheSpec
+
+ 103 + +
+   + } +
+
+ 104 + +
+   + }) +
+
+ 105 + +
+   + } +
+
+ 106 + +
+ - + '''.stripIndent(true) +
+
+ 107 + +
+   + } +
+
+ 108 + +
+   +
+
+
+ 109 + +
+ - + file('gradle.properties') << ''' +
+
+ 110 + +
+ - + __TESTING=true +
+
+ 111 + +
+ - + __TESTING_CACHE_BUST_ERRORPRONE_TRANSFORM=true +
+
+ 112 + +
+ - + '''.stripIndent(true) +
+
+ 113 + +
+   +
+
+
+ 114 + +
+ - + file('versions.lock') +
+
+ 115 + +
+   + } +
+
+ 116 + +
+   +
+
+
+ 117 + +
+   + // ***DELINEATOR FOR REVIEW: reports_a_failing_error_prone +
+
+ 118 + +
+ - + def 'reports a failing error prone'() { +
+
+ 119 + +
+ - + // language=Java +
+
+ 120 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 121 + +
+   + package app; +
+
+ 122 + +
+ - + +
+
+ 123 + +
+   + public final class App { +
+
+ 124 + +
+   + public static void main(String[] args) { +
+
+ 125 + +
+   + new int[3].toString(); +
+
+ 126 + +
+   + } +
+
+ 127 + +
+   + } +
+
+ 128 + +
+ - + '''.stripIndent(true) +
+
+ 129 + +
+   +
+
+
+ 130 + +
+ - + when: +
+
+ 131 + +
+ - + def stderr = runTasksWithFailure('compileAllErrorProne').output +
+
+ 132 + +
+   +
+
+
+ 133 + +
+ - + then: +
+
+ 134 + +
+ - + stderr.contains('[ArrayToString]') +
+
+ 135 + +
+   + } +
+
+ 136 + +
+   +
+
+
+ 137 + +
+ - + def 'can suppress an error prone with for-rollout prefix'() { +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 138 + +
+   + // This test is explicitly checking we suppress the for-rollout prefix as that is what exists +
+
+ 139 + +
+   + // in people's codebases +
+
+ 140 + +
+   +
+
+
+ 141 + +
+ - + when: +
+
+ 142 + +
+ - + // language=Java +
+
+ 143 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 144 + +
+   + package app; +
+
+ 145 + +
+ - + +
+
+ 146 + +
+   + public final class App { +
+
+ 147 + +
+   + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 148 + +
+   + public static void main(String[] args) { +
+
+ 149 + +
+   + new int[3].toString(); +
+
+ 150 + +
+   + } +
+
+ 151 + +
+   + } +
+
+ 152 + +
+ - + '''.stripIndent(true) +
+
+ 153 + +
+   +
+
+
+ 154 + +
+ - + then: +
+
+ 155 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ 156 + +
+   + } +
+
+ 157 + +
+   +
+
+
+ 158 + +
+ - + def 'ensure error prone checks are disabled in generated code'() { +
+
+ 159 + +
+ - + // language=Java +
+
+ 160 + +
+ - + def erroringCode = ''' +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 161 + +
+   + package app; +
+
+ 162 + +
+ - + +
+
+ 163 + +
+   + public final class App { +
+
+ 164 + +
+   + public static void main(String[] args) { +
+
+ 165 + +
+   + new int[3].toString(); +
+
+ 166 + +
+   + } +
+
+ 167 + +
+   + } +
+
+ 168 + +
+ - + '''.stripIndent(true) +
+
+ 169 + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 170 + +
+   +
+
+
+ 171 + +
+ - + when: +
+
+ 172 + +
+ - + def sourceDir1 = new File(projectDir, 'src/generated') +
+
+ 173 + +
+ - + def sourceDir2 = new File(projectDir, 'build/generated') +
+
+ 174 + +
+   +
+
+
+ 175 + +
+ - + writeJavaSourceFile(erroringCode, sourceDir1) +
+
+ 176 + +
+ - + writeJavaSourceFile(erroringCode.replace('App', 'App2'), sourceDir2) +
+
+ 177 + +
+   +
+
+
+ 178 + +
+ - + buildFile << """ +
+
+ 179 + +
+ - + sourceSets.main.java.srcDirs('${projectDir.relativePath(sourceDir1)}', '${projectDir.relativePath(sourceDir2)}') +
+
+ 180 + +
+ - + """.stripIndent(true) +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 181 + +
+   +
+
+
+ 182 + +
+ - + then: +
+
+ 183 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ 184 + +
+   + } +
+
+ 185 + +
+   +
+
+
+ 186 + +
+ - + def 'can apply patches for a check if added to the patchChecks list'() { +
+
+ 187 + +
+ - + // language=Gradle +
+
+ 188 + +
+ - + buildFile << ''' +
+
+ + +
+   +
+
+
+ 189 + +
+   + suppressibleErrorProne { +
+
+ 190 + +
+   + patchChecks.add('ArrayToString') +
+
+ 191 + +
+   + } +
+
+ 192 + +
+ - + '''.stripIndent(true) +
+
+ 193 + +
+   +
+
+
+ 194 + +
+ - + // language=Java +
+
+ 195 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 196 + +
+   + package app; +
+
+ 197 + +
+ - + +
+
+ 198 + +
+   + public final class App { +
+
+ 199 + +
+   + public static void main(String[] args) { +
+
+ 200 + +
+   + new int[3].toString(); +
+
+ 201 + +
+   + } +
+
+ 202 + +
+   + } +
+
+ 203 + +
+ - + '''.stripIndent(true) +
+
+ 204 + +
+   +
+
+
+ 205 + +
+ - + when: +
+
+ 206 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') +
+
+ 207 + +
+   +
+
+
+ 208 + +
+ - + then: +
+
+ 209 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ 210 + +
+   +
+
+
+ 211 + +
+ - + javaSourceContains('Arrays.toString(new int[3])') +
+
+ 212 + +
+   + } +
+
+ 213 + +
+   +
+
+
+ 214 + +
+ - + def 'does not apply patches for a check if not added to the patchChecks list'() { +
+
+ 215 + +
+ - + // language=Gradle +
+
+ 216 + +
+ - + buildFile << ''' +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 217 + +
+   + suppressibleErrorProne { +
+
+ 218 + +
+   + // To make sure set is not empty +
+
+ 219 + +
+   + patchChecks = ['SomeCheck'] +
+
+ 220 + +
+   + } +
+
+ 221 + +
+ - + '''.stripIndent(true) +
+
+ 222 + +
+ - +
+
+
+ 223 + +
+ - + // language=Java +
+
+ 224 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 225 + +
+ - + package app; +
+
+ 226 + +
+ - + +
+
+ 227 + +
+ - + public final class App { +
+
+ 228 + +
+ - + public static void main(String[] args) { +
+
+ 229 + +
+ - + new int[3].toString(); +
+
+ 230 + +
+ - + } +
+
+ 231 + +
+ - + } +
+
+ 232 + +
+ - + '''.stripIndent(true) +
+
+ 233 + +
+ - +
+
+
+ 234 + +
+ - + when: +
+
+ 235 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') +
+
+ 236 + +
+ - +
+
+
+ 237 + +
+ - + then: +
+
+ 238 + +
+ - + javaSourceContains('new int[3].toString()') +
+
+ 239 + +
+ - + } +
+
+ 240 + +
+ - +
+
+
+ 241 + +
+ - + def 'does not apply patches if there is nothing in patchChecks set'() { +
+
+ 242 + +
+ - + // language=Gradle +
+
+ 243 + +
+ - + buildFile << ''' +
+
+ 244 + +
+ - + suppressibleErrorProne { +
+
+ 245 + +
+ - + patchChecks.empty() +
+
+ 246 + +
+ - + } +
+
+ 247 + +
+ - + '''.stripIndent(true) +
+
+ 248 + +
+ - +
+
+
+ 249 + +
+ - + // language=Java +
+
+ 250 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 251 + +
+ - + package app; +
+
+ 252 + +
+ - + +
+
+ 253 + +
+ - + public final class App { +
+
+ 254 + +
+ - + public static void main(String[] args) { +
+
+ 255 + +
+ - + new int[3].toString(); +
+
+ 256 + +
+ - + } +
+
+ 257 + +
+ - + } +
+
+ 258 + +
+ - + '''.stripIndent(true) +
+
+ 259 + +
+ - +
+
+
+ 260 + +
+ - + when: +
+
+ 261 + +
+ - + // Doesn't actually do any patching as the set is empty. It just does a normal compile that fails. +
+
+ 262 + +
+ - + def stderr = runTasksWithFailure('compileAllErrorProne', '-PerrorProneApply').output +
+
+ 263 + +
+ - +
+
+
+ 264 + +
+ - + then: +
+
+ 265 + +
+ - + stderr.contains('[ArrayToString]') +
+
+ 266 + +
+ - + javaSourceContains('new int[3].toString()') +
+
+ 267 + +
+ - + } +
+
+ 268 + +
+ - +
+
+
+ 269 + +
+ - + def 'does not apply patches for check that was explicitly disabled'() { +
+
+ 270 + +
+ - + // language=Gradle +
+
+ 271 + +
+ - + buildFile << ''' +
+
+ 272 + +
+ - + suppressibleErrorProne { +
+
+ 273 + +
+ - + patchChecks.add('ArrayToString') +
+
+ 274 + +
+ - + } +
+
+ 275 + +
+ - + +
+
+ 276 + +
+ - + tasks.withType(JavaCompile).configureEach { +
+
+ 277 + +
+ - + options.errorprone.disable 'ArrayToString' +
+
+ 278 + +
+ - + } +
+
+ 279 + +
+ - + '''.stripIndent(true) +
+
+ 280 + +
+ - +
+
+
+ 281 + +
+ - + // language=Java +
+
+ 282 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 283 + +
+ - + package app; +
+
+ 284 + +
+ - + +
+
+ 285 + +
+ - + public final class App { +
+
+ 286 + +
+ - + public static void main(String[] args) { +
+
+ 287 + +
+ - + new int[3].toString(); +
+
+ 288 + +
+ - + } +
+
+ 289 + +
+ - + } +
+
+ 290 + +
+ - + '''.stripIndent(true) +
+
+ 291 + +
+ - +
+
+
+ 292 + +
+ - + when: +
+
+ 293 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') +
+
+ 294 + +
+ - +
+
+
+ 295 + +
+ - + then: +
+
+ 296 + +
+ - + javaSourceContains('new int[3].toString()') +
+
+ 297 + +
+ - + } +
+
+ 298 + +
+   +
+
+
+ 299 + +
+ - + def 'can patch specific checks using -PerrorProneApply'() { +
+
+ 300 + +
+ - + // language=Java +
+
+ 301 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 302 + +
+   + package app; +
+
+ 303 + +
+ - + +
+
+ 304 + +
+ - + public final class App { +
+
+ 305 + +
+ - + public static void main(String[] args) { +
+
+ 306 + +
+ - + new int[3].toString(); +
+
+ 307 + +
+ - + new int[2].equals(new int[1]); +
+
+ 308 + +
+ - + } +
+
+ 309 + +
+ - + } +
+
+ 310 + +
+ - + '''.stripIndent(true) +
+
+ 311 + +
+ - +
+
+
+ 312 + +
+ - + when: +
+
+ 313 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply=ArrayToString,ArrayEquals') +
+
+ 314 + +
+ - +
+
+
+ 315 + +
+ - + then: +
+
+ 316 + +
+ - + javaSourceContains('Arrays.toString(new int[3])') +
+
+ 317 + +
+ - + javaSourceContains('Arrays.equals(new int[2], new int[1])') +
+
+ 318 + +
+ - + } +
+
+ 319 + +
+   +
+
+
+ 320 + +
+ - + def 'can suppress a failing check (even if not in patchChecks set)'() { +
+
+ 321 + +
+ - + // language=Java +
+
+ 322 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 323 + +
+ - + package app; +
+
+ 324 + +
+ - + +
+
+ 325 + +
+   + public final class App { +
+
+ 326 + +
+   + public static void main(String[] args) { +
+
+ 327 + +
+   + new int[3].toString(); +
+
+ 328 + +
+   + } +
+
+ 329 + +
+   + } +
+
+ 330 + +
+ - + '''.stripIndent(true) +
+
+ 331 + +
+ - +
+
+
+ 332 + +
+ - + when: +
+
+ 333 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 334 + +
+ - +
+
+
+ 335 + +
+ - + then: +
+
+ 336 + +
+ - + javaSourceContains('@SuppressWarnings(\"for-rollout:ArrayToString\")') +
+
+ 337 + +
+ - +
+
+
+ 338 + +
+ - +
+
+
+ 339 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ 340 + +
+ - + } +
+
+ 341 + +
+ - +
+
+
+ 342 + +
+ - + def 'does not apply SuppressWarnings to implicit lambda parameters'() { +
+
+ 343 + +
+ - + // language=Java +
+
+ 344 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 345 + +
+ - + package app; +
+
+ 346 + +
+ - + +
+
+ 347 + +
+ - + import java.util.stream.Stream; +
+
+ 348 + +
+ - + +
+
+ 349 + +
+ - + public class App { +
+
+ 350 + +
+ - + void test() { +
+
+ 351 + +
+ - + Stream.of(new Object()).forEach(o -> o.toString()); +
+
+ 352 + +
+ - + } +
+
+ 353 + +
+ - + } +
+
+ 354 + +
+ - + '''.stripIndent(true) +
+
+ 355 + +
+ - +
+
+
+ 356 + +
+ - + when: +
+
+ 357 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 358 + +
+ - +
+
+
+ 359 + +
+ - + then: +
+
+ 360 + +
+ - + // Suppression should be on the method, not the lambda parameter +
+
+ 361 + +
+ - + // language=Java +
+
+ 362 + +
+ - + javaSourceIsSyntacticallyEqualTo """ +
+
+ 363 + +
+ - + package app; +
+
+ 364 + +
+ - + +
+
+ 365 + +
+ - + import java.util.stream.Stream; +
+
+ 366 + +
+ - + +
+
+ 367 + +
+ - + public class App { +
+
+ 368 + +
+ - + @SuppressWarnings("for-rollout:TestCheckNoSingleLetterVariable") +
+
+ 369 + +
+ - + void test() { +
+
+ 370 + +
+ - + Stream.of(new Object()).forEach(o -> o.toString()); +
+
+ 371 + +
+ - + } +
+
+ 372 + +
+ - + } +
+
+ 373 + +
+ - + """.stripIndent(true) +
+
+ 374 + +
+ - +
+
+
+ 375 + +
+ - + // Verify the code still compiles after the suppression has been applied, as previous versions +
+
+ 376 + +
+ - + // were adding the annotation to the lambda implicit parameter which is not valid java +
+
+ 377 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ 378 + +
+ - + } +
+
+ 379 + +
+ - +
+
+
+ 380 + +
+ - + def 'does not apply SuppressWarnings to explicit lambda parameters'() { +
+
+ 381 + +
+ - + // language=Java +
+
+ 382 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 383 + +
+ - + package app; +
+
+ 384 + +
+ - + +
+
+ 385 + +
+ - + import java.util.stream.Stream; +
+
+ 386 + +
+ - + +
+
+ 387 + +
+ - + public class App { +
+
+ 388 + +
+ - + void test() { +
+
+ 389 + +
+ - + Stream.of(new Object()).forEach((Object o) -> o.toString()); +
+
+ 390 + +
+ - + } +
+
+ 391 + +
+ - + } +
+
+ 392 + +
+ - + '''.stripIndent(true) +
+
+ 393 + +
+ - +
+
+
+ 394 + +
+ - + when: +
+
+ 395 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 396 + +
+   +
+
+
+ 397 + +
+ - + then: +
+
+ 398 + +
+ - + // Suppression should be on the method, not the lambda parameter +
+
+ 399 + +
+ - + // language=Java +
+
+ 400 + +
+ - + javaSourceIsSyntacticallyEqualTo(""" +
+
+ 401 + +
+ - + package app; +
+
+ 402 + +
+ - + +
+
+ 403 + +
+ - + import java.util.stream.Stream; +
+
+ 404 + +
+ - + +
+
+ 405 + +
+ - + public class App { +
+
+ 406 + +
+ - + @SuppressWarnings("for-rollout:TestCheckNoSingleLetterVariable") +
+
+ 407 + +
+ - + void test() { +
+
+ 408 + +
+ - + Stream.of(new Object()).forEach((Object o) -> o.toString()); +
+
+ 409 + +
+ - + } +
+
+ 410 + +
+ - + } +
+
+ 411 + +
+ - + """.stripIndent(true)) +
+
+ 412 + +
+   +
+
+
+ 413 + +
+ - + // Verify the code still compiles after the suppression has been applied, as previous versions +
+
+ 414 + +
+ - + // were adding the annotation to the lambda implicit parameter which is not valid java +
+
+ 415 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ 416 + +
+   + } +
+
+ 417 + +
+   +
+
+
+ 418 + +
+ - + def 'does not apply SuppressWarnings to anonymous classes'() { +
+
+ 419 + +
+ - + // language=Java +
+
+ 420 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 421 + +
+ - + package app; +
+
+ 422 + +
+ - + +
+
+ 423 + +
+ - + import java.util.stream.Stream; +
+
+ 424 + +
+ - + +
+
+ 425 + +
+ - + public class App { +
+
+ 426 + +
+ - + void test() { +
+
+ 427 + +
+ - + new Object() { +
+
+ 428 + +
+ - + { +
+
+ 429 + +
+ - + Stream.of(new Object()).forEach(o -> o.toString()); +
+
+ 430 + +
+ - + } +
+
+ 431 + +
+ - + }; +
+
+ 432 + +
+ - + } +
+
+ 433 + +
+ - + } +
+
+ 434 + +
+ - + '''.stripIndent(true) +
+
+ 435 + +
+ - +
+
+
+ 436 + +
+ - + when: +
+
+ 437 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 438 + +
+   +
+
+
+ 439 + +
+ - + then: +
+
+ 440 + +
+ - + // Suppression should be on the method, not the anonymous class +
+
+ 441 + +
+ - + // language=Java +
+
+ 442 + +
+ - + javaSourceIsSyntacticallyEqualTo(""" +
+
+ 443 + +
+ - + package app; +
+
+ 444 + +
+ - + +
+
+ 445 + +
+ - + import java.util.stream.Stream; +
+
+ 446 + +
+ - + +
+
+ 447 + +
+ - + public class App { +
+
+ 448 + +
+ - + @SuppressWarnings("for-rollout:TestCheckNoSingleLetterVariable") +
+
+ 449 + +
+ - + void test() { +
+
+ 450 + +
+ - + new Object() { +
+
+ 451 + +
+ - + { +
+
+ 452 + +
+ - + Stream.of(new Object()).forEach(o -> o.toString()); +
+
+ 453 + +
+ - + } +
+
+ 454 + +
+ - + }; +
+
+ 455 + +
+ - + } +
+
+ 456 + +
+ - + } +
+
+ 457 + +
+ - + """.stripIndent(true)) +
+
+ 458 + +
+   +
+
+
+ 459 + +
+ - + // Verify the code still compiles after the suppression has been applied, as previous versions +
+
+ 460 + +
+ - + // were adding the annotation to the anonymous class which is not valid java +
+
+ 461 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 462 + +
+   + } +
+
+ 463 + +
+   +
+
+
+ 464 + +
+ - + def 'demonstrate suppressions on different source elements'() { +
+
+ 465 + +
+ - + // language=Java +
+
+ 466 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 467 + +
+ - + package app; +
+
+ 468 + +
+ - + +
+
+ 469 + +
+ - + public final class App { +
+
+ 470 + +
+ - + public final String field = new int[3].toString(); +
+
+ 471 + +
+ - +
+
+
+ 472 + +
+ - + public App() { +
+
+ 473 + +
+ - + new int[3].toString(); +
+
+ 474 + +
+ - + } +
+
+ 475 + +
+ - + +
+
+ 476 + +
+ - + public void method() { +
+
+ 477 + +
+ - + new int[3].toString(); +
+
+ 478 + +
+ - + } +
+
+ 479 + +
+ - +
+
+
+ 480 + +
+ - + public void variables() { +
+
+ 481 + +
+ - + String variable = new int[3].toString(); +
+
+ 482 + +
+ - + } +
+
+ 483 + +
+ - + +
+
+ 484 + +
+ - + public static class SomeClass { +
+
+ 485 + +
+ - + static { +
+
+ 486 + +
+ - + new int[3].toString(); +
+
+ 487 + +
+ - + } +
+
+ 488 + +
+ - + } +
+
+ 489 + +
+ - + } +
+
+ 490 + +
+ - + '''.stripIndent(true) +
+
+ 491 + +
+ - +
+
+
+ 492 + +
+ - + when: +
+
+ 493 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 494 + +
+ - +
+
+
+ 495 + +
+ - + then: +
+
+ 496 + +
+ - + // language=Java +
+
+ 497 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 498 + +
+ - + package app; +
+
+ 499 + +
+ - + +
+
+ 500 + +
+ - + public final class App { +
+
+ 501 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 502 + +
+ - + public final String field = new int[3].toString(); +
+
+ 503 + +
+ - + +
+
+ 504 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 505 + +
+ - + public App() { +
+
+ 506 + +
+ - + new int[3].toString(); +
+
+ 507 + +
+ - + } +
+
+ 508 + +
+ - + +
+
+ 509 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 510 + +
+ - + public void method() { +
+
+ 511 + +
+ - + new int[3].toString(); +
+
+ 512 + +
+ - + } +
+
+ 513 + +
+ - +
+
+
+ 514 + +
+ - + public void variables() { +
+
+ 515 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 516 + +
+ - + String variable = new int[3].toString(); +
+
+ 517 + +
+ - + } +
+
+ 518 + +
+ - + +
+
+ 519 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 520 + +
+ - + public static class SomeClass { +
+
+ 521 + +
+ - + static { +
+
+ 522 + +
+ - + new int[3].toString(); +
+
+ 523 + +
+ - + } +
+
+ 524 + +
+ - + } +
+
+ 525 + +
+ - + } +
+
+ 526 + +
+ - + '''.stripIndent(true) +
+
+ 527 + +
+   +
+
+
+ 528 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 529 + +
+   + } +
+
+ 530 + +
+   +
+
+
+ 531 + +
+ - + def 'supports errorprone checks that match on a larger element than they report errors on'() { +
+
+ 532 + +
+ - + // The UnusedVariable check implements CompilationUnitTreeMatcher, so will start with a whole +
+
+ 533 + +
+ - + // CompilationUnitTree and then narrows down to the specific variable declaration that is unused. +
+
+ 534 + +
+ - + // This trips up the "naive" suppression logic, which looks at where the visitor has got to rather +
+
+ 535 + +
+ - + // than where the diagnostic description was produced. +
+
+ 536 + +
+ - +
+
+
+ 537 + +
+ - + // language=Gradle +
+
+ 538 + +
+ - + buildFile << ''' +
+
+ 539 + +
+ - + suppressibleErrorProne { +
+
+ 540 + +
+ - + configureEachErrorProneOptions { +
+
+ 541 + +
+ - + enable('UnusedVariable') +
+
+ 542 + +
+ - + } +
+
+ 543 + +
+ - + } +
+
+ 544 + +
+ - + '''.stripIndent(true) +
+
+ 545 + +
+ - +
+
+
+ 546 + +
+ - + // language=Java +
+
+ 547 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 548 + +
+ - + package app; +
+
+ 549 + +
+ - + +
+
+ 550 + +
+ - + public final class App { +
+
+ 551 + +
+ - + public void variables() { +
+
+ 552 + +
+ - + String variable; +
+
+ 553 + +
+ - + } +
+
+ 554 + +
+ - + } +
+
+ 555 + +
+ - + '''.stripIndent(true) +
+
+ 556 + +
+ - +
+
+
+ 557 + +
+ - + when: +
+
+ 558 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 559 + +
+ - +
+
+
+ 560 + +
+ - + then: +
+
+ 561 + +
+ - + // language=Java +
+
+ 562 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 563 + +
+ - + package app; +
+
+ 564 + +
+ - + +
+
+ 565 + +
+ - + public final class App { +
+
+ 566 + +
+ - + public void variables() { +
+
+ 567 + +
+ - + @SuppressWarnings("for-rollout:UnusedVariable") +
+
+ 568 + +
+ - + String variable; +
+
+ 569 + +
+ - + } +
+
+ 570 + +
+ - + } +
+
+ 571 + +
+ - + '''.stripIndent(true) +
+
+ 572 + +
+ - +
+
+
+ 573 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ 574 + +
+   + } +
+
+ 575 + +
+   +
+
+
+ 576 + +
+ - + def 'supports suppressing errorprone checks on classes, interfaces, records, enums, etc'() { +
+
+ 577 + +
+ - + // language=Java +
+
+ 578 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 579 + +
+ - + package app; +
+
+ 580 + +
+ - + +
+
+ 581 + +
+ - + public final class App { +
+
+ 582 + +
+ - + static class exports {} +
+
+ 583 + +
+ - + interface opens {} +
+
+ 584 + +
+ - + record provides(int cat) {} +
+
+ 585 + +
+ - + enum to {;} +
+
+ 586 + +
+ - + @interface module {} +
+
+ 587 + +
+ - + } +
+
+ 588 + +
+ - + '''.stripIndent(true) +
+
+ 589 + +
+ - +
+
+
+ 590 + +
+ - + when: +
+
+ 591 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 592 + +
+ - +
+
+
+ 593 + +
+ - + then: +
+
+ 594 + +
+ - + // language=Java +
+
+ 595 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 596 + +
+ - + package app; +
+
+ 597 + +
+ - + +
+
+ 598 + +
+ - + public final class App { +
+
+ 599 + +
+ - + @SuppressWarnings("for-rollout:NamedLikeContextualKeyword") +
+
+ 600 + +
+ - + static class exports {} +
+
+ 601 + +
+ - + +
+
+ 602 + +
+ - + @SuppressWarnings("for-rollout:NamedLikeContextualKeyword") +
+
+ 603 + +
+ - + interface opens {} +
+
+ 604 + +
+ - + +
+
+ 605 + +
+ - + @SuppressWarnings("for-rollout:NamedLikeContextualKeyword") +
+
+ 606 + +
+ - + record provides(int cat) {} +
+
+ 607 + +
+ - + +
+
+ 608 + +
+ - + @SuppressWarnings("for-rollout:NamedLikeContextualKeyword") +
+
+ 609 + +
+ - + enum to { +
+
+ 610 + +
+ - + ; +
+
+ 611 + +
+ - + } +
+
+ 612 + +
+ - + +
+
+ 613 + +
+ - + @SuppressWarnings("for-rollout:NamedLikeContextualKeyword") +
+
+ 614 + +
+ - + @interface module {} +
+
+ 615 + +
+ - + } +
+
+ 616 + +
+ - + '''.stripIndent(true) +
+
+ 617 + +
+ - +
+
+
+ 618 + +
+   +
+
+
+ 619 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 620 + +
+   + } +
+
+ 621 + +
+   +
+
+
+ 622 + +
+ - + def 'does not place suppress warnings annotation in the middle of a Type.Builder variables reference'() { +
+
+ 623 + +
+ - + // language=Java +
+
+ 624 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 625 + +
+ - + package app; +
+
+ 626 + +
+ - + +
+
+ 627 + +
+ - + public final class App { +
+
+ 628 + +
+ - + @SuppressWarnings("UnusedVariable") +
+
+ 629 + +
+ - + public static void main(String[] args) { +
+
+ 630 + +
+ - + App.Builder builder = new App.Builder(new int[3].toString()); +
+
+ 631 + +
+ - + } +
+
+ 632 + +
+ - + +
+
+ 633 + +
+ - + static class Builder { +
+
+ 634 + +
+ - + Builder(Object object) {} +
+
+ 635 + +
+ - + } +
+
+ 636 + +
+ - + } +
+
+ 637 + +
+ - + '''.stripIndent(true) +
+
+ 638 + +
+ - +
+
+
+ 639 + +
+ - + when: +
+
+ 640 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 641 + +
+   +
+
+
+ 642 + +
+ - + then: +
+
+ 643 + +
+ - + // language=Java +
+
+ 644 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 645 + +
+ - + package app; +
+
+ 646 + +
+ - + +
+
+ 647 + +
+ - + public final class App { +
+
+ 648 + +
+ - + @SuppressWarnings("UnusedVariable") +
+
+ 649 + +
+ - + public static void main(String[] args) { +
+
+ 650 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 651 + +
+ - + App.Builder builder = new App.Builder(new int[3].toString()); +
+
+ 652 + +
+ - + } +
+
+ 653 + +
+ - + +
+
+ 654 + +
+ - + static class Builder { +
+
+ 655 + +
+ - + Builder(Object object) {} +
+
+ 656 + +
+ - + } +
+
+ 657 + +
+ - + } +
+
+ 658 + +
+ - + '''.stripIndent(true) +
+
+ 659 + +
+ - +
+
+
+ 660 + +
+ - +
+
+
+ 661 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ 662 + +
+   + } +
+
+ 663 + +
+   +
+
+
+ 664 + +
+ - + def 'can run apply and suppress at the same time - it uses the suggested fix if a patch check, suppresses otherwise'() { +
+
+ 665 + +
+ - + // language=Gradle +
+
+ 666 + +
+ - + buildFile << ''' +
+
+ 667 + +
+ - + suppressibleErrorProne { +
+
+ 668 + +
+ - + patchChecks.add('ArrayToString') +
+
+ 669 + +
+ - + } +
+
+ 670 + +
+ - + '''.stripIndent(true) +
+
+ 671 + +
+ - +
+
+
+ 672 + +
+ - + // language=Java +
+
+ 673 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 674 + +
+ - + package app; +
+
+ 675 + +
+ - + +
+
+ 676 + +
+ - + public final class App { +
+
+ 677 + +
+ - + public static void main(String[] args) { +
+
+ 678 + +
+ - + new int[3].toString(); +
+
+ 679 + +
+ - + new int[3].equals(new int[3]); +
+
+ 680 + +
+ - + } +
+
+ 681 + +
+ - + +
+
+ 682 + +
+ - + // Does not remove existing suppressions +
+
+ 683 + +
+ - + @SuppressWarnings("checkstyle:LineLength") +
+
+ 684 + +
+ - + public static void helper() { +
+
+ 685 + +
+ - + new int[3].equals(new int[3]); +
+
+ 686 + +
+ - + } +
+
+ 687 + +
+ - + } +
+
+ 688 + +
+ - + '''.stripIndent(true) +
+
+ 689 + +
+ - +
+
+
+ 690 + +
+ - + when: +
+
+ 691 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply', '-PerrorProneSuppress') +
+
+ 692 + +
+ - +
+
+
+ 693 + +
+ - + then: +
+
+ 694 + +
+ - + // language=Java +
+
+ 695 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 696 + +
+ - + package app; +
+
+ 697 + +
+ - +
+
+
+ 698 + +
+ - + import java.util.Arrays; +
+
+ 699 + +
+ - + +
+
+ 700 + +
+ - + public final class App { +
+
+ 701 + +
+ - + @SuppressWarnings("for-rollout:ArrayEquals") +
+
+ 702 + +
+ - + public static void main(String[] args) { +
+
+ 703 + +
+ - + Arrays.toString(new int[3]); +
+
+ 704 + +
+ - + new int[3].equals(new int[3]); +
+
+ 705 + +
+ - + } +
+
+ 706 + +
+ - + +
+
+ 707 + +
+ - + // Does not remove existing suppressions +
+
+ 708 + +
+ - + @SuppressWarnings({"checkstyle:LineLength", "for-rollout:ArrayEquals"}) +
+
+ 709 + +
+ - + public static void helper() { +
+
+ 710 + +
+ - + new int[3].equals(new int[3]); +
+
+ 711 + +
+ - + } +
+
+ 712 + +
+ - + } +
+
+ 713 + +
+ - + '''.stripIndent(true) +
+
+ 714 + +
+   +
+
+
+ 715 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 716 + +
+   + } +
+
+ 717 + +
+   +
+
+
+ 718 + +
+ - + def 'can run apply and suppress at the same time with IfModuleIsUsed without exploding'() { +
+
+ 719 + +
+ - + // language=Gradle +
+
+ 720 + +
+ - + buildFile << ''' +
+
+ 721 + +
+ - + import com.palantir.gradle.suppressibleerrorprone.ConditionalPatchCheck +
+
+ 722 + +
+ - + import com.palantir.gradle.suppressibleerrorprone.IfModuleIsUsed +
+
+ 723 + +
+ - + +
+
+ 724 + +
+ - + suppressibleErrorProne { +
+
+ 725 + +
+ - + conditionalPatchChecks.add(new ConditionalPatchCheck( +
+
+ 726 + +
+ - + new IfModuleIsUsed('com.fasterxml.jackson.core', 'jackson-core'), 'ArrayToString')) +
+
+ 727 + +
+ - + } +
+
+ 728 + +
+ - + +
+
+ 729 + +
+ - + dependencies { +
+
+ 730 + +
+ - + implementation 'com.fasterxml.jackson.core:jackson-core:2.17.1' +
+
+ 731 + +
+ - + otherImplementation 'com.fasterxml.jackson.core:jackson-core:2.17.1' +
+
+ 732 + +
+ - + } +
+
+ 733 + +
+ - + '''.stripIndent(true) +
+
+ 734 + +
+ - +
+
+
+ 735 + +
+ - + // language=Java +
+
+ 736 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 737 + +
+ - + package app; +
+
+ 738 + +
+ - + +
+
+ 739 + +
+ - + public final class App { +
+
+ 740 + +
+ - + public static void main(String[] args) { +
+
+ 741 + +
+ - + new int[3].toString(); +
+
+ 742 + +
+ - + new int[3].equals(new int[3]); +
+
+ 743 + +
+ - + } +
+
+ 744 + +
+ - + } +
+
+ 745 + +
+ - + '''.stripIndent(true) +
+
+ 746 + +
+ - +
+
+
+ 747 + +
+ - + when: +
+
+ 748 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply', '-PerrorProneSuppress') +
+
+ 749 + +
+ - +
+
+
+ 750 + +
+ - + then: +
+
+ 751 + +
+ - + // language=Java +
+
+ 752 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 753 + +
+ - + package app; +
+
+ 754 + +
+ - +
+
+
+ 755 + +
+ - + import java.util.Arrays; +
+
+ 756 + +
+ - + +
+
+ 757 + +
+ - + public final class App { +
+
+ 758 + +
+ - + @SuppressWarnings("for-rollout:ArrayEquals") +
+
+ 759 + +
+ - + public static void main(String[] args) { +
+
+ 760 + +
+ - + Arrays.toString(new int[3]); +
+
+ 761 + +
+ - + new int[3].equals(new int[3]); +
+
+ 762 + +
+ - + } +
+
+ 763 + +
+ - + } +
+
+ 764 + +
+ - + '''.stripIndent(true) +
+
+ 765 + +
+   +
+
+
+ 766 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 767 + +
+   + } +
+
+ 768 + +
+   +
+
+
+ 769 + +
+ - + def 'can disable errorprone using property'() { +
+
+ 770 + +
+ - + when: 'there is java code some that will fail an errorprone during compilation' +
+
+ 771 + +
+ - + // language=Java +
+
+ 772 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 773 + +
+ - + package app; +
+
+ 774 + +
+ - + +
+
+ 775 + +
+ - + public final class App { +
+
+ 776 + +
+ - + public static void main(String[] args) { +
+
+ 777 + +
+ - + new int[3].toString(); +
+
+ 778 + +
+ - + } +
+
+ 779 + +
+ - + } +
+
+ 780 + +
+ - + '''.stripIndent(true) +
+
+ 781 + +
+ - +
+
+
+ 782 + +
+ - + then: 'compilation succeeds when errorprone is disabled' +
+
+ 783 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneDisable') +
+
+ 784 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-Pcom.palantir.baseline-error-prone.disable') +
+
+ 785 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-Pcom.palantir.baseline-error-prone.disable=true') +
+
+ 786 + +
+ - +
+
+
+ 787 + +
+ - + then: 'compilation fails the legacy baseline errorprone disable property is set to false' +
+
+ 788 + +
+ - + runTasksWithFailure('compileAllErrorProne', '-Pcom.palantir.baseline-error-prone.disable=false') +
+
+ 789 + +
+ - + } +
+
+ 790 + +
+   +
+
+
+ 791 + +
+ - + def 'should be able to refactor near usages of deprecated methods'() { +
+
+ 792 + +
+ - + // If a deprecated method usage appears in a compilation unit that is being refactored, the compiler will +
+
+ 793 + +
+ - + // raise a warning about the deprecated method usage. If -Werror is also enabled, compilation will fail +
+
+ 794 + +
+ - + // rather than succeed, even when patching checks. The code should make sure to disable the -Werror +
+
+ 795 + +
+ - + // behaviour so patching always succeeds. +
+
+ 796 + +
+ - +
+
+
+ 797 + +
+ - + // language=Gradle +
+
+ 798 + +
+ - + buildFile << ''' +
+
+ 799 + +
+ - + tasks.withType(JavaCompile) { +
+
+ 800 + +
+ - + options.compilerArgs += ['-Werror', '-Xlint:deprecation'] +
+
+ 801 + +
+ - + doFirst { +
+
+ 802 + +
+ - + println "COMPILER ARGS: ${options.compilerArgs}" +
+
+ 803 + +
+ - + } +
+
+ 804 + +
+ - + } +
+
+ 805 + +
+ - + +
+
+ 806 + +
+ - + suppressibleErrorProne { +
+
+ 807 + +
+ - + patchChecks.add('ArrayToString') +
+
+ 808 + +
+ - + } +
+
+ 809 + +
+ - + +
+
+ 810 + +
+ - + '''.stripIndent(true) +
+
+ 811 + +
+   +
+
+
+ 812 + +
+ - + // language=Java +
+
+ 813 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 814 + +
+ - + package app; +
+
+ 815 + +
+ - + +
+
+ 816 + +
+ - + public final class App { +
+
+ 817 + +
+ - + public static void main(String[] args) { +
+
+ 818 + +
+ - + Character.isJavaLetter('c'); // deprecated method +
+
+ 819 + +
+ - + new int[3].toString(); +
+
+ 820 + +
+ - + } +
+
+ 821 + +
+ - + } +
+
+ 822 + +
+ - + '''.stripIndent(true) +
+
+ 823 + +
+   +
+
+
+ 824 + +
+ - + when: +
+
+ 825 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 826 + +
+   +
+
+
+ 827 + +
+ - + then: +
+
+ 828 + +
+ - + javaSourceContains('Arrays.toString(new int[3])') +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 829 + +
+   + } +
+
+ 830 + +
+   +
+
+
+ 831 + +
+ - + def 'can conditionally add patch checks'() { +
+
+ 832 + +
+ - + // language=Gradle +
+
+ 833 + +
+ - + buildFile << ''' +
+
+ 834 + +
+ - + import com.palantir.gradle.suppressibleerrorprone.ConditionalPatchCheck +
+
+ 835 + +
+ - +
+
+
+ 836 + +
+ - + suppressibleErrorProne { +
+
+ 837 + +
+ - + patchChecks.add('Something') +
+
+ 838 + +
+ - + conditionalPatchChecks.add(new ConditionalPatchCheck({ true }, 'ArrayToString')) +
+
+ 839 + +
+ - + conditionalPatchChecks.add(new ConditionalPatchCheck({ false }, Set.of('ArrayEquals'))) +
+
+ 840 + +
+ - + } +
+
+ 841 + +
+ - + '''.stripIndent(true) +
+
+ 842 + +
+ - +
+
+
+ 843 + +
+ - + // language=Java +
+
+ 844 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 845 + +
+ - + package app; +
+
+ 846 + +
+ - + +
+
+ 847 + +
+ - + public final class App { +
+
+ 848 + +
+ - + public static void main(String[] args) { +
+
+ 849 + +
+ - + new int[3].toString(); +
+
+ 850 + +
+ - + new int[2].equals(new int[1]); +
+
+ 851 + +
+ - + } +
+
+ 852 + +
+ - + } +
+
+ 853 + +
+ - + '''.stripIndent(true) +
+
+ 854 + +
+ - + when: +
+
+ 855 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') +
+
+ 856 + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 857 + +
+   +
+
+
+ 858 + +
+ - + then: +
+
+ 859 + +
+ - + javaSourceContains('Arrays.toString(new int[3])') +
+
+ 860 + +
+ - + javaSourceContains('new int[2].equals(new int[1])') +
+
+ 861 + +
+ - + } +
+
+ 862 + +
+ - +
+
+
+ 863 + +
+ - + def 'IfModuleIsUsed works properly'() { +
+
+ 864 + +
+ - + // language=Gradle +
+
+ 865 + +
+ - + buildFile << ''' +
+
+ 866 + +
+ - + import com.palantir.gradle.suppressibleerrorprone.ConditionalPatchCheck +
+
+ 867 + +
+ - + import com.palantir.gradle.suppressibleerrorprone.IfModuleIsUsed +
+
+ 868 + +
+   +
+
+
+ 869 + +
+ - + suppressibleErrorProne { +
+
+ 870 + +
+ - + conditionalPatchChecks.add(new ConditionalPatchCheck(new IfModuleIsUsed('com.fasterxml.jackson.core', 'jackson-core'), 'ArrayToString')) +
+
+ 871 + +
+ - + conditionalPatchChecks.add(new ConditionalPatchCheck(new IfModuleIsUsed('doesnt', 'exist'), 'ArrayEquals')) +
+
+ 872 + +
+ - + } +
+
+ 873 + +
+ - + +
+
+ 874 + +
+ - + dependencies { +
+
+ 875 + +
+ - + // Depends on jackson-core +
+
+ 876 + +
+ - + implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' +
+
+ 877 + +
+ - + otherImplementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' +
+
+ 878 + +
+ - + } +
+
+ 879 + +
+ - + '''.stripIndent(true) +
+
+ 880 + +
+   +
+
+
+ 881 + +
+ - + // language=Java +
+
+ 882 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 883 + +
+ - + package app; +
+
+ 884 + +
+ - + +
+
+ 885 + +
+ - + public final class App { +
+
+ 886 + +
+ - + public static void main(String[] args) { +
+
+ 887 + +
+ - + new int[3].toString(); +
+
+ 888 + +
+ - + new int[2].equals(new int[1]); +
+
+ 889 + +
+ - + } +
+
+ 890 + +
+ - + } +
+
+ 891 + +
+ - + '''.stripIndent(true) +
+
+ 892 + +
+ - + when: +
+
+ 893 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneApply') +
+
+ 894 + +
+ - +
+
+
+ 895 + +
+ - + then: +
+
+ 896 + +
+ - + javaSourceContains('Arrays.toString(new int[3])') +
+
+ 897 + +
+ - + javaSourceContains('new int[2].equals(new int[1])') +
+
+ 898 + +
+ - + } +
+
+ 899 + +
+ - +
+
+
+ 900 + +
+ - + def 'compileAllErrorProne only depends on compile tasks with errorprone enabled'() { +
+
+ 901 + +
+ - + // language=Gradle +
+
+ 902 + +
+ - + buildFile << ''' +
+
+ 903 + +
+ - + tasks.named('compileTestJava').configure { +
+
+ 904 + +
+ - + options.errorprone.enabled = false +
+
+ 905 + +
+ - + } +
+
+ 906 + +
+ - + '''.stripIndent(true) +
+
+ 907 + +
+ - +
+
+
+ 908 + +
+ - + when: +
+
+ 909 + +
+ - + def stdout = runTasksSuccessfully('compileAllErrorProne', '--dry-run').output +
+
+ 910 + +
+ - +
+
+
+ 911 + +
+ - + then: +
+
+ 912 + +
+ - + stdout.contains(':compileJava SKIPPED') +
+
+ 913 + +
+ - + !stdout.contains(':compileTestJava SKIPPED') +
+
+ 914 + +
+ - + stdout.contains(':compileOtherJava SKIPPED') +
+
+ 915 + +
+ - + } +
+
+ 916 + +
+ - +
+
+
+ 917 + +
+ - + def 'SUGGESTION level checks are not suppressed'() { +
+
+ 918 + +
+ - + def originalBuildFile = buildFile.text +
+
+ 919 + +
+ - +
+
+
+ 920 + +
+ - + // The code below should hit the FieldCanBeFinal suggestion level check +
+
+ 921 + +
+ - + // language=Java +
+
+ 922 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 923 + +
+ - + package app; +
+
+ 924 + +
+ - + +
+
+ 925 + +
+ - + public final class App { +
+
+ 926 + +
+ - + private int field; +
+
+ 927 + +
+ - + public App() { +
+
+ 928 + +
+ - + this.field = 3; +
+
+ 929 + +
+ - + } +
+
+ 930 + +
+ - + public int getField() { +
+
+ 931 + +
+ - + return field; +
+
+ 932 + +
+ - + } +
+
+ 933 + +
+ - + } +
+
+ 934 + +
+ - + '''.stripIndent(true) +
+
+ 935 + +
+ - +
+
+
+ 936 + +
+ - + when: 'a suggestion check is made error level' +
+
+ 937 + +
+ - + // language=Gradle +
+
+ 938 + +
+ - + buildFile << ''' +
+
+ 939 + +
+ - + tasks.withType(JavaCompile).configureEach { +
+
+ 940 + +
+ - + options.errorprone.error('FieldCanBeFinal') +
+
+ 941 + +
+ - + } +
+
+ 942 + +
+ - + '''.stripIndent(true) +
+
+ 943 + +
+ - +
+
+
+ 944 + +
+ - + then: 'it causes the test code to fail compilation, confirming the check is being run on the code' +
+
+ 945 + +
+ - + def stderr = runTasksWithFailure('compileAllErrorProne').output +
+
+ 946 + +
+ - + stderr.contains('[FieldCanBeFinal]') +
+
+ 947 + +
+ - +
+
+
+ 948 + +
+ - + when: 'the check is run at the default SUGGESTION level, and then automated suppressions are not applied' +
+
+ 949 + +
+ - + buildFile.text = originalBuildFile +
+
+ 950 + +
+ - + // language=Gradle +
+
+ 951 + +
+ - + buildFile << ''' +
+
+ 952 + +
+ - + tasks.withType(JavaCompile).configureEach { +
+
+ 953 + +
+ - + // This is disabled by default in error-prone, so enable it +
+
+ 954 + +
+ - + // https://github.com/google/error-prone/blob/04f05c24882152d3c84f4caf9345efd15859b928/core/src/main/java/com/google/errorprone/scanner/BuiltInCheckerSuppliers.java#L1191 +
+
+ 955 + +
+ - + options.errorprone.enable('FieldCanBeFinal') +
+
+ 956 + +
+ - + } +
+
+ 957 + +
+ - + '''.stripIndent(true) +
+
+ 958 + +
+ - +
+
+
+ 959 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 960 + +
+ - +
+
+
+ 961 + +
+ - + then: 'it is not suppressed' +
+
+ 962 + +
+ - + javaSourceDoesNotContain("SuppressWarnings") +
+
+ 963 + +
+ - + } +
+
+ 964 + +
+ - +
+
+
+ 965 + +
+ - + def 'WARNING level checks are suppressed'() { +
+
+ 966 + +
+ - + when: 'the check is at warning level' +
+
+ 967 + +
+ - + // language=Gradle +
+
+ 968 + +
+ - + buildFile << ''' +
+
+ 969 + +
+ - + tasks.withType(JavaCompile).configureEach { +
+
+ 970 + +
+ - + options.errorprone.warn('ArrayToString') +
+
+ 971 + +
+ - + } +
+
+ 972 + +
+ - + '''.stripIndent(true) +
+
+ 973 + +
+ - +
+
+
+ 974 + +
+ - + // The code below should hit the LongDoubleConversion warning level check +
+
+ 975 + +
+ - + // language=Java +
+
+ 976 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 977 + +
+ - + package app; +
+
+ 978 + +
+ - + +
+
+ 979 + +
+ - + public final class App { +
+
+ 980 + +
+ - + public static void main(String... args) { +
+
+ 981 + +
+ - + new int[3].toString(); +
+
+ 982 + +
+ - + } +
+
+ 983 + +
+ - + } +
+
+ 984 + +
+ - + '''.stripIndent(true) +
+
+ 985 + +
+ - +
+
+
+ 986 + +
+ - + then: 'compilation does not fail' +
+
+ 987 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ 988 + +
+ - +
+
+
+ 989 + +
+ - + when: 'the check is run at the default WARNING level, and then automated suppressions are applied' +
+
+ 990 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 991 + +
+ - +
+
+
+ 992 + +
+ - + then: 'it is suppressed' +
+
+ 993 + +
+ - + // language=Java +
+
+ 994 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 995 + +
+ - + package app; +
+
+ 996 + +
+ - + +
+
+ 997 + +
+ - + public final class App { +
+
+ 998 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 999 + +
+ - + public static void main(String... args) { +
+
+ 1000 + +
+ - + new int[3].toString(); +
+
+ 1001 + +
+ - + } +
+
+ 1002 + +
+ - + } +
+
+ 1003 + +
+ - + '''.stripIndent(true) +
+
+ 1004 + +
+ - + } +
+
+ 1005 + +
+ - +
+
+
+ 1006 + +
+ - + def 'makes no changes when there is an error on an import'() { +
+
+ 1007 + +
+ - + when: 'theres an illegal import' +
+
+ 1008 + +
+ - + // language=Java +
+
+ 1009 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1010 + +
+ - + package app; +
+
+ 1011 + +
+ - + public class A { +
+
+ 1012 + +
+ - + public static class Inner {} +
+
+ 1013 + +
+ - + } +
+
+ 1014 + +
+ - + '''.stripIndent(true) +
+
+ 1015 + +
+ - +
+
+
+ 1016 + +
+ - + // language=Java +
+
+ 1017 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1018 + +
+ - + package app; +
+
+ 1019 + +
+ - + public class B extends A {} +
+
+ 1020 + +
+ - + '''.stripIndent(true) +
+
+ 1021 + +
+ - +
+
+
+ 1022 + +
+ - + // This below hits the NonCanonicalStaticImport as it should refer to app.A.Inner, not app.B.Inner +
+
+ 1023 + +
+ - + // language=Java +
+
+ 1024 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1025 + +
+ - + package app; +
+
+ 1026 + +
+ - + import static app.B.Inner; +
+
+ 1027 + +
+ - + public final class App {} +
+
+ 1028 + +
+ - + '''.stripIndent(true) +
+
+ 1029 + +
+ - +
+
+
+ 1030 + +
+ - + then: 'compilation fails' +
+
+ 1031 + +
+ - + def stderr = runTasksWithFailure('compileAllErrorProne').output +
+
+ 1032 + +
+ - + stderr.contains('[NonCanonicalStaticImport]') +
+
+ 1033 + +
+ - +
+
+
+ 1034 + +
+ - + when: 'we try to suppress it' +
+
+ 1035 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 1036 + +
+ - +
+
+
+ 1037 + +
+ - + then: 'nothing has changed as we cant put SuppressWarnings on an import' +
+
+ 1038 + +
+ - + def stderr2 = runTasksWithFailure('compileAllErrorProne').output +
+
+ 1039 + +
+ - + stderr2.contains('[NonCanonicalStaticImport]') +
+
+ 1040 + +
+ - +
+
+
+ 1041 + +
+ - + // language=Java +
+
+ 1042 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1043 + +
+ - + package app; +
+
+ 1044 + +
+ - + +
+
+ 1045 + +
+ - + import static app.B.Inner; +
+
+ 1046 + +
+ - + +
+
+ 1047 + +
+ - + public final class App {} +
+
+ 1048 + +
+ - + '''.stripIndent(true) +
+
+ 1049 + +
+ - + } +
+
+ 1050 + +
+ - +
+
+
+ 1051 + +
+ - + def 'timings are outputted'() { +
+
+ 1052 + +
+ - + // language=Java +
+
+ 1053 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1054 + +
+ - + package app; +
+
+ 1055 + +
+ - + +
+
+ 1056 + +
+ - + public final class App { +
+
+ 1057 + +
+ - + public static void main(String... args) {} +
+
+ 1058 + +
+ - + } +
+
+ 1059 + +
+ - + '''.stripIndent(true) +
+
+ 1060 + +
+ - +
+
+
+ 1061 + +
+ - + when: 'a compilation happens but -PerrorProneTimings is not applied' +
+
+ 1062 + +
+ - + runTasksSuccessfully('compileAllErrorProne') +
+
+ 1063 + +
+ - +
+
+
+ 1064 + +
+ - + then: 'timings are not outputted' +
+
+ 1065 + +
+ - + !new File(projectDir, 'build/errorprone-timings/compileJava').exists() +
+
+ 1066 + +
+ - + !new File(projectDir, 'build/errorprone-timings/compileOtherJava').exists() +
+
+ 1067 + +
+ - +
+
+
+ 1068 + +
+ - + when: 'a compilation happens and -PerrorProneTimings is applied' +
+
+ 1069 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneTimings') +
+
+ 1070 + +
+ - +
+
+
+ 1071 + +
+ - + then: 'timings are outputted' +
+
+ 1072 + +
+ - + new File(projectDir, 'build/errorprone-timings/compileJava').exists() +
+
+ 1073 + +
+ - + new File(projectDir, 'build/errorprone-timings/compileOtherJava').exists() +
+
+ 1074 + +
+ - + } +
+
+ 1075 + +
+ - +
+
+
+ 1076 + +
+ - + @Unroll +
+
+ 1077 + +
+ - + def 'compile tasks are never up-to-date when applying changes under #mode'() { +
+
+ 1078 + +
+ - + // language=Gradle +
+
+ 1079 + +
+ - + buildFile << ''' +
+
+ 1080 + +
+ - + suppressibleErrorProne { +
+
+ 1081 + +
+ - + patchChecks.add('ArrayToString') +
+
+ 1082 + +
+ - + } +
+
+ 1083 + +
+ - + '''.stripIndent(true) +
+
+ 1084 + +
+ - +
+
+
+ 1085 + +
+ - + // language=Java +
+
+ 1086 + +
+ - + def originalSource = ''' +
+
+ 1087 + +
+ - + package app; +
+
+ 1088 + +
+ - + +
+
+ 1089 + +
+ - + public final class App { +
+
+ 1090 + +
+ - + public static void main(String... args) { +
+
+ 1091 + +
+ - + new int[3].toString(); +
+
+ 1092 + +
+ - + } +
+
+ 1093 + +
+ - + } +
+
+ 1094 + +
+ - + '''.stripIndent(true) +
+
+ 1095 + +
+ - +
+
+
+ 1096 + +
+ - + writeJavaSourceFileToSourceSets originalSource +
+
+ 1097 + +
+ - +
+
+
+ 1098 + +
+ - + when: 'a compilation with code changes happens' +
+
+ 1099 + +
+ - + runTasksSuccessfully('compileAllErrorProne', *mode) +
+
+ 1100 + +
+ - +
+
+
+ 1101 + +
+ - + then: 'the source code is reset back to the original state' +
+
+ 1102 + +
+ - + writeJavaSourceFileToSourceSets originalSource +
+
+ 1103 + +
+ - +
+
+
+ 1104 + +
+ - + when: 'compilation with changes runs again' +
+
+ 1105 + +
+ - + runTasksSuccessfully('compileAllErrorProne', *mode) +
+
+ 1106 + +
+ - +
+
+
+ 1107 + +
+ - + then: 'changes are actually made, it was not up-to-date' +
+
+ 1108 + +
+ - + javaSourceIsSyntacticallyNotEqualTo originalSource +
+
+ 1109 + +
+ - +
+
+
+ 1110 + +
+ - + where: +
+
+ 1111 + +
+ - + mode << [ +
+
+ 1112 + +
+ - + ['-PerrorProneApply'], +
+
+ 1113 + +
+ - + ['-PerrorProneSuppress'], +
+
+ 1114 + +
+ - + ['-PerrorProneApply', '-PerrorProneSuppress'] +
+
+ 1115 + +
+ - + ] +
+
+ 1116 + +
+ - + } +
+
+ 1117 + +
+ - +
+
+
+ 1118 + +
+ - + def 'throws exception when -PerrorProneDisable is combined with -PerrorProneApply or -PerrorProneSuppress'() { +
+
+ 1119 + +
+ - + when: +
+
+ 1120 + +
+ - + def applyOutput = runTasksWithFailure('compileAllErrorProne', '-PerrorProneDisable', '-PerrorProneApply').output +
+
+ 1121 + +
+ - +
+
+
+ 1122 + +
+ - + then: +
+
+ 1123 + +
+ - + applyOutput.contains '-PerrorProneDisable cannot be used' +
+
+ 1124 + +
+ - +
+
+
+ 1125 + +
+ - + when: +
+
+ 1126 + +
+ - + def suppressOutput = runTasksWithFailure('compileAllErrorProne', '-PerrorProneDisable', '-PerrorProneSuppress').output +
+
+ 1127 + +
+ - +
+
+
+ 1128 + +
+ - + then: +
+
+ 1129 + +
+ - + suppressOutput.contains '-PerrorProneDisable cannot be used' +
+
+ 1130 + +
+ - + } +
+
+ 1131 + +
+ - +
+
+
+ 1132 + +
+ - + // This test also verifies we're properly passing the arguments to the errorprone plugin +
+
+ 1133 + +
+ - + def 'supports removing specific error prone suppressions'() { +
+
+ 1134 + +
+ - + // language=Java +
+
+ 1135 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1136 + +
+ - + package app; +
+
+ 1137 + +
+ - + +
+
+ 1138 + +
+ - + @SuppressWarnings("for-rollout:Test") +
+
+ 1139 + +
+ - + public final class App {} +
+
+ 1140 + +
+ - + '''.stripIndent(true) +
+
+ 1141 + +
+ - +
+
+
+ 1142 + +
+ - + when: +
+
+ 1143 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=Test') +
+
+ 1144 + +
+ - +
+
+
+ 1145 + +
+ - + then: +
+
+ 1146 + +
+ - + // language=Java +
+
+ 1147 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1148 + +
+ - + package app; +
+
+ 1149 + +
+ - + +
+
+ 1150 + +
+ - + public final class App {} +
+
+ 1151 + +
+ - + '''.stripIndent(true) +
+
+ 1152 + +
+ - + } +
+
+ 1153 + +
+ - +
+
+
+ 1154 + +
+ - + // This test also verifies we're properly passing the arguments to the errorprone plugin +
+
+ 1155 + +
+ - + def 'supports removing all error prone suppressions'() { +
+
+ 1156 + +
+ - + // language=Java +
+
+ 1157 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1158 + +
+ - + package app; +
+
+ 1159 + +
+ - + +
+
+ 1160 + +
+ - + // We can remove entire lines +
+
+ 1161 + +
+ - + @SuppressWarnings("for-rollout:Test") +
+
+ 1162 + +
+ - + public final class App { +
+
+ 1163 + +
+ - + // We keep non-rollout suppressions untouched +
+
+ 1164 + +
+ - + @SuppressWarnings({"for-rollout:Test", "Test"}) +
+
+ 1165 + +
+ - + void nested() {} +
+
+ 1166 + +
+ - + } +
+
+ 1167 + +
+ - + '''.stripIndent(true) +
+
+ 1168 + +
+ - +
+
+
+ 1169 + +
+ - + when: +
+
+ 1170 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout') +
+
+ 1171 + +
+ - +
+
+
+ 1172 + +
+ - + then: +
+
+ 1173 + +
+ - + // language=Java +
+
+ 1174 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1175 + +
+ - + package app; +
+
+ 1176 + +
+ - + +
+
+ 1177 + +
+ - + // We can remove entire lines +
+
+ 1178 + +
+ - + public final class App { +
+
+ 1179 + +
+ - + // We keep non-rollout suppressions untouched +
+
+ 1180 + +
+ - + @SuppressWarnings("Test") +
+
+ 1181 + +
+ - + void nested() {} +
+
+ 1182 + +
+ - + } +
+
+ 1183 + +
+ - + '''.stripIndent(true) +
+
+ 1184 + +
+ - + } +
+
+ 1185 + +
+ - +
+
+
+ 1186 + +
+ - + // This test also verifies we're properly passing the arguments to the errorprone plugin +
+
+ 1187 + +
+ - + def 'does not remove suppressions other than requested'() { +
+
+ 1188 + +
+ - + // language=Java +
+
+ 1189 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1190 + +
+ - + package app; +
+
+ 1191 + +
+ - + +
+
+ 1192 + +
+ - + @SuppressWarnings("for-rollout:Test") +
+
+ 1193 + +
+ - + public final class App {} +
+
+ 1194 + +
+ - + '''.stripIndent(true) +
+
+ 1195 + +
+ - +
+
+
+ 1196 + +
+ - + when: +
+
+ 1197 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=Other') +
+
+ 1198 + +
+ - +
+
+
+ 1199 + +
+ - + then: +
+
+ 1200 + +
+ - + // language=Java +
+
+ 1201 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1202 + +
+ - + package app; +
+
+ 1203 + +
+ - + +
+
+ 1204 + +
+ - + @SuppressWarnings("for-rollout:Test") +
+
+ 1205 + +
+ - + public final class App {} +
+
+ 1206 + +
+ - + '''.stripIndent(true) +
+
+ 1207 + +
+ - + } +
+
+ 1208 + +
+ - +
+
+
+ 1209 + +
+ - + def 'does not suppress RemoveRolloutSuppressions'() { +
+
+ 1210 + +
+ - + // language=Java +
+
+ 1211 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1212 + +
+ - + package app; +
+
+ 1213 + +
+ - + +
+
+ 1214 + +
+ - + @SuppressWarnings("for-rollout:Test") +
+
+ 1215 + +
+ - + public final class App {} +
+
+ 1216 + +
+ - + '''.stripIndent(true) +
+
+ 1217 + +
+ - +
+
+
+ 1218 + +
+ - + when: +
+
+ 1219 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress') +
+
+ 1220 + +
+ - +
+
+
+ 1221 + +
+ - + then: +
+
+ 1222 + +
+ - + // language=Java +
+
+ 1223 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1224 + +
+ - + package app; +
+
+ 1225 + +
+ - + +
+
+ 1226 + +
+ - + @SuppressWarnings("for-rollout:Test") +
+
+ 1227 + +
+ - + public final class App {} +
+
+ 1228 + +
+ - + '''.stripIndent(true) +
+
+ 1229 + +
+ - + } +
+
+ 1230 + +
+ - +
+
+
+ 1231 + +
+ - + def 'RemoveRolloutSuppressions can remove itself'() { +
+
+ 1232 + +
+ - + // language=Java +
+
+ 1233 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1234 + +
+ - + package app; +
+
+ 1235 + +
+ - + +
+
+ 1236 + +
+ - + @SuppressWarnings("for-rollout:RemoveRolloutSuppressions") +
+
+ 1237 + +
+ - + public final class App {} +
+
+ 1238 + +
+ - + '''.stripIndent(true) +
+
+ 1239 + +
+ - +
+
+
+ 1240 + +
+ - + when: +
+
+ 1241 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=RemoveRolloutSuppressions') +
+
+ 1242 + +
+ - +
+
+
+ 1243 + +
+ - + then: +
+
+ 1244 + +
+ - + // language=Java +
+
+ 1245 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1246 + +
+ - + package app; +
+
+ 1247 + +
+ - + +
+
+ 1248 + +
+ - + public final class App {} +
+
+ 1249 + +
+ - + '''.stripIndent(true) +
+
+ 1250 + +
+ - + } +
+
+ 1251 + +
+ - +
+
+
+ 1252 + +
+ - + def 'can patch checks while using -PerrorProneRemoveRollout, even if suppressed for rollout'() { +
+
+ 1253 + +
+ - + // language=Java +
+
+ 1254 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1255 + +
+ - + package app; +
+
+ 1256 + +
+ - + +
+
+ 1257 + +
+ - + public final class App { +
+
+ 1258 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 1259 + +
+ - + public static void main(String[] args) { +
+
+ 1260 + +
+ - + new int[3].toString(); +
+
+ 1261 + +
+ - + } +
+
+ 1262 + +
+ - + } +
+
+ 1263 + +
+ - + '''.stripIndent(true) +
+
+ 1264 + +
+ - +
+
+
+ 1265 + +
+ - + when: +
+
+ 1266 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ArrayToString', '-PerrorProneApply=ArrayToString') +
+
+ 1267 + +
+ - +
+
+
+ 1268 + +
+ - + then: +
+
+ 1269 + +
+ - + // language=Java +
+
+ 1270 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1271 + +
+ - + package app; +
+
+ 1272 + +
+ - +
+
+
+ 1273 + +
+ - + import java.util.Arrays; +
+
+ 1274 + +
+ - + +
+
+ 1275 + +
+ - + public final class App { +
+
+ 1276 + +
+ - + public static void main(String[] args) { +
+
+ 1277 + +
+ - + Arrays.toString(new int[3]); +
+
+ 1278 + +
+ - + } +
+
+ 1279 + +
+ - + } +
+
+ 1280 + +
+ - + '''.stripIndent(true) +
+
+ 1281 + +
+ - + } +
+
+ 1282 + +
+ - +
+
+
+ 1283 + +
+ - + def 'can patch checks while using -PerrorProneRemoveRollout, which also add annotations as fixes'() { +
+
+ 1284 + +
+ - + // language=Java +
+
+ 1285 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1286 + +
+ - + package app; +
+
+ 1287 + +
+ - +
+
+
+ 1288 + +
+ - + public final class App { +
+
+ 1289 + +
+ - + @SuppressWarnings("for-rollout:ShouldBeNullable") +
+
+ 1290 + +
+ - + private Object fixme() { +
+
+ 1291 + +
+ - + return null; +
+
+ 1292 + +
+ - + } +
+
+ 1293 + +
+ - +
+
+
+ 1294 + +
+ - + @SuppressWarnings("for-rollout:ShouldBeNullable") +
+
+ 1295 + +
+ - + private Object fixme(Object andMySuppressionHasWhiteSpaceAfterIt) { +
+
+ 1296 + +
+ - + return null; +
+
+ 1297 + +
+ - + } +
+
+ 1298 + +
+ - + +
+
+ 1299 + +
+ - + @SuppressWarnings({"for-rollout:ShouldBePrivate", "for-rollout:ShouldBeNullable"}) +
+
+ 1300 + +
+ - + Integer fixme(Integer i) { +
+
+ 1301 + +
+ - + return null; +
+
+ 1302 + +
+ - + } +
+
+ 1303 + +
+ - + } +
+
+ 1304 + +
+ - + '''.stripIndent(true) +
+
+ 1305 + +
+ - + when: +
+
+ 1306 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ShouldBeNullable,ShouldBePrivate', '-PerrorProneApply=ShouldBeNullable,ShouldBePrivate') +
+
+ 1307 + +
+ - +
+
+
+ 1308 + +
+ - + then: +
+
+ 1309 + +
+ - + // language=Java +
+
+ 1310 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1311 + +
+ - + package app; +
+
+ 1312 + +
+ - +
+
+
+ 1313 + +
+ - + import javax.annotation.Nullable; +
+
+ 1314 + +
+ - +
+
+
+ 1315 + +
+ - + public final class App { +
+
+ 1316 + +
+ - + @Nullable +
+
+ 1317 + +
+ - + private Object fixme() { +
+
+ 1318 + +
+ - + return null; +
+
+ 1319 + +
+ - + } +
+
+ 1320 + +
+ - +
+
+
+ 1321 + +
+ - + @Nullable +
+
+ 1322 + +
+ - + private Object fixme(Object andMySuppressionHasWhiteSpaceAfterIt) { +
+
+ 1323 + +
+ - + return null; +
+
+ 1324 + +
+ - + } +
+
+ 1325 + +
+ - + +
+
+ 1326 + +
+ - + @Nullable +
+
+ 1327 + +
+ - + private Integer fixme(Integer i) { +
+
+ 1328 + +
+ - + return null; +
+
+ 1329 + +
+ - + } +
+
+ 1330 + +
+ - + } +
+
+ 1331 + +
+ - + '''.stripIndent(true) +
+
+ 1332 + +
+ - + } +
+
+ 1333 + +
+ - +
+
+
+ 1334 + +
+ - + def '-PerrorProneSuppress then -PerrorProneRemoveRollout does not add newlines'() { +
+
+ 1335 + +
+ - + // language=Java +
+
+ 1336 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1337 + +
+ - + package app; +
+
+ 1338 + +
+ - +
+
+
+ 1339 + +
+ - + public final class App { +
+
+ 1340 + +
+ - + private Object fixme() { +
+
+ 1341 + +
+ - + return null; +
+
+ 1342 + +
+ - + } +
+
+ 1343 + +
+ - + } +
+
+ 1344 + +
+ - + '''.stripIndent(true) +
+
+ 1345 + +
+ - + when: +
+
+ 1346 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneSuppress=ShouldBeNullable') +
+
+ 1347 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ShouldBeNullable') +
+
+ 1348 + +
+ - +
+
+
+ 1349 + +
+ - + then: +
+
+ 1350 + +
+ - + // language=Java +
+
+ 1351 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1352 + +
+ - + package app; +
+
+ 1353 + +
+ - +
+
+
+ 1354 + +
+ - + public final class App { +
+
+ 1355 + +
+ - + private Object fixme() { +
+
+ 1356 + +
+ - + return null; +
+
+ 1357 + +
+ - + } +
+
+ 1358 + +
+ - + } +
+
+ 1359 + +
+ - + '''.stripIndent(true) +
+
+ 1360 + +
+ - + } +
+
+ 1361 + +
+ - +
+
+
+ 1362 + +
+ - +
+
+
+ 1363 + +
+ - + def 'does not patch checks while using -PerrorProneRemoveRollout, if suppressed normally'() { +
+
+ 1364 + +
+ - + // language=Java +
+
+ 1365 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1366 + +
+ - + package app; +
+
+ 1367 + +
+ - + +
+
+ 1368 + +
+ - + public final class App { +
+
+ 1369 + +
+ - + @SuppressWarnings("ArrayToString") +
+
+ 1370 + +
+ - + public static void main(String[] args) { +
+
+ 1371 + +
+ - + new int[3].toString(); +
+
+ 1372 + +
+ - + } +
+
+ 1373 + +
+ - + } +
+
+ 1374 + +
+ - + '''.stripIndent(true) +
+
+ 1375 + +
+ - +
+
+
+ 1376 + +
+ - + when: +
+
+ 1377 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ArrayToString', '-PerrorProneApply=ArrayToString') +
+
+ 1378 + +
+ - +
+
+
+ 1379 + +
+ - + then: +
+
+ 1380 + +
+ - + // language=Java +
+
+ 1381 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1382 + +
+ - + package app; +
+
+ 1383 + +
+ - + +
+
+ 1384 + +
+ - + public final class App { +
+
+ 1385 + +
+ - + @SuppressWarnings("ArrayToString") +
+
+ 1386 + +
+ - + public static void main(String[] args) { +
+
+ 1387 + +
+ - + new int[3].toString(); +
+
+ 1388 + +
+ - + } +
+
+ 1389 + +
+ - + } +
+
+ 1390 + +
+ - + '''.stripIndent(true) +
+
+ 1391 + +
+ - + } +
+
+ 1392 + +
+ - +
+
+
+ 1393 + +
+ - + def 'can patch checks while using -PerrorProneRemoveRollout, if not suppressed'() { +
+
+ 1394 + +
+ - + // language=Java +
+
+ 1395 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1396 + +
+ - + package app; +
+
+ 1397 + +
+ - + +
+
+ 1398 + +
+ - + public final class App { +
+
+ 1399 + +
+ - + public static void main(String[] args) { +
+
+ 1400 + +
+ - + new int[3].toString(); +
+
+ 1401 + +
+ - + } +
+
+ 1402 + +
+ - + } +
+
+ 1403 + +
+ - + '''.stripIndent(true) +
+
+ 1404 + +
+ - +
+
+
+ 1405 + +
+ - + when: +
+
+ 1406 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ArrayToString', '-PerrorProneApply=ArrayToString') +
+
+ 1407 + +
+ - +
+
+
+ 1408 + +
+ - + then: +
+
+ 1409 + +
+ - + // language=Java +
+
+ 1410 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1411 + +
+ - + package app; +
+
+ 1412 + +
+ - +
+
+
+ 1413 + +
+ - + import java.util.Arrays; +
+
+ 1414 + +
+ - + +
+
+ 1415 + +
+ - + public final class App { +
+
+ 1416 + +
+ - + public static void main(String[] args) { +
+
+ 1417 + +
+ - + Arrays.toString(new int[3]); +
+
+ 1418 + +
+ - + } +
+
+ 1419 + +
+ - + } +
+
+ 1420 + +
+ - + '''.stripIndent(true) +
+
+ 1421 + +
+ - + } +
+
+ 1422 + +
+ - +
+
+
+ 1423 + +
+ - + def 'errorProneRemoveRollout does not patch unrelated checks'() { +
+
+ 1424 + +
+ - + // language=Java +
+
+ 1425 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1426 + +
+ - + package app; +
+
+ 1427 + +
+ - + +
+
+ 1428 + +
+ - + public final class App { +
+
+ 1429 + +
+ - + public static void main(String[] args) { +
+
+ 1430 + +
+ - + new int[3].toString(); +
+
+ 1431 + +
+ - + } +
+
+ 1432 + +
+ - + } +
+
+ 1433 + +
+ - + '''.stripIndent(true) +
+
+ 1434 + +
+ - +
+
+
+ 1435 + +
+ - + when: +
+
+ 1436 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=NullAway', '-PerrorProneApply=NullAway') +
+
+ 1437 + +
+ - +
+
+
+ 1438 + +
+ - + then: +
+
+ 1439 + +
+ - + // language=Java +
+
+ 1440 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1441 + +
+ - + package app; +
+
+ 1442 + +
+ - + +
+
+ 1443 + +
+ - + public final class App { +
+
+ 1444 + +
+ - + public static void main(String[] args) { +
+
+ 1445 + +
+ - + new int[3].toString(); +
+
+ 1446 + +
+ - + } +
+
+ 1447 + +
+ - + } +
+
+ 1448 + +
+ - + '''.stripIndent(true) +
+
+ 1449 + +
+ - + } +
+
+ 1450 + +
+ - +
+
+
+ 1451 + +
+ - + def 'errorProneRemoveRollout does not patch by itself'() { +
+
+ 1452 + +
+ - + // language=Java +
+
+ 1453 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1454 + +
+ - + package app; +
+
+ 1455 + +
+ - + +
+
+ 1456 + +
+ - + public final class App { +
+
+ 1457 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 1458 + +
+ - + public static void main(String[] args) { +
+
+ 1459 + +
+ - + new int[3].toString(); +
+
+ 1460 + +
+ - + } +
+
+ 1461 + +
+ - + } +
+
+ 1462 + +
+ - + '''.stripIndent(true) +
+
+ 1463 + +
+ - +
+
+
+ 1464 + +
+ - + when: +
+
+ 1465 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ArrayToString') +
+
+ 1466 + +
+ - +
+
+
+ 1467 + +
+ - + then: +
+
+ 1468 + +
+ - + // language=Java +
+
+ 1469 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1470 + +
+ - + package app; +
+
+ 1471 + +
+ - + +
+
+ 1472 + +
+ - + public final class App { +
+
+ 1473 + +
+ - + public static void main(String[] args) { +
+
+ 1474 + +
+ - + new int[3].toString(); +
+
+ 1475 + +
+ - + } +
+
+ 1476 + +
+ - + } +
+
+ 1477 + +
+ - + '''.stripIndent(true) +
+
+ 1478 + +
+ - + } +
+
+ 1479 + +
+ - +
+
+
+ 1480 + +
+ - + def 'errorProneRemoveRollout does not patch if specific check is not selected'() { +
+
+ 1481 + +
+ - + // language=Java +
+
+ 1482 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1483 + +
+ - + package app; +
+
+ 1484 + +
+ - + @SuppressWarnings("for-rollout:Test") +
+
+ 1485 + +
+ - + public final class App { +
+
+ 1486 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 1487 + +
+ - + public static void main(String[] args) { +
+
+ 1488 + +
+ - + new int[3].toString(); +
+
+ 1489 + +
+ - + } +
+
+ 1490 + +
+ - + } +
+
+ 1491 + +
+ - + '''.stripIndent(true) +
+
+ 1492 + +
+ - +
+
+
+ 1493 + +
+ - + when: +
+
+ 1494 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout=ArrayToString,Test', '-PerrorProneApply=Test') +
+
+ 1495 + +
+ - +
+
+
+ 1496 + +
+ - + then: +
+
+ 1497 + +
+ - + // language=Java +
+
+ 1498 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1499 + +
+ - + package app; +
+
+ 1500 + +
+ - + +
+
+ 1501 + +
+ - + public final class App { +
+
+ 1502 + +
+ - + public static void main(String[] args) { +
+
+ 1503 + +
+ - + new int[3].toString(); +
+
+ 1504 + +
+ - + } +
+
+ 1505 + +
+ - + } +
+
+ 1506 + +
+ - + '''.stripIndent(true) +
+
+ 1507 + +
+ - + } +
+
+ 1508 + +
+ - +
+
+
+ 1509 + +
+ - + def 'can patch specific checks even if errorProneRemoveRollout argument is empty'() { +
+
+ 1510 + +
+ - + // language=Java +
+
+ 1511 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1512 + +
+ - + package app; +
+
+ 1513 + +
+ - + +
+
+ 1514 + +
+ - + public final class App { +
+
+ 1515 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 1516 + +
+ - + public static void main(String[] args) { +
+
+ 1517 + +
+ - + new int[3].toString(); +
+
+ 1518 + +
+ - + } +
+
+ 1519 + +
+ - + } +
+
+ 1520 + +
+ - + '''.stripIndent(true) +
+
+ 1521 + +
+ - +
+
+
+ 1522 + +
+ - + when: +
+
+ 1523 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout', '-PerrorProneApply=ArrayToString') +
+
+ 1524 + +
+ - +
+
+
+ 1525 + +
+ - + then: +
+
+ 1526 + +
+ - + // language=Java +
+
+ 1527 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1528 + +
+ - + package app; +
+
+ 1529 + +
+ - +
+
+
+ 1530 + +
+ - + import java.util.Arrays; +
+
+ 1531 + +
+ - + +
+
+ 1532 + +
+ - + public final class App { +
+
+ 1533 + +
+ - + public static void main(String[] args) { +
+
+ 1534 + +
+ - + Arrays.toString(new int[3]); +
+
+ 1535 + +
+ - + } +
+
+ 1536 + +
+ - + } +
+
+ 1537 + +
+ - + '''.stripIndent(true) +
+
+ 1538 + +
+ - + } +
+
+ 1539 + +
+ - +
+
+
+ 1540 + +
+ - + def 'can patch configured checks even if errorProneRemoveRollout argument is empty'() { +
+
+ 1541 + +
+ - + // language=Gradle +
+
+ 1542 + +
+ - + buildFile << ''' +
+
+ 1543 + +
+ - + suppressibleErrorProne { +
+
+ 1544 + +
+ - + patchChecks.add('ArrayToString') +
+
+ 1545 + +
+ - + } +
+
+ 1546 + +
+ - + '''.stripIndent(true) +
+
+ 1547 + +
+ - +
+
+
+ 1548 + +
+ - + // language=Java +
+
+ 1549 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1550 + +
+ - + package app; +
+
+ 1551 + +
+ - + +
+
+ 1552 + +
+ - + public final class App { +
+
+ 1553 + +
+ - + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 1554 + +
+ - + public static void main(String[] args) { +
+
+ 1555 + +
+ - + new int[3].toString(); +
+
+ 1556 + +
+ - + } +
+
+ 1557 + +
+ - + } +
+
+ 1558 + +
+ - + '''.stripIndent(true) +
+
+ 1559 + +
+ - +
+
+
+ 1560 + +
+ - + when: +
+
+ 1561 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveRollout', '-PerrorProneApply') +
+
+ 1562 + +
+ - +
+
+
+ 1563 + +
+ - + then: +
+
+ 1564 + +
+ - + // language=Java +
+
+ 1565 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1566 + +
+ - + package app; +
+
+ 1567 + +
+ - +
+
+
+ 1568 + +
+ - + import java.util.Arrays; +
+
+ 1569 + +
+ - + +
+
+ 1570 + +
+ - + public final class App { +
+
+ 1571 + +
+ - + public static void main(String[] args) { +
+
+ 1572 + +
+ - + Arrays.toString(new int[3]); +
+
+ 1573 + +
+ - + } +
+
+ 1574 + +
+ - + } +
+
+ 1575 + +
+ - + '''.stripIndent(true) +
+
+ 1576 + +
+ - + } +
+
+ 1577 + +
+ - +
+
+
+ 1578 + +
+ - + def 'RemoveRolloutSuppressions does not appear as a Note: [RemoveRolloutSuppressions] in unrelated errors'() { +
+
+ 1579 + +
+ - + // language=Java +
+
+ 1580 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1581 + +
+ - + package app; +
+
+ 1582 + +
+ - + +
+
+ 1583 + +
+ - + public final class App { +
+
+ 1584 + +
+ - + @SuppressWarnings("for-rollout:NullAway") +
+
+ 1585 + +
+ - + public static void method() { +
+
+ 1586 + +
+ - + new int[3].toString(); +
+
+ 1587 + +
+ - + } +
+
+ 1588 + +
+ - + } +
+
+ 1589 + +
+ - + '''.stripIndent(true) +
+
+ 1590 + +
+ - +
+
+
+ 1591 + +
+ - + when: +
+
+ 1592 + +
+ - + def output = runTasksWithFailure('compileAllErrorProne').output +
+
+ 1593 + +
+ - +
+
+
+ 1594 + +
+ - + then: +
+
+ 1595 + +
+ - + !output.contains('[RemoveRolloutSuppressions]') +
+
+ 1596 + +
+ - + } +
+
+ 1597 + +
+ - +
+
+
+ 1598 + +
+ - + def 'errorProneRemoveUnused removes only unused error-prone suppressions, and leaves unknown suppressions untouched'() { +
+
+ 1599 + +
+ - + // language=Java +
+
+ 1600 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1601 + +
+ - + package app; +
+
+ 1602 + +
+ - +
+
+
+ 1603 + +
+ - + @SuppressWarnings({"ArrayToString", "UnnecessaryFinal", "InlineTrivialConstant", "NotAnErrorProne", "checkstyle:Bla",}) +
+
+ 1604 + +
+ - + public final class App { +
+
+ 1605 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1606 + +
+ - + +
+
+ 1607 + +
+ - + public static void main(String[] args) { +
+
+ 1608 + +
+ - + new int[3].toString(); +
+
+ 1609 + +
+ - + } +
+
+ 1610 + +
+ - + } +
+
+ 1611 + +
+ - + '''.stripIndent(true) +
+
+ 1612 + +
+ - +
+
+
+ 1613 + +
+ - + when: +
+
+ 1614 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused=ArrayToString') +
+
+ 1615 + +
+ - +
+
+
+ 1616 + +
+ - + then: +
+
+ 1617 + +
+ - + // language=Java +
+
+ 1618 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1619 + +
+ - + package app; +
+
+ 1620 + +
+ - +
+
+
+ 1621 + +
+ - + @SuppressWarnings({"ArrayToString", "InlineTrivialConstant", "NotAnErrorProne", "checkstyle:Bla"}) +
+
+ 1622 + +
+ - + public final class App { +
+
+ 1623 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1624 + +
+ - + +
+
+ 1625 + +
+ - + public static void main(String[] args) { +
+
+ 1626 + +
+ - + new int[3].toString(); +
+
+ 1627 + +
+ - + } +
+
+ 1628 + +
+ - + } +
+
+ 1629 + +
+ - + '''.stripIndent(true) +
+
+ 1630 + +
+ - + } +
+
+ 1631 + +
+ - +
+
+
+ 1632 + +
+ - + def 'errorProneRemoveUnused does not apply fixes'() { +
+
+ 1633 + +
+ - + given: +
+
+ 1634 + +
+ - + // language=Java +
+
+ 1635 + +
+ - + def initialSource = ''' +
+
+ 1636 + +
+ - + package app; +
+
+ 1637 + +
+ - +
+
+
+ 1638 + +
+ - + public final class App { +
+
+ 1639 + +
+ - + class Inner { +
+
+ 1640 + +
+ - + class InnerInner {} +
+
+ 1641 + +
+ - + } +
+
+ 1642 + +
+ - + } +
+
+ 1643 + +
+ - + '''.stripIndent(true) +
+
+ 1644 + +
+ - +
+
+
+ 1645 + +
+ - + writeJavaSourceFileToSourceSets initialSource +
+
+ 1646 + +
+ - +
+
+
+ 1647 + +
+ - + when: 'errorProneRemoveUnused is run by itself' +
+
+ 1648 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused') +
+
+ 1649 + +
+ - +
+
+
+ 1650 + +
+ - + then: 'No checks are applied' +
+
+ 1651 + +
+ - + javaSourceIsSyntacticallyEqualTo initialSource +
+
+ 1652 + +
+ - +
+
+
+ 1653 + +
+ - + when: 'ClassCanBeStatic is applied alongside errorProneRemoveUnused' +
+
+ 1654 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused', '-PerrorProneApply=ClassCanBeStatic') +
+
+ 1655 + +
+ - +
+
+
+ 1656 + +
+ - + then: 'ClassCanBeStatic is applied' +
+
+ 1657 + +
+ - + // language=Java +
+
+ 1658 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1659 + +
+ - + package app; +
+
+ 1660 + +
+ - +
+
+
+ 1661 + +
+ - + public final class App { +
+
+ 1662 + +
+ - + static class Inner { +
+
+ 1663 + +
+ - + static class InnerInner {} +
+
+ 1664 + +
+ - + } +
+
+ 1665 + +
+ - + } +
+
+ 1666 + +
+ - + '''.stripIndent(true) +
+
+ 1667 + +
+ - + } +
+
+ 1668 + +
+ - +
+
+
+ 1669 + +
+ - + def 'errorProneRemoveUnused only keeps the closest suppression to a violation'() { +
+
+ 1670 + +
+ - + // language=Java +
+
+ 1671 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1672 + +
+ - + package app; +
+
+ 1673 + +
+ - +
+
+
+ 1674 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1675 + +
+ - + public final class App { +
+
+ 1676 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1677 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1678 + +
+ - + +
+
+ 1679 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1680 + +
+ - + class Inner { +
+
+ 1681 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1682 + +
+ - + class InnerInner { +
+
+ 1683 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1684 + +
+ - + class InnerInnerInner { +
+
+ 1685 + +
+ - + private static final String EMPTY = ""; +
+
+ 1686 + +
+ - + } +
+
+ 1687 + +
+ - + } +
+
+ 1688 + +
+ - + } +
+
+ 1689 + +
+ - + } +
+
+ 1690 + +
+ - + '''.stripIndent(true) +
+
+ 1691 + +
+ - +
+
+
+ 1692 + +
+ - + when: +
+
+ 1693 + +
+ - + def result = runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused') +
+
+ 1694 + +
+ - +
+
+
+ 1695 + +
+ - + then: +
+
+ 1696 + +
+ - +
+
+
+ 1697 + +
+ - + // language=Java +
+
+ 1698 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1699 + +
+ - + package app; +
+
+ 1700 + +
+ - +
+
+
+ 1701 + +
+ - + public final class App { +
+
+ 1702 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1703 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1704 + +
+ - + +
+
+ 1705 + +
+ - + class Inner { +
+
+ 1706 + +
+ - + class InnerInner { +
+
+ 1707 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1708 + +
+ - + class InnerInnerInner { +
+
+ 1709 + +
+ - + private static final String EMPTY = ""; +
+
+ 1710 + +
+ - + } +
+
+ 1711 + +
+ - + } +
+
+ 1712 + +
+ - + } +
+
+ 1713 + +
+ - + } +
+
+ 1714 + +
+ - + '''.stripIndent(true) +
+
+ 1715 + +
+ - + } +
+
+ 1716 + +
+ - +
+
+
+ 1717 + +
+ - + def 'errorProneRemoveUnused handles multiple suppressions on different tree types gracefully'() { +
+
+ 1718 + +
+ - + // Here we test the three types of trees you can suppress — ClassTree, MethodTree, VariableTree +
+
+ 1719 + +
+ - +
+
+
+ 1720 + +
+ - + // language=Java +
+
+ 1721 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1722 + +
+ - + package app; +
+
+ 1723 + +
+ - +
+
+
+ 1724 + +
+ - + @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) +
+
+ 1725 + +
+ - + public final class App { +
+
+ 1726 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1727 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1728 + +
+ - + +
+
+ 1729 + +
+ - + // Doesn't move an already existing suppression, even if it could be closer to the violation +
+
+ 1730 + +
+ - + @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) +
+
+ 1731 + +
+ - + static class Inner { +
+
+ 1732 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1733 + +
+ - + private static final String EMPTY = ""; +
+
+ 1734 + +
+ - + boolean truism = new int[3].equals(new int[3]); +
+
+ 1735 + +
+ - + +
+
+ 1736 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1737 + +
+ - + static class InnerInner { +
+
+ 1738 + +
+ - + @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) +
+
+ 1739 + +
+ - + void method() { +
+
+ 1740 + +
+ - + new int[3].equals(new int[3]); +
+
+ 1741 + +
+ - + } +
+
+ 1742 + +
+ - + } +
+
+ 1743 + +
+ - + } +
+
+ 1744 + +
+ - + } +
+
+ 1745 + +
+ - + '''.stripIndent(true) +
+
+ 1746 + +
+ - +
+
+
+ 1747 + +
+ - + when: +
+
+ 1748 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused') +
+
+ 1749 + +
+ - +
+
+
+ 1750 + +
+ - + then: +
+
+ 1751 + +
+ - +
+
+
+ 1752 + +
+ - + // language=Java +
+
+ 1753 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1754 + +
+ - + package app; +
+
+ 1755 + +
+ - +
+
+
+ 1756 + +
+ - + public final class App { +
+
+ 1757 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1758 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1759 + +
+ - +
+
+
+ 1760 + +
+ - + // Doesn't move an already existing suppression, even if it could be closer to the violation +
+
+ 1761 + +
+ - + @SuppressWarnings("ArrayEquals") +
+
+ 1762 + +
+ - + static class Inner { +
+
+ 1763 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1764 + +
+ - + private static final String EMPTY = ""; +
+
+ 1765 + +
+ - +
+
+
+ 1766 + +
+ - + boolean truism = new int[3].equals(new int[3]); +
+
+ 1767 + +
+ - +
+
+
+ 1768 + +
+ - + static class InnerInner { +
+
+ 1769 + +
+ - + @SuppressWarnings("ArrayEquals") +
+
+ 1770 + +
+ - + void method() { +
+
+ 1771 + +
+ - + new int[3].equals(new int[3]); +
+
+ 1772 + +
+ - + } +
+
+ 1773 + +
+ - + } +
+
+ 1774 + +
+ - + } +
+
+ 1775 + +
+ - + } +
+
+ 1776 + +
+ - + '''.stripIndent(true) +
+
+ 1777 + +
+ - + } +
+
+ 1778 + +
+ - +
+
+
+ 1779 + +
+ - + def 'errorProneRemoveUnused removes entire SuppressWarnings annotation when all suppressions are unused'() { +
+
+ 1780 + +
+ - + // language=Java +
+
+ 1781 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1782 + +
+ - + package app; +
+
+ 1783 + +
+ - +
+
+
+ 1784 + +
+ - + @SuppressWarnings({"UnusedVariable", "ArrayToString"}) +
+
+ 1785 + +
+ - + public final class App { +
+
+ 1786 + +
+ - + public static void main(String[] args) { +
+
+ 1787 + +
+ - + System.out.println("No violations here"); +
+
+ 1788 + +
+ - + } +
+
+ 1789 + +
+ - + } +
+
+ 1790 + +
+ - + '''.stripIndent(true) +
+
+ 1791 + +
+ - +
+
+
+ 1792 + +
+ - + when: +
+
+ 1793 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused') +
+
+ 1794 + +
+ - +
+
+
+ 1795 + +
+ - + then: +
+
+ 1796 + +
+ - + // language=Java +
+
+ 1797 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1798 + +
+ - + package app; +
+
+ 1799 + +
+ - +
+
+
+ 1800 + +
+ - + public final class App { +
+
+ 1801 + +
+ - + public static void main(String[] args) { +
+
+ 1802 + +
+ - + System.out.println("No violations here"); +
+
+ 1803 + +
+ - + } +
+
+ 1804 + +
+   + } +
+
+ 1805 + +
+ - + '''.stripIndent(true) +
+
+ 1806 + +
+ - + } +
+
+ 1807 + +
+ - +
+
+
+ 1808 + +
+ - + def 'errorProneRemoveUnused and errorProneSuppress uses existing suppressions if possible'() { +
+
+ 1809 + +
+ - + // language=Java +
+
+ 1810 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1811 + +
+ - + package app; +
+
+ 1812 + +
+ - +
+
+
+ 1813 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1814 + +
+ - + public final class App { +
+
+ 1815 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1816 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1817 + +
+ - + +
+
+ 1818 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1819 + +
+ - + static class Inner { +
+
+ 1820 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1821 + +
+ - + private static final String EMPTY = ""; +
+
+ 1822 + +
+ - + boolean truism = new int[3].equals(new int[3]); +
+
+ 1823 + +
+ - + +
+
+ 1824 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1825 + +
+ - + static class InnerInner { +
+
+ 1826 + +
+ - + @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) +
+
+ 1827 + +
+ - + void method() { +
+
+ 1828 + +
+ - + new int[3].equals(new int[3]); +
+
+ 1829 + +
+ - + } +
+
+ 1830 + +
+ - + } +
+
+ 1831 + +
+ - + } +
+
+ 1832 + +
+ - + } +
+
+ 1833 + +
+ - + '''.stripIndent(true) +
+
+ 1834 + +
+ - +
+
+
+ 1835 + +
+ - + when: +
+
+ 1836 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused', '-PerrorProneSuppress') +
+
+ 1837 + +
+ - +
+
+
+ 1838 + +
+ - + then: +
+
+ 1839 + +
+ - +
+
+
+ 1840 + +
+ - + // language=Java +
+
+ 1841 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1842 + +
+ - + package app; +
+
+ 1843 + +
+ - +
+
+
+ 1844 + +
+ - + public final class App { +
+
+ 1845 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1846 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1847 + +
+ - + +
+
+ 1848 + +
+ - + static class Inner { +
+
+ 1849 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1850 + +
+ - + private static final String EMPTY = ""; +
+
+ 1851 + +
+ - +
+
+
+ 1852 + +
+ - + @SuppressWarnings("for-rollout:ArrayEquals") +
+
+ 1853 + +
+ - + boolean truism = new int[3].equals(new int[3]); +
+
+ 1854 + +
+ - + +
+
+ 1855 + +
+ - + static class InnerInner { +
+
+ 1856 + +
+ - + @SuppressWarnings("ArrayEquals") +
+
+ 1857 + +
+ - + void method() { +
+
+ 1858 + +
+ - + new int[3].equals(new int[3]); +
+
+ 1859 + +
+ - + } +
+
+ 1860 + +
+ - + } +
+
+ 1861 + +
+ - + } +
+
+ 1862 + +
+ - + } +
+
+ 1863 + +
+ - + '''.stripIndent(true) +
+
+ 1864 + +
+ - + } +
+
+ 1865 + +
+ - +
+
+
+ 1866 + +
+ - + def 'errorProneRemoveUnused and errorProneApply applies fixes on previously suppressed elements'() { +
+
+ 1867 + +
+ - + // language=Java +
+
+ 1868 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1869 + +
+ - + package app; +
+
+ 1870 + +
+ - + @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) +
+
+ 1871 + +
+ - + public final class App { +
+
+ 1872 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1873 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1874 + +
+ - + +
+
+ 1875 + +
+ - + @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) +
+
+ 1876 + +
+ - + static class Inner { +
+
+ 1877 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1878 + +
+ - + private static final String EMPTY = ""; +
+
+ 1879 + +
+ - + boolean truism = new int[3].equals(new int[3]); +
+
+ 1880 + +
+ - + +
+
+ 1881 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1882 + +
+ - + static class InnerInner { +
+
+ 1883 + +
+ - + @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) +
+
+ 1884 + +
+ - + void method() { +
+
+ 1885 + +
+ - + new int[3].equals(new int[3]); +
+
+ 1886 + +
+ - + } +
+
+ 1887 + +
+ - + } +
+
+ 1888 + +
+ - + } +
+
+ 1889 + +
+ - + } +
+
+ 1890 + +
+ - + '''.stripIndent(true) +
+
+ 1891 + +
+ - +
+
+
+ 1892 + +
+ - + when: +
+
+ 1893 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused', '-PerrorProneApply=ArrayEquals') +
+
+ 1894 + +
+ - +
+
+
+ 1895 + +
+ - + then: +
+
+ 1896 + +
+ - +
+
+
+ 1897 + +
+ - + // language=Java +
+
+ 1898 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1899 + +
+ - + package app; +
+
+ 1900 + +
+ - +
+
+
+ 1901 + +
+ - + import java.util.Arrays; +
+
+ 1902 + +
+ - +
+
+
+ 1903 + +
+ - + public final class App { +
+
+ 1904 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1905 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1906 + +
+ - + +
+
+ 1907 + +
+ - + static class Inner { +
+
+ 1908 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1909 + +
+ - + private static final String EMPTY = ""; +
+
+ 1910 + +
+ - +
+
+
+ 1911 + +
+ - + boolean truism = Arrays.equals(new int[3], new int[3]); +
+
+ 1912 + +
+ - + +
+
+ 1913 + +
+ - + static class InnerInner { +
+
+ 1914 + +
+ - + void method() { +
+
+ 1915 + +
+ - + Arrays.equals(new int[3], new int[3]); +
+
+ 1916 + +
+ - + } +
+
+ 1917 + +
+ - + } +
+
+ 1918 + +
+ - + } +
+
+ 1919 + +
+ - + } +
+
+ 1920 + +
+ - + '''.stripIndent(true) +
+
+ 1921 + +
+ - + } +
+
+ 1922 + +
+ - +
+
+
+ 1923 + +
+ - + def 'errorProneRemoveUnused + errorProneApply + errorProneSuppress applies fixes and suppressions on previously suppressed elements'() { +
+
+ 1924 + +
+ - + // language=Java +
+
+ 1925 + +
+ - + writeJavaSourceFileToSourceSets ''' +
+
+ 1926 + +
+ - + package app; +
+
+ 1927 + +
+ - +
+
+
+ 1928 + +
+ - + @SuppressWarnings("ArrayEquals") +
+
+ 1929 + +
+ - + public final class App { +
+
+ 1930 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1931 + +
+ - + +
+
+ 1932 + +
+ - + // Although InlineTrivialConstant can be placed lower in the AST hierarchy, +
+
+ 1933 + +
+ - + // we preserve existing suppressions whenever possible rather than move suppressions around. +
+
+ 1934 + +
+ - + // Also, note that we don't add for-rollout here. +
+
+ 1935 + +
+ - + @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) +
+
+ 1936 + +
+ - + static class Inner { +
+
+ 1937 + +
+ - + private static final String EMPTY = ""; +
+
+ 1938 + +
+ - + boolean truism = new int[3].equals(new int[3]); +
+
+ 1939 + +
+ - +
+
+
+ 1940 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1941 + +
+ - + static class InnerInner { +
+
+ 1942 + +
+ - + @SuppressWarnings({"ArrayEquals", "InlineTrivialConstant"}) +
+
+ 1943 + +
+ - + void method() { +
+
+ 1944 + +
+ - + new int[3].equals(new int[3]); +
+
+ 1945 + +
+ - + } +
+
+ 1946 + +
+ - + } +
+
+ 1947 + +
+ - + } +
+
+ 1948 + +
+ - + } +
+
+ 1949 + +
+ - + '''.stripIndent(true) +
+
+ 1950 + +
+ - +
+
+
+ 1951 + +
+ - + when: +
+
+ 1952 + +
+ - + runTasksSuccessfully('compileAllErrorProne', '-PerrorProneRemoveUnused', '-PerrorProneSuppress', '-PerrorProneApply=ArrayEquals') +
+
+ 1953 + +
+ - +
+
+
+ 1954 + +
+ - + then: +
+
+ 1955 + +
+ - +
+
+
+ 1956 + +
+ - + // language=Java +
+
+ 1957 + +
+ - + javaSourceIsSyntacticallyEqualTo ''' +
+
+ 1958 + +
+ - + package app; +
+
+ 1959 + +
+ - +
+
+
+ 1960 + +
+ - + import java.util.Arrays; +
+
+ 1961 + +
+ - +
+
+
+ 1962 + +
+ - + public final class App { +
+
+ 1963 + +
+ - + @SuppressWarnings("for-rollout:InlineTrivialConstant") +
+
+ 1964 + +
+ - + private static final String EMPTY_STRING = ""; +
+
+ 1965 + +
+ - +
+
+
+ 1966 + +
+ - + // Although InlineTrivialConstant can be placed lower in the AST hierarchy, +
+
+ 1967 + +
+ - + // we preserve existing suppressions whenever possible rather than move suppressions around. +
+
+ 1968 + +
+ - + // Also, note that we don't add for-rollout here. +
+
+ 1969 + +
+ - + @SuppressWarnings("InlineTrivialConstant") +
+
+ 1970 + +
+ - + static class Inner { +
+
+ 1971 + +
+ - + private static final String EMPTY = ""; +
+
+ 1972 + +
+ - + boolean truism = Arrays.equals(new int[3], new int[3]); +
+
+ 1973 + +
+ - + +
+
+ 1974 + +
+ - + static class InnerInner { +
+
+ 1975 + +
+ - + void method() { +
+
+ 1976 + +
+ - + Arrays.equals(new int[3], new int[3]); +
+
+ 1977 + +
+ - + } +
+
+ 1978 + +
+ - + } +
+
+ 1979 + +
+ - + } +
+
+ 1980 + +
+ - + } +
+
+ 1981 + +
+ - + '''.stripIndent(true) +
+
+ 1982 + +
+ - + } +
+
+ 1983 + +
+ - +
+
+
+ 1984 + +
+ - + def 'error-prone dependencies have versions bound together by a virtual platform'() { +
+
+ 1985 + +
+ - + setup: 'when an error-prone dependency is forced to certain version' +
+
+ 1986 + +
+ - + // language=Gradle +
+
+ 1987 + +
+ - + buildFile << ''' +
+
+ 1988 + +
+ - + configurations.named('annotationProcessor') { +
+
+ 1989 + +
+ - + resolutionStrategy { +
+
+ 1990 + +
+ - + force 'com.google.errorprone:error_prone_annotation:2.3.4' +
+
+ 1991 + +
+ - + } +
+
+ 1992 + +
+ - + } +
+
+ 1993 + +
+ - + +
+
+ 1994 + +
+ - + tasks.register('printErrorProneVersions') { +
+
+ 1995 + +
+ - + inputs.files(configurations.named('annotationProcessor')) +
+
+ 1996 + +
+ - + doLast { +
+
+ 1997 + +
+ - + inputs.files.files.each { +
+
+ 1998 + +
+ - + println("ERROR-PRONE: ${it.name}") +
+
+ 1999 + +
+ - + } +
+
+ 2000 + +
+ - + } +
+
+ 2001 + +
+ - + } +
+
+ 2002 + +
+ - + '''.stripIndent(true) +
+
+ 2003 + +
+ - +
+
+
+ 2004 + +
+ - + when: +
+
+ 2005 + +
+ - + def output = runTasksSuccessfully('printErrorProneVersions').output +
+
+ 2006 + +
+ - +
+
+
+ 2007 + +
+ - + then: 'every single error-prone dependency has the same version' +
+
+ 2008 + +
+ - + output.contains('ERROR-PRONE: error_prone_annotation-2.3.4.jar') +
+
+ 2009 + +
+ - + output.contains('ERROR-PRONE: error_prone_core-2.3.4.jar') +
+
+ 2010 + +
+ - + } +
+
+ 2011 + +
+ - +
+
+
+ 2012 + +
+ - + // Running CC with debuggingErrorPrones (see setup method above) causes this issue: +
+
+ 2013 + +
+ - + // ERROR: transport error 202: connect failed: Connection refused +
+
+ 2014 + +
+ - + // ERROR: JDWP Transport dt_socket failed to initialize, TRANSPORT_INIT(510) +
+
+ 2015 + +
+ - + // JDWP exit error AGENT_ERROR_TRANSPORT_INIT(197): No transports initialized [src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c:700] +
+
+ 2016 + +
+ - + BuildResult runTasksSuccessfully(String... tasks) { +
+
+ 2017 + +
+ - + def projectVersion = Optional.ofNullable(System.getProperty('projectVersion')).orElseThrow() +
+
+ 2018 + +
+ - + String[] strings = tasks + ["-PsuppressibleErrorProneVersion=${projectVersion}".toString()] +
+
+ 2019 + +
+ - + if (debuggingErrorPrones) { +
+
+ 2020 + +
+ - + return super.runTasks(strings) +
+
+ 2021 + +
+ - + } else { +
+
+ 2022 + +
+ - + return super.runTasksWithConfigurationCache(strings) +
+
+ 2023 + +
+ - + } +
+
+ 2024 + +
+ - + } +
+
+ 2025 + +
+ - +
+
+
+ 2026 + +
+ - + BuildResult runTasksWithFailure(String... tasks) { +
+
+ 2027 + +
+ - + def projectVersion = Optional.ofNullable(System.getProperty('projectVersion')).orElseThrow() +
+
+ 2028 + +
+ - + String[] strings = tasks + ["-PsuppressibleErrorProneVersion=${projectVersion}".toString()] +
+
+ 2029 + +
+ - + if (debuggingErrorPrones) { +
+
+ 2030 + +
+ - + return super.runTasksAndFail(strings) +
+
+ 2031 + +
+ - + } else { +
+
+ 2032 + +
+ - + return super.runTasksAndFailWithConfigurationCache(strings) +
+
+ 2033 + +
+ - + } +
+
+ 2034 + +
+ - + } +
+
+ 2035 + +
+ - +
+
+
+ 2036 + +
+ - + void writeJavaSourceFileToSourceSets(String source) { +
+
+ 2037 + +
+ - + super.writeJavaSourceFile(source, 'src/main/java') +
+
+ 2038 + +
+ - + super.writeJavaSourceFile(source, 'src/other/java') +
+
+ 2039 + +
+ - + } +
+
+ 2040 + +
+ - +
+
+
+ 2041 + +
+ - + void javaSourceContains(String substring) { +
+
+ 2042 + +
+ - + assert file('src/main/java/app/App.java').text.contains(substring) +
+
+ 2043 + +
+ - + assert file('src/other/java/app/App.java').text.contains(substring) +
+
+ 2044 + +
+ - + } +
+
+ 2045 + +
+ - +
+
+
+ 2046 + +
+ - + void javaSourceDoesNotContain(String substring) { +
+
+ 2047 + +
+ - + assert !file('src/main/java/app/App.java').text.contains(substring) +
+
+ 2048 + +
+ - + assert !file('src/other/java/app/App.java').text.contains(substring) +
+
+ 2049 + +
+ - + } +
+
+ 2050 + +
+ - +
+
+
+ 2051 + +
+ - + // Normalizes Java source by trimming whitespace and applying consistent formatting. +
+
+ 2052 + +
+ - + // Preserves newlines since the formatter allows them within methods, and we need +
+
+ 2053 + +
+ - + // to test that error-prone doesn't introduce unwanted line breaks. +
+
+ 2054 + +
+ - + private static String normalizeSource(String content) { +
+
+ 2055 + +
+ - + String stripped = content.readLines() +
+
+ 2056 + +
+ - + .collect { it.trim() } // Remove leading/trailing whitespace +
+
+ 2057 + +
+ - + .join('\n') +
+
+ 2058 + +
+ - +
+
+
+ 2059 + +
+ - + return formatter.formatSource(stripped) +
+
+ 2060 + +
+ - + } +
+
+ 2061 + +
+ - +
+
+
+ 2062 + +
+ - + void javaSourceIsSyntacticallyEqualTo(String source) { +
+
+ 2063 + +
+ - + def output = normalizeSource(file('src/main/java/app/App.java').text) +
+
+ 2064 + +
+ - + def expected = normalizeSource(source) +
+
+ 2065 + +
+ - +
+
+
+ 2066 + +
+ - + // Ensure test fixtures are properly formatted +
+
+ 2067 + +
+ - + assert "\n" + expected == source, "Please update your text fixtures to be in palantir-java-format" +
+
+ 2068 + +
+ - + assert output == expected +
+
+ 2069 + +
+ - +
+
+
+ 2070 + +
+ - + def outputOther = normalizeSource(file('src/other/java/app/App.java').text) +
+
+ 2071 + +
+ - + def expectedOther = normalizeSource(source) +
+
+ 2072 + +
+ - + assert outputOther == expectedOther +
+
+ 2073 + +
+ - + } +
+
+ 2074 + +
+ - +
+
+
+ 2075 + +
+ - + void javaSourceIsSyntacticallyNotEqualTo(String source) { +
+
+ 2076 + +
+ - + def output = normalizeSource(file('src/main/java/app/App.java').text) +
+
+ 2077 + +
+ - + def expected = normalizeSource(source) +
+
+ 2078 + +
+ - +
+
+
+ 2079 + +
+ - + // Ensure test fixtures are properly formatted +
+
+ 2080 + +
+ - + assert "\n" + expected == source, "Please update your text fixtures to be in palantir-java-format" +
+
+ 2081 + +
+ - + assert output != expected +
+
+ 2082 + +
+ - +
+
+
+ 2083 + +
+ - + def outputOther = normalizeSource(file('src/other/java/app/App.java').text) +
+
+ 2084 + +
+ - + def expectedOther = normalizeSource(source) +
+
+ 2085 + +
+ - + assert outputOther != expectedOther +
+
+ 2086 + +
+   + } +
+
+ 2087 + +
+   + } +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
 
+
+ 14 + +
+   + * limitations under the License. +
+
+ 15 + +
+   + */ +
+
+ 16 + +
+   +
+
+
+ 17 + +
+ + + package com.palantir.gradle.suppressibleerrorprone; +
+
+ 18 + +
+ + +
+
+
+ 19 + +
+ + + import static org.assertj.core.api.Assertions.assertThat; +
+
+ 20 + +
+ + +
+
+
+ 21 + +
+ + + import com.palantir.gradle.testing.execution.GradleInvoker; +
+
+ 22 + +
+ + + import com.palantir.gradle.testing.execution.InvocationResult; +
+
+ 23 + +
+ + + import com.palantir.gradle.testing.junit.DisabledConfigurationCache; +
+
+ 24 + +
+ + + import com.palantir.gradle.testing.junit.GradlePluginTests; +
+
+ 25 + +
+ + + import com.palantir.gradle.testing.project.RootProject; +
+
+ 26 + +
+ + + import com.palantir.javaformat.java.Formatter; +
+
+ 27 + +
+ + + import com.palantir.javaformat.java.FormatterException; +
+
+ 28 + +
+ + + import com.palantir.javaformat.java.JavaFormatterOptions; +
+
+ 29 + +
+ + + import java.io.IOException; +
+
+ 30 + +
+ + + import java.io.UncheckedIOException; +
+
+ 31 + +
+ + + import java.nio.file.Files; +
+
+ 32 + +
+ + + import java.nio.file.Path; +
+
+ 33 + +
+ + + import java.util.Optional; +
+
+ 34 + +
+ + + import org.junit.jupiter.api.BeforeEach; +
+
+ 35 + +
+ + + import org.junit.jupiter.api.Test; +
+
+ 36 + +
+ + +
+
+
+ 37 + +
+ + + @GradlePluginTests +
+
+ 38 + +
+ + + @DisabledConfigurationCache("Tests manipulate files directly which is not compatible with configuration cache") +
+
+ 39 + +
+ + + final class SuppressibleErrorPronePluginIntegrationTest { +
+
+ 40 + +
+   + // ***DELINEATOR FOR REVIEW: formatter +
+
+ 41 + +
+ + + private static final Formatter FORMATTER = Formatter.createFormatter(JavaFormatterOptions.builder() +
+
+ 42 + +
+ + + .style(JavaFormatterOptions.Style.PALANTIR) +
+
+ 43 + +
+ + + .build()); +
+
+ 44 + +
+   +
+
+
+ 45 + +
+   + // This makes debugging the errorprone check code running inside the compiler (including the bytecode +
+
+ 46 + +
+   + // edited modifications we have made) "just work" from inside these tests. +
+
+
 
+
+ 53 + +
+   + // If the variable below is true the tests will fail as the compilation process will try to +
+
+ 54 + +
+   + // attach to a non-existent debugger. Set it to false before you push any code. +
+
+ 55 + +
+   + // ***DELINEATOR FOR REVIEW: debuggingErrorPrones +
+
+ 56 + +
+ + + private static final boolean DEBUGGING_ERROR_PRONES = false; +
+
+ 57 + +
+ + +
+
+
+ 58 + +
+ + + private String projectVersion; +
+
+ 59 + +
+   +
+
+
+ 60 + +
+   + // ***DELINEATOR FOR REVIEW: setup +
+
+ 61 + +
+ + + @BeforeEach +
+
+ 62 + +
+ + + @SuppressWarnings("GradleTestPluginsBlock") // buildscript and apply plugin need special handling +
+
+ 63 + +
+ + + void setup(RootProject rootProject) { +
+
+ 64 + +
+ + + projectVersion = Optional.ofNullable(System.getProperty("projectVersion")) +
+
+ 65 + +
+ + + .orElseThrow(() -> new IllegalStateException("projectVersion system property must be set")); +
+
+ 66 + +
+ + +
+
+
+ 67 + +
+ + + // Note: We use append() with buildscript block and apply plugin because these need special handling +
+
+ 68 + +
+ + + // that can't use the plugins() API +
+
+ 69 + +
+ + + rootProject.buildGradle().append(""" +
+
+ 70 + +
+   + buildscript { +
+
+ 71 + +
+   + repositories { +
+
+ 72 + +
+   + mavenCentral() +
+
+
 
+
+ 80 + +
+   + apply plugin: 'com.palantir.consistent-versions' +
+
+ 81 + +
+   +
+
+
+ 82 + +
+   + apply plugin: 'com.palantir.suppressible-error-prone' +
+
+ 83 + +
+ + + """); +
+
+ 84 + +
+ + +
+
+
+ 85 + +
+ + + rootProject.buildGradle().plugins().add("java"); +
+
+ 86 + +
+ + +
+
+
+ 87 + +
+ + + rootProject.buildGradle().append(""" +
+
+ 88 + +
+   + repositories { +
+
+ 89 + +
+   + mavenCentral() +
+
+ 90 + +
+   + // Needed so that suppressible-error-prone and suppressible-error-prone-annotations can be added +
+
+ 91 + +
+   + // as jars to the various configurations. We make sure to publish these to maven local before the +
+
+ 92 + +
+ + + // test task runs. +
+
+ 93 + +
+   + mavenLocal() +
+
+ 94 + +
+   + } +
+
+ 95 + +
+ + +
+
+
+ 96 + +
+   + sourceSets { +
+
+ 97 + +
+   + other +
+
+ 98 + +
+   + } +
+
+ 99 + +
+ + +
+
+
+ 100 + +
+   + dependencies { +
+
+ 101 + +
+   + errorprone 'com.google.errorprone:error_prone_core:2.31.0' +
+
+ 102 + +
+   + // Mimick the way SuppressibleErrorPronePlugin adds the dependency on suppressible-error-prone +
+
+
 
+
+ 104 + +
+   + // and be the current version +
+
+ 105 + +
+   + errorprone 'com.palantir.suppressible-error-prone:test-error-prone-checks:' + project.findProperty("suppressibleErrorProneVersion") +
+
+ 106 + +
+   + } +
+
+ 107 + +
+ + +
+
+
+ 108 + +
+   + suppressibleErrorProne { +
+
+ 109 + +
+   + configureEachErrorProneOptions { +
+
+ 110 + +
+   + // These interfere with some tests, so disable them +
+
+
 
+
+ 114 + +
+   + ignoreUnknownCheckNames = true +
+
+ 115 + +
+   + } +
+
+ 116 + +
+   + } +
+
+ 117 + +
+ + + """); +
+
+ 118 + +
+   +
+
+
+ 119 + +
+ + + if (DEBUGGING_ERROR_PRONES) { +
+
+ 120 + +
+ + + rootProject.buildGradle().append(""" +
+
+ + +
+   +
+
+
+ 121 + +
+   + tasks.withType(JavaCompile).configureEach { +
+
+ 122 + +
+   + it.options.forkOptions.jvmArgumentProviders.add(new CommandLineArgumentProvider() { +
+
+ 123 + +
+   + @Override +
+
+
 
+
+ 126 + +
+   + } +
+
+ 127 + +
+   + }) +
+
+ 128 + +
+   + } +
+
+ 129 + +
+ + + """); +
+
+ 130 + +
+   + } +
+
+ 131 + +
+   +
+
+
+ 132 + +
+ + + rootProject +
+
+ 133 + +
+ + + .gradlePropertiesFile() +
+
+ 134 + +
+ + + .appendProperty("__TESTING", "true") +
+
+ 135 + +
+ + + .appendProperty("__TESTING_CACHE_BUST_ERRORPRONE_TRANSFORM", "true"); +
+
+ 136 + +
+   +
+
+
+ 137 + +
+ + + rootProject.file("versions.lock").createEmpty(); +
+
+ 138 + +
+   + } +
+
+ 139 + +
+   +
+
+
+ 140 + +
+   + // ***DELINEATOR FOR REVIEW: reports_a_failing_error_prone +
+
+ 141 + +
+ + + @Test +
+
+ 142 + +
+ + + void reports_a_failing_error_prone(GradleInvoker gradle, RootProject rootProject) { +
+
+ 143 + +
+ + + writeJavaSourceFileToSourceSets(rootProject, """ +
+
+ 144 + +
+   + package app; +
+
+ 145 + +
+ + +
+
+
+ 146 + +
+   + public final class App { +
+
+ 147 + +
+   + public static void main(String[] args) { +
+
+ 148 + +
+   + new int[3].toString(); +
+
+ 149 + +
+   + } +
+
+ 150 + +
+   + } +
+
+ 151 + +
+ + + """); +
+
+ 152 + +
+   +
+
+
+ 153 + +
+ + + // ***DELINEATOR FOR REVIEW: when +
+
+ 154 + +
+ + + InvocationResult result = runTasksWithFailure(gradle, "compileAllErrorProne"); +
+
+ 155 + +
+   +
+
+
+ 156 + +
+ + + // ***DELINEATOR FOR REVIEW: then +
+
+ 157 + +
+ + + assertThat(result.output()).contains("[ArrayToString]"); +
+
+ 158 + +
+   + } +
+
+ 159 + +
+   +
+
+
+ 160 + +
+ + + // ***DELINEATOR FOR REVIEW: can_suppress_an_error_prone_with_for_rollout_prefix +
+
+ 161 + +
+ + + @Test +
+
+ 162 + +
+ + + void can_suppress_an_error_prone_with_for_rollout_prefix(GradleInvoker gradle, RootProject rootProject) { +
+
+ 163 + +
+   + // This test is explicitly checking we suppress the for-rollout prefix as that is what exists +
+
+ 164 + +
+   + // in people's codebases +
+
+ 165 + +
+   +
+
+
+ 166 + +
+ + + writeJavaSourceFileToSourceSets(rootProject, """ +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 167 + +
+   + package app; +
+
+ 168 + +
+ + +
+
+
+ 169 + +
+   + public final class App { +
+
+ 170 + +
+   + @SuppressWarnings("for-rollout:ArrayToString") +
+
+ 171 + +
+   + public static void main(String[] args) { +
+
+ 172 + +
+   + new int[3].toString(); +
+
+ 173 + +
+   + } +
+
+ 174 + +
+   + } +
+
+ 175 + +
+ + + """); +
+
+ 176 + +
+   +
+
+
+ 177 + +
+ + + // ***DELINEATOR FOR REVIEW: then +
+
+ 178 + +
+ + + runTasksSuccessfully(gradle, "compileAllErrorProne"); +
+
+ 179 + +
+   + } +
+
+ 180 + +
+   +
+
+
+ 181 + +
+ + + // ***DELINEATOR FOR REVIEW: ensure_error_prone_checks_are_disabled_in_generated_code +
+
+ 182 + +
+ + + @Test +
+
+ 183 + +
+ + + void ensure_error_prone_checks_are_disabled_in_generated_code(GradleInvoker gradle, RootProject rootProject) +
+
+ 184 + +
+ + + throws IOException { +
+
+ 185 + +
+ + + String erroringCode = """ +
+
+ 186 + +
+   + package app; +
+
+ 187 + +
+ + +
+
+
+ 188 + +
+   + public final class App { +
+
+ 189 + +
+   + public static void main(String[] args) { +
+
+ 190 + +
+   + new int[3].toString(); +
+
+ 191 + +
+   + } +
+
+ 192 + +
+   + } +
+
+ 193 + +
+ + + """; +
+
+ 194 + +
+   +
+
+
+ 195 + +
+ + + // ***DELINEATOR FOR REVIEW: when +
+
+ 196 + +
+ + + Path sourceDir1 = rootProject.path().resolve("src/generated"); +
+
+ 197 + +
+ + + Path sourceDir2 = rootProject.path().resolve("build/generated"); +
+
+ 198 + +
+   +
+
+
+ 199 + +
+ + + Files.createDirectories(sourceDir1); +
+
+ 200 + +
+ + + Files.createDirectories(sourceDir2); +
+
+ + +
+   +
+
+
+ 201 + +
+   +
+
+
+ 202 + +
+ + + writeJavaSourceFile(rootProject, erroringCode, sourceDir1); +
+
+ 203 + +
+ + + writeJavaSourceFile(rootProject, erroringCode.replace("App", "App2"), sourceDir2); +
+
+ 204 + +
+   +
+
+
+ 205 + +
+ + + rootProject +
+
+ 206 + +
+ + + .buildGradle() +
+
+ 207 + +
+ + + .append( +
+
+ 208 + +
+ + + "sourceSets.main.java.srcDirs('%s', '%s')", +
+
+ 209 + +
+ + + rootProject.path().relativize(sourceDir1), +
+
+ 210 + +
+ + + rootProject.path().relativize(sourceDir2)); +
+
+ 211 + +
+   +
+
+
+ 212 + +
+ + + // ***DELINEATOR FOR REVIEW: then +
+
+ 213 + +
+ + + runTasksSuccessfully(gradle, "compileAllErrorProne"); +
+
+ 214 + +
+   + } +
+
+ 215 + +
+   +
+
+
+ 216 + +
+ + + // ***DELINEATOR FOR REVIEW: can_apply_patches_for_a_check_if_added_to_the_patchChecks_list +
+
+ 217 + +
+ + + @Test +
+
+ 218 + +
+ + + void can_apply_patches_for_a_check_if_added_to_the_patchChecks_list(GradleInvoker gradle, RootProject rootProject) { +
+
+ 219 + +
+ + + rootProject.buildGradle().append(""" +
+
+ 220 + +
+   + suppressibleErrorProne { +
+
+ 221 + +
+   + patchChecks.add('ArrayToString') +
+
+ 222 + +
+   + } +
+
+ 223 + +
+ + + """); +
+
+ 224 + +
+   +
+
+
+ 225 + +
+ + + writeJavaSourceFileToSourceSets(rootProject, """ +
+
+ + +
+   +
+
+
+ 226 + +
+   + package app; +
+
+ 227 + +
+ + +
+
+
+ 228 + +
+   + public final class App { +
+
+ 229 + +
+   + public static void main(String[] args) { +
+
+ 230 + +
+   + new int[3].toString(); +
+
+ 231 + +
+   + } +
+
+ 232 + +
+   + } +
+
+ 233 + +
+ + + """); +
+
+ 234 + +
+   +
+
+
+ 235 + +
+ + + // ***DELINEATOR FOR REVIEW: when +
+
+ 236 + +
+ + + runTasksSuccessfully(gradle, "compileAllErrorProne", "-PerrorProneApply"); +
+
+ 237 + +
+   +
+
+
+ 238 + +
+ + + // ***DELINEATOR FOR REVIEW: then +
+
+ 239 + +
+ + + runTasksSuccessfully(gradle, "compileAllErrorProne"); +
+
+ 240 + +
+   +
+
+
+ 241 + +
+ + + javaSourceContains(rootProject, "Arrays.toString(new int[3])"); +
+
+ 242 + +
+   + } +
+
+ 243 + +
+   +
+
+
+ 244 + +
+ + + // ***DELINEATOR FOR REVIEW: does_not_apply_patches_for_a_check_if_not_added_to_the_patchChecks_list +
+
+ 245 + +
+ + + @Test +
+
+ 246 + +
+ + + void does_not_apply_patches_for_a_check_if_not_added_to_the_patchChecks_list( +
+
+ 247 + +
+ + + GradleInvoker gradle, RootProject rootProject) { +
+
+ 248 + +
+ + + rootProject.buildGradle().append(""" +
+
+ 249 + +
+   + suppressibleErrorProne { +
+
+ 250 + +
+   + // To make sure set is not empty +
+
+ 251 + +
+   + patchChecks = ['SomeCheck'] +
+
+ 252 + +
+   + } +
+
+ 253 + +
+ + + """); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 254 + +
+   +
+
+
+ 255 + +
+ + + writeJavaSourceFileToSourceSets(rootProject, """ +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 256 + +
+   + package app; +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 257 + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 258 + +
+   + public final class App { +
+
+ 259 + +
+   + public static void main(String[] args) { +
+
+ 260 + +
+   + new int[3].toString(); +
+
+ 261 + +
+   + } +
+
+ 262 + +
+   + } +
+
+ 263 + +
+ + + """); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 264 + +
+   +
+
+
+ 265 + +
+ + + // ***DELINEATOR FOR REVIEW: when +
+
+ 266 + +
+ + + runTasksSuccessfully(gradle, "compileAllErrorProne", "-PerrorProneApply"); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 267 + +
+   +
+
+
+ 268 + +
+ + + // ***DELINEATOR FOR REVIEW: then +
+
+ 269 + +
+ + + javaSourceContains(rootProject, "new int[3].toString()"); +
+
+ + +
+   +
+
+
+ 270 + +
+   + } +
+
+ 271 + +
+   +
+
+
+ 272 + +
+ + + // Helper methods +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 273 + +
+   +
+
+
+ 274 + +
+ + + private InvocationResult runTasksSuccessfully(GradleInvoker gradle, String... tasks) { +
+
+ 275 + +
+ + + String[] allTasks = new String[tasks.length + 1]; +
+
+ 276 + +
+ + + System.arraycopy(tasks, 0, allTasks, 0, tasks.length); +
+
+ 277 + +
+ + + allTasks[tasks.length] = "-PsuppressibleErrorProneVersion=" + projectVersion; +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 278 + +
+   +
+
+
+ 279 + +
+ + + if (DEBUGGING_ERROR_PRONES) { +
+
+ 280 + +
+ + + return gradle.withArgs(allTasks).buildsSuccessfully(); +
+
+ 281 + +
+ + + } else { +
+
+ 282 + +
+ + + // Note: Configuration cache is disabled at class level due to file manipulation +
+
+ 283 + +
+ + + return gradle.withArgs(allTasks).buildsSuccessfully(); +
+
+ 284 + +
+ + + } +
+
+ 285 + +
+   + } +
+
+ 286 + +
+   +
+
+
+ 287 + +
+ + + private InvocationResult runTasksWithFailure(GradleInvoker gradle, String... tasks) { +
+
+ 288 + +
+ + + String[] allTasks = new String[tasks.length + 1]; +
+
+ 289 + +
+ + + System.arraycopy(tasks, 0, allTasks, 0, tasks.length); +
+
+ 290 + +
+ + + allTasks[tasks.length] = "-PsuppressibleErrorProneVersion=" + projectVersion; +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 291 + +
+   +
+
+
+ 292 + +
+ + + if (DEBUGGING_ERROR_PRONES) { +
+
+ 293 + +
+ + + return gradle.withArgs(allTasks).buildsWithFailure(); +
+
+ 294 + +
+ + + } else { +
+
+ 295 + +
+ + + // Note: Configuration cache is disabled at class level due to file manipulation +
+
+ 296 + +
+ + + return gradle.withArgs(allTasks).buildsWithFailure(); +
+
+ 297 + +
+ + + } +
+
+ 298 + +
+   + } +
+
+ 299 + +
+   +
+
+
+ 300 + +
+ + + private void writeJavaSourceFileToSourceSets(RootProject rootProject, String source) { +
+
+ 301 + +
+ + + rootProject.sourceSet("main").java().writeClass(source.stripIndent()); +
+
+ 302 + +
+ + + rootProject.sourceSet("other").java().writeClass(source.stripIndent()); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 303 + +
+   + } +
+
+ 304 + +
+   +
+
+
+ 305 + +
+ + + private void writeJavaSourceFile(RootProject rootProject, String source, Path sourceDir) throws IOException { +
+
+ 306 + +
+ + + // Extract package and class name to create proper file structure +
+
+ 307 + +
+ + + String packageName = "app"; +
+
+ 308 + +
+ + + String className = "App"; +
+
+ 309 + +
+ + + if (source.contains("App2")) { +
+
+ 310 + +
+ + + className = "App2"; +
+
+ 311 + +
+ + + } +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 312 + +
+   +
+
+
+ 313 + +
+ + + Path packageDir = sourceDir.resolve(packageName.replace('.', '/')); +
+
+ 314 + +
+ + + Files.createDirectories(packageDir); +
+
+ 315 + +
+ + + Path javaFile = packageDir.resolve(className + ".java"); +
+
+ 316 + +
+ + + Files.writeString(javaFile, source.stripIndent()); +
+
+ 317 + +
+   + } +
+
+ 318 + +
+   +
+
+
+ 319 + +
+ + + private void javaSourceContains(RootProject rootProject, String substring) { +
+
+ 320 + +
+ + + Path mainJava = rootProject.path().resolve("src/main/java/app/App.java"); +
+
+ 321 + +
+ + + Path otherJava = rootProject.path().resolve("src/other/java/app/App.java"); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 322 + +
+   +
+
+
+ 323 + +
+ + + try { +
+
+ 324 + +
+ + + String mainContent = Files.readString(mainJava); +
+
+ 325 + +
+ + + String otherContent = Files.readString(otherJava); +
+
+ 326 + +
+ + + assertThat(mainContent).contains(substring); +
+
+ 327 + +
+ + + assertThat(otherContent).contains(substring); +
+
+ 328 + +
+ + + } catch (IOException e) { +
+
+ 329 + +
+ + + throw new UncheckedIOException("Failed to read Java source files", e); +
+
+ 330 + +
+ + + } +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 331 + +
+   + } +
+
+ 332 + +
+   +
+
+
+ 333 + +
+ + + @SuppressWarnings("UnusedMethod") +
+
+ 334 + +
+ + + private void javaSourceDoesNotContain(RootProject rootProject, String substring) { +
+
+ 335 + +
+ + + Path mainJava = rootProject.path().resolve("src/main/java/app/App.java"); +
+
+ 336 + +
+ + + Path otherJava = rootProject.path().resolve("src/other/java/app/App.java"); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 337 + +
+   +
+
+
+ 338 + +
+ + + try { +
+
+ 339 + +
+ + + String mainContent = Files.readString(mainJava); +
+
+ 340 + +
+ + + String otherContent = Files.readString(otherJava); +
+
+ 341 + +
+ + + assertThat(mainContent).doesNotContain(substring); +
+
+ 342 + +
+ + + assertThat(otherContent).doesNotContain(substring); +
+
+ 343 + +
+ + + } catch (IOException e) { +
+
+ 344 + +
+ + + throw new UncheckedIOException("Failed to read Java source files", e); +
+
+ 345 + +
+ + + } +
+
+ 346 + +
+   + } +
+
+ 347 + +
+   +
+
+
+ 348 + +
+ + + // Normalizes Java source by trimming whitespace and applying consistent formatting. +
+
+ 349 + +
+ + + // Preserves newlines since the formatter allows them within methods, and we need +
+
+ 350 + +
+ + + // to test that error-prone doesn't introduce unwanted line breaks. +
+
+ 351 + +
+ + + @SuppressWarnings("StringSplitter") +
+
+ 352 + +
+ + + private static String normalizeSource(String content) { +
+
+ 353 + +
+ + + String[] lines = content.split("\n"); +
+
+ 354 + +
+ + + StringBuilder stripped = new StringBuilder(); +
+
+ 355 + +
+ + + for (String line : lines) { +
+
+ 356 + +
+ + + stripped.append(line.trim()).append('\n'); +
+
+ 357 + +
+ + + } +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 358 + +
+   +
+
+
+ 359 + +
+ + + try { +
+
+ 360 + +
+ + + return FORMATTER.formatSource(stripped.toString()); +
+
+ 361 + +
+ + + } catch (FormatterException e) { +
+
+ 362 + +
+ + + throw new RuntimeException("Failed to format source", e); +
+
+ 363 + +
+ + + } +
+
+ 364 + +
+   + } +
+
+ 365 + +
+   +
+
+
+ 366 + +
+ + + private void javaSourceIsSyntacticallyEqualTo(RootProject rootProject, String source) { +
+
+ 367 + +
+ + + Path mainJava = rootProject.path().resolve("src/main/java/app/App.java"); +
+
+ 368 + +
+ + + Path otherJava = rootProject.path().resolve("src/other/java/app/App.java"); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 369 + +
+   +
+
+
+ 370 + +
+ + + try { +
+
+ 371 + +
+ + + String mainContent = Files.readString(mainJava); +
+
+ 372 + +
+ + + String otherContent = Files.readString(otherJava); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 373 + +
+   +
+
+
+ 374 + +
+ + + String output = normalizeSource(mainContent); +
+
+ 375 + +
+ + + String expected = normalizeSource(source); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 376 + +
+   +
+
+
+ 377 + +
+ + + // Ensure test fixtures are properly formatted +
+
+ 378 + +
+ + + assertThat("\n" + expected) +
+
+ 379 + +
+ + + .as("Please update your text fixtures to be in palantir-java-format") +
+
+ 380 + +
+ + + .isEqualTo(source); +
+
+ 381 + +
+ + + assertThat(output).isEqualTo(expected); +
+
+ 382 + +
+   +
+
+
+ 383 + +
+ + + String outputOther = normalizeSource(otherContent); +
+
+ 384 + +
+ + + String expectedOther = normalizeSource(source); +
+
+ 385 + +
+ + + assertThat(outputOther).isEqualTo(expectedOther); +
+
+ 386 + +
+ + + } catch (IOException e) { +
+
+ 387 + +
+ + + throw new UncheckedIOException("Failed to read Java source files", e); +
+
+ 388 + +
+ + + } +
+
+ 389 + +
+   + } +
+
+ 390 + +
+   +
+
+
+ 391 + +
+ + + @SuppressWarnings("UnusedMethod") +
+
+ 392 + +
+ + + private void javaSourceIsSyntacticallyNotEqualTo(RootProject rootProject, String source) { +
+
+ 393 + +
+ + + Path mainJava = rootProject.path().resolve("src/main/java/app/App.java"); +
+
+ 394 + +
+ + + Path otherJava = rootProject.path().resolve("src/other/java/app/App.java"); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 395 + +
+   +
+
+
+ 396 + +
+ + + try { +
+
+ 397 + +
+ + + String mainContent = Files.readString(mainJava); +
+
+ 398 + +
+ + + String otherContent = Files.readString(otherJava); +
+
+ 399 + +
+   +
+
+
+ 400 + +
+ + + String output = normalizeSource(mainContent); +
+
+ 401 + +
+ + + String expected = normalizeSource(source); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 402 + +
+   +
+
+
+ 403 + +
+ + + // Ensure test fixtures are properly formatted +
+
+ 404 + +
+ + + assertThat("\n" + expected) +
+
+ 405 + +
+ + + .as("Please update your text fixtures to be in palantir-java-format") +
+
+ 406 + +
+ + + .isEqualTo(source); +
+
+ 407 + +
+ + + assertThat(output).isNotEqualTo(expected); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 408 + +
+   +
+
+
+ 409 + +
+ + + String outputOther = normalizeSource(otherContent); +
+
+ 410 + +
+ + + String expectedOther = normalizeSource(source); +
+
+ 411 + +
+ + + assertThat(outputOther).isNotEqualTo(expectedOther); +
+
+ 412 + +
+ + + } catch (IOException e) { +
+
+ 413 + +
+ + + throw new UncheckedIOException("Failed to read Java source files", e); +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 414 + +
+   + } +
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ + +
+   +
+
+
+ 415 + +
+   + } +
+
+ 416 + +
+   + } +
+
+
+
+
+
+
+
+ +