Skip to content

Commit 1d191a1

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Implement more "why not promoted" functionality in the front end.
This CL ensures that we produce appropriate "why not promoted" context messages in all the scenarios where the CFE reports the error code `NullableExpressionCallError`. Analyzer support for these scenarios will be in a follow-up CL. Bug: #44898 Change-Id: Id1407b28a97fc917189e885f995a4efc0bf0c250 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/185920 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent 79de274 commit 1d191a1

File tree

4 files changed

+204
-17
lines changed

4 files changed

+204
-17
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright (c) 2020, 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 contains a test case for each condition that can lead to the front
6+
// end's `NullableExpressionCallError`, for which we wish to report "why not
7+
// promoted" context information.
8+
9+
class C1 {
10+
C2? bad;
11+
}
12+
13+
class C2 {
14+
void call() {}
15+
}
16+
17+
instance_method_invocation(C1 c) {
18+
if (c.bad == null) return;
19+
c.bad
20+
/*cfe.invoke: notPromoted(propertyNotPromoted(member:C1.bad))*/
21+
();
22+
}
23+
24+
class C3 {
25+
C4? ok;
26+
C5? bad;
27+
}
28+
29+
class C4 {}
30+
31+
class C5 {}
32+
33+
extension on C4? {
34+
void call() {}
35+
}
36+
37+
extension on C5 {
38+
void call() {}
39+
}
40+
41+
extension_invocation_method(C3 c) {
42+
if (c.ok == null) return;
43+
c.ok();
44+
if (c.bad == null) return;
45+
c.bad
46+
/*cfe.invoke: notPromoted(propertyNotPromoted(member:C3.bad))*/
47+
();
48+
}
49+
50+
class C6 {
51+
C7? bad;
52+
}
53+
54+
class C7 {
55+
void Function() get call => () {};
56+
}
57+
58+
instance_getter_invocation(C6 c) {
59+
if (c.bad == null) return;
60+
c.bad
61+
/*cfe.invoke: notPromoted(propertyNotPromoted(member:C6.bad))*/
62+
();
63+
}
64+
65+
class C8 {
66+
C9? ok;
67+
C10? bad;
68+
}
69+
70+
class C9 {}
71+
72+
class C10 {}
73+
74+
extension on C9? {
75+
void Function() get call => () {};
76+
}
77+
78+
extension on C10 {
79+
void Function() get call => () {};
80+
}
81+
82+
extension_invocation_getter(C8 c) {
83+
if (c.ok == null) return;
84+
c.ok();
85+
if (c.bad == null) return;
86+
c.bad
87+
/*cfe.invoke: notPromoted(propertyNotPromoted(member:C8.bad))*/
88+
();
89+
}
90+
91+
class C11 {
92+
void Function()? bad;
93+
}
94+
95+
function_invocation(C11 c) {
96+
if (c.bad == null) return;
97+
c.bad
98+
/*cfe.invoke: notPromoted(propertyNotPromoted(member:C11.bad))*/
99+
();
100+
}
101+
102+
class C12 {
103+
C13? bad;
104+
}
105+
106+
class C13 {
107+
void Function() foo;
108+
C13(this.foo);
109+
}
110+
111+
instance_field_invocation(C12 c) {
112+
if (c.bad == null) return;
113+
c.bad
114+
.
115+
/*analyzer.notPromoted(propertyNotPromoted(member:C12.bad))*/
116+
foo
117+
/*cfe.invoke: notPromoted(propertyNotPromoted(member:C12.bad))*/
118+
();
119+
}

pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_error_scenarios.dart renamed to pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_method_call_error.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
// This test contains a test case for each kind of error that can arise from an
6-
// expression being nullable, for which we wish to report "why not promoted"
7-
// errors.
5+
// This test contains a test case for each condition that can lead to the front
6+
// end's `NullableMethodCallError`, for which we wish to report "why not
7+
// promoted" context information.
88

99
class C {
1010
int? i;

pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4736,7 +4736,8 @@ class InferenceVisitor
47364736
propertyName.text, receiverType, inferrer.isNonNullableByDefault),
47374737
read.fileOffset,
47384738
propertyName.text.length,
4739-
context: inferrer.getWhyNotPromotedContext(receiver, read));
4739+
context: inferrer.getWhyNotPromotedContext(
4740+
receiver, inferrer.flowAnalysis?.whyNotPromoted(receiver), read));
47404741
}
47414742
return readResult;
47424743
}

pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart

