Skip to content

Commit f161e22

Browse files
stereotype441commit-bot@chromium.org
authored andcommitted
Flow analysis: implement "why not promoted" logic for assignments.
This CL handles ordinary assignment expressions, variable declaration initializers, and constructor field initializers. Bug: #44898 Change-Id: I06bae2c7d57213bd7769c931a03080d148af3dbe Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/192724 Reviewed-by: Konstantin Shcheglov <[email protected]>
1 parent 6929718 commit f161e22

File tree

5 files changed

+139
-18
lines changed

5 files changed

+139
-18
lines changed

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

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,102 @@ whileCondition(C20 c) {
290290
while (/*analyzer.notPromoted(propertyNotPromoted(target: member:C20.bad, type: bool?))*/ c
291291
. /*cfe.notPromoted(propertyNotPromoted(target: member:C20.bad, type: bool?))*/ bad) {}
292292
}
293+
294+
class C21 {
295+
int? bad;
296+
}
297+
298+
assignmentRhs(C21 c, int i) {
299+
if (c.bad == null) return;
300+
i =
301+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C21.bad, type: int?))*/ c
302+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C21.bad, type: int?))*/ bad;
303+
}
304+
305+
class C22 {
306+
int? bad;
307+
}
308+
309+
variableInitializer(C22 c) {
310+
if (c.bad == null) return;
311+
int i =
312+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C22.bad, type: int?))*/ c
313+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C22.bad, type: int?))*/ bad;
314+
}
315+
316+
class C23 {
317+
int? bad;
318+
final int x;
319+
final int y;
320+
C23.constructorInitializer(C23 c)
321+
: x = c.bad!,
322+
y =
323+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C23.bad, type: int?))*/ c
324+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C23.bad, type: int?))*/ bad;
325+
}
326+
327+
class C24 {
328+
int? bad;
329+
}
330+
331+
forVariableInitializer(C24 c) {
332+
if (c.bad == null) return;
333+
for (int i =
334+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
335+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
336+
false;) {}
337+
[
338+
for (int i =
339+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
340+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
341+
false;)
342+
null
343+
];
344+
({
345+
for (int i =
346+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
347+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
348+
false;)
349+
null
350+
});
351+
({
352+
for (int i =
353+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
354+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
355+
false;)
356+
null: null
357+
});
358+
}
359+
360+
class C25 {
361+
int? bad;
362+
}
363+
364+
forAssignmentInitializer(C25 c, int i) {
365+
if (c.bad == null) return;
366+
for (i =
367+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
368+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
369+
false;) {}
370+
[
371+
for (i =
372+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
373+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
374+
false;)
375+
null
376+
];
377+
({
378+
for (i =
379+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
380+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
381+
false;)
382+
null
383+
});
384+
({
385+
for (i =
386+
/*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
387+
. /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
388+
false;)
389+
null: null
390+
});
391+
}

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
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+
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
56
import 'package:analyzer/dart/ast/ast.dart';
67
import 'package:analyzer/dart/ast/token.dart';
78
import 'package:analyzer/dart/element/element.dart';
@@ -84,8 +85,10 @@ class AssignmentExpressionResolver {
8485
}
8586

8687
right.accept(_resolver);
88+
right = node.rightHandSide;
89+
var whyNotPromoted = flow?.whyNotPromoted(right);
8790

88-
_resolveTypes(node);
91+
_resolveTypes(node, whyNotPromoted: whyNotPromoted);
8992

9093
if (flow != null) {
9194
if (writeElement is PromotableElement) {
@@ -103,8 +106,9 @@ class AssignmentExpressionResolver {
103106
void _checkForInvalidAssignment(
104107
DartType writeType,
105108
Expression right,
106-
DartType rightType,
107-
) {
109+
DartType rightType, {
110+
required Map<DartType, NonPromotionReason> Function()? whyNotPromoted,
111+
}) {
108112
if (!writeType.isVoid && _checkForUseOfVoidResult(right)) {
109113
return;
110114
}
@@ -117,6 +121,8 @@ class AssignmentExpressionResolver {
117121
CompileTimeErrorCode.INVALID_ASSIGNMENT,
118122
right,
119123
[rightType, writeType],
124+
_resolver.computeWhyNotPromotedMessages(
125+
right, right, whyNotPromoted?.call()),
120126
);
121127
}
122128

@@ -192,7 +198,8 @@ class AssignmentExpressionResolver {
192198
}
193199
}
194200

195-
void _resolveTypes(AssignmentExpressionImpl node) {
201+
void _resolveTypes(AssignmentExpressionImpl node,
202+
{required Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
196203
DartType assignedType;
197204
DartType nodeType;
198205

@@ -239,6 +246,7 @@ class AssignmentExpressionResolver {
239246
node.writeType!,
240247
node.rightHandSide,
241248
assignedType,
249+
whyNotPromoted: operator == TokenType.EQ ? whyNotPromoted : null,
242250
);
243251
}
244252

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ class VariableDeclarationResolver {
5252
}
5353

5454
initializer.accept(_resolver);
55-
initializer = node.initializer;
55+
initializer = node.initializer!;
56+
var whyNotPromoted =
57+
_resolver.flowAnalysis?.flow?.whyNotPromoted(initializer);
5658

5759
if (parent.type == null) {
58-
_setInferredType(element, initializer!.typeOrThrow);
60+
_setInferredType(element, initializer.typeOrThrow);
5961
}
6062

6163
if (isTopLevel) {
@@ -72,6 +74,8 @@ class VariableDeclarationResolver {
7274
(element as ConstVariableElement).constantInitializer =
7375
ConstantAstCloner().cloneNullableNode(initializer);
7476
}
77+
_resolver.checkForInvalidAssignment(node.name, initializer,
78+
whyNotPromoted: whyNotPromoted);
7579
}
7680

7781
void _setInferredType(VariableElement element, DartType initializerType) {

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

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ mixin ErrorDetectionHelpers {
4141

4242
_checkForAssignableExpressionAtType(
4343
expression, actualStaticType, expectedStaticType, errorCode,
44-
whyNotPromotedInfo: whyNotPromotedInfo);
44+
whyNotPromoted: whyNotPromotedInfo);
4545
}
4646
}
4747

@@ -89,7 +89,8 @@ mixin ErrorDetectionHelpers {
8989
/// [StaticWarningCode.FIELD_INITIALIZER_NOT_ASSIGNABLE].
9090
void checkForFieldInitializerNotAssignable(
9191
ConstructorFieldInitializer initializer, FieldElement fieldElement,
92-
{required bool isConstConstructor}) {
92+
{required bool isConstConstructor,
93+
required Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
9394
// prepare field type
9495
DartType fieldType = fieldElement.type;
9596
// prepare expression type
@@ -102,19 +103,23 @@ mixin ErrorDetectionHelpers {
102103
}
103104
return;
104105
}
106+
var messages = computeWhyNotPromotedMessages(
107+
expression, expression, whyNotPromoted?.call());
105108
// report problem
106109
if (isConstConstructor) {
107110
// TODO(paulberry): this error should be based on the actual type of the
108111
// constant, not the static type. See dartbug.com/21119.
109112
errorReporter.reportErrorForNode(
110113
CompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE,
111114
expression,
112-
[staticType, fieldType]);
115+
[staticType, fieldType],
116+
messages);
113117
}
114118
errorReporter.reportErrorForNode(
115119
CompileTimeErrorCode.FIELD_INITIALIZER_NOT_ASSIGNABLE,
116120
expression,
117-
[staticType, fieldType]);
121+
[staticType, fieldType],
122+
messages);
118123
// TODO(brianwilkerson) Define a hint corresponding to these errors and
119124
// report it if appropriate.
120125
// // test the propagated type of the expression
@@ -143,7 +148,8 @@ mixin ErrorDetectionHelpers {
143148
/// represent a valid assignment.
144149
///
145150
/// See [CompileTimeErrorCode.INVALID_ASSIGNMENT].
146-
void checkForInvalidAssignment(Expression? lhs, Expression? rhs) {
151+
void checkForInvalidAssignment(Expression? lhs, Expression? rhs,
152+
{Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
147153
if (lhs == null || rhs == null) {
148154
return;
149155
}
@@ -173,7 +179,8 @@ mixin ErrorDetectionHelpers {
173179
}
174180

175181
_checkForAssignableExpression(
176-
rhs, leftType, CompileTimeErrorCode.INVALID_ASSIGNMENT);
182+
rhs, leftType, CompileTimeErrorCode.INVALID_ASSIGNMENT,
183+
whyNotPromoted: whyNotPromoted);
177184
}
178185

179186
/// Check for situations where the result of a method or function is used,
@@ -247,18 +254,20 @@ mixin ErrorDetectionHelpers {
247254
}
248255

249256
bool _checkForAssignableExpression(
250-
Expression expression, DartType expectedStaticType, ErrorCode errorCode) {
257+
Expression expression, DartType expectedStaticType, ErrorCode errorCode,
258+
{required Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
251259
DartType actualStaticType = expression.typeOrThrow;
252260
return _checkForAssignableExpressionAtType(
253-
expression, actualStaticType, expectedStaticType, errorCode);
261+
expression, actualStaticType, expectedStaticType, errorCode,
262+
whyNotPromoted: whyNotPromoted);
254263
}
255264

256265
bool _checkForAssignableExpressionAtType(
257266
Expression expression,
258267
DartType actualStaticType,
259268
DartType expectedStaticType,
260269
ErrorCode errorCode,
261-
{Map<DartType, NonPromotionReason> Function()? whyNotPromotedInfo}) {
270+
{Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
262271
if (!typeSystem.isAssignableTo(actualStaticType, expectedStaticType)) {
263272
AstNode getErrorNode(AstNode node) {
264273
if (node is CascadeExpression) {
@@ -275,7 +284,7 @@ mixin ErrorDetectionHelpers {
275284
getErrorNode(expression),
276285
[actualStaticType, expectedStaticType],
277286
computeWhyNotPromotedMessages(
278-
expression, expression, whyNotPromotedInfo?.call()),
287+
expression, expression, whyNotPromoted?.call()),
279288
);
280289
return false;
281290
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,12 +1297,14 @@ class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
12971297
var fieldElement = enclosingClass!.getField(node.fieldName.name);
12981298
InferenceContext.setType(node.expression, fieldElement?.type);
12991299
node.expression.accept(this);
1300+
var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(node.expression);
13001301
node.accept(elementResolver);
13011302
node.accept(typeAnalyzer);
13021303
var enclosingConstructor = enclosingFunction as ConstructorElement;
13031304
if (fieldElement != null) {
13041305
checkForFieldInitializerNotAssignable(node, fieldElement,
1305-
isConstConstructor: enclosingConstructor.isConst);
1306+
isConstConstructor: enclosingConstructor.isConst,
1307+
whyNotPromoted: whyNotPromoted);
13061308
}
13071309
}
13081310

@@ -2160,7 +2162,6 @@ class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
21602162
isFinal: parent.isFinal, isLate: parent.isLate);
21612163
}
21622164
}
2163-
checkForInvalidAssignment(node.name, node.initializer);
21642165
}
21652166

21662167
@override

0 commit comments

Comments
 (0)