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};
''');
}