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