Skip to content

Commit 533986c

Browse files
authored
Record when generic method inference fails (#1280)
1 parent cb72c5a commit 533986c

2 files changed

Lines changed: 45 additions & 12 deletions

File tree

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

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,37 @@
6161
/** Methods for performing checks related to generic types and nullability. */
6262
public final class GenericsChecks {
6363

64+
/** Marker interface for results of attempting to infer nullability of type variables at a call */
65+
private interface MethodInferenceResult {}
66+
67+
/**
68+
* Indicates successful inference of nullability of type variables at a call. Stores the inferred
69+
* type variable nullability.
70+
*/
71+
private static final class InferenceSuccess implements MethodInferenceResult {
72+
final Map<Element, ConstraintSolver.InferredNullability> typeVarNullability;
73+
74+
InferenceSuccess(Map<Element, ConstraintSolver.InferredNullability> typeVarNullability) {
75+
this.typeVarNullability = typeVarNullability;
76+
}
77+
}
78+
79+
/** Indicates failed inference of nullability of type variables at a call */
80+
private static final class InferenceFailure implements MethodInferenceResult {
81+
@SuppressWarnings("UnusedVariable") // keep this as it may be useful in the future
82+
final @Nullable String errorMessage;
83+
84+
InferenceFailure(@Nullable String errorMessage) {
85+
this.errorMessage = errorMessage;
86+
}
87+
}
88+
6489
/**
65-
* Maps a Tree representing a call to a generic method or constructor to the inferred nullability
66-
* of its type arguments. The call must not have any explicit type arguments.
90+
* Maps a Tree representing a call to a generic method or constructor to the result of inferring
91+
* its type argument nullability. The call must not have any explicit type arguments. If a tree is
92+
* not present as a key in this map, it means inference has not yet been attempted for that call.
6793
*/
68-
private final Map<MethodInvocationTree, Map<Element, ConstraintSolver.InferredNullability>>
94+
private final Map<MethodInvocationTree, MethodInferenceResult>
6995
inferredTypeVarNullabilityForGenericCalls = new LinkedHashMap<>();
7096

7197
private final NullAway analysis;
@@ -496,9 +522,11 @@ private Type inferGenericMethodCallType(
496522
Verify.verify(isGenericCallNeedingInference(invocationTree));
497523
Symbol.MethodSymbol methodSymbol = ASTHelpers.getSymbol(invocationTree);
498524
Type type = methodSymbol.type;
499-
Map<Element, ConstraintSolver.InferredNullability> typeVarNullability =
500-
inferredTypeVarNullabilityForGenericCalls.get(invocationTree);
501-
if (typeVarNullability == null) {
525+
Map<Element, ConstraintSolver.InferredNullability> typeVarNullability = null;
526+
MethodInferenceResult result = inferredTypeVarNullabilityForGenericCalls.get(invocationTree);
527+
if (result instanceof InferenceSuccess) {
528+
typeVarNullability = ((InferenceSuccess) result).typeVarNullability;
529+
} else if (result == null) { // have not yet attempted inference for this call
502530
// generic method call with no explicit generic arguments
503531
// update inferred type arguments based on the assignment context
504532
ConstraintSolver solver = makeSolver(state, analysis);
@@ -516,7 +544,8 @@ private Type inferGenericMethodCallType(
516544
allInvocations);
517545
typeVarNullability = solver.solve();
518546
for (MethodInvocationTree invTree : allInvocations) {
519-
inferredTypeVarNullabilityForGenericCalls.put(invTree, typeVarNullability);
547+
inferredTypeVarNullabilityForGenericCalls.put(
548+
invTree, new InferenceSuccess(typeVarNullability));
520549
}
521550
} catch (UnsatisfiableConstraintsException e) {
522551
if (config.warnOnGenericInferenceFailure()) {
@@ -531,6 +560,10 @@ private Type inferGenericMethodCallType(
531560
errorBuilder.createErrorDescription(
532561
errorMessage, analysis.buildDescription(invocationTree), state, null));
533562
}
563+
for (MethodInvocationTree invTree : allInvocations) {
564+
inferredTypeVarNullabilityForGenericCalls.put(
565+
invTree, new InferenceFailure(e.getMessage()));
566+
}
534567
}
535568
}
536569
// we get the return type of the method call with inferred nullability of type variables
@@ -1127,11 +1160,11 @@ private Type substituteTypeArgsInGenericMethodType(
11271160
com.sun.tools.javac.util.List<Type> explicitTypeArgs = convertTreesToTypes(typeArgumentTrees);
11281161

11291162
// There are no explicit type arguments, so use the inferred types
1130-
if (explicitTypeArgs.isEmpty()) {
1131-
if (inferredTypeVarNullabilityForGenericCalls.containsKey(tree)
1132-
&& tree instanceof MethodInvocationTree) {
1163+
if (explicitTypeArgs.isEmpty() && tree instanceof MethodInvocationTree) {
1164+
MethodInferenceResult result = inferredTypeVarNullabilityForGenericCalls.get(tree);
1165+
if (result instanceof InferenceSuccess) {
11331166
return getTypeWithInferredNullability(
1134-
state, methodType, inferredTypeVarNullabilityForGenericCalls.get(tree));
1167+
state, methodType, ((InferenceSuccess) result).typeVarNullability);
11351168
}
11361169
}
11371170
return TypeSubstitutionUtils.subst(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ public void varargsInference() {
820820
" Foo<String> foo3 = make(\"hello\", null, \"world\");",
821821
" Foo<@Nullable String> foo4 = make(\"hello\", \"world\");",
822822
" Foo<@Nullable String> foo5 = make(\"hello\", \"world\", makeStr(null));",
823-
" // BUG: Diagnostic contains: passing @Nullable parameter 'makeStr(null)' where @NonNull is required",
823+
" // BUG: Diagnostic contains: passing @Nullable parameter 'null' where @NonNull is required",
824824
" Foo<String> foo6 = make(\"hello\", \"world\", makeStr(null));",
825825
" // Inference from assignment context only (no args)",
826826
" Foo<String> foo7 = make();",

0 commit comments

Comments
 (0)