diff --git a/CHANGELOG.md b/CHANGELOG.md index c7a8e0c7e9..eda86594a4 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ Changelog ========= +Version 0.10.17 +--------------- +* Fix bug with computing direct type use annotations on parameters (#864) +* Model Apache Flink's RichFunction.open as an @Initializer method (#862) +* Support for JSpecify's 0.3.0 annotation [experimental] + - JSpecify: adding com.google.common to annotated packages in build.gradle (#857) + - JSpecify: handling the return of a diamond operator anonymous object method caller (#858) + - Create com.uber.nullaway.generics package (#855) + - Clarifications and small fixes for checking JSpecify @Nullable annotation (#859) + - Apply minor cleanups suggested by IntelliJ in generics code (#860) + Version 0.10.16 --------------- NOTE: Maven Central signing key rotated for this release following a revocation. diff --git a/gradle.properties b/gradle.properties index bfd4cd076d..fe3f54f81d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ org.gradle.caching=true org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m GROUP=com.uber.nullaway -VERSION_NAME=0.10.17-SNAPSHOT +VERSION_NAME=0.10.18-SNAPSHOT POM_DESCRIPTION=A fast annotation-based null checker for Java 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/ErrorProneCLIFlagsConfig.java b/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java index 9a189fe821..0240d77bec 100644 --- a/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java +++ b/nullaway/src/main/java/com/uber/nullaway/ErrorProneCLIFlagsConfig.java @@ -130,7 +130,11 @@ final class ErrorProneCLIFlagsConfig extends AbstractConfig { "androidx.fragment.app.Fragment.onActivityCreated", "androidx.fragment.app.Fragment.onViewCreated", // Multidex app - "android.support.multidex.Application.onCreate"); + "android.support.multidex.Application.onCreate", + // Apache Flink + // See docs: + // https://nightlies.apache.org/flink/flink-docs-master/api/java/org/apache/flink/api/common/functions/RichFunction.html#open-org.apache.flink.api.common.functions.OpenContext- + "org.apache.flink.api.common.functions.RichFunction.open"); static final ImmutableSet DEFAULT_INITIALIZER_ANNOT = ImmutableSet.of( 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/main/java/com/uber/nullaway/generics/CompareNullabilityVisitor.java b/nullaway/src/main/java/com/uber/nullaway/generics/CompareNullabilityVisitor.java index 760d2008ad..025c3ee1fb 100644 --- a/nullaway/src/main/java/com/uber/nullaway/generics/CompareNullabilityVisitor.java +++ b/nullaway/src/main/java/com/uber/nullaway/generics/CompareNullabilityVisitor.java @@ -24,7 +24,7 @@ public Boolean visitClassType(Type.ClassType lhsType, Type rhsType) { Types types = state.getTypes(); // The base type of rhsType may be a subtype of lhsType's base type. In such cases, we must // compare lhsType against the supertype of rhsType with a matching base type. - rhsType = (Type.ClassType) types.asSuper(rhsType, lhsType.tsym); + rhsType = types.asSuper(rhsType, lhsType.tsym); // This is impossible, considering the fact that standard Java subtyping succeeds before // running NullAway if (rhsType == null) { diff --git a/nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java b/nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java index 247988b3b8..23affc4ea2 100644 --- a/nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java +++ b/nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java @@ -40,7 +40,7 @@ public final class GenericsChecks { /** * Supplier for the JSpecify {@code @Nullable} annotation. Required since for now, certain checks - * related to generics specifically look for {@code @org.jspecify.ananotations.Nullable} + * related to generics specifically look for {@code @org.jspecify.annotations.Nullable} * annotations and do not apply to other {@code @Nullable} annotations. */ static final Supplier JSPECIFY_NULLABLE_TYPE_SUPPLIER = @@ -64,7 +64,7 @@ public static void checkInstantiationForParameterizedTypedTree( return; } List typeArguments = tree.getTypeArguments(); - if (typeArguments.size() == 0) { + if (typeArguments.isEmpty()) { return; } Map nullableTypeArguments = new HashMap<>(); @@ -302,8 +302,7 @@ public static void checkTypeParameterNullnessForAssignability( Type rhsType = getTreeType(rhsTree, state); if (lhsType instanceof Type.ClassType && rhsType instanceof Type.ClassType) { - boolean isAssignmentValid = - compareNullabilityAnnotations((Type.ClassType) lhsType, (Type.ClassType) rhsType, state); + boolean isAssignmentValid = compareNullabilityAnnotations(lhsType, rhsType, state); if (!isAssignmentValid) { reportInvalidAssignmentInstantiationError(tree, lhsType, rhsType, state, analysis); } @@ -337,8 +336,7 @@ public static void checkTypeParameterNullnessForFunctionReturnType( if (formalReturnType instanceof Type.ClassType && returnExpressionType instanceof Type.ClassType) { boolean isReturnTypeValid = - compareNullabilityAnnotations( - (Type.ClassType) formalReturnType, (Type.ClassType) returnExpressionType, state); + compareNullabilityAnnotations(formalReturnType, returnExpressionType, state); if (!isReturnTypeValid) { reportInvalidReturnTypeError( retExpr, formalReturnType, returnExpressionType, state, analysis); @@ -411,15 +409,13 @@ public static void checkTypeParameterNullnessForConditionalExpression( // type of the whole expression if (condExprType instanceof Type.ClassType) { if (truePartType instanceof Type.ClassType) { - if (!compareNullabilityAnnotations( - (Type.ClassType) condExprType, (Type.ClassType) truePartType, state)) { + if (!compareNullabilityAnnotations(condExprType, truePartType, state)) { reportMismatchedTypeForTernaryOperator( truePartTree, condExprType, truePartType, state, analysis); } } if (falsePartType instanceof Type.ClassType) { - if (!compareNullabilityAnnotations( - (Type.ClassType) condExprType, (Type.ClassType) falsePartType, state)) { + if (!compareNullabilityAnnotations(condExprType, falsePartType, state)) { reportMismatchedTypeForTernaryOperator( falsePartTree, condExprType, falsePartType, state, analysis); } @@ -458,8 +454,7 @@ public static void compareGenericTypeParameterNullabilityForCall( Type actualParameter = getTreeType(actualParams.get(i), state); if (formalParameter instanceof Type.ClassType && actualParameter instanceof Type.ClassType) { - if (!compareNullabilityAnnotations( - (Type.ClassType) formalParameter, (Type.ClassType) actualParameter, state)) { + if (!compareNullabilityAnnotations(formalParameter, actualParameter, state)) { reportInvalidParametersNullabilityError( formalParameter, actualParameter, actualParams.get(i), state, analysis); } @@ -470,13 +465,12 @@ public static void compareGenericTypeParameterNullabilityForCall( Type.ArrayType varargsArrayType = (Type.ArrayType) formalParams.get(formalParams.size() - 1).type; Type varargsElementType = varargsArrayType.elemtype; - if (varargsElementType.getTypeArguments().size() > 0) { + if (!varargsElementType.getTypeArguments().isEmpty()) { for (int i = formalParams.size() - 1; i < actualParams.size(); i++) { Type actualParameter = getTreeType(actualParams.get(i), state); if (varargsElementType instanceof Type.ClassType && actualParameter instanceof Type.ClassType) { - if (!compareNullabilityAnnotations( - (Type.ClassType) varargsElementType, (Type.ClassType) actualParameter, state)) { + if (!compareNullabilityAnnotations(varargsElementType, actualParameter, state)) { reportInvalidParametersNullabilityError( varargsElementType, actualParameter, actualParams.get(i), state, analysis); } @@ -786,9 +780,7 @@ private static void checkTypeParameterNullnessForOverridingMethodParameterType( if (overriddenMethodParameterType instanceof Type.ClassType && overridingMethodParameterType instanceof Type.ClassType) { if (!compareNullabilityAnnotations( - (Type.ClassType) overriddenMethodParameterType, - (Type.ClassType) overridingMethodParameterType, - state)) { + overriddenMethodParameterType, overridingMethodParameterType, state)) { reportInvalidOverridingMethodParamTypeError( methodParameters.get(i), overriddenMethodParameterType, @@ -819,9 +811,7 @@ private static void checkTypeParameterNullnessForOverridingMethodReturnType( } Preconditions.checkArgument(overridingMethodReturnType instanceof Type.ClassType); if (!compareNullabilityAnnotations( - (Type.ClassType) overriddenMethodReturnType, - (Type.ClassType) overridingMethodReturnType, - state)) { + overriddenMethodReturnType, overridingMethodReturnType, state)) { reportInvalidOverridingMethodReturnTypeError( tree, overriddenMethodReturnType, overridingMethodReturnType, analysis, state); } 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();", " }",