Skip to content

Commit 9f111ce

Browse files
srawlinsCommit Queue
authored and
Commit Queue
committed
analyzer: look at primitive equality for const lists
Work towards #54356 Change-Id: I6acfba900597ce3eef4065bf5eff3738aceb5a21 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/399881 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Phil Quitslund <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent b3f839b commit 9f111ce

File tree

11 files changed

+247
-163
lines changed

11 files changed

+247
-163
lines changed

pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,8 @@ CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT:
427427
status: needsEvaluation
428428
CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION:
429429
status: needsEvaluation
430+
CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY:
431+
status: needsEvaluation
430432
CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS:
431433
status: needsEvaluation
432434
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION:

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,8 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
649649
identical(errorCode, CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT) ||
650650
identical(
651651
errorCode, CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION) ||
652+
identical(
653+
errorCode, CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY) ||
652654
identical(errorCode, CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS) ||
653655
identical(
654656
errorCode, CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION) ||

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,8 +521,9 @@ class DartObjectImpl implements DartObject, Constant {
521521
);
522522
}
523523
}
524-
throw EvaluationException(
525-
CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING);
524+
throw EvaluationException(featureSet.isEnabled(Feature.patterns)
525+
? CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY
526+
: CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING);
526527
}
527528

528529
@override
@@ -2600,7 +2601,12 @@ class ListState extends InstanceState {
26002601
if (isUnknown || rightOperand.isUnknown) {
26012602
return BoolState.UNKNOWN_VALUE;
26022603
}
2603-
return BoolState.from(this == rightOperand);
2604+
if (rightOperand is! ListState) {
2605+
return BoolState.FALSE_STATE;
2606+
}
2607+
return BoolState.from(typeSystem.normalize(elementType) ==
2608+
typeSystem.normalize(rightOperand.elementType) &&
2609+
this == rightOperand);
26042610
}
26052611

26062612
@override

pkg/analyzer/lib/src/error/codes.g.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,15 @@ class CompileTimeErrorCode extends ErrorCode {
10261026
"Methods can't be invoked in constant expressions.",
10271027
);
10281028

1029+
/// See https://spec.dart.dev/DartLangSpecDraft.pdf#constants, "Constants",
1030+
/// for text about "An expression of the form e1 == e2".
1031+
static const CompileTimeErrorCode CONST_EVAL_PRIMITIVE_EQUALITY =
1032+
CompileTimeErrorCode(
1033+
'CONST_EVAL_PRIMITIVE_EQUALITY',
1034+
"In constant expressions, operands of the equality operator must have "
1035+
"primitive equality.",
1036+
);
1037+
10291038
/// Parameters:
10301039
/// 0: the name of the property being accessed
10311040
/// 1: the type with the property being accessed

