Support set and map literals with type arguments
Change-Id: I2e5ab2b81a37b0ebcfab4c60d0ed04c7c6aeba5e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106424
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/nnbd_migration/lib/src/graph_builder.dart b/pkg/nnbd_migration/lib/src/graph_builder.dart
index 358601b..97b1092 100644
--- a/pkg/nnbd_migration/lib/src/graph_builder.dart
+++ b/pkg/nnbd_migration/lib/src/graph_builder.dart
@@ -555,7 +555,47 @@
@override
DecoratedType visitSetOrMapLiteral(SetOrMapLiteral node) {
- throw new UnimplementedError('TODO(brianwilkerson)');
+ var listType = node.staticType as InterfaceType;
+ var typeArguments = node.typeArguments?.arguments;
+ if (typeArguments == null) {
+ // TODO(brianwilkerson) We might want to create fake nodes in the graph to
+ // represent the type arguments so that we can still create edges from
+ // the elements to them.
+ throw new UnimplementedError('TODO(brianwilkerson)');
+ } else if (typeArguments.length == 1) {
+ var elementType =
+ _variables.decoratedTypeAnnotation(_source, typeArguments[0]);
+ for (var element in node.elements) {
+ if (element is Expression) {
+ _handleAssignment(elementType, element);
+ } else {
+ // Handle spread and control flow elements.
+ element.accept(this);
+ throw new UnimplementedError('TODO(brianwilkerson)');
+ }
+ }
+ return DecoratedType(listType, _graph.never,
+ typeArguments: [elementType]);
+ } else if (typeArguments.length == 2) {
+ var keyType =
+ _variables.decoratedTypeAnnotation(_source, typeArguments[0]);
+ var valueType =
+ _variables.decoratedTypeAnnotation(_source, typeArguments[1]);
+ for (var element in node.elements) {
+ if (element is MapLiteralEntry) {
+ _handleAssignment(keyType, element.key);
+ _handleAssignment(valueType, element.value);
+ } else {
+ // Handle spread and control flow elements.
+ element.accept(this);
+ throw new UnimplementedError('TODO(brianwilkerson)');
+ }
+ }
+ return DecoratedType(listType, _graph.never,
+ typeArguments: [keyType, valueType]);
+ } else {
+ throw new UnimplementedError('TODO(brianwilkerson)');
+ }
}
@override
diff --git a/pkg/nnbd_migration/test/migration_visitor_test.dart b/pkg/nnbd_migration/test/migration_visitor_test.dart
index e7e51b5..85e574a 100644
--- a/pkg/nnbd_migration/test/migration_visitor_test.dart
+++ b/pkg/nnbd_migration/test/migration_visitor_test.dart
@@ -1271,6 +1271,166 @@
checkExpression('null'), assertEdge(always, tNode, hard: false));
}
+ @failingTest
+ test_setOrMapLiteral_map_noTypeArgument_noNullableKeysAndValues() async {
+ // Failing because we're not yet handling collection literals without a
+ // type argument.
+ await analyze('''
+Map<String, int> f() {
+ return {'a' : 1, 'b' : 2};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+ // TODO(brianwilkerson) Add an assertion that there is an edge from the set
+ // literal's fake type argument to the return type's type argument.
+ }
+
+ @failingTest
+ test_setOrMapLiteral_map_noTypeArgument_nullableKey() async {
+ // Failing because we're not yet handling collection literals without a
+ // type argument.
+ await analyze('''
+Map<String, int> f() {
+ return {'a' : 1, null : 2, 'c' : 3};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+ assertEdge(always, decoratedTypeAnnotation('String').node, hard: false);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
+ }
+
+ @failingTest
+ test_setOrMapLiteral_map_noTypeArgument_nullableKeyAndValue() async {
+ // Failing because we're not yet handling collection literals without a
+ // type argument.
+ await analyze('''
+Map<String, int> f() {
+ return {'a' : 1, null : null, 'c' : 3};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+ assertEdge(always, decoratedTypeAnnotation('String').node, hard: false);
+ assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+ }
+
+ @failingTest
+ test_setOrMapLiteral_map_noTypeArgument_nullableValue() async {
+ // Failing because we're not yet handling collection literals without a
+ // type argument.
+ await analyze('''
+Map<String, int> f() {
+ return {'a' : 1, 'b' : null, 'c' : 3};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('String').node);
+ assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
+ }
+
+ test_setOrMapLiteral_map_typeArguments_noNullableKeysAndValues() async {
+ await analyze('''
+Map<String, int> f() {
+ return <String, int>{'a' : 1, 'b' : 2};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+
+ var keyForLiteral = decoratedTypeAnnotation('String, int>{').node;
+ var keyForReturnType = decoratedTypeAnnotation('String, int> ').node;
+ assertNoUpstreamNullability(keyForLiteral);
+ assertEdge(keyForLiteral, keyForReturnType, hard: false);
+
+ var valueForLiteral = decoratedTypeAnnotation('int>{').node;
+ var valueForReturnType = decoratedTypeAnnotation('int> ').node;
+ assertNoUpstreamNullability(valueForLiteral);
+ assertEdge(valueForLiteral, valueForReturnType, hard: false);
+ }
+
+ test_setOrMapLiteral_map_typeArguments_nullableKey() async {
+ await analyze('''
+Map<String, int> f() {
+ return <String, int>{'a' : 1, null : 2, 'c' : 3};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+ assertEdge(always, decoratedTypeAnnotation('String, int>{').node,
+ hard: false);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('int>{').node);
+ }
+
+ test_setOrMapLiteral_map_typeArguments_nullableKeyAndValue() async {
+ await analyze('''
+Map<String, int> f() {
+ return <String, int>{'a' : 1, null : null, 'c' : 3};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+ assertEdge(always, decoratedTypeAnnotation('String, int>{').node,
+ hard: false);
+ assertEdge(always, decoratedTypeAnnotation('int>{').node, hard: false);
+ }
+
+ test_setOrMapLiteral_map_typeArguments_nullableValue() async {
+ await analyze('''
+Map<String, int> f() {
+ return <String, int>{'a' : 1, 'b' : null, 'c' : 3};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Map').node);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('String, int>{').node);
+ assertEdge(always, decoratedTypeAnnotation('int>{').node, hard: false);
+ }
+
+ @failingTest
+ test_setOrMapLiteral_set_noTypeArgument_noNullableElements() async {
+ // Failing because we're not yet handling collection literals without a
+ // type argument.
+ await analyze('''
+Set<String> f() {
+ return {'a', 'b'};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node);
+ // TODO(brianwilkerson) Add an assertion that there is an edge from the set
+ // literal's fake type argument to the return type's type argument.
+ }
+
+ @failingTest
+ test_setOrMapLiteral_set_noTypeArgument_nullableElement() async {
+ // Failing because we're not yet handling collection literals without a
+ // type argument.
+ await analyze('''
+Set<String> f() {
+ return {'a', null, 'c'};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node);
+ assertEdge(always, decoratedTypeAnnotation('String').node, hard: false);
+ }
+
+ test_setOrMapLiteral_set_typeArgument_noNullableElements() async {
+ await analyze('''
+Set<String> f() {
+ return <String>{'a', 'b'};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node);
+ var typeArgForLiteral = decoratedTypeAnnotation('String>{').node;
+ var typeArgForReturnType = decoratedTypeAnnotation('String> ').node;
+ assertNoUpstreamNullability(typeArgForLiteral);
+ assertEdge(typeArgForLiteral, typeArgForReturnType, hard: false);
+ }
+
+ test_setOrMapLiteral_set_typeArgument_nullableElement() async {
+ await analyze('''
+Set<String> f() {
+ return <String>{'a', null, 'c'};
+}
+''');
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Set').node);
+ assertEdge(always, decoratedTypeAnnotation('String>{').node, hard: false);
+ }
+
test_simpleIdentifier_local() async {
await analyze('''
main() {