Skip to content

Commit 7d4bc6a

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Fix handling of null awareness in cascade expressions.
Change-Id: Id8f9f45d04bac135c516e543542991c4cb4a0c16 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121909 Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
1 parent 4e44906 commit 7d4bc6a

File tree

7 files changed

+149
-15
lines changed

7 files changed

+149
-15
lines changed

pkg/analyzer/lib/src/dart/ast/ast.dart

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4711,6 +4711,7 @@ class ForPartsWithExpressionImpl extends ForPartsImpl
47114711

47124712
@override
47134713
Token get beginToken => initialization?.beginToken ?? super.beginToken;
4714+
47144715
@override
47154716
Iterable<SyntacticEntity> get childEntities => new ChildEntities()
47164717
..add(_initialization)
@@ -5996,7 +5997,10 @@ class IndexExpressionImpl extends ExpressionImpl implements IndexExpression {
59965997

59975998
@override
59985999
bool get isNullAware =>
5999-
leftBracket.type == TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET;
6000+
leftBracket.type == TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET ||
6001+
(leftBracket.type == TokenType.OPEN_SQUARE_BRACKET &&
6002+
period != null &&
6003+
period.type == TokenType.QUESTION_PERIOD_PERIOD);
60006004

60016005
@override
60026006
Precedence get precedence => Precedence.postfix;
@@ -7193,7 +7197,10 @@ class MethodInvocationImpl extends InvocationExpressionImpl
71937197
operator.type == TokenType.QUESTION_PERIOD_PERIOD);
71947198

71957199
@override
7196-
bool get isNullAware => operator?.type == TokenType.QUESTION_PERIOD;
7200+
bool get isNullAware =>
7201+
operator != null &&
7202+
(operator.type == TokenType.QUESTION_PERIOD ||
7203+
operator.type == TokenType.QUESTION_PERIOD_PERIOD);
71977204

71987205
@override
71997206
SimpleIdentifier get methodName => _methodName;
@@ -8456,7 +8463,9 @@ class PropertyAccessImpl extends ExpressionImpl implements PropertyAccess {
84568463

84578464
@override
84588465
bool get isNullAware =>
8459-
operator != null && operator.type == TokenType.QUESTION_PERIOD;
8466+
operator != null &&
8467+
(operator.type == TokenType.QUESTION_PERIOD ||
8468+
operator.type == TokenType.QUESTION_PERIOD_PERIOD);
84608469

84618470
@override
84628471
Precedence get precedence => Precedence.postfix;

pkg/analyzer/lib/src/generated/error_verifier.dart

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -507,11 +507,6 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
507507
}
508508
}
509509

510-
void visitCascadeExpression(CascadeExpression node) {
511-
_checkForNullableDereference(node.target);
512-
super.visitCascadeExpression(node);
513-
}
514-
515510
@override
516511
void visitCatchClause(CatchClause node) {
517512
_duplicateDefinitionVerifier.checkCatchClause(node);
@@ -990,7 +985,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
990985
void visitIndexExpression(IndexExpression node) {
991986
_checkForArgumentTypeNotAssignableForArgument(node.index);
992987
if (!node.isNullAware) {
993-
_checkForNullableDereference(node.target);
988+
_checkForNullableDereference(node.realTarget);
994989
}
995990
super.visitIndexExpression(node);
996991
}
@@ -1213,7 +1208,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
12131208
typeReference, node.target, propertyName);
12141209
if (!node.isNullAware &&
12151210
!_objectPropertyNames.contains(propertyName.name)) {
1216-
_checkForNullableDereference(node.target);
1211+
_checkForNullableDereference(node.realTarget);
12171212
}
12181213
_checkForUnnecessaryNullAware(node.target, node.operator);
12191214
super.visitPropertyAccess(node);

pkg/analyzer/lib/src/generated/testing/ast_test_factory.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,14 @@ class AstTestFactory {
787787
index,
788788
TokenFactory.tokenFromType(TokenType.CLOSE_SQUARE_BRACKET));
789789

790+
static IndexExpression indexExpressionForCascade(Expression array,
791+
Expression index, TokenType period, TokenType leftBracket) =>
792+
astFactory.indexExpressionForCascade(
793+
TokenFactory.tokenFromType(period),
794+
TokenFactory.tokenFromType(leftBracket),
795+
index,
796+
TokenFactory.tokenFromType(TokenType.CLOSE_SQUARE_BRACKET));
797+
790798
static InstanceCreationExpression instanceCreationExpression(
791799
Keyword keyword, ConstructorName name,
792800
[List<Expression> arguments]) =>

pkg/analyzer/test/dart/ast/ast_test.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,32 @@ class IndexExpressionTest {
325325
expect(expression.inSetterContext(), isTrue);
326326
}
327327