pkg/analyzer/lib/src/error/error_code_values.g.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ const List<ErrorCode> errorCodeValues = [
134134
CompileTimeErrorCode.CONST_EVAL_EXTENSION_TYPE_METHOD,
135135
CompileTimeErrorCode.CONST_EVAL_FOR_ELEMENT,
136136
CompileTimeErrorCode.CONST_EVAL_METHOD_INVOCATION,
137+
CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY,
137138
CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS,
138139
CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION,
139140
CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE,

pkg/analyzer/messages.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2866,6 +2866,12 @@ CompileTimeErrorCode:
28662866
comment: |-
28672867
See https://spec.dart.dev/DartLangSpecDraft.pdf#constants, "Constants",
28682868
for text about "A literal string".
2869+
CONST_EVAL_PRIMITIVE_EQUALITY:
2870+
problemMessage: "In constant expressions, operands of the equality operator must have primitive equality."
2871+
hasPublishedDocs: false
2872+
comment: |-
2873+
See https://spec.dart.dev/DartLangSpecDraft.pdf#constants, "Constants",
2874+
for text about "An expression of the form e1 == e2".
28692875
CONST_EVAL_TYPE_INT:
28702876
problemMessage: "In constant expressions, operands of this operator must be of type 'int'."
28712877
hasPublishedDocs: false

pkg/analyzer/test/src/dart/constant/evaluation_test.dart

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,28 @@ bool false
246246
''');
247247
}
248248

249+
test_equalEqual_hasPrimitiveEquality_false() async {
250+
await assertNoErrorsInCode('''
251+
const v = const <int>[1, 2] == const <num>[1, 2];
252+
''');
253+
var result = _topLevelVar('v');
254+
assertDartObjectText(result, '''
255+
bool false
256+
variable: <testLibraryFragment>::@topLevelVariable::v
257+
''');
258+
}
259+
260+
test_equalEqual_hasPrimitiveEquality_true() async {
261+
await assertNoErrorsInCode('''
262+
const v = [1, 2] == [1, 2];
263+
''');
264+
var result = _topLevelVar('v');
265+
assertDartObjectText(result, '''
266+
bool true
267+
variable: <testLibraryFragment>::@topLevelVariable::v
268+
''');
269+
}
270+
249271
test_equalEqual_int_int_false() async {
250272
await assertNoErrorsInCode('''
251273
const v = 1 == 2;
@@ -358,7 +380,7 @@ class A {
358380
359381
const v = A() == 0;
360382
''', [
361-
error(CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING, 72, 8),
383+
error(CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY, 72, 8),
362384
]);
363385
var result = _topLevelVar('v');
364386
_assertNull(result);
@@ -373,7 +395,7 @@ class A {
373395
374396
const v = A() == 0;
375397
''', [
376-
error(CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING, 61, 8),
398+
error(CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY, 61, 8),
377399
]);
378400
var result = _topLevelVar('v');
379401
_assertNull(result);
@@ -426,6 +448,19 @@ bool true
426448
''');
427449
}
428450

451+
test_equalEqual_userClass_noPrimitiveEquality() async {
452+
await assertErrorsInCode('''
453+
class A {
454+
const A();
455+
bool operator ==(other) => false;
456+
}
457+
458+
const v = A() == A();
459+
''', [
460+
error(CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY, 72, 10),
461+
]);
462+
}
463+
429464
test_hasPrimitiveEquality_bool() async {
430465
await assertNoErrorsInCode('''
431466
const v = true;

pkg/analyzer/test/src/dart/constant/value_test.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -859,8 +859,11 @@ class DartObjectImplTest {
859859
}
860860

861861
void test_identical_list_false_differentValues() {
862-
_assertIdentical(_boolValue(false), _listValue(_typeProvider.intType, []),
863-
_listValue(_typeProvider.intType, [_intValue(3)]));
862+
_assertIdentical(
863+
_boolValue(false),
864+
_listValue(_typeProvider.intType, []),
865+
_listValue(_typeProvider.intType, [_intValue(3)]),
866+
);
864867
}
865868

866869
void test_identical_list_false_equalTypes_differentValues() {
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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(ConstEvalPrimitiveEqualityTest);
13+
});
14+
}
15+
16+
@reflectiveTest
17+
class ConstEvalPrimitiveEqualityTest extends PubPackageResolutionTest {
18+
test_equal_double_object() async {
19+
await assertNoErrorsInCode(r'''
20+
const a = 0.1;
21+
const b = a == Object();
22+
''');
23+
}
24+
25+
test_equal_int_object() async {
26+
await assertNoErrorsInCode(r'''
27+
const a = 0;
28+
const b = a == Object();
29+
''');
30+
}
31+
32+
test_equal_int_userClass() async {
33+
await assertNoErrorsInCode(r'''
34+
class A {
35+
const A();
36+
}
37+
38+
const a = 0;
39+
const b = a == A();
40+
''');
41+
}
42+
43+
test_equal_null_object() async {
44+
await assertNoErrorsInCode(r'''
45+
const a = null;
46+
const b = a == Object();
47+
''');
48+
}
49+
50+
test_equal_string_object() async {
51+
await assertNoErrorsInCode(r'''
52+
const a = 'foo';
53+
const b = a == Object();
54+
''');
55+
}
56+
57+
test_equal_userClass_int_hasEqEq() async {
58+
await assertErrorsInCode(r'''
59+
class A {
60+
const A();
61+
bool operator ==(other) => false;
62+
}
63+
64+
const a = A();
65+
const b = a == 0;
66+
''', [
67+
error(CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY, 87, 6),
68+
]);
69+
}
70+
71+
test_equal_userClass_int_hasHashCode() async {
72+
await assertErrorsInCode(r'''
73+
class A {
74+
const A();
75+
int get hashCode => 0;
76+
}
77+
78+
const a = A();
79+
const b = a == 0;
80+
''', [
81+
error(CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY, 76, 6),
82+
]);
83+
}
84+
85+
test_equal_userClass_int_hasPrimitiveEquality() async {
86+
await assertNoErrorsInCode(r'''
87+
class A {
88+
const A();
89+
}
90+
91+
const a = A();
92+
const b = a == 0;
93+
''');
94+
}
95+
96+
test_notEqual_double_object() async {
97+
await assertNoErrorsInCode(r'''
98+
const a = 0.1;
99+
const b = a != Object();
100+
''');
101+
}
102+
103+
test_notEqual_int_object() async {
104+
await assertNoErrorsInCode(r'''
105+
const a = 0;
106+
const b = a != Object();
107+
''');
108+
}
109+
110+
test_notEqual_int_userClass() async {
111+
await assertNoErrorsInCode(r'''
112+
class A {
113+
const A();
114+
}
115+
116+
const a = 0;
117+
const b = a != A();
118+
''');
119+
}
120+
121+
test_notEqual_null_object() async {
122+
await assertNoErrorsInCode(r'''
123+
const a = null;
124+
const b = a != Object();
125+
''');
126+
}
127+
128+
test_notEqual_string_object() async {
129+
await assertNoErrorsInCode(r'''
130+
const a = 'foo';
131+
const b = a != Object();
132+
''');
133+
}
134+
135+
test_notEqual_userClass_int_hasEqEq() async {
136+
await assertErrorsInCode(r'''
137+
class A {
138+
const A();
139+
bool operator ==(other) => false;
140+
}
141+
142+
const a = A();
143+
const b = a != 0;
144+
''', [
145+
error(CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY, 87, 6),
146+
]);
147+
}
148+
149+
test_notEqual_userClass_int_hasHashCode() async {
150+
await assertErrorsInCode(r'''
151+
class A {
152+
const A();
153+
int get hashCode => 0;
154+
}
155+
156+
const a = A();
157+
const b = a != 0;
158+
''', [
159+
error(CompileTimeErrorCode.CONST_EVAL_PRIMITIVE_EQUALITY, 76, 6),
160+
]);
161+
}
162+
163+
test_notEqual_userClass_int_hasPrimitiveEquality() async {
164+
await assertNoErrorsInCode(r'''
165+
class A {
166+
const A();
167+
}
168+
169+
const a = A();
170+
const b = a != 0;
171+
''');
172+
}
173+
}

0 commit comments

Comments
 (0)