in nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java [563:613]
private void handleEnhancedForOverKeySet(
LocalVariableNode lhs,
Node rhs,
TransferInput<Nullness, NullnessStore> input,
ReadableUpdates updates) {
if (isEnhancedForIteratorVariable(lhs)) {
// Based on the structure of Checker Framework CFGs, rhs must be a call of the form
// e.iterator(). We check if e is a call to keySet() on a Map, and if so, propagate
// NONNULL for an access path for e.get(iteratorContents(lhs))
MethodInvocationNode rhsInv = (MethodInvocationNode) rhs;
Node mapNode = getMapNodeForKeySetIteratorCall(rhsInv);
if (mapNode != null) {
AccessPath mapWithIteratorContentsKey =
AccessPath.mapWithIteratorContentsKey(mapNode, lhs, apContext);
if (mapWithIteratorContentsKey != null) {
// put sanity check here to minimize perf impact
if (!isCallToMethod(rhsInv, SET_TYPE_SUPPLIER, "iterator")) {
throw new VerifyException(
"expected call to iterator(), instead saw "
+ state.getSourceForNode(rhsInv.getTree()));
}
updates.set(mapWithIteratorContentsKey, NONNULL);
}
}
} else if (rhs instanceof MethodInvocationNode) {
// Check for an assignment lhs = iter#numX.next(). From the structure of Checker Framework
// CFGs, we know that if iter#numX is the receiver of a call on the rhs of an assignment, it
// must be a call to next().
MethodInvocationNode methodInv = (MethodInvocationNode) rhs;
Node receiver = methodInv.getTarget().getReceiver();
if (receiver instanceof LocalVariableNode
&& isEnhancedForIteratorVariable((LocalVariableNode) receiver)) {
// See if we are tracking an access path e.get(iteratorContents(receiver)). If so, since
// lhs is being assigned from the iterator contents, propagate NONNULL for an access path
// e.get(lhs)
AccessPath mapGetPath =
input
.getRegularStore()
.getMapGetIteratorContentsAccessPath((LocalVariableNode) receiver);
if (mapGetPath != null) {
// put sanity check here to minimize perf impact
if (!isCallToMethod(methodInv, ITERATOR_TYPE_SUPPLIER, "next")) {
throw new VerifyException(
"expected call to next(), instead saw "
+ state.getSourceForNode(methodInv.getTree()));
}
updates.set(AccessPath.replaceMapKey(mapGetPath, AccessPath.fromLocal(lhs)), NONNULL);
}
}
}
}