328+
void test_isNullAware_cascade_false() {
329+
final expression = AstTestFactory.indexExpressionForCascade(
330+
AstTestFactory.nullLiteral(),
331+
AstTestFactory.nullLiteral(),
332+
TokenType.PERIOD_PERIOD,
333+
TokenType.OPEN_SQUARE_BRACKET);
334+
expect(expression.isNullAware, isFalse);
335+
}
336+
337+
void test_isNullAware_cascade_true() {
338+
final expression = AstTestFactory.indexExpressionForCascade(
339+
AstTestFactory.nullLiteral(),
340+
AstTestFactory.nullLiteral(),
341+
TokenType.QUESTION_PERIOD_PERIOD,
342+
TokenType.OPEN_SQUARE_BRACKET);
343+
expect(expression.isNullAware, isTrue);
344+
}
345+
346+
void test_isNullAware_false() {
347+
final expression = AstTestFactory.indexExpression(
348+
AstTestFactory.nullLiteral(),
349+
AstTestFactory.nullLiteral(),
350+
TokenType.OPEN_SQUARE_BRACKET);
351+
expect(expression.isNullAware, isFalse);
352+
}
353+
328354
void test_isNullAware_regularIndex() {
329355
final expression = AstTestFactory.indexExpression(
330356
AstTestFactory.nullLiteral(),
@@ -388,6 +414,16 @@ class MethodInvocationTest extends ParserTestCase {
388414
expect(invocation.isNullAware, isFalse);
389415
}
390416

417+
void test_isNullAware_cascade_true() {
418+
final invocation = AstTestFactory.methodInvocation3(
419+
AstTestFactory.nullLiteral(),
420+
'foo',
421+
null,
422+
[],
423+
TokenType.QUESTION_PERIOD_PERIOD);
424+
expect(invocation.isNullAware, isTrue);
425+
}
426+
391427
void test_isNullAware_regularInvocation() {
392428
final invocation = AstTestFactory.methodInvocation3(
393429
AstTestFactory.nullLiteral(), 'foo', null, [], TokenType.PERIOD);
@@ -762,6 +798,12 @@ class PropertyAccessTest extends ParserTestCase {
762798
expect(invocation.isNullAware, isFalse);
763799
}
764800

801+
void test_isNullAware_cascade_true() {
802+
final invocation = AstTestFactory.propertyAccess2(
803+
AstTestFactory.nullLiteral(), 'foo', TokenType.QUESTION_PERIOD_PERIOD);
804+
expect(invocation.isNullAware, isTrue);
805+
}
806+
765807
void test_isNullAware_regularPropertyAccess() {
766808
final invocation = AstTestFactory.propertyAccess2(
767809
AstTestFactory.nullLiteral(), 'foo', TokenType.PERIOD);

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,12 @@ m() async {
4040
}
4141

4242
test_cascade() async {
43-
await assertErrorsInCode(r'''
43+
await assertNoErrorsInCode(r'''
4444
m() {
4545
Null x;
4646
x..toString;
4747
}
48-
''', [
49-
error(StaticWarningCode.INVALID_USE_OF_NULL_VALUE, 18, 1),
50-
]);
48+
''');
5149
}
5250

5351
test_eq() async {

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

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,47 @@ m() {
215215
''');
216216
}
217217

218-
test_cascade_nullable() async {
218+
test_cascade_nullable_indexed_assignment() async {
219+
await assertErrorsInCode(r'''
220+
m() {
221+
List<int>? x;
222+
x..[0] = 1;
223+
}
224+
''', [
225+
error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 24, 1),
226+
]);
227+
}
228+
229+
test_cascade_nullable_indexed_assignment_null_aware() async {
230+
await assertNoErrorsInCode(r'''
231+
m() {
232+
List<int>? x;
233+
x?..[0] = 1;
234+
}
235+
''');
236+
}
237+
238+
test_cascade_nullable_method_invocation() async {
239+
await assertErrorsInCode(r'''
240+
m() {
241+
int? x;
242+
x..abs();
243+
}
244+
''', [
245+
error(StaticWarningCode.UNCHECKED_USE_OF_NULLABLE_VALUE, 18, 1),
246+
]);
247+
}
248+
249+
test_cascade_nullable_method_invocation_null_aware() async {
250+
await assertNoErrorsInCode(r'''
251+
m() {
252+
int? x;
253+
x?..abs();
254+
}
255+
''');
256+
}
257+
258+
test_cascade_nullable_property_access() async {
219259
await assertErrorsInCode(r'''
220260
m() {
221261
int? x;
@@ -226,6 +266,15 @@ m() {
226266
]);
227267
}
228268

269+
test_cascade_nullable_property_access_null_aware() async {
270+
await assertNoErrorsInCode(r'''
271+
m() {
272+
int? x;
273+
x?..isEven;
274+
}
275+
''');
276+
}
277+
229278
test_eqEq_nullable() async {
230279
await assertNoErrorsInCode(r'''
231280
m() {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) 2019, 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+
// SharedOptions=--enable-experiment=non-nullable
6+
7+
// Test that it is an error to use nullable types in unsound ways.
8+
void main() {
9+
List? list;
10+
list..add(0); //# 00: compile-time error
11+
list?..add(0); //# 01: ok
12+
list..toString(); //# 02: ok
13+
list?..toString(); //# 03: ok
14+
list..last = 0; //# 04: compile-time error
15+
list?..last = 0; //# 05: ok
16+
list..[0] = 0; //# 06: compile-time error
17+
list?..[0] = 0; //# 07: ok
18+
19+
// Note: the following look weird because they call a getter (or
20+
// getter-like operator) and discard the result, but they are
21+
// permitted by both the analyzer and the front end, so we should
22+
// test them.
23+
list..add; //# 08: compile-time error
24+
list?..add; //# 09: ok
25+
list..toString; //# 10: ok
26+
list?..toString; //# 11: ok
27+
list..last; //# 12: compile-time error
28+
list?..last; //# 13: ok
29+
list..runtimeType; //# 14: ok
30+
list?..runtimeType; //# 15: ok
31+
list..[0]; //# 16: compile-time error
32+
list?..[0]; //# 17: ok
33+
}

0 commit comments

Comments
 (0)