Skip to content

Commit 9be682a

Browse files
johnniwintherCommit Queue
authored andcommitted
[analyzer] Support ?.length on null
This adds support for constant evaluation of null aware access of length on null. Null aware access of length on String was already supported in both analyzer and CFE, and the spec has been updated to support it. Closes #60509 Change-Id: Ie7e26d3e969bc82f5c65147f243350fbf4c68e67 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/429921 Commit-Queue: Johnni Winther <[email protected]> Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent b4ac0a1 commit 9be682a

File tree

3 files changed

+116
-6
lines changed

3 files changed

+116
-6
lines changed

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,7 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
12141214
prefixResult,
12151215
node.identifier,
12161216
node,
1217+
isNullAware: false,
12171218
);
12181219
if (propertyAccessResult != null) {
12191220
return propertyAccessResult;
@@ -1293,6 +1294,7 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
12931294
prefixResult,
12941295
node.propertyName,
12951296
node,
1297+
isNullAware: node.isNullAware,
12961298
);
12971299
if (propertyAccessResult != null) {
12981300
return propertyAccessResult;
@@ -1854,8 +1856,9 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
18541856
Constant? _evaluatePropertyAccess(
18551857
DartObjectImpl targetResult,
18561858
SimpleIdentifier identifier,
1857-
AstNode errorNode,
1858-
) {
1859+
AstNode errorNode, {
1860+
required bool isNullAware,
1861+
}) {
18591862
var propertyElement = identifier.element;
18601863
if (propertyElement is GetterElement && propertyElement.isStatic) {
18611864
return null;
@@ -1878,10 +1881,12 @@ class ConstantVisitor extends UnifyingAstVisitor<Constant> {
18781881
var targetType = targetResult.type;
18791882

18801883
// Evaluate a constant that reads the length of a `String`.
1881-
if (identifier.name == 'length' &&
1882-
targetType is InterfaceType &&
1883-
targetType.isDartCoreString) {
1884-
return _dartObjectComputer.stringLength(errorNode, targetResult);
1884+
if (identifier.name == 'length') {
1885+
if (targetType is InterfaceType && targetType.isDartCoreString) {
1886+
return _dartObjectComputer.stringLength(errorNode, targetResult);
1887+
} else if (targetType.isDartCoreNull && isNullAware) {
1888+
return ConstantEvaluationEngine._nullObject(_library);
1889+
}
18851890
}
18861891

18871892
var element = identifier.element;

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,6 +1099,66 @@ void f(Object? x) {
10991099
''');
11001100
}
11011101

1102+
test_propertyAccess_nullAware_dynamic_length_notNull() async {
1103+
await assertNoErrorsInCode(r'''
1104+
const dynamic d = 'foo';
1105+
const int? c = d?.length;
1106+
''');
1107+
var result = _topLevelVar('c');
1108+
assertDartObjectText(result, '''
1109+
int 3
1110+
variable: <testLibrary>::@topLevelVariable::c
1111+
''');
1112+
}
1113+
1114+
test_propertyAccess_nullAware_dynamic_length_null() async {
1115+
await assertNoErrorsInCode(r'''
1116+
const dynamic d = null;
1117+
const int? c = d?.length;
1118+
''');
1119+
var result = _topLevelVar('c');
1120+
assertDartObjectText(result, '''
1121+
Null null
1122+
variable: <testLibrary>::@topLevelVariable::c
1123+
''');
1124+
}
1125+
1126+
test_propertyAccess_nullAware_list_length_null() async {
1127+
await assertNoErrorsInCode(r'''
1128+
const List? l = null;
1129+
const int? c = l?.length;
1130+
''');
1131+
var result = _topLevelVar('c');
1132+
assertDartObjectText(result, '''
1133+
Null null
1134+
variable: <testLibrary>::@topLevelVariable::c
1135+
''');
1136+
}
1137+
1138+
test_propertyAccess_nullAware_string_length_notNull() async {
1139+
await assertNoErrorsInCode(r'''
1140+
const String? s = 'foo';
1141+
const int? c = s?.length;
1142+
''');
1143+
var result = _topLevelVar('c');
1144+
assertDartObjectText(result, '''
1145+
int 3
1146+
variable: <testLibrary>::@topLevelVariable::c
1147+
''');
1148+
}
1149+
1150+
test_propertyAccess_nullAware_string_length_null() async {
1151+
await assertNoErrorsInCode(r'''
1152+
const String? s = null;
1153+
const int? c = s?.length;
1154+
''');
1155+
var result = _topLevelVar('c');
1156+
assertDartObjectText(result, '''
1157+
Null null
1158+
variable: <testLibrary>::@topLevelVariable::c
1159+
''');
1160+
}
1161+
11021162
test_recordTypeAnnotation() async {
11031163
await assertNoErrorsInCode(r'''
11041164
const a = ('',) is (int,);

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ const a = const A();
4747
);
4848
}
4949

50+
test_length_dynamic_notNull() async {
51+
await assertNoErrorsInCode(r'''
52+
const dynamic d = 'foo';
53+
const int? c = d.length;''');
54+
}
55+
56+
test_length_dynamic_null() async {
57+
await assertErrorsInCode(
58+
r'''
59+
const dynamic d = null;
60+
const int? c = d.length;''',
61+
[error(CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS, 39, 8)],
62+
);
63+
}
64+
5065
test_length_invalidTarget() async {
5166
await assertErrorsInCode(
5267
'''
@@ -90,4 +105,34 @@ const x = const C().t;
90105
[error(CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS, 59, 11)],
91106
);
92107
}
108+
109+
test_nullAware_isEven_null() async {
110+
await assertErrorsInCode(
111+
r'''
112+
const int? s = null;
113+
const bool? c = s?.isEven;''',
114+
[error(CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS, 37, 9)],
115+
);
116+
}
117+
118+
test_nullAware_length_dynamic_null() async {
119+
await assertNoErrorsInCode(r'''
120+
const dynamic d = 'foo';
121+
const int? c = d?.length;''');
122+
}
123+
124+
test_nullAware_length_list_notNull() async {
125+
await assertErrorsInCode(
126+
r'''
127+
const List? l = [];
128+
const int? c = l?.length;''',
129+
[error(CompileTimeErrorCode.CONST_EVAL_PROPERTY_ACCESS, 35, 9)],
130+
);
131+
}
132+
133+
test_nullAware_length_string_notNull() async {
134+
await assertNoErrorsInCode(r'''
135+
const String? s = '';
136+
const int? c = s?.length;''');
137+
}
93138
}

0 commit comments

Comments
 (0)