Skip to content

Commit 8af2934

Browse files
author
Dart CI
committed
Version 2.11.0-239.0.dev
Merge commit 'f1843b0abed6b43eb2e737b0edd52671c9c31eda' into 'dev'
2 parents 8be6a08 + f1843b0 commit 8af2934

18 files changed

+1306
-36
lines changed

pkg/analyzer/lib/src/test_utilities/mock_sdk.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,12 @@ abstract class Iterable<E> {
381381
382382
void forEach(void f(E element));
383383
384+
E lastWhere(bool test(E element), {E orElse()?});
385+
384386
Iterable<R> map<R>(R f(E e));
385387
388+
E singleWhere(bool test(E element), {E orElse()?});
389+
386390
List<E> toList({bool growable = true});
387391
388392
Set<E> toSet();

pkg/nnbd_migration/lib/instrumentation.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ enum EdgeOriginKind {
277277
instantiateToBounds,
278278
isCheckComponentType,
279279
isCheckMainType,
280+
iteratorMethodReturn,
280281
listLengthConstructor,
281282
literal,
282283
namedParameterNotSupplied,

pkg/nnbd_migration/lib/nnbd_migration.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,12 +291,18 @@ abstract class NullabilityMigration {
291291
/// Optional parameter [warnOnWeakCode] indicates whether weak-only code
292292
/// should be warned about or removed (in the way specified by
293293
/// [removeViaComments]).
294+
///
295+
/// Optional parameter [transformWhereOrNull] indicates whether Iterable
296+
/// methods should be transformed to their "OrNull" equivalents when possible.
297+
/// This feature is a work in progress, so by default they are not
298+
/// transformed.
294299
factory NullabilityMigration(NullabilityMigrationListener listener,
295300
LineInfo Function(String) getLineInfo,
296301
{bool permissive,
297302
NullabilityMigrationInstrumentation instrumentation,
298303
bool removeViaComments,
299-
bool warnOnWeakCode}) = NullabilityMigrationImpl;
304+
bool warnOnWeakCode,
305+
bool transformWhereOrNull}) = NullabilityMigrationImpl;
300306

301307
/// Check if this migration is being run permissively.
302308
bool get isPermissive;

pkg/nnbd_migration/lib/src/edge_builder.dart

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import 'package:nnbd_migration/src/utilities/hint_utils.dart';
3232
import 'package:nnbd_migration/src/utilities/permissive_mode.dart';
3333
import 'package:nnbd_migration/src/utilities/resolution_utils.dart';
3434
import 'package:nnbd_migration/src/utilities/scoped_set.dart';
35+
import 'package:nnbd_migration/src/utilities/where_or_null_transformer.dart';
3536
import 'package:nnbd_migration/src/variables.dart';
3637

3738
import '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);

pkg/nnbd_migration/lib/src/edge_origin.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,19 @@ class IsCheckMainTypeOrigin extends EdgeOrigin {
371371
EdgeOriginKind get kind => EdgeOriginKind.isCheckMainType;
372372
}
373373

374+
/// An edge origin used for the return type of an iterator method that might be
375+
/// changed into an extension method from package:collection.
376+
class IteratorMethodReturnOrigin extends EdgeOrigin {
377+
IteratorMethodReturnOrigin(Source source, AstNode node) : super(source, node);
378+
379+
@override
380+
String get description =>
381+
'Call to iterator method with orElse that returns null';
382+
383+
@override
384+
EdgeOriginKind get kind => EdgeOriginKind.iteratorMethodReturn;
385+
}
386+
374387
/// An edge origin used for the type argument of a list constructor that
375388
/// specified an initial length, because that type argument must be nullable.
376389
class ListLengthConstructorOrigin extends EdgeOrigin {

0 commit comments

Comments
 (0)