Lines changed: 80 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -305,10 +305,8 @@ class TypeInferrerImpl implements TypeInferrer {
305305
/// Computes a list of context messages explaining why [receiver] was not
306306
/// promoted, to be used when reporting an error for a larger expression
307307
/// containing [receiver]. [expression] is the containing expression.
308-
List<LocatedMessage> getWhyNotPromotedContext(
309-
Expression receiver, Expression expression) {
310-
Map<DartType, NonPromotionReason> whyNotPromoted =
311-
flowAnalysis?.whyNotPromoted(receiver);
308+
List<LocatedMessage> getWhyNotPromotedContext(Expression receiver,
309+
Map<DartType, NonPromotionReason> whyNotPromoted, Expression expression) {
312310
List<LocatedMessage> context;
313311
if (whyNotPromoted != null && whyNotPromoted.isNotEmpty) {
314312
_WhyNotPromotedVisitor whyNotPromotedVisitor =
@@ -2755,12 +2753,22 @@ class TypeInferrerImpl implements TypeInferrer {
27552753
implicitInvocationPropertyName: name);
27562754

27572755
if (!isTopLevel && target.isNullable) {
2756+
// Handles cases like:
2757+
// C? c;
2758+
// c();
2759+
// where there is an extension on C defined as:
2760+
// extension on C {
2761+
// void Function() get call => () {};
2762+
// }
2763+
List<LocatedMessage> context = getWhyNotPromotedContext(
2764+
receiver, flowAnalysis?.whyNotPromoted(receiver), staticInvocation);
27582765
result = wrapExpressionInferenceResultInProblem(
27592766
result,
27602767
templateNullableExpressionCallError.withArguments(
27612768
receiverType, isNonNullableByDefault),
27622769
fileOffset,
2763-
noLength);
2770+
noLength,
2771+
context: context);
27642772
}
27652773

27662774
return result;
@@ -2780,13 +2788,23 @@ class TypeInferrerImpl implements TypeInferrer {
27802788

27812789
Expression replacement = result.applyResult(staticInvocation);
27822790
if (!isTopLevel && target.isNullable) {
2791+
List<LocatedMessage> context = getWhyNotPromotedContext(
2792+
receiver, flowAnalysis?.whyNotPromoted(receiver), staticInvocation);
27832793
if (isImplicitCall) {
2794+
// Handles cases like:
2795+
// int? i;
2796+
// i();
2797+
// where there is an extension:
2798+
// extension on int {
2799+
// void call() {}
2800+
// }
27842801
replacement = helper.wrapInProblem(
27852802
replacement,
27862803
templateNullableExpressionCallError.withArguments(
27872804
receiverType, isNonNullableByDefault),
27882805
fileOffset,
2789-
noLength);
2806+
noLength,
2807+
context: context);
27902808
} else {
27912809
// Handles cases like:
27922810
// int? i;
@@ -2801,7 +2819,7 @@ class TypeInferrerImpl implements TypeInferrer {
28012819
name.text, receiverType, isNonNullableByDefault),
28022820
fileOffset,
28032821
name.text.length,
2804-
context: getWhyNotPromotedContext(receiver, staticInvocation));
2822+
context: context);
28052823
}
28062824
}
28072825
return createNullAwareExpressionInferenceResult(
@@ -2865,13 +2883,19 @@ class TypeInferrerImpl implements TypeInferrer {
28652883
}
28662884
Expression replacement = result.applyResult(expression);
28672885
if (!isTopLevel && target.isNullableCallFunction) {
2886+
List<LocatedMessage> context = getWhyNotPromotedContext(
2887+
receiver, flowAnalysis?.whyNotPromoted(receiver), expression);
28682888
if (isImplicitCall) {
2889+
// Handles cases like:
2890+
// void Function()? f;
2891+
// f();
28692892
replacement = helper.wrapInProblem(
28702893
replacement,
28712894
templateNullableExpressionCallError.withArguments(
28722895
receiverType, isNonNullableByDefault),
28732896
fileOffset,
2874-
noLength);
2897+
noLength,
2898+
context: context);
28752899
} else {
28762900
// Handles cases like:
28772901
// void Function()? f;
@@ -2882,7 +2906,7 @@ class TypeInferrerImpl implements TypeInferrer {
28822906
callName.text, receiverType, isNonNullableByDefault),
28832907
fileOffset,
28842908
callName.text.length,
2885-
context: getWhyNotPromotedContext(receiver, expression));
2909+
context: context);
28862910
}
28872911
}
28882912
// TODO(johnniwinther): Check that type arguments against the bounds.
@@ -3031,13 +3055,23 @@ class TypeInferrerImpl implements TypeInferrer {
30313055

30323056
replacement = result.applyResult(replacement);
30333057
if (!isTopLevel && target.isNullable) {
3058+
List<LocatedMessage> context = getWhyNotPromotedContext(
3059+
receiver, flowAnalysis?.whyNotPromoted(receiver), expression);
30343060
if (isImplicitCall) {
3061+
// Handles cases like:
3062+
// C? c;
3063+
// c();
3064+
// Where C is defined as:
3065+
// class C {
3066+
// void call();
3067+
// }
30353068
replacement = helper.wrapInProblem(
30363069
replacement,
30373070
templateNullableExpressionCallError.withArguments(
30383071
receiverType, isNonNullableByDefault),
30393072
fileOffset,
3040-
noLength);
3073+
noLength,
3074+
context: context);
30413075
} else {
30423076
// Handles cases like:
30433077
// int? i;
@@ -3048,7 +3082,7 @@ class TypeInferrerImpl implements TypeInferrer {
30483082
methodName.text, receiverType, isNonNullableByDefault),
30493083
fileOffset,
30503084
methodName.text.length,
3051-
context: getWhyNotPromotedContext(receiver, expression));
3085+
context: context);
30523086
}
30533087
}
30543088

