Skip to content

Commit 18b753c

Browse files
scheglovcommit-bot@chromium.org
authored andcommitted
Support for null-aware ExtensionOverride.
Bug: #41780 Change-Id: Ib5d11000847434c679df1ec882ee6d9777670fe0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151097 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent d39d4d0 commit 18b753c

File tree

6 files changed

+147
-20
lines changed

6 files changed

+147
-20
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2106,6 +2106,9 @@ abstract class ExtensionOverride implements Expression {
21062106
/// Return the name of the extension being selected.
21072107
Identifier get extensionName;
21082108

2109+
/// Whether this override is null aware (as opposed to non-null).
2110+
bool get isNullAware;
2111+
21092112
/// Return the forced extension element.
21102113
///
21112114
/// Return `null` if the AST structure has not been resolved.

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3925,6 +3925,13 @@ class ExtensionOverrideImpl extends ExpressionImpl
39253925
_extensionName = _becomeParentOf(extensionName as IdentifierImpl);
39263926
}
39273927

3928+
@override
3929+
bool get isNullAware {
3930+
var nextType = argumentList.endToken.next.type;
3931+
return nextType == TokenType.QUESTION_PERIOD ||
3932+
nextType == TokenType.QUESTION_PERIOD_OPEN_SQUARE_BRACKET;
3933+
}
3934+
39283935
@override
39293936
Precedence get precedence => Precedence.postfix;
39303937

pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ class ExtensionMemberResolver {
136136
var receiverExpression = arguments[0];
137137
var receiverType = receiverExpression.staticType;
138138

139+
if (node.isNullAware) {
140+
receiverType = _typeSystem.promoteToNonNull(receiverType);
141+
}
142+
139143
var typeArgumentTypes = _inferTypeArguments(node, receiverType);
140144
nodeImpl.typeArgumentTypes = typeArgumentTypes;
141145

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,11 @@ class ElementResolver extends SimpleAstVisitor<void> {
412412
}
413413

414414
if (node.isNullAware) {
415-
targetType = _typeSystem.promoteToNonNull(targetType);
415+
if (target is ExtensionOverride) {
416+
// https://github.com/dart-lang/language/pull/953
417+
} else {
418+
targetType = _typeSystem.promoteToNonNull(targetType);
419+
}
416420
}
417421

418422
String getterMethodName = TokenType.INDEX.lexeme;

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

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -847,10 +847,13 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
847847
void visitIndexExpression(IndexExpression node) {
848848
_checkForArgumentTypeNotAssignableForArgument(node.index);
849849
if (node.isNullAware) {
850-
_checkForUnnecessaryNullAware(
851-
node.realTarget,
852-
node.question ?? node.period ?? node.leftBracket,
853-
);
850+
var target = node.realTarget;
851+
if (_isExpressionWithType(target)) {
852+
_checkForUnnecessaryNullAware(
853+
target,
854+
node.question ?? node.period ?? node.leftBracket,
855+
);
856+
}
854857
}
855858
super.visitIndexExpression(node);
856859
}
@@ -944,9 +947,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
944947
_checkForStaticAccessToInstanceMember(typeReference, methodName);
945948
_checkForInstanceAccessToStaticMember(
946949
typeReference, node.target, methodName);
947-
948-
// For `C?.foo()` the type of `C` is not set, it is not an expression.
949-
if (target.staticType != null) {
950+
if (_isExpressionWithType(target)) {
950951
_checkForUnnecessaryNullAware(target, node.operator);
951952
}
952953
} else {
@@ -1051,9 +1052,7 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
10511052
_checkForStaticAccessToInstanceMember(typeReference, propertyName);
10521053
_checkForInstanceAccessToStaticMember(
10531054
typeReference, node.target, propertyName);
1054-
1055-
// For `C?.x` the type of `C` is not set, because it is not an expression.
1056-
if (target.staticType != null) {
1055+
if (_isExpressionWithType(target)) {
10571056
_checkForUnnecessaryNullAware(target, node.operator);
10581057
}
10591058

@@ -5390,6 +5389,22 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
53905389
}
53915390
return null;
53925391
}
5392+
5393+
static bool _isExpressionWithType(Expression node) {
5394+
if (node is ExtensionOverride) {
5395+
return false;
5396+
}
5397+
5398+
// For `C?.foo()` the type of `C` is not set, it is not an expression.
5399+
if (node is Identifier) {
5400+
var element = node.staticElement;
5401+
if (element is ClassElement || element is ExtensionElement) {
5402+
return false;
5403+
}
5404+
}
5405+
5406+
return true;
5407+
}
53935408
}
53945409

