Skip to content

Commit 1e83ccf

Browse files
authored
JSpecify: assume NONNULL in generic method inference for unconstrained type variables (#1471)
1 parent bd25690 commit 1e83ccf

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,14 @@ private MethodInferenceResult runInferenceForCall(
729729
methodSymbol,
730730
invocationTree,
731731
allInvocations);
732-
typeVarNullability = solver.solve();
732+
typeVarNullability = new HashMap<>(solver.solve());
733+
// The solver only computes a solution for variables that appear in constraints. For
734+
// unconstrained variables, treat them as NONNULL, consistent with solver behavior for
735+
// unconstrained variables that do appear in the constraint graph.
736+
for (int i = 0; i < methodSymbol.getTypeParameters().size(); i++) {
737+
Symbol.TypeVariableSymbol typeVar = methodSymbol.getTypeParameters().get(i);
738+
typeVarNullability.putIfAbsent(typeVar, ConstraintSolver.InferredNullability.NONNULL);
739+
}
733740

734741
// Store inferred types for lambda arguments
735742
new InvocationArguments(invocationTree, methodSymbol.type.asMethodType())

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,45 @@ public static <T> T notNull(@Nullable T object, String message) {
15281528
.doTest();
15291529
}
15301530

1531+
@Test
1532+
public void issue1453() {
1533+
makeHelper()
1534+
.addSourceLines(
1535+
"NullUtil.java",
1536+
"""
1537+
package com.uber;
1538+
import org.jspecify.annotations.NullMarked;
1539+
import org.jspecify.annotations.Nullable;
1540+
@NullMarked
1541+
public class NullUtil {
1542+
@SuppressWarnings("NullAway")
1543+
public static <T> T assumeNonNull(@Nullable T object) {
1544+
return object;
1545+
}
1546+
}
1547+
""")
1548+
.addSourceLines(
1549+
"Test.java",
1550+
"""
1551+
package com.uber;
1552+
import java.util.List;
1553+
import org.jspecify.annotations.NullMarked;
1554+
import org.jspecify.annotations.Nullable;
1555+
import static com.uber.NullUtil.assumeNonNull;
1556+
import java.util.concurrent.atomic.AtomicReference;
1557+
@NullMarked
1558+
class Test {
1559+
private final AtomicReference<@Nullable List<String>> ref = new AtomicReference<>();
1560+
void test() {
1561+
for (String s: assumeNonNull(ref.get())) {
1562+
System.out.println(s);
1563+
}
1564+
}
1565+
}
1566+
""")
1567+
.doTest();
1568+
}
1569+
15311570
private CompilationTestHelper makeHelper() {
15321571
return makeTestHelperWithArgs(
15331572
JSpecifyJavacConfig.withJSpecifyModeArgs(

0 commit comments

Comments
 (0)