Report key/value type mismatch.

R=brianwilkerson@google.com

Change-Id: If4d4710228935816b98d1250f73b2c1fc600be56
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97061
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
index d2cbd86..e6281f5 100644
--- a/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
+++ b/pkg/analyzer/lib/src/dart/constant/constant_verifier.dart
@@ -192,6 +192,9 @@
         }
       }
     } else if (node.isMap) {
+      InterfaceType nodeType = node.staticType;
+      var keyType = nodeType.typeArguments[0];
+      var valueType = nodeType.typeArguments[1];
       bool reportEqualKeys = true;
       var duplicateKeyElements = <Expression>[];
       var verifier = _ConstLiteralVerifier(
@@ -199,6 +202,8 @@
         isConst: isConst,
         errorCode: CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT,
         forMap: true,
+        mapKeyType: keyType,
+        mapValueType: valueType,
         mapUniqueKeys: Set<DartObject>(),
         mapDuplicateKeyElements: duplicateKeyElements,
       );
@@ -696,19 +701,21 @@
     } else if (element is IfElement) {
       if (!isConst) return true;
 
-      DartObject conditionResult =
-          verifier._validate(element.condition, errorCode);
-      bool conditionValue = conditionResult?.toBoolValue();
-      if (conditionValue == null) {
-        // The errors have already been reported.
-        return false;
-      } else if (conditionValue) {
-        return verify(element.thenElement);
-      } else if (element.elseElement != null) {
-        return verify(element.elseElement);
-      } else {
-        return true;
-      }
+      var conditionValue = verifier._validate(element.condition, errorCode);
+      var conditionBool = conditionValue?.toBoolValue();
+
+      // The errors have already been reported.
+      if (conditionBool == null) return false;
+
+      var thenValid = !conditionBool || verify(element.thenElement);
+
+      var elseValid = conditionBool ||
+          element.elseElement == null ||
+          verify(element.elseElement);
+
+      // TODO(scheglov) Check that not taken branches are constants
+
+      return thenValid && elseValid;
     } else if (element is MapLiteralEntry) {
       return _validateMapLiteralEntry(element);
     } else if (element is SpreadElement) {
@@ -784,29 +791,31 @@
     if (!forMap) return false;
 
     var keyExpression = entry.key;
+    var valueExpression = entry.value;
+
     if (isConst) {
       var keyValue = verifier._validate(
         keyExpression,
         CompileTimeErrorCode.NON_CONSTANT_MAP_KEY,
       );
 
-      var valueExpression = entry.value;
       var valueValue = verifier._validate(
         valueExpression,
         CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE,
       );
 
       if (keyValue != null) {
-        verifier._reportErrorIfFromDeferredLibrary(
-          keyExpression,
-          CompileTimeErrorCode.NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY,
-        );
+        var keyType = keyValue.type;
 
-        if (!mapUniqueKeys.add(keyValue)) {
-          mapDuplicateKeyElements.add(keyExpression);
+        if (!verifier._evaluationEngine
+            .runtimeTypeMatch(keyValue, mapKeyType)) {
+          verifier._errorReporter.reportErrorForNode(
+            StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE,
+            keyExpression,
+            [keyType, mapKeyType],
+          );
         }
 
-        var keyType = keyValue.type;
         if (verifier._implementsEqualsWhenNotAllowed(keyType)) {
           verifier._errorReporter.reportErrorForNode(
             CompileTimeErrorCode
@@ -815,9 +824,27 @@
             [keyType],
           );
         }
+
+        verifier._reportErrorIfFromDeferredLibrary(
+          keyExpression,
+          CompileTimeErrorCode.NON_CONSTANT_MAP_KEY_FROM_DEFERRED_LIBRARY,
+        );
+
+        if (!mapUniqueKeys.add(keyValue)) {
+          mapDuplicateKeyElements.add(keyExpression);
+        }
       }
 
       if (valueValue != null) {
+        if (!verifier._evaluationEngine
+            .runtimeTypeMatch(valueValue, mapValueType)) {
+          verifier._errorReporter.reportErrorForNode(
+            StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE,
+            valueExpression,
+            [valueValue.type, mapValueType],
+          );
+        }
+
         verifier._reportErrorIfFromDeferredLibrary(
           valueExpression,
           CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE_FROM_DEFERRED_LIBRARY,
diff --git a/pkg/analyzer/test/src/diagnostics/list_element_type_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/list_element_type_not_assignable_test.dart
index 0c27dc1..c92a8a0 100644
--- a/pkg/analyzer/test/src/diagnostics/list_element_type_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/list_element_type_not_assignable_test.dart
@@ -55,7 +55,7 @@
     await assertNoErrorsInCode('''
 const dynamic a = 0;
 const dynamic b = 0;
-var v = <int>[if (1 < 0) a else b];
+var v = const <int>[if (1 < 0) a else b];
 ''');
   }
 
@@ -83,7 +83,7 @@
   test_const_ifElement_thenTrue_intInt() async {
     await assertNoErrorsInCode('''
 const dynamic a = 0;
-var v = <int>[if (true) a];
+var v = const <int>[if (true) a];
 ''');
   }
 
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 1f17622..9167018 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
@@ -17,30 +17,43 @@
 
 @reflectiveTest
 class MapKeyTypeNotAssignableTest extends DriverResolutionTest {
-  test_explicitTypeArgs_const() async {
-    await assertErrorsInCode('''
-var v = const <String, int>{42 : 1};
-''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
-  }
-
-  test_explicitTypeArgs_const_actualTypeMatch() async {
+  test_const_intInt_dynamic() async {
     await assertNoErrorsInCode('''
-const dynamic x = null;
-var v = const <String, int>{x : 1};
+const dynamic a = 0;
+var v = const <int, bool>{a : true};
 ''');
   }
 
-  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/21119')
-  test_explicitTypeArgs_const_actualTypeMismatch() async {
+  test_const_intString_dynamic() async {
     await assertErrorsInCode('''
-const dynamic x = 42;
-var v = const <String, int>{x : 1};
-''', [CheckedModeCompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
+const dynamic a = 'a';
+var v = const <int, bool>{a : true};
+''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
   }
 
-  test_explicitTypeArgs_notConst() async {
+  test_const_intString_value() async {
     await assertErrorsInCode('''
-var v = <String, int>{42 : 1};
+var v = const <int, bool>{'a' : true};
+''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  test_nonConst_intInt_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = <int, bool>{a : true};
+''');
+  }
+
+  test_nonConst_intString_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 'a';
+var v = <int, bool>{a : true};
+''');
+  }
+
+  test_nonConst_intString_value() async {
+    await assertErrorsInCode('''
+var v = <int, bool>{'a' : true};
 ''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
   }
 }
@@ -52,15 +65,123 @@
   AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
     ..enabledExperiments = ['control-flow-collections', 'spread-collections'];
 
-  test_spread_valid_const() async {
+  test_const_ifElement_thenElseFalse_intInt_dynamic() async {
     await assertNoErrorsInCode('''
-var v = const <String, int>{...{'a' : 1, 'b' : 2}};
+const dynamic a = 0;
+const dynamic b = 0;
+var v = const <int, bool>{if (1 < 0) a: true else b: false};
 ''');
   }
 
-  test_spread_valid_nonConst() async {
+  test_const_ifElement_thenElseFalse_intString_dynamic() async {
+    await assertErrorsInCode('''
+const dynamic a = 0;
+const dynamic b = 'b';
+var v = const <int, bool>{if (1 < 0) a: true else b: false};
+''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  test_const_ifElement_thenFalse_intString_dynamic() async {
     await assertNoErrorsInCode('''
-var v = <String, int>{...{'a' : 1, 'b' : 2}};
+const dynamic a = 'a';
+var v = const <int, bool>{if (1 < 0) a: true};
+''');
+  }
+
+  test_const_ifElement_thenFalse_intString_value() async {
+    await assertErrorsInCode('''
+var v = const <int, bool>{if (1 < 0) 'a': true};
+''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  @failingTest
+  test_const_ifElement_thenFalse_notConst() async {
+    await assertErrorsInCode('''
+final a = 0;
+var v = const <int, bool>{if (1 > 2) a: true};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_KEY]);
+  }
+
+  test_const_ifElement_thenTrue_intInt_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = const <int, bool>{if (true) a: true};
+''');
+  }
+
+  test_const_ifElement_thenTrue_intString_dynamic() async {
+    await assertErrorsInCode('''
+const dynamic a = 'a';
+var v = const <int, bool>{if (true) a: true};
+''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  test_const_ifElement_thenTrue_notConst() async {
+    await assertErrorsInCode('''
+final a = 0;
+var v = const <int, bool>{if (1 < 2) a: true};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_KEY]);
+  }
+
+  test_const_spread_intInt() async {
+    await assertNoErrorsInCode('''
+var v = const <int, String>{...{1: 'a'}};
+''');
+  }
+
+  test_const_spread_intString_dynamic() async {
+    await assertErrorsInCode('''
+const dynamic a = 'a';
+var v = const <int, String>{...{a: 'a'}};
+''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  test_nonConst_ifElement_thenElseFalse_intInt_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 0;
+const dynamic b = 0;
+var v = <int, bool>{if (1 < 0) a: true else b: false};
+''');
+  }
+
+  test_nonConst_ifElement_thenElseFalse_intString_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 0;
+const dynamic b = 'b';
+var v = <int, bool>{if (1 < 0) a: true else b: false};
+''');
+  }
+
+  test_nonConst_ifElement_thenFalse_intString_value() async {
+    await assertErrorsInCode('''
+var v = <int, bool>{if (1 < 0) 'a': true};
+''', [StaticWarningCode.MAP_KEY_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  test_nonConst_ifElement_thenTrue_intInt_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = <int, bool>{if (true) a: true};
+''');
+  }
+
+  test_nonConst_ifElement_thenTrue_intString_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 'a';
+var v = <int, bool>{if (true) a: true};
+''');
+  }
+
+  test_nonConst_spread_intInt() async {
+    await assertNoErrorsInCode('''
+var v = <int, String>{...{1: 'a'}};
+''');
+  }
+
+  test_nonConst_spread_intString_dynamic() async {
+    await assertNoErrorsInCode('''
+dynamic a = 'a';
+var v = <int, String>{...{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 d5a111d..62ae81e 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
@@ -17,30 +17,43 @@
 
 @reflectiveTest
 class MapValueTypeNotAssignableTest extends DriverResolutionTest {
-  test_explicitTypeArgs_const() async {
-    await assertErrorsInCode('''
-var v = const <String, String>{'a' : 1};
-''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
-  }
-
-  test_explicitTypeArgs_const_actualTypeMatch() async {
+  test_const_intInt_dynamic() async {
     await assertNoErrorsInCode('''
-const dynamic x = null;
-var v = const <String, String>{'a' : x};
+const dynamic a = 0;
+var v = const <bool, int>{true: a};
 ''');
   }
 
-  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/21119')
-  test_explicitTypeArgs_const_actualTypeMismatch() async {
+  test_const_intString_dynamic() async {
     await assertErrorsInCode('''
-const dynamic x = 1;
-var v = const <String, String>{'a' : x};
-''', [CheckedModeCompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
+const dynamic a = 'a';
+var v = const <bool, int>{true: a};
+''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
   }
 
-  test_explicitTypeArgs_notConst() async {
+  test_const_intString_value() async {
     await assertErrorsInCode('''
-var v = <String, String>{'a' : 1};
+var v = const <bool, int>{true: 'a'};
+''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  test_nonConst_intInt_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = <bool, int>{true: a};
+''');
+  }
+
+  test_nonConst_intString_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 'a';
+var v = <bool, int>{true: a};
+''');
+  }
+
+  test_nonConst_intString_value() async {
+    await assertErrorsInCode('''
+var v = <bool, int>{true: 'a'};
 ''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
   }
 }
@@ -52,15 +65,123 @@
   AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
     ..enabledExperiments = ['control-flow-collections', 'spread-collections'];
 
-  test_spread_valid_const() async {
+  test_const_ifElement_thenElseFalse_intInt_dynamic() async {
     await assertNoErrorsInCode('''
-var v = const <String, int>{...{'a' : 1, 'b' : 2}};
+const dynamic a = 0;
+const dynamic b = 0;
+var v = const <bool, int>{if (1 < 0) true: a else false: b};
 ''');
   }
 
-  test_spread_valid_nonConst() async {
+  test_const_ifElement_thenElseFalse_intString_dynamic() async {
+    await assertErrorsInCode('''
+const dynamic a = 0;
+const dynamic b = 'b';
+var v = const <bool, int>{if (1 < 0) true: a else false: b};
+''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  test_const_ifElement_thenFalse_intString_dynamic() async {
     await assertNoErrorsInCode('''
-var v = <String, int>{...{'a' : 1, 'b' : 2}};
+const dynamic a = 'a';
+var v = const <bool, int>{if (1 < 0) true: a};
+''');
+  }
+
+  test_const_ifElement_thenFalse_intString_value() async {
+    await assertErrorsInCode('''
+var v = const <bool, int>{if (1 < 0) true: 'a'};
+''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  @failingTest
+  test_const_ifElement_thenFalse_notConst() async {
+    await assertErrorsInCode('''
+final a = 0;
+var v = const <bool, int>{if (1 > 2) true: a};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE]);
+  }
+
+  test_const_ifElement_thenTrue_intInt_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = const <bool, int>{if (true) true: a};
+''');
+  }
+
+  test_const_ifElement_thenTrue_intString_dynamic() async {
+    await assertErrorsInCode('''
+const dynamic a = 'a';
+var v = const <bool, int>{if (true) true: a};
+''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  test_const_ifElement_thenTrue_notConst() async {
+    await assertErrorsInCode('''
+final a = 0;
+var v = const <bool, int>{if (1 < 2) true: a};
+''', [CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE]);
+  }
+
+  test_const_spread_intInt() async {
+    await assertNoErrorsInCode('''
+var v = const <bool, int>{...{true: 1}};
+''');
+  }
+
+  test_const_spread_intString_dynamic() async {
+    await assertErrorsInCode('''
+const dynamic a = 'a';
+var v = const <bool, int>{...{true: a}};
+''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  test_nonConst_ifElement_thenElseFalse_intInt_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 0;
+const dynamic b = 0;
+var v = <bool, int>{if (1 < 0) true: a else false: b};
+''');
+  }
+
+  test_nonConst_ifElement_thenElseFalse_intString_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 0;
+const dynamic b = 'b';
+var v = <bool, int>{if (1 < 0) true: a else false: b};
+''');
+  }
+
+  test_nonConst_ifElement_thenFalse_intString_value() async {
+    await assertErrorsInCode('''
+var v = <bool, int>{if (1 < 0) true: 'a'};
+''', [StaticWarningCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE]);
+  }
+
+  test_nonConst_ifElement_thenTrue_intInt_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 0;
+var v = <bool, int>{if (true) true: a};
+''');
+  }
+
+  test_nonConst_ifElement_thenTrue_intString_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 'a';
+var v = <bool, int>{if (true) true: a};
+''');
+  }
+
+  test_nonConst_spread_intInt() async {
+    await assertNoErrorsInCode('''
+var v = <bool, int>{...{true: 1}};
+''');
+  }
+
+  test_nonConst_spread_intString_dynamic() async {
+    await assertNoErrorsInCode('''
+const dynamic a = 'a';
+var v = <bool, int>{...{true: a}};
 ''');
   }
 }
diff --git a/pkg/analyzer/test/src/diagnostics/set_element_type_not_assignable_test.dart b/pkg/analyzer/test/src/diagnostics/set_element_type_not_assignable_test.dart
index 585a462..10364de 100644
--- a/pkg/analyzer/test/src/diagnostics/set_element_type_not_assignable_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/set_element_type_not_assignable_test.dart
@@ -56,7 +56,7 @@
     await assertNoErrorsInCode('''
 const dynamic a = 0;
 const dynamic b = 0;
-var v = <int>{if (1 < 0) a else b};
+var v = const <int>{if (1 < 0) a else b};
 ''');
   }
 
@@ -84,7 +84,7 @@
   test_const_ifElement_thenTrue_intInt() async {
     await assertNoErrorsInCode('''
 const dynamic a = 0;
-var v = <int>{if (true) a};
+var v = const <int>{if (true) a};
 ''');
   }