Skip to content

Commit 42364b1

Browse files
chloestefantsovaCommit Queue
authored andcommitted
[analyzer] Verify null-aware elements and map entries
This CL updates the constant verifier to account for the null-aware elements and map entries. Namely, the following is done: * The entries and elements that won't be added to the map or set literal due to being both null-aware and `null` won't be included in the uniqueness checks, since they aren't added into the literal. * The type the null-aware entries and elements are checked against is updated to be the nullable version of the corresponding type argument of the enclosing literal. Change-Id: I20661d84dc26ee87c3893afe294bae5e4274b75f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/390242 Commit-Queue: Chloe Stefantsova <[email protected]> Reviewed-by: Erik Ernst <[email protected]> Reviewed-by: Keerti Parthasarathy <[email protected]>
1 parent 4c3e916 commit 42364b1

File tree

5 files changed

+520
-9
lines changed

5 files changed

+520
-9
lines changed

pkg/analyzer/lib/src/dart/constant/constant_verifier.dart

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,26 @@ class _ConstLiteralVerifier {
11551155
return _validateMapSpread(mapConfig, element, value);
11561156
}
11571157

1158+
return true;
1159+
} else if (element is NullAwareElement) {
1160+
var value = verifier._evaluateAndReportError(element.value, errorCode);
1161+
if (value is! DartObjectImpl) return false;
1162+
1163+
var listElementType = this.listElementType;
1164+
if (listElementType != null) {
1165+
return _validateListExpression(
1166+
verifier._typeSystem.makeNullable(listElementType),
1167+
element.value,
1168+
value);
1169+
}
1170+
1171+
// If the value is `null`, skip verifying it with the set, as it won't be
1172+
// added as an element.
1173+
var setConfig = this.setConfig;
1174+
if (setConfig != null && !value.type.isDartCoreNull) {
1175+
return _validateSetExpression(setConfig, element.value, value);
1176+
}
1177+
11581178
return true;
11591179
}
11601180
throw UnsupportedError(
@@ -1272,6 +1292,9 @@ class _ConstLiteralVerifier {
12721292
var keyExpression = entry.key;
12731293
var valueExpression = entry.value;
12741294

1295+
var isKeyNullAware = entry.keyQuestion != null;
1296+
var isValueNullAware = entry.valueQuestion != null;
1297+
12751298
var keyValue = verifier._evaluateAndReportError(
12761299
keyExpression,
12771300
CompileTimeErrorCode.NON_CONSTANT_MAP_KEY,
@@ -1283,12 +1306,16 @@ class _ConstLiteralVerifier {
12831306

12841307
if (keyValue is DartObjectImpl) {
12851308
var keyType = keyValue.type;
1309+
var expectedKeyType = config.keyType;
1310+
if (isKeyNullAware) {
1311+
expectedKeyType = verifier._typeSystem.makeNullable(expectedKeyType);
1312+
}
12861313

1287-
if (!verifier._runtimeTypeMatch(keyValue, config.keyType)) {
1314+
if (!verifier._runtimeTypeMatch(keyValue, expectedKeyType)) {
12881315
verifier._errorReporter.atNode(
12891316
keyExpression,
12901317
CompileTimeErrorCode.MAP_KEY_TYPE_NOT_ASSIGNABLE,
1291-
arguments: [keyType, config.keyType],
1318+
arguments: [keyType, expectedKeyType],
12921319
);
12931320
}
12941321

@@ -1301,20 +1328,32 @@ class _ConstLiteralVerifier {
13011328
);
13021329
}
13031330

1304-
var existingKey = config.uniqueKeys[keyValue];
1305-
if (existingKey != null) {
1306-
config.duplicateKeys[keyExpression] = existingKey;
1307-
} else {
1308-
config.uniqueKeys[keyValue] = keyExpression;
1331+
// Don't check the key for uniqueness if the key is null aware and is
1332+
// `null` or the value is null aware and is `null`, since it won't be
1333+
// added to the map in that case.
1334+
if ((!isKeyNullAware || !keyValue.type.isDartCoreNull) &&
1335+
(!isValueNullAware ||
1336+
valueValue is DartObjectImpl &&
1337+
!valueValue.type.isDartCoreNull)) {
1338+
var existingKey = config.uniqueKeys[keyValue];
1339+
if (existingKey != null) {
1340+
config.duplicateKeys[keyExpression] = existingKey;
1341+
} else {
1342+
config.uniqueKeys[keyValue] = keyExpression;
1343+
}
13091344
}
13101345
}
13111346

1347+
var expectedValueType = config.valueType;
1348+
if (isValueNullAware) {
1349+
expectedValueType = verifier._typeSystem.makeNullable(expectedValueType);
1350+
}
13121351
if (valueValue is DartObjectImpl) {
1313-
if (!verifier._runtimeTypeMatch(valueValue, config.valueType)) {
1352+
if (!verifier._runtimeTypeMatch(valueValue, expectedValueType)) {
13141353
verifier._errorReporter.atNode(
13151354
valueExpression,
13161355
CompileTimeErrorCode.MAP_VALUE_TYPE_NOT_ASSIGNABLE,
1317-
arguments: [valueValue.type, config.valueType],
1356+
arguments: [valueValue.type, expectedValueType],
13181357
);
13191358
}
13201359
}
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:analyzer/src/error/codes.dart';
6+
import 'package:test_reflective_loader/test_reflective_loader.dart';
7+
8+
import '../dart/resolution/context_collection_resolution.dart';
9+
10+
main() {
11+
defineReflectiveSuite(() {
12+
defineReflectiveTests(NullAwareElementsConstLiteralsErrorTest);
13+
});
14+
}
15+
16+
@reflectiveTest
17+
class NullAwareElementsConstLiteralsErrorTest extends PubPackageResolutionTest {
18+
test_duplicated_in_key_named_null_aware_key_in_map() async {
19+
await assertErrorsInCode('''
20+
const intConst = 0;
21+
const stringConst = "";
22+
const map = {intConst: null, 0: ?intConst, null: 1, stringConst: 1};
23+
''', [
24+
error(CompileTimeErrorCode.EQUAL_KEYS_IN_CONST_MAP, 73, 1,
25+
contextMessages: [message(testFile, 57, 8)]),
26+
]);
27+
}
28+
29+
test_duplicated_int_in_set() async {
30+
await assertErrorsInCode('''
31+
const intConst = 0;
32+
const stringConst = "";
33+
const set = {0, ?intConst, stringConst};
34+
''', [
35+
error(CompileTimeErrorCode.EQUAL_ELEMENTS_IN_CONST_SET, 61, 8,
36+
contextMessages: [message(testFile, 57, 1)]),
37+
]);
38+
}
39+
40+
test_duplicated_int_key_null_aware_key_in_map() async {
41+
await assertErrorsInCode('''
42+
const intConst = 0;
43+
const stringConst = "";
44+
const map = {intConst: null, ?0: intConst, null: 1, stringConst: 1};
45+
''', [
46+
error(CompileTimeErrorCode.EQUAL_KEYS_IN_CONST_MAP, 74, 1,
47+
contextMessages: [message(testFile, 57, 8)]),
48+
]);
49+
}
50+
51+
test_duplicated_null_in_set() async {
52+
await assertErrorsInCode('''
53+
const nullConst = null;
54+
const intConst = 0;
55+
const stringConst = "";
56+
const set = {nullConst, null, intConst, stringConst};
57+
''', [
58+
error(CompileTimeErrorCode.EQUAL_ELEMENTS_IN_CONST_SET, 92, 4,
59+
contextMessages: [message(testFile, 81, 9)]),
60+
]);
61+
}
62+
63+
test_duplicated_null_key_in_map() async {
64+
await assertErrorsInCode('''
65+
const nullConst = null;
66+
const intConst = 0;
67+
const stringConst = "";
68+
const map = {null: 1, nullConst: 1, intConst: 1, stringConst: 1};
69+
''', [
70+
error(CompileTimeErrorCode.EQUAL_KEYS_IN_CONST_MAP, 90, 9,
71+
contextMessages: [message(testFile, 81, 4)]),
72+
]);
73+
}
74+
75+
test_duplicated_null_key_null_aware_value_in_map() async {
76+
await assertErrorsInCode('''
77+
const nullConst = null;
78+
const intConst = 0;
79+
const stringConst = "";
80+
const map = {null: 1, nullConst: ?intConst, stringConst: 1};
81+
''', [
82+
error(CompileTimeErrorCode.EQUAL_KEYS_IN_CONST_MAP, 90, 9,
83+
contextMessages: [message(testFile, 81, 4)]),
84+
]);
85+
}
86+
87+
test_duplicated_string_in_set() async {
88+
await assertErrorsInCode('''
89+
const intConst = 0;
90+
const stringConst = "";
91+
const set = {null, intConst, "", ?stringConst};
92+
''', [
93+
error(CompileTimeErrorCode.EQUAL_ELEMENTS_IN_CONST_SET, 78, 11,
94+
contextMessages: [message(testFile, 73, 2)]),
95+
]);
96+
}
97+
98+
test_non_const_and_null_under_question_in_list() async {
99+
await assertErrorsInCode('''
100+
var nullVar = null;
101+
const intConst = 0;
102+
const stringConst = "";
103+
const list = [?null, ?nullVar, intConst, stringConst];
104+
''', [
105+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 86,
106+
7),
107+
error(CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT, 86, 7),
108+
]);
109+
}
110+
111+
test_non_const_in_key_under_question_in_map() async {
112+
await assertErrorsInCode('''
113+
const stringConst = "";
114+
var intVar = 0;
115+
const map = {null: 1, ?intVar: 1, stringConst: 1};
116+
''', [
117+
error(CompileTimeErrorCode.NON_CONSTANT_MAP_KEY, 63, 6),
118+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 63,
119+
6),
120+
]);
121+
}
122+
123+
test_non_const_int_under_question_in_set() async {
124+
await assertErrorsInCode('''
125+
const nullConst = null;
126+
const stringConst = "";
127+
var intVar = 0;
128+
const set = {nullConst, ?intVar, stringConst};
129+
''', [
130+
error(CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT, 89, 6),
131+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 89,
132+
6),
133+
]);
134+
}
135+
136+
test_non_const_int_value_under_question_map() async {
137+
await assertErrorsInCode('''
138+
const stringConst = "";
139+
var intVar = 0;
140+
const map = {null: 1, 0: ?intVar, stringConst: 1};
141+
''', [
142+
error(CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE, 66, 6),
143+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 66,
144+
6),
145+
]);
146+
}
147+
148+
test_non_const_null_key_under_question_in_map() async {
149+
await assertErrorsInCode('''
150+
var nullVar = null;
151+
const intConst = 0;
152+
const stringConst = "";
153+
const map = {?nullVar: 1, intConst: 1, stringConst: 1};
154+
''', [
155+
error(CompileTimeErrorCode.NON_CONSTANT_MAP_KEY, 78, 7),
156+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 78,
157+
7),
158+
]);
159+
}
160+
161+
test_non_const_null_under_question_in_set() async {
162+
await assertErrorsInCode('''
163+
var nullVar = null;
164+
const intConst = 0;
165+
const stringConst = "";
166+
const set = {?nullVar, intConst, stringConst};
167+
''', [
168+
error(CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT, 78, 7),
169+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 78,
170+
7),
171+
]);
172+
}
173+
174+
test_non_const_null_value_under_question_in_map() async {
175+
await assertErrorsInCode('''
176+
var nullVar = null;
177+
const intConst = 0;
178+
const stringConst = "";
179+
const map = {null: ?nullVar, intConst: 1, stringConst: 1};
180+
''', [
181+
error(CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE, 84, 7),
182+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 84,
183+
7),
184+
]);
185+
}
186+
187+
test_non_const_string_key_under_question_in_map() async {
188+
await assertErrorsInCode('''
189+
var stringVar = "";
190+
const map = {null: 1, 0: 1, ?stringVar: 1};
191+
''', [
192+
error(CompileTimeErrorCode.NON_CONSTANT_MAP_KEY, 49, 9),
193+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 49,
194+
9),
195+
]);
196+
}
197+
198+
test_non_const_string_under_question_in_set() async {
199+
await assertErrorsInCode('''
200+
const nullConst = null;
201+
const intConst = 0;
202+
var stringVar = "";
203+
const set = {nullConst, intConst, ?stringVar};
204+
''', [
205+
error(CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT, 99, 9),
206+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 99,
207+
9),
208+
]);
209+
}
210+
211+
test_non_const_string_value_under_question_in_map() async {
212+
await assertErrorsInCode('''
213+
var stringVar = "";
214+
const map = {null: 1, 0: 1, "": ?stringVar};
215+
''', [
216+
error(CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE, 53, 9),
217+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 53,
218+
9),
219+
]);
220+
}
221+
222+
test_non_const_under_question_in_list() async {
223+
await assertErrorsInCode('''
224+
var nullVar = null;
225+
const intConst = 0;
226+
const stringConst = "";
227+
const list = [?nullVar, intConst, stringConst];
228+
''', [
229+
error(CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT, 79, 7),
230+
error(CompileTimeErrorCode.CONST_INITIALIZED_WITH_NON_CONSTANT_VALUE, 79,
231+
7),
232+
]);
233+
}
234+
}

pkg/analyzer/test/src/diagnostics/test_all.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,8 @@ import 'not_map_spread_test.dart' as not_map_spread;
664664
import 'not_null_aware_null_spread_test.dart' as not_null_aware_null_spread;
665665
import 'null_argument_to_non_null_type_test.dart'
666666
as null_argument_to_non_null_type;
667+
import 'null_aware_elements_const_literals_error_test.dart'
668+
as null_aware_elements_const_literals_error;
667669
import 'null_check_always_fails_test.dart' as null_check_always_fails;
668670
import 'null_safety_read_write_test.dart' as null_safety_read_write;
669671
import 'nullable_type_in_catch_clause_test.dart'
@@ -1351,6 +1353,7 @@ main() {
13511353
not_map_spread.main();
13521354
not_null_aware_null_spread.main();
13531355
null_argument_to_non_null_type.main();
1356+
null_aware_elements_const_literals_error.main();
13541357
null_check_always_fails.main();
13551358
null_safety_read_write.main();
13561359
nullable_type_in_catch_clause.main();

0 commit comments

Comments
 (0)