Skip to content

Commit b661bf1

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Front end: Additional "why not promoted" functionality.
This CL implements "why not promoted" functionality in the front end for the following scenarios: - null iterable in for-loop - null iterable in yield* statement - null iterable or map after spread (`...`) operator Bug: #44898 Change-Id: I471b8bf558341514207fad527dde009f1372182c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/188160 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent 3794264 commit b661bf1

File tree

6 files changed

+254
-50
lines changed

6 files changed

+254
-50
lines changed

pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/for_in_loop_type_not_iterable_nullability_error.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
// `ForInLoopTypeNotIterablePartNullability` errors, for which we wish to report
88
// "why not promoted" context information.
99

10-
// TODO(paulberry): get this to work with the CFE and add additional test cases
11-
// if needed.
12-
1310
class C1 {
1411
List<int>? bad;
1512
}
@@ -18,5 +15,5 @@ test(C1 c) {
1815
if (c.bad == null) return;
1916
for (var x
2017
in /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
21-
.bad) {}
18+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad) {}
2219
}

pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/invalid_assignment_error_nullability_error.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,12 @@
77
// `InvalidAssignmentErrorPartNullability` errors, for which we wish to report
88
// "why not promoted" context information.
99

10-
// TODO(paulberry): get this to work with the CFE and add additional test cases
11-
// if needed.
12-
1310
class C1 {
1411
List<int>? bad;
1512
}
1613

1714
test(C1 c) sync* {
1815
if (c.bad == null) return;
1916
yield* /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
20-
.bad;
17+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ bad;
2118
}

pkg/_fe_analyzer_shared/test/flow_analysis/why_not_promoted/data/nullable_spread_error.dart

Lines changed: 137 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,146 @@
66
// end's `NullableSpreadError` error, for which we wish to report "why not
77
// promoted" context information.
88

9-
// TODO(paulberry): get this to work with the CFE and add additional test cases
10-
// if needed.
9+
class C {
10+
List<int>? listQuestion;
11+
Object? objectQuestion;
12+
Set<int>? setQuestion;
13+
Map<int, int>? mapQuestion;
14+
}
15+
16+
list_from_list_question(C c) {
17+
if (c.listQuestion == null) return;
18+
return [
19+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ c
20+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ listQuestion
21+
];
22+
}
23+
24+
list_from_set_question(C c) {
25+
if (c.setQuestion == null) return;
26+
return [
27+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ c
28+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ setQuestion
29+
];
30+
}
1131

