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();
+ }
+}