Fix DDC cast -- cast key/value/element individually & don't clobber names.

Change-Id: Id5175782667b760545dac15b02c2a41836137571
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97178
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
Auto-Submit: Mike Fairhurst <mfairhurst@google.com>
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index 7380a9c..5e25e17 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -180,12 +180,17 @@
           typeProvider.iterableType.instantiate([DynamicTypeImpl.instance]);
       checkAssignment(element.expression, expressionCastType);
 
-      // Items in the spread will then potentially be downcast to the expected
-      // type.
-      DartType elementsCastType =
-          typeProvider.iterableType.instantiate([expectedType]);
-      _checkImplicitCast(element.expression, elementsCastType,
-          from: element.expression.staticType, forSpread: true);
+      var exprType = element.expression.staticType;
+      var asIterableType = exprType is InterfaceTypeImpl
+          ? exprType.asInstanceOf(typeProvider.iterableType.element)
+          : null;
+      if (asIterableType != null) {
+        var elementType = asIterableType.typeArguments[0];
+        // Items in the spread will then potentially be downcast to the expected
+        // type.
+        _checkImplicitCast(element.expression, expectedType,
+            from: elementType, forSpread: true);
+      }
     }
   }
 
@@ -209,21 +214,26 @@
       checkAssignment(element.value, expectedValueType);
     } else if (element is SpreadElement) {
       // Spread expression may be dynamic in which case it's implicitly downcast
-      // to Iterable<dynamic>
+      // to Map<dynamic, dynamic>
       DartType expressionCastType = typeProvider.mapType
           .instantiate([DynamicTypeImpl.instance, DynamicTypeImpl.instance]);
       checkAssignment(element.expression, expressionCastType);
 
-      // The keys and values in the spread will then potentially be downcast to
-      // the expected types.
-      DartType keyCastType = typeProvider.mapType
-          .instantiate([expectedKeyType, DynamicTypeImpl.instance]);
-      _checkImplicitCast(element.expression, keyCastType,
-          from: element.expression.staticType, forSpreadKey: true);
-      DartType valueCastType = typeProvider.mapType
-          .instantiate([DynamicTypeImpl.instance, expectedValueType]);
-      _checkImplicitCast(element.expression, valueCastType,
-          from: element.expression.staticType, forSpreadValue: true);
+      var exprType = element.expression.staticType;
+      var asMapType = exprType is InterfaceTypeImpl
+          ? exprType.asInstanceOf(typeProvider.mapType.element)
+          : null;
+
+      if (asMapType != null) {
+        var elementKeyType = asMapType.typeArguments[0];
+        var elementValueType = asMapType.typeArguments[1];
+        // Keys and values in the spread will then potentially be downcast to
+        // the expected types.
+        _checkImplicitCast(element.expression, expectedKeyType,
+            from: elementKeyType, forSpreadKey: true);
+        _checkImplicitCast(element.expression, expectedValueType,
+            from: elementValueType, forSpreadValue: true);
+      }
     }
   }
 
@@ -1230,7 +1240,11 @@
           : StrongModeCode.DOWN_CAST_IMPLICIT;
     }
     _recordMessage(expr, errorCode, [from, to]);
-    _markImplicitCast(expr, to, opAssign: opAssign);
+    _markImplicitCast(expr, to,
+        opAssign: opAssign,
+        forSpread: forSpread,
+        forSpreadKey: forSpreadKey,
+        forSpreadValue: forSpreadValue);
   }
 
   void _recordMessage(AstNode node, ErrorCode errorCode, List arguments) {
diff --git a/pkg/analyzer/test/src/task/strong/checker_test.dart b/pkg/analyzer/test/src/task/strong/checker_test.dart
index d56773e..2021bed 100644
--- a/pkg/analyzer/test/src/task/strong/checker_test.dart
+++ b/pkg/analyzer/test/src/task/strong/checker_test.dart
@@ -2,6 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'package:analyzer/src/dart/analysis/experiments.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'strong_test_helper.dart';
@@ -9,6 +10,7 @@
 void main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(CheckerTest);
+    defineReflectiveTests(CheckerTest_WithSpreadCollections);
   });
 }
 
