diff --git a/code-coverage-report/build.gradle b/code-coverage-report/build.gradle index c7cb38eb71..4dc990a048 100644 --- a/code-coverage-report/build.gradle +++ b/code-coverage-report/build.gradle @@ -91,5 +91,6 @@ dependencies { implementation project(':nullaway') implementation project(':jar-infer:jar-infer-lib') implementation project(':jar-infer:nullaway-integration-test') + implementation project(':guava-recent-unit-tests') implementation project(':jdk17-unit-tests') } diff --git a/guava-recent-unit-tests/build.gradle b/guava-recent-unit-tests/build.gradle new file mode 100644 index 0000000000..f831222651 --- /dev/null +++ b/guava-recent-unit-tests/build.gradle @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022. Uber Technologies + * + * 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. + */ +plugins { + id 'java-library' + id 'nullaway.jacoco-conventions' +} + +// We need this separate build target to test newer versions of Guava +// (e.g. 31+) than that which NullAway currently depends on. + +dependencies { + testImplementation project(":nullaway") + testImplementation deps.test.junit4 + testImplementation(deps.build.errorProneTestHelpers) { + exclude group: "junit", module: "junit" + } + testImplementation deps.build.jsr305Annotations + testImplementation "com.google.guava:guava:31.1-jre" +} + +test { + maxHeapSize = "1024m" + if (!JavaVersion.current().java9Compatible) { + jvmArgs "-Xbootclasspath/p:${configurations.errorproneJavac.asPath}" + } else { + // to expose necessary JDK types on JDK 16+; see https://errorprone.info/docs/installation#java-9-and-newer + jvmArgs += [ + "--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED", + // Accessed by Lombok tests + "--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED", + ] + } +} diff --git a/guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java b/guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java new file mode 100644 index 0000000000..9dc64e6c4c --- /dev/null +++ b/guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Uber Technologies, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import com.google.errorprone.CompilationTestHelper; +import com.uber.nullaway.NullAway; +import java.util.Arrays; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +public class NullAwayGuavaParametricNullnessTests { + @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private CompilationTestHelper defaultCompilationHelper; + + @Before + public void setup() { + defaultCompilationHelper = + CompilationTestHelper.newInstance(NullAway.class, getClass()) + .setArgs( + Arrays.asList( + "-d", + temporaryFolder.getRoot().getAbsolutePath(), + "-XepOpt:NullAway:AnnotatedPackages=com.uber,com.google.common")); + } + + @Test + public void testFutureCallbackParametricNullness() { + defaultCompilationHelper + .addSourceLines( + "Test.java", + "package com.uber;", + "import com.google.common.util.concurrent.FutureCallback;", + "import javax.annotation.Nullable;", + "class Test {", + " public static FutureCallback wrapFutureCallback(FutureCallback futureCallback) {", + " return new FutureCallback() {", + " @Override", + " public void onSuccess(@Nullable T result) {", + " futureCallback.onSuccess(result);", + " }", + " @Override", + " public void onFailure(Throwable throwable) {", + " futureCallback.onFailure(throwable);", + " }", + " };", + " }", + "}") + .doTest(); + } + + @Test + public void testIterableParametricNullness() { + defaultCompilationHelper + .addSourceLines( + "Test.java", + "package com.uber;", + "import com.google.common.collect.ImmutableList;", + "import com.google.common.collect.Iterables;", + "import javax.annotation.Nullable;", + "class Test {", + " public static String test1() {", + " // BUG: Diagnostic contains: returning @Nullable expression", + " return Iterables.getFirst(ImmutableList.of(), null);", + " }", + " public static @Nullable String test2() {", + " return Iterables.getFirst(ImmutableList.of(), null);", + " }", + "}") + .doTest(); + } +} diff --git a/nullaway/src/main/java/com/uber/nullaway/Nullness.java b/nullaway/src/main/java/com/uber/nullaway/Nullness.java index d8a042ae56..4587064972 100644 --- a/nullaway/src/main/java/com/uber/nullaway/Nullness.java +++ b/nullaway/src/main/java/com/uber/nullaway/Nullness.java @@ -156,6 +156,12 @@ public static boolean isNullableAnnotation(String annotName, Config config) { || annotName.endsWith(".checkerframework.checker.nullness.compatqual.NullableDecl") // matches javax.annotation.CheckForNull and edu.umd.cs.findbugs.annotations.CheckForNull || annotName.endsWith(".CheckForNull") + // matches any of the multiple @ParametricNullness annotations used within Guava + // (see https://github.com/google/guava/issues/6126) + // We check the simple name first and the package prefix second for boolean short + // circuiting, as Guava includes + // many annotations + || (annotName.endsWith(".ParametricNullness") && annotName.startsWith("com.google.common.")) || (config.acknowledgeAndroidRecent() && annotName.equals("androidx.annotation.RecentlyNullable")) || config.isCustomNullableAnnotation(annotName); diff --git a/settings.gradle b/settings.gradle index 6d5619e670..6ac4955a6e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,6 +22,7 @@ include ':jar-infer:jar-infer-cli' include ':jar-infer:test-java-lib-jarinfer' include ':jar-infer:nullaway-integration-test' include ':jmh' +include ':guava-recent-unit-tests' include ':jdk17-unit-tests' // The following modules require JDK 11 and fail during Gradle configuration on JDK 8