Skip to content

Commit 2f33461

Browse files
stereotype441Commit Queue
authored andcommitted
[analyzer] Fix flow analysis of null-aware extension overrides.
When an extension override is used in a null-aware fashion (for example, `E(x)?.foo(...)`, where `E` refers to an extension declaration), the expression appearing inside the extension override (`x` in this example) needs to be passed to flow analysis, so that it can be type promoted if appropriate. Fixes #59654. Bug: #59654 Change-Id: I9968f7403eee63f209eb39b12d4904151f7d6914 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/399623 Commit-Queue: Paul Berry <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 47269eb commit 2f33461

File tree

4 files changed

+185
-3
lines changed

4 files changed

+185
-3
lines changed

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4261,9 +4261,14 @@ class ResolverVisitor extends ThrowingAstVisitor<void>
42614261
case SimpleIdentifier(staticElement: InterfaceElement()):
42624262
// `?.` to access static methods is equivalent to `.`, so do nothing.
42634263
break;
4264-
default:
4265-
flow.nullAwareAccess_rightBegin(target,
4266-
SharedTypeView(target.staticType ?? typeProvider.dynamicType));
4264+
case ExtensionOverride(
4265+
argumentList: ArgumentList(arguments: [var expression])
4266+
):
4267+
case var expression:
4268+
flow.nullAwareAccess_rightBegin(
4269+
expression,
4270+
SharedTypeView(
4271+
expression.staticType ?? typeProvider.dynamicType));
42674272
_unfinishedNullShorts.add(node.nullShortingTermination);
42684273
}
42694274
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import 'package:analyzer/src/error/codes.dart';
66
import 'package:test_reflective_loader/test_reflective_loader.dart';
77

88
import 'context_collection_resolution.dart';
9+
import 'node_text_expectations.dart';
910

1011
main() {
1112
defineReflectiveSuite(() {
1213
defineReflectiveTests(ExtensionOverrideResolutionTest);
14+
defineReflectiveTests(UpdateNodeTextExpectations);
1315
});
1416
}
1517