12-
class C1 {
13-
List<int>? bad;
32+
list_from_map_question(C c) {
33+
if (c.mapQuestion == null) return;
34+
return [
35+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ c
36+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ mapQuestion
37+
];
1438
}
1539

16-
test(C1 c) {
17-
if (c.bad == null) return;
40+
list_from_object_question(C c) {
41+
if (c.objectQuestion is! List<int>) return;
1842
return [
19-
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: List<int>?))*/ c
20-
.bad
43+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
44+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
2145
];
2246
}
47+
48+
set_from_list_question(C c) {
49+
if (c.listQuestion == null) return;
50+
return {
51+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ c
52+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ listQuestion
53+
};
54+
}
55+
56+
set_from_set_question(C c) {
57+
if (c.setQuestion == null) return;
58+
return {
59+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ c
60+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ setQuestion
61+
};
62+
}
63+
64+
set_from_map_question(C c) {
65+
if (c.mapQuestion == null) return;
66+
return {
67+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ c
68+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ mapQuestion
69+
};
70+
}
71+
72+
set_from_object_question_type_disambiguate_by_entry(C c) {
73+
if (c.objectQuestion is! Set<int>) return;
74+
return {
75+
null,
76+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
77+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
78+
};
79+
}
80+
81+
set_from_object_question_type_disambiguate_by_previous_spread(C c) {
82+
if (c.objectQuestion is! Set<int>) return;
83+
return {
84+
...<int>{},
85+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
86+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
87+
};
88+
}
89+
90+
set_from_object_question_type_disambiguate_by_literal_args(C c) {
91+
if (c.objectQuestion is! Set<int>) return;
92+
return <int>{
93+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
94+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
95+
};
96+
}
97+
98+
map_from_list_question(C c) {
99+
if (c.listQuestion == null) return;
100+
return {
101+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ c
102+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.listQuestion, type: List<int>?))*/ listQuestion
103+
};
104+
}
105+
106+
map_from_set_question(C c) {
107+
if (c.setQuestion == null) return;
108+
return {
109+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ c
110+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ setQuestion
111+
};
112+
}
113+
114+
map_from_map_question(C c) {
115+
if (c.mapQuestion == null) return;
116+
return {
117+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ c
118+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.mapQuestion, type: Map<int, int>?))*/ mapQuestion
119+
};
120+
}
121+
122+
map_from_object_question_type_disambiguate_by_key_value_pair(C c) {
123+
if (c.objectQuestion is! Map<int, int>) return;
124+
return {
125+
null: null,
126+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
127+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
128+
};
129+
}
130+
131+
map_from_object_question_type_disambiguate_by_previous_spread(C c) {
132+
if (c.objectQuestion is! Map<int, int>) return;
133+
return {
134+
...<int, int>{},
135+
... /*analyzer.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ c
136+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C.objectQuestion, type: Object?))*/ objectQuestion
137+
};
138+
}
139+
140+
map_from_set_question_type_disambiguate_by_literal_args(C c) {
141+
// Note: analyzer shows "why not promoted" information here, but CFE doesn't.
142+
// That's probably ok, since there are two problems here (set/map mismatch and
143+
// null safety); it's a matter of interpretation whether to prioritize one or
144+
// the other.
145+
if (c.setQuestion == null) return;
146+
return <int, int>{
147+
...
148+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C.setQuestion, type: Set<int>?))*/ c
149+
.setQuestion
150+
};
151+
}

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

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,15 +1473,22 @@ class InferenceVisitor
14731473
spreadType is! DynamicType &&
14741474
spreadType is! NullType &&
14751475
!element.isNullAware) {
1476+
Expression receiver = element.expression;
14761477
replacement = inferrer.helper.buildProblem(
1477-
messageNullableSpreadError, element.expression.fileOffset, 1);
1478+
messageNullableSpreadError, receiver.fileOffset, 1,
1479+
context: inferrer.getWhyNotPromotedContext(
1480+
receiver,
1481+
inferrer.flowAnalysis?.whyNotPromoted(receiver),
1482+
element,
1483+
(type) => !type.isPotentiallyNullable));
14781484
}
14791485

14801486
replacement = inferrer.helper.buildProblem(
14811487
templateSpreadTypeMismatch.withArguments(
14821488
spreadType, inferrer.isNonNullableByDefault),
14831489
element.expression.fileOffset,
14841490
1);
1491+
_copyNonPromotionReasonToReplacement(element, replacement);
14851492
}
14861493
} else if (spreadTypeBound is InterfaceType) {
14871494
if (!inferrer.isAssignable(inferredTypeArgument, spreadElementType)) {
@@ -1536,8 +1543,15 @@ class InferenceVisitor
15361543
spreadType is! DynamicType &&
15371544
spreadType is! NullType &&
15381545
!element.isNullAware) {
1546+
Expression receiver = element.expression;
15391547
replacement = inferrer.helper.buildProblem(
1540-
messageNullableSpreadError, element.expression.fileOffset, 1);
1548+
messageNullableSpreadError, receiver.fileOffset, 1,
1549+
context: inferrer.getWhyNotPromotedContext(
1550+
receiver,
1551+
inferrer.flowAnalysis?.whyNotPromoted(receiver),
1552+
element,
1553+
(type) => !type.isPotentiallyNullable));
1554+
_copyNonPromotionReasonToReplacement(element, replacement);
15411555
}
15421556
}
15431557
}
@@ -1716,6 +1730,17 @@ class InferenceVisitor
17161730
}
17171731
}
17181732

