[nnbd_migration] support spread elements
Change-Id: I170a820f1049856115e6d7c4302c75038d14f27b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116367
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Auto-Submit: Mike Fairhurst <mfairhurst@google.com>
diff --git a/pkg/nnbd_migration/lib/src/decorated_type.dart b/pkg/nnbd_migration/lib/src/decorated_type.dart
index 8ce96e7..9982d1f 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type.dart
@@ -140,20 +140,38 @@
/// nodes everywhere that don't correspond to any source location. These
/// nodes can later be unioned with other nodes.
factory DecoratedType.forImplicitType(
- TypeProvider typeProvider, DartType type, NullabilityGraph graph) {
+ TypeProvider typeProvider, DartType type, NullabilityGraph graph,
+ {List<DecoratedType> typeArguments}) {
if (type.isDynamic || type.isVoid) {
+ assert(typeArguments == null);
return DecoratedType(type, graph.always);
} else if (type is InterfaceType) {
+ assert(() {
+ if (typeArguments != null) {
+ assert(typeArguments.length == type.typeArguments.length);
+ for (var i = 0; i < typeArguments.length; ++i) {
+ assert(typeArguments[i].type == type.typeArguments[i]);
+ }
+ }
+ return true;
+ }());
+
+ typeArguments ??= type.typeArguments
+ .map((t) => DecoratedType.forImplicitType(typeProvider, t, graph))
+ .toList();
return DecoratedType(type, NullabilityNode.forInferredType(),
- typeArguments: type.typeArguments
- .map((t) => DecoratedType.forImplicitType(typeProvider, t, graph))
- .toList());
+ typeArguments: typeArguments);
} else if (type is FunctionType) {
+ if (typeArguments != null) {
+ throw "Not supported: implicit function type with explicit type arguments";
+ }
return DecoratedType.forImplicitFunction(
typeProvider, type, NullabilityNode.forInferredType(), graph);
} else if (type is TypeParameterType) {
+ assert(typeArguments == null);
return DecoratedType(type, NullabilityNode.forInferredType());
} else if (type is BottomTypeImpl) {
+ assert(typeArguments == null);
return DecoratedType(type, NullabilityNode.forInferredType());
}
// TODO(paulberry)
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 9784703a0..a2400b2 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -1132,6 +1132,42 @@
}
@override
+ DecoratedType visitSpreadElement(SpreadElement node) {
+ final spreadType = node.expression.staticType;
+ if (_typeSystem.isSubtypeOf(
+ spreadType, _typeProvider.mapObjectObjectType)) {
+ assert(_currentMapKeyType != null && _currentMapValueType != null);
+ final expectedType = _typeSystem.instantiateType(_typeProvider.mapType,
+ [_currentMapKeyType.type, _currentMapValueType.type]);
+ final expectedDecoratedType = DecoratedType.forImplicitType(
+ _typeProvider, expectedType, _graph,
+ typeArguments: [_currentMapKeyType, _currentMapValueType]);
+
+ _handleAssignment(node.expression,
+ destinationType: expectedDecoratedType);
+ } else if (_typeSystem.isSubtypeOf(
+ spreadType, _typeProvider.iterableDynamicType)) {
+ assert(_currentLiteralElementType != null);
+ final expectedType = _typeSystem.instantiateType(
+ _typeProvider.iterableType, [_currentLiteralElementType.type]);
+ final expectedDecoratedType = DecoratedType.forImplicitType(
+ _typeProvider, expectedType, _graph,
+ typeArguments: [_currentLiteralElementType]);
+
+ _handleAssignment(node.expression,
+ destinationType: expectedDecoratedType);
+ } else {
+ // Downcast. We can't assume nullability here, so do nothing.
+ }
+
+ if (!node.isNullAware) {
+ _checkExpressionNotNull(node.expression);
+ }
+
+ return null;
+ }
+
+ @override
DecoratedType visitStringLiteral(StringLiteral node) {
node.visitChildren(this);
return DecoratedType(node.staticType, _graph.never);
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index cf76670..8cce491a 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -4409,6 +4409,93 @@
assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
}
+ test_spread_element_list() async {
+ await analyze('''
+void f(List<int> ints) {
+ <int>[...ints];
+}
+''');
+
+ assertEdge(decoratedTypeAnnotation('List<int>').node, never, hard: true);
+ assertEdge(
+ substitutionNode(decoratedTypeAnnotation('int> ints').node, anyNode),
+ decoratedTypeAnnotation('int>[').node,
+ hard: false);
+ }
+
+ test_spread_element_list_dynamic() async {
+ await analyze('''
+void f(dynamic ints) {
+ <int>[...ints];
+}
+''');
+
+ // Mostly just check this doesn't crash.
+ assertEdge(decoratedTypeAnnotation('dynamic').node, never, hard: true);
+ }
+
+ test_spread_element_list_nullable() async {
+ await analyze('''
+void f(List<int> ints) {
+ <int>[...?ints];
+}
+''');
+
+ assertNoEdge(decoratedTypeAnnotation('List<int>').node, never);
+ assertEdge(
+ substitutionNode(decoratedTypeAnnotation('int> ints').node, anyNode),
+ decoratedTypeAnnotation('int>[').node,
+ hard: false);
+ }
+
+ test_spread_element_map() async {
+ await analyze('''
+void f(Map<String, int> map) {
+ <String, int>{...map};
+}
+''');
+
+ assertEdge(decoratedTypeAnnotation('Map<String, int>').node, never,
+ hard: true);
+ assertEdge(decoratedTypeAnnotation('String, int> map').node,
+ decoratedTypeAnnotation('String, int>{').node,
+ hard: false);
+ assertEdge(decoratedTypeAnnotation('int> map').node,
+ decoratedTypeAnnotation('int>{').node,
+ hard: false);
+ }
+
+ test_spread_element_set() async {
+ await analyze('''
+void f(Set<int> ints) {
+ <int>{...ints};
+}
+''');
+
+ assertEdge(decoratedTypeAnnotation('Set<int>').node, never, hard: true);
+ assertEdge(
+ substitutionNode(decoratedTypeAnnotation('int> ints').node, anyNode),
+ decoratedTypeAnnotation('int>{').node,
+ hard: false);
+ }
+
+ test_spread_element_subtype() async {
+ await analyze('''
+abstract class C<T, R> implements Iterable<R> {}
+void f(C<dynamic, int> ints) {
+ <int>[...ints];
+}
+''');
+
+ assertEdge(decoratedTypeAnnotation('C<dynamic, int>').node, never,
+ hard: true);
+ assertEdge(
+ substitutionNode(decoratedTypeAnnotation('int> ints').node,
+ decoratedTypeAnnotation('R> {}').node),
+ decoratedTypeAnnotation('int>[').node,
+ hard: false);
+ }
+
test_static_method_call_prefixed() async {
await analyze('''
import 'dart:async' as a;