@@ -1105,6 +1107,31 @@ BinaryExpression
11051107
''');
11061108
}
11071109

1110+
test_promotion() async {
1111+
await assertNoErrorsInCode(r'''
1112+
class C {
1113+
int f() => 0;
1114+
}
1115+
1116+
extension E on C {
1117+
int g(dynamic d) => 0;
1118+
}
1119+
1120+
void test(C? c) {
1121+
E(c)?.g(c); // `c` is promoted to `C` on the RHS of `?.`
1122+
}
1123+
''');
1124+
var node = findNode.simple('c);');
1125+
assertResolvedNodeText(node, r'''
1126+
SimpleIdentifier
1127+
token: c
1128+
parameter: <testLibraryFragment>::@extension::E::@method::g::@parameter::d
1129+
staticElement: <testLibraryFragment>::@function::test::@parameter::c
1130+
element: <testLibraryFragment>::@function::test::@parameter::c#element
1131+
staticType: C
1132+
''');
1133+
}
1134+
11081135
test_propertyAccess_getter_nullAware() async {
11091136
await assertNoErrorsInCode('''
11101137
extension E on int {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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+
// This test verifies that a null-aware extension method invocation properly
6+
// promotes its target to non-nullable.
7+
8+
import '../static_type_helper.dart';
9+
10+
class B {
11+
final C? _c;
12+
B(this._c);
13+
}
14+
15+
class C {
16+
void method(Object? o) {}
17+
C operator +(Object? other) => this;
18+
}
19+
20+
extension E on C {
21+
C extensionMethod(Object? o) => this;
22+
C get extensionProperty => this;
23+
set extensionProperty(Object? value) {}
24+
C? get nullableExtensionProperty => this;
25+
set nullableExtensionProperty(Object? value) {}
26+
C operator [](Object? index) => this;
27+
operator []=(Object? index, Object? value) {}
28+
}
29+
30+
extension E2 on C {
31+
C? operator [](Object? index) => this;
32+
operator []=(Object? index, Object? value) {}
33+
}
34+
35+
testVariable(C? c) {
36+
E(c)?.extensionMethod(c..expectStaticType<Exactly<C>>());
37+
E(c)?.extensionProperty.method(c..expectStaticType<Exactly<C>>());
38+
E(c)?.extensionProperty = c..expectStaticType<Exactly<C>>();
39+
E(c)?.extensionProperty += c..expectStaticType<Exactly<C>>();
40+
E(c)?.nullableExtensionProperty ??= c..expectStaticType<Exactly<C>>();
41+
E(c)?[c
42+
..expectStaticType<Exactly<C>>()].method(c..expectStaticType<Exactly<C>>());
43+
E(c)?[c..expectStaticType<Exactly<C>>()] = c..expectStaticType<Exactly<C>>();
44+
E(c)?[c..expectStaticType<Exactly<C>>()] += c..expectStaticType<Exactly<C>>();
45+
E2(c)?[c..expectStaticType<Exactly<C>>()] ??=
46+
c..expectStaticType<Exactly<C>>();
47+
}
48+
49+
testProperty(B b) {
50+
E(b._c)?.extensionMethod(b._c..expectStaticType<Exactly<C>>());
51+
E(b._c)?.extensionProperty.method(b._c..expectStaticType<Exactly<C>>());
52+
E(b._c)?.extensionProperty = b._c..expectStaticType<Exactly<C>>();
53+
E(b._c)?.extensionProperty += b._c..expectStaticType<Exactly<C>>();
54+
E(b._c)?.nullableExtensionProperty ??= b._c..expectStaticType<Exactly<C>>();
55+
E(b._c)?[b._c..expectStaticType<Exactly<C>>()].method(
56+
b._c..expectStaticType<Exactly<C>>(),
57+
);
58+
E(b._c)?[b._c..expectStaticType<Exactly<C>>()] =
59+
b._c..expectStaticType<Exactly<C>>();
60+
E(b._c)?[b._c..expectStaticType<Exactly<C>>()] +=
61+
b._c..expectStaticType<Exactly<C>>();
62+
E2(b._c)?[b._c..expectStaticType<Exactly<C>>()] ??=
63+
b._c..expectStaticType<Exactly<C>>();
64+
}
65+
66+
main() {
67+
for (var value in [null, C()]) {
68+
testVariable(value);
69+
testProperty(B(value));
70+
}
71+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
// This test verifies that a null-aware extension method invocation is properly
6+
// treated as unreachable when the target has type `Null`.
7+
8+
import '../static_type_helper.dart';
9+
10+
class C {
11+
void method(Object? o) {}
12+
C operator +(Object? other) => this;
13+
}
14+
15+
extension E on Null {
16+
C extensionMethod(Object? o) => C();
17+
C get extensionProperty => C();
18+
set extensionProperty(Object? value) {}
19+
C? get nullableExtensionProperty => C();
20+
set nullableExtensionProperty(Object? value) {}
21+
C operator [](Object? index) => C();
22+
operator []=(Object? index, Object? value) {}
23+
}
24+
25+
extension E2 on C {
26+
C? operator [](Object? index) => C();
27+
operator []=(Object? index, Object? value) {}
28+
}
29+
30+
testLiteralNull() {
31+
int? i = 0; // Promotes to non-null.
32+
i.expectStaticType<Exactly<int>>();
33+
E(null)?.extensionMethod(i = null);
34+
i.expectStaticType<Exactly<int>>();
35+
E(null)?.extensionProperty.method(i = null);
36+
i.expectStaticType<Exactly<int>>();
37+
E(null)?.extensionProperty = i = null;
38+
i.expectStaticType<Exactly<int>>();
39+
E(null)?.extensionProperty += i = null;
40+
i.expectStaticType<Exactly<int>>();
41+
E(null)?.nullableExtensionProperty ??= i = null;
42+
i.expectStaticType<Exactly<int>>();
43+
E(null)?[i = null].method(i = null);
44+
i.expectStaticType<Exactly<int>>();
45+
E(null)?[i = null] = i = null;
46+
i.expectStaticType<Exactly<int>>();
47+
E(null)?[i = null] += i = null;
48+
i.expectStaticType<Exactly<int>>();
49+
E2(null)?[i = null] ??= i = null;
50+
i.expectStaticType<Exactly<int>>();
51+
}
52+
53+
testNullVariable(Null n) {
54+
int? i = 0; // Promotes to non-null.
55+
i.expectStaticType<Exactly<int>>();
56+
E(n)?.extensionMethod(i = null);
57+
i.expectStaticType<Exactly<int>>();
58+
E(n)?.extensionProperty.method(i = null);
59+
i.expectStaticType<Exactly<int>>();
60+
E(n)?.extensionProperty = i = null;
61+
i.expectStaticType<Exactly<int>>();
62+
E(n)?.extensionProperty += i = null;
63+
i.expectStaticType<Exactly<int>>();
64+
E(n)?.nullableExtensionProperty ??= i = null;
65+
i.expectStaticType<Exactly<int>>();
66+
E(n)?[i = null].method(i = null);
67+
i.expectStaticType<Exactly<int>>();
68+
E(n)?[i = null] = i = null;
69+
i.expectStaticType<Exactly<int>>();
70+
E(n)?[i = null] += i = null;
71+
i.expectStaticType<Exactly<int>>();
72+
E2(n)?[i = null] ??= i = null;
73+
i.expectStaticType<Exactly<int>>();
74+
}
75+
76+
main() {
77+
testLiteralNull();
78+
testNullVariable(null);
79+
}

0 commit comments

Comments
 (0)