@@ -32,6 +32,7 @@ import 'package:nnbd_migration/src/utilities/hint_utils.dart';
3232import 'package:nnbd_migration/src/utilities/permissive_mode.dart' ;
3333import 'package:nnbd_migration/src/utilities/resolution_utils.dart' ;
3434import 'package:nnbd_migration/src/utilities/scoped_set.dart' ;
35+ import 'package:nnbd_migration/src/utilities/where_or_null_transformer.dart' ;
3536import 'package:nnbd_migration/src/variables.dart' ;
3637
3738import 'decorated_type_operations.dart' ;
@@ -213,10 +214,29 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
213214
214215 final Map <Token , HintComment > _nullCheckHints = {};
215216
216- EdgeBuilder (this .typeProvider, this ._typeSystem, this ._variables, this ._graph,
217- this .source, this .listener, this ._decoratedClassHierarchy,
217+ /// Helper that assists us in transforming Iterable methods to their "OrNull"
218+ /// equivalents, or `null` if we are not doing such transformations.
219+ final WhereOrNullTransformer _whereOrNullTransformer;
220+
221+ /// Deferred processing that should be performed once we have finished
222+ /// evaluating the decorated type of a method invocation.
223+ final Map <MethodInvocation , DecoratedType Function (DecoratedType )>
224+ _deferredMethodInvocationProcessing = {};
225+
226+ EdgeBuilder (
227+ this .typeProvider,
228+ this ._typeSystem,
229+ this ._variables,
230+ this ._graph,
231+ this .source,
232+ this .listener,
233+ this ._decoratedClassHierarchy,
234+ bool transformWhereOrNull,
218235 {this .instrumentation})
219- : _inheritanceManager = InheritanceManager3 ();
236+ : _inheritanceManager = InheritanceManager3 (),
237+ _whereOrNullTransformer = transformWhereOrNull
238+ ? WhereOrNullTransformer (typeProvider, _typeSystem)
239+ : null ;
220240
221241 /// Gets the decorated type of [element] from [_variables] , performing any
222242 /// necessary substitutions.
@@ -880,6 +900,7 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
880900 _postDominatedLocals.doScoped (
881901 elements: node.declaredElement.parameters,
882902 action: () => _dispatch (node.body));
903+ _variables.recordDecoratedExpressionType (node, _currentFunctionType);
883904 return _currentFunctionType;
884905 } finally {
885906 if (node.parent is ! FunctionDeclaration ) {
@@ -1228,11 +1249,16 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
12281249 calleeType,
12291250 null ,
12301251 invokeType: node.staticInvokeType);
1252+ // Do any deferred processing for this method invocation.
1253+ var deferredProcessing = _deferredMethodInvocationProcessing.remove (node);
1254+ if (deferredProcessing != null ) {
1255+ expressionType = deferredProcessing (expressionType);
1256+ }
12311257 if (isNullAware) {
12321258 expressionType = expressionType.withNode (
12331259 NullabilityNode .forLUB (targetType.node, expressionType.node));
1234- _variables.recordDecoratedExpressionType (node, expressionType);
12351260 }
1261+ _variables.recordDecoratedExpressionType (node, expressionType);
12361262 }
12371263 _handleArgumentErrorCheckNotNull (node);
12381264 _handleQuiverCheckNotNull (node);
@@ -2260,18 +2286,40 @@ class EdgeBuilder extends GeneralizingAstVisitor<DecoratedType>
22602286 sourceType = _makeNullableDynamicType (compoundOperatorInfo);
22612287 }
22622288 } else {
2263- var unwrappedExpression = expression.unParenthesized;
2264- var hard = (questionAssignNode == null &&
2265- _postDominatedLocals.isReferenceInScope (expression)) ||
2266- // An edge from a cast should be hard, so that the cast type
2267- // annotation is appropriately made nullable according to the
2268- // destination type.
2269- unwrappedExpression is AsExpression ;
2270- _checkAssignment (edgeOrigin, FixReasonTarget .root,
2271- source: sourceType,
2272- destination: destinationType,
2273- hard: hard,
2274- sourceIsFunctionLiteral: expression is FunctionExpression );
2289+ var transformationInfo =
2290+ _whereOrNullTransformer? .tryTransformOrElseArgument (expression);
2291+ if (transformationInfo != null ) {
2292+ // Don't build any edges for this argument; if necessary we'll transform
2293+ // it rather than make things nullable. But do save the nullability of
2294+ // the return value of the `orElse` method, so that we can later connect
2295+ // it to the nullability of the value returned from the method
2296+ // invocation.
2297+ var extraNullability = sourceType.returnType.node;
2298+ _deferredMethodInvocationProcessing[
2299+ transformationInfo.methodInvocation] = (methodInvocationType) {
2300+ var newNode = NullabilityNode .forInferredType (
2301+ NullabilityNodeTarget .text (
2302+ 'return value from ${transformationInfo .originalName }' ));
2303+ var origin = IteratorMethodReturnOrigin (
2304+ source, transformationInfo.methodInvocation);
2305+ _graph.connect (methodInvocationType.node, newNode, origin);
2306+ _graph.connect (extraNullability, newNode, origin);
2307+ return methodInvocationType.withNode (newNode);
2308+ };
2309+ } else {
2310+ var unwrappedExpression = expression.unParenthesized;
2311+ var hard = (questionAssignNode == null &&
2312+ _postDominatedLocals.isReferenceInScope (expression)) ||
2313+ // An edge from a cast should be hard, so that the cast type
2314+ // annotation is appropriately made nullable according to the
2315+ // destination type.
2316+ unwrappedExpression is AsExpression ;
2317+ _checkAssignment (edgeOrigin, FixReasonTarget .root,
2318+ source: sourceType,
2319+ destination: destinationType,
2320+ hard: hard,
2321+ sourceIsFunctionLiteral: expression is FunctionExpression );
2322+ }
22752323 }
22762324 if (destinationLocalVariable != null ) {
22772325 _flowAnalysis.write (destinationLocalVariable, sourceType);
0 commit comments