1733+
void _copyNonPromotionReasonToReplacement(
1734+
TreeNode oldNode, TreeNode replacement) {
1735+
if (!identical(oldNode, replacement) &&
1736+
inferrer.dataForTesting?.flowAnalysisResult != null) {
1737+
inferrer.dataForTesting.flowAnalysisResult
1738+
.nonPromotionReasons[replacement] =
1739+
inferrer
1740+
.dataForTesting.flowAnalysisResult.nonPromotionReasons[oldNode];
1741+
}
1742+
}
1743+
17191744
void checkElement(
17201745
Expression item,
17211746
Expression parent,
@@ -1956,24 +1981,36 @@ class InferenceVisitor
19561981
spreadType is! DynamicType &&
19571982
spreadType is! NullType &&
19581983
!entry.isNullAware) {
1959-
replacement = new SpreadMapEntry(
1960-
inferrer.helper.buildProblem(messageNullableSpreadError,
1961-
entry.expression.fileOffset, 1),
1962-
false)
1984+
Expression receiver = entry.expression;
1985+
Expression problem = inferrer.helper.buildProblem(
1986+
messageNullableSpreadError, receiver.fileOffset, 1,
1987+
context: inferrer.getWhyNotPromotedContext(
1988+
receiver,
1989+
inferrer.flowAnalysis?.whyNotPromoted(receiver),
1990+
entry,
1991+
(type) => !type.isPotentiallyNullable));
1992+
_copyNonPromotionReasonToReplacement(entry, problem);
1993+
replacement = new SpreadMapEntry(problem, false)
19631994
..fileOffset = entry.fileOffset;
19641995
}
19651996

19661997
// Don't report the error here, it might be an ambiguous Set. The
19671998
// error is reported in checkMapEntry if it's disambiguated as map.
19681999
iterableSpreadType = spreadType;
19692000
} else {
1970-
replacement = new MapEntry(
1971-
inferrer.helper.buildProblem(
1972-
templateSpreadMapEntryTypeMismatch.withArguments(
1973-
spreadType, inferrer.isNonNullableByDefault),
1974-
entry.expression.fileOffset,
1975-
1),
1976-
new NullLiteral())
2001+
Expression receiver = entry.expression;
2002+
Expression problem = inferrer.helper.buildProblem(
2003+
templateSpreadMapEntryTypeMismatch.withArguments(
2004+
spreadType, inferrer.isNonNullableByDefault),
2005+
receiver.fileOffset,
2006+
1,
2007+
context: inferrer.getWhyNotPromotedContext(
2008+
receiver,
2009+
inferrer.flowAnalysis?.whyNotPromoted(receiver),
2010+
entry,
2011+
(type) => !type.isPotentiallyNullable));
2012+
_copyNonPromotionReasonToReplacement(entry, problem);
2013+
replacement = new MapEntry(problem, new NullLiteral())
19772014
..fileOffset = entry.fileOffset;
19782015
}
19792016
} else if (spreadTypeBound is InterfaceType) {
@@ -2075,8 +2112,15 @@ class InferenceVisitor
20752112
spreadType is! DynamicType &&
20762113
spreadType is! NullType &&
20772114
!entry.isNullAware) {
2115+
Expression receiver = entry.expression;
20782116
keyError = inferrer.helper.buildProblem(
2079-
messageNullableSpreadError, entry.expression.fileOffset, 1);
2117+
messageNullableSpreadError, receiver.fileOffset, 1,
2118+
context: inferrer.getWhyNotPromotedContext(
2119+
receiver,
2120+
inferrer.flowAnalysis?.whyNotPromoted(receiver),
2121+
entry,
2122+
(type) => !type.isPotentiallyNullable));
2123+
_copyNonPromotionReasonToReplacement(entry, keyError);
20802124
}
20812125
if (keyError != null || valueError != null) {
20822126
keyError ??= new NullLiteral();
@@ -2343,7 +2387,8 @@ class InferenceVisitor
23432387
iterableSpreadType, inferrer.isNonNullableByDefault),
23442388
iterableSpreadOffset,
23452389
1),
2346-
new NullLiteral());
2390+
new NullLiteral())
2391+
..fileOffset = iterableSpreadOffset;
23472392
}
23482393
if (entry is SpreadMapEntry) {
23492394
DartType spreadType = inferredSpreadTypes[entry.expression];
@@ -4832,7 +4877,10 @@ class InferenceVisitor
48324877
read.fileOffset,
48334878
propertyName.text.length,
48344879
context: inferrer.getWhyNotPromotedContext(
4835-
receiver, inferrer.flowAnalysis?.whyNotPromoted(receiver), read));
4880+
receiver,
4881+
inferrer.flowAnalysis?.whyNotPromoted(receiver),
4882+
read,
4883+
(type) => !type.isPotentiallyNullable));
48364884
}
48374885
return readResult;
48384886
}

0 commit comments

Comments
 (0)