53955410
/**

pkg/analyzer/test/src/dart/resolution/extension_override_test.dart

Lines changed: 103 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import 'package:meta/meta.dart';
1010
import 'package:test/test.dart';
1111
import 'package:test_reflective_loader/test_reflective_loader.dart';
1212

13+
import '../constant/potentially_constant_test.dart';
1314
import 'driver_resolution.dart';
1415

1516
main() {
1617
defineReflectiveSuite(() {
1718
defineReflectiveTests(ExtensionOverrideTest);
19+
defineReflectiveTests(ExtensionOverrideWithNullSafetyTest);
1820
});
1921
}
2022

@@ -311,20 +313,20 @@ void f(A a) {
311313
validateBinaryExpression();
312314
}
313315

314-
test_operator_onTearoff() async {
316+
test_operator_onTearOff() async {
315317
// https://github.com/dart-lang/sdk/issues/38653
316318
await assertErrorsInCode('''
317-
f(){
318-
E(null).v++;
319-
}
320-
extension E on Object{
319+
extension E on int {
321320
v() {}
322321
}
322+
323+
f(){
324+
E(0).v++;
325+
}
323326
''', [
324-
error(CompileTimeErrorCode.UNDEFINED_EXTENSION_SETTER, 15, 1),
327+
error(CompileTimeErrorCode.UNDEFINED_EXTENSION_SETTER, 45, 1),
325328
]);
326-
findDeclarationAndOverride(
327-
declarationName: 'E ', overrideSearch: 'E(null)');
329+
findDeclarationAndOverride(declarationName: 'E ', overrideSearch: 'E(0)');
328330
validateOverride();
329331
}
330332

@@ -518,7 +520,7 @@ void f(p.A a) {
518520
validatePropertyAccess();
519521
}
520522

521-
test_tearoff() async {
523+
test_tearOff() async {
522524
await assertNoErrorsInCode('''
523525
class C {}
524526
@@ -601,3 +603,95 @@ f(C c) => E(c).a;
601603
expect(resolvedElement, expectedElement);
602604
}
603605
}
606+
607+
@reflectiveTest
608+
class ExtensionOverrideWithNullSafetyTest extends ExtensionOverrideTest
609+
with WithNullSafetyMixin {
610+
test_indexExpression_read_nullAware() async {
611+
await assertNoErrorsInCode('''
612+
extension E on int {
613+
int operator [](int index) => 0;
614+
}
615+
616+
void f(int? a) {
617+
E(a)?.[0];
618+
}
619+
''');
620+
621+
assertIndexExpression(
622+
findNode.index('[0]'),
623+
readElement: findElement.method('[]', of: 'E'),
624+
writeElement: null,
625+
type: 'int?',
626+
);
627+
}
628+
629+
test_indexExpression_write_nullAware() async {
630+
await assertNoErrorsInCode('''
631+
extension E on int {
632+
operator []=(int index, int value) {}
633+
}
634+
635+
void f(int? a) {
636+
E(a)?.[0] = 1;
637+
}
638+
''');
639+
640+
assertIndexExpression(
641+
findNode.index('[0]'),
642+
readElement: null,
643+
writeElement: findElement.method('[]=', of: 'E'),
644+
type: 'int?',
645+
);
646+
}
647+
648+
test_methodInvocation_nullAware() async {
649+
await assertNoErrorsInCode('''
650+
extension E on int {
651+
int foo() => 0;
652+
}
653+
654+
void f(int? a) {
655+
E(a)?.foo();
656+
}
657+
''');
658+
659+
assertMethodInvocation2(
660+
findNode.methodInvocation('foo();'),
661+
element: findElement.method('foo'),
662+
typeArgumentTypes: [],
663+
invokeType: 'int Function()',
664+
type: 'int?',
665+
);
666+
}
667+
668+
test_propertyAccess_getter_nullAware() async {
669+
await assertNoErrorsInCode('''
670+
extension E on int {
671+
int get foo => 0;
672+
}
673+
674+
void f(int? a) {
675+
E(a)?.foo;
676+
}
677+
''');
678+
679+
assertPropertyAccess2(
680+
findNode.propertyAccess('?.foo'),
681+
element: findElement.getter('foo'),
682+
type: 'int?',
683+
);
684+
}
685+
686+
test_propertyAccess_setter_nullAware() async {
687+
await assertNoErrorsInCode('''
688+
extension E on int {
689+
set foo(int _) {}
690+
}
691+
692+
void f(int? a) {
693+
E(a)?.foo = 0;
694+
}
695+
''');
696+
}
697+
}

0 commit comments

Comments
 (0)