mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 11:58:13 +00:00
[analyzer] Allow spreads between const lists and sets in the const evaluator
The change allows const lists to be in the spread of a const set and vice-versa. It also reports the correct error if we use a map in a list/set (and vice-versa) which is necessary when we combine all the error reporting in the `ConstantVisitor` to one location. Adds some additional tests to make sure the behaviour we want is still working. Change-Id: I0f64d58c857bd905ac8521346cd34a113b6d4a40 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/323780 Commit-Queue: Kallen Tu <kallentu@google.com> Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
4a04ec3201
commit
f2e11bdeee
|
@ -605,6 +605,10 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
|
|||
identical(dataErrorCode,
|
||||
CompileTimeErrorCode.CONST_CONSTRUCTOR_PARAM_TYPE_MISMATCH) ||
|
||||
identical(dataErrorCode, CompileTimeErrorCode.CONST_TYPE_PARAMETER) ||
|
||||
identical(
|
||||
dataErrorCode, CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP) ||
|
||||
identical(dataErrorCode,
|
||||
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET) ||
|
||||
identical(
|
||||
dataErrorCode,
|
||||
CompileTimeErrorCode
|
||||
|
|
|
@ -1264,6 +1264,17 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
|
|||
for (CollectionElement element in node.elements) {
|
||||
var result = _addElementsToMap(map, element);
|
||||
if (result is InvalidConstant) {
|
||||
if (result.errorCode ==
|
||||
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP &&
|
||||
node.isMap) {
|
||||
// Only report the error if we know this is a non-ambiguous map.
|
||||
// TODO(kallentu): Don't report error here and avoid reporting this
|
||||
// error in this case.
|
||||
_errorReporter.reportErrorForOffset(
|
||||
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP,
|
||||
result.entity.offset,
|
||||
result.entity.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -1404,8 +1415,16 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
|
|||
case InvalidConstant():
|
||||
return spread;
|
||||
case DartObjectImpl():
|
||||
var listValue = spread.toListValue();
|
||||
// Special case for ...?
|
||||
if (spread.isNull && element.isNullAware) {
|
||||
return null;
|
||||
}
|
||||
var listValue = spread.toListValue() ?? spread.toSetValue();
|
||||
if (listValue == null) {
|
||||
// TODO(kallentu): Don't report error here.
|
||||
_errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET,
|
||||
element.expression);
|
||||
return InvalidConstant(element.expression,
|
||||
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET);
|
||||
}
|
||||
|
@ -1471,6 +1490,10 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
|
|||
case InvalidConstant():
|
||||
return spread;
|
||||
case DartObjectImpl():
|
||||
// Special case for ...?
|
||||
if (spread.isNull && element.isNullAware) {
|
||||
return null;
|
||||
}
|
||||
var mapValue = spread.toMapValue();
|
||||
if (mapValue == null) {
|
||||
return InvalidConstant(element.expression,
|
||||
|
@ -1533,8 +1556,16 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
|
|||
case InvalidConstant():
|
||||
return spread;
|
||||
case DartObjectImpl():
|
||||
var setValue = spread.toSetValue();
|
||||
// Special case for ...?
|
||||
if (spread.isNull && element.isNullAware) {
|
||||
return null;
|
||||
}
|
||||
var setValue = spread.toSetValue() ?? spread.toListValue();
|
||||
if (setValue == null) {
|
||||
// TODO(kallentu): Don't report error here.
|
||||
_errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET,
|
||||
element.expression);
|
||||
return InvalidConstant(element.expression,
|
||||
CompileTimeErrorCode.CONST_SPREAD_EXPECTED_LIST_OR_SET);
|
||||
}
|
||||
|
|
|
@ -2050,6 +2050,43 @@ const x = <int>[...a];
|
|||
_assertNull(result);
|
||||
}
|
||||
|
||||
test_visitListLiteral_spreadElement_null() async {
|
||||
await assertNoErrorsInCode('''
|
||||
const a = null;
|
||||
const List<String> x = [
|
||||
'anotherString',
|
||||
...?a,
|
||||
];
|
||||
''');
|
||||
final result = _topLevelVar('x');
|
||||
assertDartObjectText(result, '''
|
||||
List
|
||||
elementType: String
|
||||
elements
|
||||
String anotherString
|
||||
variable: self::@variable::x
|
||||
''');
|
||||
}
|
||||
|
||||
test_visitListLiteral_spreadElement_set() async {
|
||||
await assertNoErrorsInCode('''
|
||||
const a = {'string'};
|
||||
const List<String> x = [
|
||||
'anotherString',
|
||||
...a,
|
||||
];
|
||||
''');
|
||||
final result = _topLevelVar('x');
|
||||
assertDartObjectText(result, '''
|
||||
List
|
||||
elementType: String
|
||||
elements
|
||||
String anotherString
|
||||
String string
|
||||
variable: self::@variable::x
|
||||
''');
|
||||
}
|
||||
|
||||
test_visitMethodInvocation_notIdentical() async {
|
||||
await assertErrorsInCode(r'''
|
||||
int f() {
|
||||
|
@ -2373,6 +2410,34 @@ Record(int, String, {bool c})
|
|||
''');
|
||||
}
|
||||
|
||||
test_visitSetOrMapLiteral_ambiguous() async {
|
||||
await assertErrorsInCode(r'''
|
||||
const l = [];
|
||||
const ambiguous = {...l, 1: 2};
|
||||
''', [
|
||||
error(CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH, 32, 12),
|
||||
]);
|
||||
}
|
||||
|
||||
test_visitSetOrMapLiteral_ambiguous_either() async {
|
||||
await assertErrorsInCode(r'''
|
||||
const int? i = 1;
|
||||
const res = {...?i};
|
||||
''', [
|
||||
error(CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_EITHER, 31, 7),
|
||||
]);
|
||||
}
|
||||
|
||||
test_visitSetOrMapLiteral_ambiguous_inList() async {
|
||||
await assertErrorsInCode(r'''
|
||||
const l = [];
|
||||
const ambiguous = {...l, 1: 2};
|
||||
const anotherList = [...ambiguous];
|
||||
''', [
|
||||
error(CompileTimeErrorCode.AMBIGUOUS_SET_OR_MAP_LITERAL_BOTH, 32, 12),
|
||||
]);
|
||||
}
|
||||
|
||||
test_visitSetOrMapLiteral_map_complexKey() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
class A {
|
||||
|
@ -2454,6 +2519,57 @@ Map
|
|||
''');
|
||||
}
|
||||
|
||||
test_visitSetOrMapLiteral_map_spread() async {
|
||||
await assertNoErrorsInCode('''
|
||||
const x = {'string': 1};
|
||||
const Map<String, int> alwaysInclude = {
|
||||
'anotherString': 0,
|
||||
...x,
|
||||
};
|
||||
''');
|
||||
final result = _topLevelVar('x');
|
||||
assertDartObjectText(result, '''
|
||||
Map
|
||||
entries
|
||||
entry
|
||||
key: String string
|
||||
value: int 1
|
||||
variable: self::@variable::x
|
||||
''');
|
||||
}
|
||||
|
||||
test_visitSetOrMapLiteral_map_spread_notMap() async {
|
||||
await assertErrorsInCode('''
|
||||
const x = ['string'];
|
||||
const Map<String, int> alwaysInclude = {
|
||||
'anotherString': 0,
|
||||
...x,
|
||||
};
|
||||
''', [
|
||||
error(CompileTimeErrorCode.CONST_SPREAD_EXPECTED_MAP, 90, 1),
|
||||
error(CompileTimeErrorCode.NOT_MAP_SPREAD, 90, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_visitSetOrMapLiteral_map_spread_null() async {
|
||||
await assertNoErrorsInCode('''
|
||||
const a = null;
|
||||
const Map<String, int> x = {
|
||||
'anotherString': 0,
|
||||
...?a,
|
||||
};
|
||||
''');
|
||||
final result = _topLevelVar('x');
|
||||
assertDartObjectText(result, '''
|
||||
Map
|
||||
entries
|
||||
entry
|
||||
key: String anotherString
|
||||
value: int 0
|
||||
variable: self::@variable::x
|
||||
''');
|
||||
}
|
||||
|
||||
test_visitSetOrMapLiteral_set_double_zeros() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
class C {
|
||||
|
@ -2504,6 +2620,41 @@ const c = const {if (nonBool) 3};
|
|||
_assertNull(result);
|
||||
}
|
||||
|
||||
test_visitSetOrMapLiteral_set_spread_list() async {
|
||||
await assertNoErrorsInCode('''
|
||||
const a = ['string'];
|
||||
const Set<String> x = {
|
||||
'anotherString',
|
||||
...a,
|
||||
};
|
||||
''');
|
||||
final result = _topLevelVar('x');
|
||||
assertDartObjectText(result, '''
|
||||
Set
|
||||
elements
|
||||
String anotherString
|
||||
String string
|
||||
variable: self::@variable::x
|
||||
''');
|
||||
}
|
||||
|
||||
test_visitSetOrMapLiteral_set_spread_null() async {
|
||||
await assertNoErrorsInCode('''
|
||||
const a = null;
|
||||
const Set<String> x = {
|
||||
'anotherString',
|
||||
...?a,
|
||||
};
|
||||
''');
|
||||
final result = _topLevelVar('x');
|
||||
assertDartObjectText(result, '''
|
||||
Set
|
||||
elements
|
||||
String anotherString
|
||||
variable: self::@variable::x
|
||||
''');
|
||||
}
|
||||
|
||||
test_visitSimpleIdentifier_className() async {
|
||||
await assertNoErrorsInCode('''
|
||||
const a = C;
|
||||
|
|
Loading…
Reference in a new issue