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() {