@@ -4511,3 +4513,101 @@
     ''', name: '/meta.dart');
   }
 }
+
+@reflectiveTest
+class CheckerTest_WithSpreadCollections extends AbstractStrongTest {
+  @override
+  List<String> get enabledExperiments => [EnableString.spread_collections];
+
+  @override
+  bool get enableNewAnalysisDriver => true;
+
+  @failingTest
+  test_spread_dynamicInList_disableImplicitCasts() async {
+    // TODO(mfairhurst) fix this, see https://github.com/dart-lang/sdk/issues/35569
+    addFile(
+        'dynamic dyn; void main() { [.../*error:INVALID_ASSIGNMENT*/dyn]; }');
+    await check(implicitCasts: false);
+  }
+
+  test_spread_dynamicInList_implicitCasts() async {
+    addFile('dynamic dyn; void main() { [.../*info:DYNAMIC_CAST*/dyn]; }');
+    await check();
+  }
+
+  @failingTest
+  test_spread_dynamicInMap_disableImplicitCasts() async {
+    // TODO(mfairhurst) fix this, see https://github.com/dart-lang/sdk/issues/35569
+    addFile(
+        'dynamic dyn; void main() { <dynamic, dynamic>{.../*error:INVALID_ASSIGNMENT*/dyn}; }');
+    await check(implicitCasts: false);
+  }
+
+  test_spread_dynamicInMap_implicitCasts() async {
+    addFile(
+        'dynamic dyn; void main() { <dynamic, dynamic>{.../*info:DYNAMIC_CAST*/dyn}; }');
+    await check();
+  }
+
+  @failingTest
+  test_spread_dynamicInSet_disableImplicitCasts() async {
+    // TODO(mfairhurst) fix this, see https://github.com/dart-lang/sdk/issues/35569
+    addFile(
+        'dynamic dyn; void main() { <dynamic>{.../*error:INVALID_ASSIGNMENT*/dyn}; }');
+    await check(implicitCasts: false);
+  }
+
+  test_spread_dynamicInSet_implicitCasts() async {
+    addFile(
+        'dynamic dyn; void main() { <dynamic>{.../*info:DYNAMIC_CAST*/dyn}; }');
+    await check();
+  }
+
+  test_spread_listElement_disableImplicitCasts() async {
+    addFile(
+        'Iterable<num> i; void main() { <int>[.../*error:LIST_ELEMENT_TYPE_NOT_ASSIGNABLE*/i]; }');
+    await check(implicitCasts: false);
+  }
+
+  test_spread_listElement_implicitCasts() async {
+    addFile(
+        'Iterable<num> i; void main() { <int>[.../*info:DOWN_CAST_IMPLICIT*/i]; }');
+    await check();
+  }
+
+  test_spread_mapKey_disableImplicitCasts() async {
+    addFile(
+        'Map<num, dynamic> map; void main() { <int, dynamic>{1: 2, .../*error:MAP_KEY_TYPE_NOT_ASSIGNABLE*/map}; }');
+    await check(implicitCasts: false);
+  }
+
+  test_spread_mapKey_implicitCasts() async {
+    addFile(
+        'Map<num, dynamic> map; void main() { <int, dynamic>{1: 2, .../*info:DOWN_CAST_IMPLICIT*/map}; }');
+    await check();
+  }
+
+  test_spread_mapValue_disableImplicitCasts() async {
+    addFile(
+        'Map<dynamic, num> map; void main() { <dynamic, int>{1: 2, .../*error:MAP_VALUE_TYPE_NOT_ASSIGNABLE*/map}; }');
+    await check(implicitCasts: false);
+  }
+
+  test_spread_mapValue_implicitCasts() async {
+    addFile(
+        'Map<dynamic, num> map; void main() { <dynamic, int>{1: 2, .../*info:DOWN_CAST_IMPLICIT*/map}; }');
+    await check();
+  }
+
+  test_spread_setElement_disableImplicitCasts() async {
+    addFile(
+        'Iterable<num> i; void main() { <int>{.../*error:SET_ELEMENT_TYPE_NOT_ASSIGNABLE*/i}; }');
+    await check(implicitCasts: false);
+  }
+
+  test_spread_setElement_implicitCasts() async {
+    addFile(
+        'Iterable<num> i; void main() { <int>{.../*info:DOWN_CAST_IMPLICIT*/i}; }');
+    await check();
+  }
+}