Skip to content

Commit 33588de

Browse files
authored
Infer @Nullable type arguments for type variables from unmarked code (#1181)
Fixes #1178
1 parent dd0fe71 commit 33588de

File tree

3 files changed

+35
-5
lines changed

3 files changed

+35
-5
lines changed

nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -453,8 +453,12 @@ public void checkTypeParameterNullnessForAssignability(
453453
&& methodInvocationTree.getTypeArguments().isEmpty()) {
454454
// generic method call with no explicit generic arguments
455455
// update inferred type arguments based on the assignment context
456-
InferSubstitutionViaAssignmentContextVisitor inferVisitor =
457-
new InferSubstitutionViaAssignmentContextVisitor(state, config);
456+
boolean invokedMethodIsNullUnmarked =
457+
CodeAnnotationInfo.instance(state.context)
458+
.isSymbolUnannotated(methodSymbol, config, analysis.getHandler());
459+
InferGenericMethodSubstitutionViaAssignmentContextVisitor inferVisitor =
460+
new InferGenericMethodSubstitutionViaAssignmentContextVisitor(
461+
state, config, invokedMethodIsNullUnmarked);
458462
Type returnType = methodSymbol.getReturnType();
459463
returnType.accept(inferVisitor, lhsType);
460464

nullaway/src/main/java/com/uber/nullaway/generics/InferSubstitutionViaAssignmentContextVisitor.java renamed to nullaway/src/main/java/com/uber/nullaway/generics/InferGenericMethodSubstitutionViaAssignmentContextVisitor.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,19 @@
1414
* Visitor that infers a substitution for type variables via types appearing at the same position in
1515
* a type provided via the assignment context.
1616
*/
17-
public class InferSubstitutionViaAssignmentContextVisitor
17+
public class InferGenericMethodSubstitutionViaAssignmentContextVisitor
1818
extends Types.DefaultTypeVisitor<Void, Type> {
1919

2020
private final VisitorState state;
2121
private final Config config;
22+
private final boolean invokedMethodIsNullUnmarked;
2223
private final Map<TypeVariable, Type> inferredSubstitution = new LinkedHashMap<>();
2324

24-
InferSubstitutionViaAssignmentContextVisitor(VisitorState state, Config config) {
25+
InferGenericMethodSubstitutionViaAssignmentContextVisitor(
26+
VisitorState state, Config config, boolean invokedMethodIsNullUnmarked) {
2527
this.state = state;
2628
this.config = config;
29+
this.invokedMethodIsNullUnmarked = invokedMethodIsNullUnmarked;
2730
}
2831

2932
@Override
@@ -64,9 +67,12 @@ public Void visitTypeVar(Type.TypeVar typeVar, Type lhsType) {
6467
Type upperBound = typeVar.getUpperBound();
6568
boolean typeVarHasNullableUpperBound =
6669
Nullness.hasNullableAnnotation(upperBound.getAnnotationMirrors().stream(), config);
67-
if (typeVarHasNullableUpperBound) { // can just use the lhs type nullability
70+
if (typeVarHasNullableUpperBound
71+
|| invokedMethodIsNullUnmarked) { // can just use the lhs type nullability
6872
inferredSubstitution.put(typeVar, lhsType);
6973
} else { // rhs can't be nullable. use lhsType but strip @Nullable annotation
74+
// TODO we should just strip out the top-level @Nullable annotation;
75+
// stripMetadata() also removes nested @Nullable annotations
7076
inferredSubstitution.put(typeVar, lhsType.stripMetadata());
7177
}
7278
return null;

nullaway/src/test/java/com/uber/nullaway/jspecify/GenericMethodTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,26 @@ public void issue1176() {
506506
.doTest();
507507
}
508508

509+
@Test
510+
public void issue1178() {
511+
makeHelper()
512+
.addSourceLines(
513+
"SampleNullUnmarkedCall.java",
514+
"import org.jspecify.annotations.*;",
515+
"@NullMarked",
516+
"class SampleNullUnmarkedCall {",
517+
" @NullUnmarked",
518+
" static class Foo<T> {",
519+
" Foo(T t) {}",
520+
" static <U> Foo<U> id(U u) { return new Foo<>(u); }",
521+
" }",
522+
" static void test() {",
523+
" Foo<@Nullable Object> x = Foo.id(null);",
524+
" }",
525+
"}")
526+
.doTest();
527+
}
528+
509529
private CompilationTestHelper makeHelper() {
510530
return makeTestHelperWithArgs(
511531
Arrays.asList(

0 commit comments

Comments
 (0)