diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 1af2c45d71..566b5cfdbb 100755 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -102,7 +102,7 @@ def test = [ "org.junit.jupiter:junit-jupiter-api:5.0.2", "org.apiguardian:apiguardian-api:1.0.0" ], - jetbrainsAnnotations : "org.jetbrains:annotations:13.0", + jetbrainsAnnotations : "org.jetbrains:annotations:24.1.0", cfQual : "org.checkerframework:checker-qual:${versions.checkerFramework}", // 2.5.5 is the last release to contain this artifact cfCompatQual : "org.checkerframework:checker-compat-qual:2.5.5", diff --git a/nullaway/src/main/java/com/uber/nullaway/NullabilityUtil.java b/nullaway/src/main/java/com/uber/nullaway/NullabilityUtil.java index 2a04d46cb1..cca114b296 100644 --- a/nullaway/src/main/java/com/uber/nullaway/NullabilityUtil.java +++ b/nullaway/src/main/java/com/uber/nullaway/NullabilityUtil.java @@ -175,7 +175,8 @@ public static TreePath findEnclosingMethodOrLambdaOrInitializer(TreePath path) { /** * NOTE: this method does not work for getting all annotations of parameters of methods from class - * files. For that case, use {@link #getAllAnnotationsForParameter(Symbol.MethodSymbol, int)} + * files. For that case, use {@link #getAllAnnotationsForParameter(Symbol.MethodSymbol, int, + * Config)} * * @param symbol the symbol * @return all annotations on the symbol and on the type of the symbol @@ -259,10 +260,11 @@ public static Stream getAllAnnotations(Symbol symbol * * @param symbol the method symbol * @param paramInd index of the parameter + * @param config NullAway configuration * @return all declaration and type-use annotations for the parameter */ public static Stream getAllAnnotationsForParameter( - Symbol.MethodSymbol symbol, int paramInd) { + Symbol.MethodSymbol symbol, int paramInd, Config config) { Symbol.VarSymbol varSymbol = symbol.getParameters().get(paramInd); return Stream.concat( varSymbol.getAnnotationMirrors().stream(), @@ -270,7 +272,8 @@ public static Stream getAllAnnotationsForParameter( .filter( t -> t.position.type.equals(TargetType.METHOD_FORMAL_PARAMETER) - && t.position.parameter_index == paramInd)); + && t.position.parameter_index == paramInd + && NullabilityUtil.isDirectTypeUseAnnotation(t, config))); } /** diff --git a/nullaway/src/main/java/com/uber/nullaway/Nullness.java b/nullaway/src/main/java/com/uber/nullaway/Nullness.java index 845d547a57..0ed694b60d 100644 --- a/nullaway/src/main/java/com/uber/nullaway/Nullness.java +++ b/nullaway/src/main/java/com/uber/nullaway/Nullness.java @@ -218,7 +218,7 @@ public static boolean paramHasNullableAnnotation( return true; } return hasNullableAnnotation( - NullabilityUtil.getAllAnnotationsForParameter(symbol, paramInd), config); + NullabilityUtil.getAllAnnotationsForParameter(symbol, paramInd, config), config); } private static boolean isRecordEqualsParam(Symbol.MethodSymbol symbol, int paramInd) { @@ -249,6 +249,6 @@ private static boolean isRecordEqualsParam(Symbol.MethodSymbol symbol, int param public static boolean paramHasNonNullAnnotation( Symbol.MethodSymbol symbol, int paramInd, Config config) { return hasNonNullAnnotation( - NullabilityUtil.getAllAnnotationsForParameter(symbol, paramInd), config); + NullabilityUtil.getAllAnnotationsForParameter(symbol, paramInd, config), config); } } diff --git a/nullaway/src/test/java/com/uber/nullaway/NullAwayJSpecifyTests.java b/nullaway/src/test/java/com/uber/nullaway/NullAwayJSpecifyTests.java index ed9acb1f72..246d7e8a00 100644 --- a/nullaway/src/test/java/com/uber/nullaway/NullAwayJSpecifyTests.java +++ b/nullaway/src/test/java/com/uber/nullaway/NullAwayJSpecifyTests.java @@ -987,6 +987,31 @@ public void nullUnmarkedAndAcknowledgeRestrictiveAnnotations() { .doTest(); } + @Test + public void nullUnmarkedRestrictiveAnnotationsAndGenerics() { + makeTestHelperWithArgs( + Arrays.asList( + "-d", + temporaryFolder.getRoot().getAbsolutePath(), + "-XepOpt:NullAway:AnnotatedPackages=com.uber", + "-XepOpt:NullAway:AcknowledgeRestrictiveAnnotations=true")) + .addSourceLines( + "Test.java", + "package com.uber;", + "import org.jspecify.annotations.NullUnmarked;", + "import org.jetbrains.annotations.Nullable;", + "import org.jetbrains.annotations.NotNull;", + "import java.util.List;", + "public class Test {", + " @NullUnmarked", + " public static void takesNullable(@Nullable List<@NotNull String> l) {}", + " public static void test() {", + " takesNullable(null);", + " }", + "}") + .doTest(); + } + @Test public void nullMarkedStaticImports() { makeTestHelperWithArgs( diff --git a/nullaway/src/test/java/com/uber/nullaway/NullAwayTypeUseAnnotationsTests.java b/nullaway/src/test/java/com/uber/nullaway/NullAwayTypeUseAnnotationsTests.java index 7af8b253d4..5e13b02313 100644 --- a/nullaway/src/test/java/com/uber/nullaway/NullAwayTypeUseAnnotationsTests.java +++ b/nullaway/src/test/java/com/uber/nullaway/NullAwayTypeUseAnnotationsTests.java @@ -38,16 +38,28 @@ public void annotationAppliedToTypeParameter() { "import java.util.List;", "import java.util.ArrayList;", "import org.checkerframework.checker.nullness.qual.Nullable;", + "import org.checkerframework.checker.nullness.qual.NonNull;", "class TypeArgumentAnnotation {", " List<@Nullable String> fSafe = new ArrayList<>();", " @Nullable List fUnsafe = new ArrayList<>();", " void useParamSafe(List<@Nullable String> list) {", " list.hashCode();", " }", + " void unsafeCall() {", + " // BUG: Diagnostic contains: passing @Nullable parameter", + " useParamSafe(null);", + " }", " void useParamUnsafe(@Nullable List list) {", " // BUG: Diagnostic contains: dereferenced", " list.hashCode();", " }", + " void useParamUnsafeNonNullElements(@Nullable List<@NonNull String> list) {", + " // BUG: Diagnostic contains: dereferenced", + " list.hashCode();", + " }", + " void safeCall() {", + " useParamUnsafeNonNullElements(null);", + " }", " void useFieldSafe() {", " fSafe.hashCode();", " }",