diff --git a/nullaway/src/main/java/com/uber/nullaway/NullabilityUtil.java b/nullaway/src/main/java/com/uber/nullaway/NullabilityUtil.java index eb037662a7..ee1e6e8a82 100644 --- a/nullaway/src/main/java/com/uber/nullaway/NullabilityUtil.java +++ b/nullaway/src/main/java/com/uber/nullaway/NullabilityUtil.java @@ -272,19 +272,27 @@ public static Stream getAllAnnotationsForParameter( && t.position.parameter_index == paramInd)); } + /** + * Gets the type use annotations on a symbol, ignoring annotations on components of the type (type + * arguments, wildcards, etc.) + */ private static Stream getTypeUseAnnotations(Symbol symbol) { Stream rawTypeAttributes = symbol.getRawTypeAttributes().stream(); if (symbol instanceof Symbol.MethodSymbol) { - // for methods, we want the type-use annotations on the return type + // for methods, we want annotations on the return type return rawTypeAttributes.filter( - (t) -> - // location is a list of TypePathEntry objects, indicating whether the annotation is - // on an array, inner type, wildcard, or type argument. If it's empty, then the - // annotation - // is directly on the return type. - t.position.type.equals(TargetType.METHOD_RETURN) && t.position.location.isEmpty()); + (t) -> t.position.type.equals(TargetType.METHOD_RETURN) && isDirectTypeUseAnnotation(t)); + } else { + // filter for annotations directly on the type + return rawTypeAttributes.filter(NullabilityUtil::isDirectTypeUseAnnotation); } - return rawTypeAttributes; + } + + private static boolean isDirectTypeUseAnnotation(Attribute.TypeCompound t) { + // location is a list of TypePathEntry objects, indicating whether the annotation is + // on an array, inner type, wildcard, or type argument. If it's empty, then the + // annotation is directly on the type. + return t.position.location.isEmpty(); } /** diff --git a/nullaway/src/test/java/com/uber/nullaway/NullAwayCoreTests.java b/nullaway/src/test/java/com/uber/nullaway/NullAwayCoreTests.java index f8f3ed622c..0f4afe44a0 100644 --- a/nullaway/src/test/java/com/uber/nullaway/NullAwayCoreTests.java +++ b/nullaway/src/test/java/com/uber/nullaway/NullAwayCoreTests.java @@ -960,4 +960,34 @@ public void primitiveCastsRememberNullChecks() { "}") .doTest(); } + + @Test + public void annotationAppliedToTypeParameter() { + defaultCompilationHelper + .addSourceLines( + "Test.java", + "package com.uber;", + "import java.util.List;", + "import java.util.ArrayList;", + "import org.checkerframework.checker.nullness.qual.Nullable;", + "class TypeArgumentAnnotation {", + " List<@Nullable String> fSafe = new ArrayList<>();", + " @Nullable List fUnsafe = new ArrayList<>();", + " void useParamSafe(List<@Nullable String> list) {", + " list.hashCode();", + " }", + " void useParamUnsafe(@Nullable List list) {", + " // BUG: Diagnostic contains: dereferenced", + " list.hashCode();", + " }", + " void useFieldSafe() {", + " fSafe.hashCode();", + " }", + " void useFieldUnsafe() {", + " // BUG: Diagnostic contains: dereferenced", + " fUnsafe.hashCode();", + " }", + "}") + .doTest(); + } }