Skip to content

Commit

Permalink
Fix logic for @nullable annotation on type parameter (uber#702)
Browse files Browse the repository at this point in the history
We had logic to not consider type use annotations on type parameters when they appeared in return types, but not parameter types or field types.

Fixes uber#701
  • Loading branch information
msridhar committed Dec 23, 2022
1 parent c1427da commit 5ccffb5
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 8 deletions.
24 changes: 16 additions & 8 deletions nullaway/src/main/java/com/uber/nullaway/NullabilityUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -272,19 +272,27 @@ public static Stream<? extends AnnotationMirror> 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<? extends AnnotationMirror> getTypeUseAnnotations(Symbol symbol) {
Stream<Attribute.TypeCompound> 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();
}

/**
Expand Down
30 changes: 30 additions & 0 deletions nullaway/src/test/java/com/uber/nullaway/NullAwayCoreTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> fUnsafe = new ArrayList<>();",
" void useParamSafe(List<@Nullable String> list) {",
" list.hashCode();",
" }",
" void useParamUnsafe(@Nullable List<String> list) {",
" // BUG: Diagnostic contains: dereferenced",
" list.hashCode();",
" }",
" void useFieldSafe() {",
" fSafe.hashCode();",
" }",
" void useFieldUnsafe() {",
" // BUG: Diagnostic contains: dereferenced",
" fUnsafe.hashCode();",
" }",
"}")
.doTest();
}
}

0 comments on commit 5ccffb5

Please sign in to comment.