Report NOT_ITERABLE_SPREAD and NOT_MAP_SPREAD.
R=brianwilkerson@google.com
Change-Id: I6668787b5b88d5125852e38bd9d30010e36c354b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97160
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 459a7c0..a4ba3c5 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -232,6 +232,8 @@
CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR,
CompileTimeErrorCode.NON_SYNC_FACTORY,
CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS,
+ CompileTimeErrorCode.NOT_ITERABLE_SPREAD,
+ CompileTimeErrorCode.NOT_MAP_SPREAD,
CompileTimeErrorCode.NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS,
CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_EXPLICIT,
CompileTimeErrorCode.NO_DEFAULT_SUPER_CONSTRUCTOR_IMPLICIT,
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index d6c964c..b1161d4 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -2279,6 +2279,14 @@
"{0} required argument(s) expected, but {1} found.",
correction: "Try adding the missing arguments.");
+ static const CompileTimeErrorCode NOT_ITERABLE_SPREAD =
+ const CompileTimeErrorCode('NOT_ITERABLE_SPREAD',
+ "Spread elements in list or set literals must implement 'Iterable'.");
+
+ static const CompileTimeErrorCode NOT_MAP_SPREAD = const CompileTimeErrorCode(
+ 'NOT_MAP_SPREAD',
+ "Spread elements in map literals must implement 'Map'.");
+
/**
* 7.6.1 Generative Constructors: Let <i>C</i> be the class in which the
* superinitializer appears and let <i>S</i> be the superclass of <i>C</i>.
diff --git a/pkg/analyzer/lib/src/error/literal_element_verifier.dart b/pkg/analyzer/lib/src/error/literal_element_verifier.dart
new file mode 100644
index 0000000..29b65b9
--- /dev/null
+++ b/pkg/analyzer/lib/src/error/literal_element_verifier.dart
@@ -0,0 +1,203 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// 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/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+
+/// Verifier for [CollectionElement]s in list, set, or map literals.
+class LiteralElementVerifier {
+ final TypeProvider typeProvider;
+ final TypeSystem typeSystem;
+ final ErrorReporter errorReporter;
+ final bool Function(Expression) checkForUseOfVoidResult;
+
+ final bool forList;
+ final bool forSet;
+ final DartType elementType;
+
+ final bool forMap;
+ final DartType mapKeyType;
+ final DartType mapValueType;
+
+ LiteralElementVerifier(
+ this.typeProvider,
+ this.typeSystem,
+ this.errorReporter,
+ this.checkForUseOfVoidResult, {
+ this.forList = false,
+ this.forSet = false,
+ this.elementType,
+ this.forMap = false,
+ this.mapKeyType,
+ this.mapValueType,
+ });
+
+ void verify(CollectionElement element) {
+ _verifyElement(element);
+ }
+
+ /// Check that the given [type] is assignable to the [elementType], otherwise
+ /// report the list or set error on the [errorNode].
+ void _checkAssignableToElementType(DartType type, AstNode errorNode) {
+ if (!typeSystem.isAssignableTo(type, elementType)) {
+ var errorCode = forList
+ ? StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE
+ : StaticWarningCode.SET_ELEMENT_TYPE_NOT_ASSIGNABLE;
+ errorReporter.reportTypeErrorForNode(
+ errorCode,
+ errorNode,
+ [type, elementType],
+ );
+ }
+ }
+
+ /// Verify that the given [element] can be assigned to the [elementType] of
+ /// the enclosing list, set, of map literal.
+ void _verifyElement(CollectionElement element) {
+ if (element is Expression) {
+ if (forList || forSet) {
+ if (!elementType.isVoid && checkForUseOfVoidResult(element)) {
+ return;
+ }
+ _checkAssignableToElementType(element.staticType, element);
+ } else {
+ errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.EXPRESSION_IN_MAP, element);
+ }
+ } else if (element is ForElement) {
+ _verifyElement(element.body);
+ } else if (element is IfElement) {
+ _verifyElement(element.thenElement);
+ _verifyElement(element.elseElement);
+ } else if (element is MapLiteralEntry) {
+ if (forMap) {
+ _verifyMapLiteralEntry(element);
+ } else {
+ errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.MAP_ENTRY_NOT_IN_MAP, element);
+ }
+ } else if (element is SpreadElement) {
+ Expression expression = element.expression;
+ if (forList || forSet) {
+ _verifySpreadForListOrSet(expression);
+ } else if (forMap) {
+ _verifySpreadForMap(expression);
+ }
+ }
+ }
+
+ /// Verify that the [entry]'s key and value are assignable to [mapKeyType]
+ /// and [mapValueType].
+ void _verifyMapLiteralEntry(MapLiteralEntry entry) {
+ if (!mapKeyType.isVoid && checkForUseOfVoidResult(entry.key)) {
+ return;
+ }
+
+ if (!mapValueType.isVoid && checkForUseOfVoidResult(entry.value)) {
+ return;
+ }
+
+ var keyType = entry.key.staticType;
+ if (!typeSystem.isAssignableTo(keyType, mapKeyType)) {
+ errorReporter.reportTypeErrorForNode(
+ StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE,
+ entry.key,
+ [keyType, mapKeyType],
+ );
+ }
+
+ var valueType = entry.value.staticType;
+ if (!typeSystem.isAssignableTo(valueType, mapValueType)) {
+ errorReporter.reportTypeErrorForNode(
+ StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE,
+ entry.value,
+ [valueType, mapValueType],
+ );
+ }
+ }
+
+ /// Verify that the type of the elements of the given [expression] can be
+ /// assigned to the [elementType] of the enclosing collection.
+ void _verifySpreadForListOrSet(Expression expression) {
+ var expressionType = expression.staticType;
+ if (expressionType.isDynamic) return;
+
+ // TODO(scheglov) Check for non-null-aware spread?
+ if (expressionType.isDartCoreNull) return;
+
+ InterfaceType iterableType;
+ var iterableObjectType = typeProvider.iterableObjectType;
+ if (expressionType is InterfaceTypeImpl &&
+ typeSystem.isSubtypeOf(expressionType, iterableObjectType)) {
+ iterableType = expressionType.asInstanceOf(
+ iterableObjectType.element,
+ );
+ }
+
+ if (iterableType == null) {
+ return errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.NOT_ITERABLE_SPREAD,
+ expression,
+ );
+ }
+
+ var iterableElementType = iterableType.typeArguments[0];
+ if (!typeSystem.isAssignableTo(iterableElementType, elementType)) {
+ var errorCode = forList
+ ? StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE
+ : StaticWarningCode.SET_ELEMENT_TYPE_NOT_ASSIGNABLE;
+ errorReporter.reportTypeErrorForNode(
+ errorCode,
+ expression,
+ [iterableElementType, elementType],
+ );
+ }
+ }
+
+ /// Verify that the [expression] is a subtype of `Map<Object, Object>`, and
+ /// its key and values are assignable to [mapKeyType] and [mapValueType].
+ void _verifySpreadForMap(Expression expression) {
+ var expressionType = expression.staticType;
+ if (expressionType.isDynamic) return;
+
+ // TODO(scheglov) Check for non-null-aware spread?
+ if (expressionType.isDartCoreNull) return;
+
+ InterfaceType mapType;
+ var mapObjectObjectType = typeProvider.mapObjectObjectType;
+ if (expressionType is InterfaceTypeImpl &&
+ typeSystem.isSubtypeOf(expressionType, mapObjectObjectType)) {
+ mapType = expressionType.asInstanceOf(mapObjectObjectType.element);
+ }
+
+ if (mapType == null) {
+ return errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.NOT_MAP_SPREAD,
+ expression,
+ );
+ }
+
+ var keyType = mapType.typeArguments[0];
+ if (!typeSystem.isAssignableTo(keyType, mapKeyType)) {
+ errorReporter.reportTypeErrorForNode(
+ StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE,
+ expression,
+ [keyType, mapKeyType],
+ );
+ }
+
+ var valueType = mapType.typeArguments[1];
+ if (!typeSystem.isAssignableTo(valueType, mapValueType)) {
+ errorReporter.reportTypeErrorForNode(
+ StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE,
+ expression,
+ [valueType, mapValueType],
+ );
+ }
+ }
+}
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 9bd55f1..50697a0 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -21,6 +21,7 @@
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/error/literal_element_verifier.dart';
import 'package:analyzer/src/error/pending_error.dart';
import 'package:analyzer/src/generated/element_resolver.dart';
import 'package:analyzer/src/generated/engine.dart';
@@ -1204,7 +1205,6 @@
_checkForImplicitDynamicTypedLiteral(node);
_checkForMapTypeNotAssignable(node);
_checkForNonConstMapAsExpressionStatement3(node);
- _checkForExpressions(node.elements2);
} else if (node.isSet) {
if (typeArguments != null) {
if (node.isConst) {
@@ -1220,7 +1220,6 @@
_checkForRawTypedLiteral(node);
_checkForImplicitDynamicTypedLiteral(node);
_checkForSetElementTypeNotAssignable3(node);
- _checkForMapEntries(node.elements2);
}
super.visitSetOrMapLiteral(node);
}
@@ -2229,63 +2228,6 @@
}
/**
- * Verify that the type of the elements of the given [spreadExpression] (the
- * [spreadExpressionType]) can be assigned to the element type of the
- * enclosing collection (the [elementType]). If not, report an error with the
- * given [errorCode].
- */
- void _checkForArgumentTypeNotAssignableInSpread(
- Expression spreadExpression,
- DartType spreadExpressionType,
- DartType elementType,
- ErrorCode errorCode) {
- if (spreadExpressionType != null && elementType != null) {
- if (!elementType.isVoid && _checkForUseOfVoidResult(spreadExpression)) {
- return;
- }
- if (spreadExpressionType is InterfaceType) {
- if (_typeSystem.isSubtypeOf(
- spreadExpressionType, _typeProvider.iterableObjectType)) {
- InterfaceType iterableType =
- (spreadExpressionType as InterfaceTypeImpl)
- .asInstanceOf(_typeProvider.iterableType.element);
- if (iterableType != null) {
- // The `iterableType` will be `null` when `spreadExpressionType` is
- // `Null`. Fall through in that case to perform the default type
- // check.
- List<DartType> typeArguments = iterableType.typeArguments;
- if (typeArguments.length == 1) {
- _checkForAssignableExpressionAtType(
- spreadExpression, typeArguments[0], elementType, errorCode);
- return;
- }
- }
- } else if (_typeSystem.isSubtypeOf(
- spreadExpressionType, _typeProvider.mapObjectObjectType)) {
- // TODO(brianwilkerson) Handle spreads involving maps? This method
- // isn't currently called for maps, but might be if it's reworked as
- // expected.
-// InterfaceType mapType =
-// (spreadExpressionType as InterfaceTypeImpl)
-// .asInstanceOf(_typeProvider.mapType.element);
-// if (mapType != null) {
-// List<DartType> typeArguments = mapType.typeArguments;
-// if (typeArguments.length == 2) {
-// _checkForAssignableExpressionAtType(
-// spreadExpression, typeArguments[0], keyType, errorCode);
-// _checkForAssignableExpressionAtType(
-// spreadExpression, typeArguments[1], valueType, errorCode);
-// return;
-// }
-// }
- }
- }
- _checkForAssignableExpressionAtType(
- spreadExpression, spreadExpressionType, elementType, errorCode);
- }
- }
-
- /**
* Verify that the given [expression] can be assigned to its corresponding
* parameters. The [expectedStaticType] is the expected static type.
*
@@ -2544,34 +2486,6 @@
}
/**
- * Verify that the given [element] can be assigned to the [elementType] of the
- * enclosing list or set literal. Report an error with the given [errorCode]
- * if not.
- *
- * This method corresponds to
- * [BestPracticesVerifier.checkForArgumentTypeNotAssignableWithExpectedTypes].
- */
- void _checkForCollectionElementTypeNotAssignableWithElementType(
- CollectionElement element, DartType elementType, ErrorCode errorCode) {
- if (element is ForElement) {
- _checkForCollectionElementTypeNotAssignableWithElementType(
- element.body, elementType, errorCode);
- } else if (element is IfElement) {
- _checkForCollectionElementTypeNotAssignableWithElementType(
- element.thenElement, elementType, errorCode);
- _checkForCollectionElementTypeNotAssignableWithElementType(
- element.elseElement, elementType, errorCode);
- } else if (element is Expression) {
- _checkForArgumentTypeNotAssignable(
- element, elementType, getStaticType(element), errorCode);
- } else if (element is SpreadElement) {
- Expression expression = element.expression;
- _checkForArgumentTypeNotAssignableInSpread(
- expression, getStaticType(expression), elementType, errorCode);
- }
- }
-
- /**
* Verify that the [_enclosingClass] does not have a method and getter pair
* with the same name on, via inheritance.
*
@@ -3175,32 +3089,6 @@
}
/**
- * Create a diagnostic if any of the leaf elements in the given collection of
- * [elements] is an expression.
- */
- void _checkForExpression(CollectionElement element) {
- if (element is ForElement) {
- _checkForExpression(element.body);
- } else if (element is IfElement) {
- _checkForExpression(element.thenElement);
- _checkForExpression(element.elseElement);
- } else if (element is Expression) {
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode.EXPRESSION_IN_MAP, element);
- }
- }
-
- /**
- * Create a diagnostic if any of the leaf elements in the given collection of
- * [elements] is an expression.
- */
- void _checkForExpressions(NodeList<CollectionElement> elements) {
- for (CollectionElement element in elements) {
- _checkForExpression(element);
- }
- }
-
- /**
* Verify that the given extends [clause] does not extend a deferred class.
*
* See [CompileTimeErrorCode.EXTENDS_DEFERRED_CLASS].
@@ -3975,85 +3863,16 @@
DartType listElementType = typeArguments[0];
// Check every list element.
+ var verifier = LiteralElementVerifier(
+ _typeProvider,
+ _typeSystem,
+ _errorReporter,
+ _checkForUseOfVoidResult,
+ forList: true,
+ elementType: listElementType,
+ );
for (CollectionElement element in literal.elements2) {
- _checkForCollectionElementTypeNotAssignableWithElementType(element,
- listElementType, StaticWarningCode.LIST_ELEMENT_TYPE_NOT_ASSIGNABLE);
- }
- }
-
- /**
- * Verify that the given [element] can be assigned to the [elementType] of the
- * enclosing list or set literal. Report an error with the given [errorCode]
- * if not.
- *
- * This method corresponds to
- * [BestPracticesVerifier.checkForArgumentTypeNotAssignableWithExpectedTypes].
- */
- void _checkForMapElementTypeNotAssignableWithKeyOrValueType(
- CollectionElement element,
- DartType keyType,
- DartType valueType,
- ErrorCode keyErrorCode,
- ErrorCode valueErrorCode) {
- if (element is ForElement) {
- _checkForMapElementTypeNotAssignableWithKeyOrValueType(
- element.body, keyType, valueType, keyErrorCode, valueErrorCode);
- } else if (element is IfElement) {
- _checkForMapElementTypeNotAssignableWithKeyOrValueType(
- element.thenElement,
- keyType,
- valueType,
- keyErrorCode,
- valueErrorCode);
- _checkForMapElementTypeNotAssignableWithKeyOrValueType(
- element.elseElement,
- keyType,
- valueType,
- keyErrorCode,
- valueErrorCode);
- } else if (element is MapLiteralEntry) {
- _checkForArgumentTypeNotAssignableWithExpectedTypes(
- element.key, keyType, keyErrorCode);
- _checkForArgumentTypeNotAssignableWithExpectedTypes(
- element.value, valueType, valueErrorCode);
- } else if (element is SpreadElement) {
- Expression expression = element.expression;
- DartType expressionType = getStaticType(expression);
- if (expressionType is ParameterizedType) {
- List<DartType> typeArguments = expressionType.typeArguments;
- if (typeArguments.length == 2) {
- _checkForArgumentTypeNotAssignable(
- expression, keyType, typeArguments[0], keyErrorCode);
- _checkForArgumentTypeNotAssignable(
- expression, valueType, typeArguments[1], valueErrorCode);
- }
- }
- }
- }
-
- /**
- * Create a diagnostic if any of the leaf elements in the given collection of
- * [elements] is a map entry.
- */
- void _checkForMapEntries(NodeList<CollectionElement> elements) {
- for (CollectionElement element in elements) {
- _checkForMapEntry(element);
- }
- }
-
- /**
- * Create a diagnostic if any of the leaf elements in the given collection of
- * [elements] is a map entry.
- */
- void _checkForMapEntry(CollectionElement element) {
- if (element is ForElement) {
- _checkForMapEntry(element.body);
- } else if (element is IfElement) {
- _checkForMapEntry(element.thenElement);
- _checkForMapEntry(element.elseElement);
- } else if (element is MapLiteralEntry) {
- _errorReporter.reportErrorForNode(
- CompileTimeErrorCode.MAP_ENTRY_NOT_IN_MAP, element);
+ verifier.verify(element);
}
}
@@ -4078,14 +3897,17 @@
DartType keyType = typeArguments[0];
DartType valueType = typeArguments[1];
- NodeList<CollectionElement> entries = literal.elements2;
- for (CollectionElement entry in entries) {
- _checkForMapElementTypeNotAssignableWithKeyOrValueType(
- entry,
- keyType,
- valueType,
- StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE,
- StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE);
+ var verifier = LiteralElementVerifier(
+ _typeProvider,
+ _typeSystem,
+ _errorReporter,
+ _checkForUseOfVoidResult,
+ forMap: true,
+ mapKeyType: keyType,
+ mapValueType: valueType,
+ );
+ for (CollectionElement element in literal.elements2) {
+ verifier.verify(element);
}
}
}
@@ -5298,9 +5120,16 @@
DartType setElementType = typeArguments[0];
// Check every set element.
+ var verifier = LiteralElementVerifier(
+ _typeProvider,
+ _typeSystem,
+ _errorReporter,
+ _checkForUseOfVoidResult,
+ forSet: true,
+ elementType: setElementType,
+ );
for (CollectionElement element in literal.elements2) {
- _checkForCollectionElementTypeNotAssignableWithElementType(element,
- setElementType, StaticWarningCode.SET_ELEMENT_TYPE_NOT_ASSIGNABLE);
+ verifier.verify(element);
}
}
}
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index feb81ad..8d9da75 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -198,10 +198,6 @@
} else if (element is MapLiteralEntry) {
checkAssignment(element.key, expectedKeyType);
checkAssignment(element.value, expectedValueType);
- } else if (element is SpreadElement) {
- DartType mapType = typeProvider.mapType
- .instantiate([expectedKeyType, expectedValueType]);
- checkAssignment(element.expression, mapType);
}
}
diff --git a/pkg/analyzer/test/generated/test_all.dart b/pkg/analyzer/test/generated/test_all.dart
index 969d112..4481253 100644
--- a/pkg/analyzer/test/generated/test_all.dart
+++ b/pkg/analyzer/test/generated/test_all.dart
@@ -31,7 +31,6 @@
import 'static_type_analyzer_test.dart' as static_type_analyzer_test;
import 'static_type_warning_code_test.dart' as static_type_warning_code;
import 'static_warning_code_test.dart' as static_warning_code;
-import 'static_warning_code_test.dart' as static_warning_code_test;
import 'strong_mode_test.dart' as strong_mode;
import 'type_system_test.dart' as type_system_test;
import 'utilities_dart_test.dart' as utilities_dart_test;
@@ -64,7 +63,6 @@
static_type_analyzer_test.main();
static_type_warning_code.main();
static_warning_code.main();
- static_warning_code_test.main();
strong_mode.main();
type_system_test.main();
utilities_dart_test.main();
diff --git a/pkg/analyzer/test/src/diagnostics/map_key_type_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/map_key_type_not_assignable_test.dart
index 30eea8f..2ea6a69 100644
--- a/pkg/analyzer/test/src/diagnostics/map_key_type_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/map_key_type_not_assignable_test.dart
@@ -170,6 +170,18 @@
''');
}
+ test_nonConst_spread_intNum() async {
+ await assertNoErrorsInCode('''
+var v = <int, int>{...<num, num>{1: 1}};
+''');
+ }
+
+ test_nonConst_spread_intString() async {
+ await assertErrorsInCode('''
+var v = <int, String>{...{'a': 'a'}};
+''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
+ }
+
test_nonConst_spread_intString_dynamic() async {
await assertNoErrorsInCode('''
dynamic a = 'a';
diff --git a/pkg/analyzer/test/src/diagnostics/map_value_type_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/map_value_type_not_assignable_test.dart
index 35e036a..3a9bc4a 100644
--- a/pkg/analyzer/test/src/diagnostics/map_value_type_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/map_value_type_not_assignable_test.dart
@@ -170,6 +170,18 @@
''');
}
+ test_nonConst_spread_intNum() async {
+ await assertNoErrorsInCode('''
+var v = <int, int>{...<num, num>{1: 1}};
+''');
+ }
+
+ test_nonConst_spread_intString() async {
+ await assertErrorsInCode('''
+var v = <bool, int>{...{true: 'a'}};
+''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
+ }
+
test_nonConst_spread_intString_dynamic() async {
await assertNoErrorsInCode('''
const dynamic a = 'a';
diff --git a/pkg/analyzer/test/src/diagnostics/not_iterable_spread_test.dart b/pkg/analyzer/test/src/diagnostics/not_iterable_spread_test.dart
new file mode 100644
index 0000000..f326006
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/not_iterable_spread_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// 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:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/driver_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(NotIterableSpreadTest);
+ });
+}
+
+@reflectiveTest
+class NotIterableSpreadTest extends DriverResolutionTest {
+ @override
+ AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+ ..enabledExperiments = [
+ EnableString.control_flow_collections,
+ EnableString.spread_collections,
+ ];
+
+ test_iterable_list() async {
+ await assertNoErrorsInCode('''
+var a = [0];
+var v = [...a];
+''');
+ }
+
+ test_iterable_null() async {
+ await assertNoErrorsInCode('''
+var v = [...?null];
+''');
+ }
+
+ test_notIterable_direct() async {
+ await assertErrorsInCode('''
+var a = 0;
+var v = [...a];
+''', [CompileTimeErrorCode.NOT_ITERABLE_SPREAD]);
+ }
+
+ test_notIterable_forElement() async {
+ await assertErrorsInCode('''
+var a = 0;
+var v = [for (var i in []) ...a];
+''', [CompileTimeErrorCode.NOT_ITERABLE_SPREAD]);
+ }
+
+ test_notIterable_ifElement_else() async {
+ await assertErrorsInCode('''
+var a = 0;
+var v = [if (1 > 0) ...[] else ...a];
+''', [CompileTimeErrorCode.NOT_ITERABLE_SPREAD]);
+ }
+
+ test_notIterable_ifElement_then() async {
+ await assertErrorsInCode('''
+var a = 0;
+var v = [if (1 > 0) ...a];
+''', [CompileTimeErrorCode.NOT_ITERABLE_SPREAD]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/not_map_spread_test.dart b/pkg/analyzer/test/src/diagnostics/not_map_spread_test.dart
new file mode 100644
index 0000000..a9bf651
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/not_map_spread_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
+// 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:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/driver_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(NotMapSpreadTest);
+ });
+}
+
+@reflectiveTest
+class NotMapSpreadTest extends DriverResolutionTest {
+ @override
+ AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
+ ..enabledExperiments = [
+ EnableString.control_flow_collections,
+ EnableString.spread_collections,
+ ];
+
+ test_map() async {
+ await assertNoErrorsInCode('''
+var a = {0: 0};
+var v = <int, int>{...a};
+''');
+ }
+
+ test_map_null() async {
+ await assertNoErrorsInCode('''
+var v = <int, int>{...?null};
+''');
+ }
+
+ test_notMap_direct() async {
+ await assertErrorsInCode('''
+var a = 0;
+var v = <int, int>{...a};
+''', [CompileTimeErrorCode.NOT_MAP_SPREAD]);
+ }
+
+ test_notMap_forElement() async {
+ await assertErrorsInCode('''
+var a = 0;
+var v = <int, int>{for (var i in []) ...a};
+''', [CompileTimeErrorCode.NOT_MAP_SPREAD]);
+ }
+
+ test_notMap_ifElement_else() async {
+ await assertErrorsInCode('''
+var a = 0;
+var v = <int, int>{if (1 > 0) ...<int, int>{} else ...a};
+''', [CompileTimeErrorCode.NOT_MAP_SPREAD]);
+ }
+
+ test_notMap_ifElement_then() async {
+ await assertErrorsInCode('''
+var a = 0;
+var v = <int, int>{if (1 > 0) ...a};
+''', [CompileTimeErrorCode.NOT_MAP_SPREAD]);
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 27a10b6..b060c83 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -46,6 +46,8 @@
import 'non_constant_list_element_test.dart' as non_constant_list_element;
import 'non_constant_map_element_test.dart' as non_constant_map_element;
import 'non_constant_set_element_test.dart' as non_constant_set_element;
+import 'not_iterable_spread_test.dart' as not_iterable_spread;
+import 'not_map_spread_test.dart' as not_map_spread;
import 'set_element_type_not_assignable_test.dart'
as set_element_type_not_assignable;
import 'subtype_of_sealed_class_test.dart' as subtype_of_sealed_class;
@@ -111,6 +113,8 @@
non_constant_list_element.main();
non_constant_map_element.main();
non_constant_set_element.main();
+ not_iterable_spread.main();
+ not_map_spread.main();
set_element_type_not_assignable.main();
subtype_of_sealed_class.main();
top_level_instance_getter.main();