@@ -3172,12 +3206,22 @@ class TypeInferrerImpl implements TypeInferrer {
31723206
}
31733207

31743208
if (!isTopLevel && target.isNullable) {
3209+
// Handles cases like:
3210+
// C? c;
3211+
// c.foo();
3212+
// Where C is defined as:
3213+
// class C {
3214+
// void Function() get foo => () {};
3215+
// }
3216+
List<LocatedMessage> context = getWhyNotPromotedContext(receiver,
3217+
flowAnalysis?.whyNotPromoted(receiver), invocationResult.expression);
31753218
invocationResult = wrapExpressionInferenceResultInProblem(
31763219
invocationResult,
31773220
templateNullableExpressionCallError.withArguments(
31783221
receiverType, isNonNullableByDefault),
31793222
fileOffset,
3180-
noLength);
3223+
noLength,
3224+
context: context);
31813225
}
31823226

31833227
if (!library.loader.target.backendTarget.supportsExplicitGetterCalls) {
@@ -3274,6 +3318,14 @@ class TypeInferrerImpl implements TypeInferrer {
32743318
receiver = _hoist(receiver, receiverType, hoistedExpressions);
32753319
}
32763320

3321+
Map<DartType, NonPromotionReason> whyNotPromotedInfo;
3322+
if (!isTopLevel && target.isNullable) {
3323+
// We won't report the error until later (after we have an
3324+
// invocationResult), but we need to gather "why not promoted" info now,
3325+
// before we tell flow analysis about the property get.
3326+
whyNotPromotedInfo = flowAnalysis?.whyNotPromoted(receiver);
3327+
}
3328+
32773329
Name originalName = field.name;
32783330
Expression originalReceiver = receiver;
32793331
Member originalTarget = field;
@@ -3297,6 +3349,8 @@ class TypeInferrerImpl implements TypeInferrer {
32973349
kind, originalReceiver, originalName,
32983350
resultType: calleeType, interfaceTarget: originalTarget)
32993351
..fileOffset = fileOffset;
3352+
flowAnalysis.propertyGet(
3353+
originalPropertyGet, originalReceiver, originalName.name);
33003354
} else {
33013355
originalPropertyGet =
33023356
new PropertyGet(originalReceiver, originalName, originalTarget)
@@ -3343,12 +3397,25 @@ class TypeInferrerImpl implements TypeInferrer {
33433397
}
33443398

33453399
if (!isTopLevel && target.isNullable) {
3400+
// Handles cases like:
3401+
// C? c;
3402+
// c.foo();
3403+
// Where C is defined as:
3404+
// class C {
3405+
// void Function() foo;
3406+
// C(this.foo);
3407+
// }
3408+
// TODO(paulberry): would it be better to report NullableMethodCallError
3409+
// in this scenario?
3410+
List<LocatedMessage> context = getWhyNotPromotedContext(
3411+
receiver, whyNotPromotedInfo, invocationResult.expression);
33463412
invocationResult = wrapExpressionInferenceResultInProblem(
33473413
invocationResult,
33483414
templateNullableExpressionCallError.withArguments(
33493415
receiverType, isNonNullableByDefault),
33503416
fileOffset,
3351-
noLength);
3417+
noLength,
3418+
context: context);
33523419
}
33533420

33543421
if (!library.loader.target.backendTarget.supportsExplicitGetterCalls) {

0 commit comments

Comments
 (0)