mirror of
https://github.com/dart-lang/sdk
synced 2024-10-08 19:27:37 +00:00
Flow analysis: implement "why not promoted" logic for assignments.
This CL handles ordinary assignment expressions, variable declaration initializers, and constructor field initializers. Bug: https://github.com/dart-lang/sdk/issues/44898 Change-Id: I06bae2c7d57213bd7769c931a03080d148af3dbe Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/192724 Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
6929718456
commit
f161e228e4
|
@ -290,3 +290,102 @@ whileCondition(C20 c) {
|
|||
while (/*analyzer.notPromoted(propertyNotPromoted(target: member:C20.bad, type: bool?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C20.bad, type: bool?))*/ bad) {}
|
||||
}
|
||||
|
||||
class C21 {
|
||||
int? bad;
|
||||
}
|
||||
|
||||
assignmentRhs(C21 c, int i) {
|
||||
if (c.bad == null) return;
|
||||
i =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C21.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C21.bad, type: int?))*/ bad;
|
||||
}
|
||||
|
||||
class C22 {
|
||||
int? bad;
|
||||
}
|
||||
|
||||
variableInitializer(C22 c) {
|
||||
if (c.bad == null) return;
|
||||
int i =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C22.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C22.bad, type: int?))*/ bad;
|
||||
}
|
||||
|
||||
class C23 {
|
||||
int? bad;
|
||||
final int x;
|
||||
final int y;
|
||||
C23.constructorInitializer(C23 c)
|
||||
: x = c.bad!,
|
||||
y =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C23.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C23.bad, type: int?))*/ bad;
|
||||
}
|
||||
|
||||
class C24 {
|
||||
int? bad;
|
||||
}
|
||||
|
||||
forVariableInitializer(C24 c) {
|
||||
if (c.bad == null) return;
|
||||
for (int i =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
|
||||
false;) {}
|
||||
[
|
||||
for (int i =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
|
||||
false;)
|
||||
null
|
||||
];
|
||||
({
|
||||
for (int i =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
|
||||
false;)
|
||||
null
|
||||
});
|
||||
({
|
||||
for (int i =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C24.bad, type: int?))*/ bad;
|
||||
false;)
|
||||
null: null
|
||||
});
|
||||
}
|
||||
|
||||
class C25 {
|
||||
int? bad;
|
||||
}
|
||||
|
||||
forAssignmentInitializer(C25 c, int i) {
|
||||
if (c.bad == null) return;
|
||||
for (i =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
|
||||
false;) {}
|
||||
[
|
||||
for (i =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
|
||||
false;)
|
||||
null
|
||||
];
|
||||
({
|
||||
for (i =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
|
||||
false;)
|
||||
null
|
||||
});
|
||||
({
|
||||
for (i =
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ c
|
||||
. /*cfe.notPromoted(propertyNotPromoted(target: member:C25.bad, type: int?))*/ bad;
|
||||
false;)
|
||||
null: null
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:_fe_analyzer_shared/src/flow_analysis/flow_analysis.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/token.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
|
@ -84,8 +85,10 @@ class AssignmentExpressionResolver {
|
|||
}
|
||||
|
||||
right.accept(_resolver);
|
||||
right = node.rightHandSide;
|
||||
var whyNotPromoted = flow?.whyNotPromoted(right);
|
||||
|
||||
_resolveTypes(node);
|
||||
_resolveTypes(node, whyNotPromoted: whyNotPromoted);
|
||||
|
||||
if (flow != null) {
|
||||
if (writeElement is PromotableElement) {
|
||||
|
@ -103,8 +106,9 @@ class AssignmentExpressionResolver {
|
|||
void _checkForInvalidAssignment(
|
||||
DartType writeType,
|
||||
Expression right,
|
||||
DartType rightType,
|
||||
) {
|
||||
DartType rightType, {
|
||||
required Map<DartType, NonPromotionReason> Function()? whyNotPromoted,
|
||||
}) {
|
||||
if (!writeType.isVoid && _checkForUseOfVoidResult(right)) {
|
||||
return;
|
||||
}
|
||||
|
@ -117,6 +121,8 @@ class AssignmentExpressionResolver {
|
|||
CompileTimeErrorCode.INVALID_ASSIGNMENT,
|
||||
right,
|
||||
[rightType, writeType],
|
||||
_resolver.computeWhyNotPromotedMessages(
|
||||
right, right, whyNotPromoted?.call()),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -192,7 +198,8 @@ class AssignmentExpressionResolver {
|
|||
}
|
||||
}
|
||||
|
||||
void _resolveTypes(AssignmentExpressionImpl node) {
|
||||
void _resolveTypes(AssignmentExpressionImpl node,
|
||||
{required Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
|
||||
DartType assignedType;
|
||||
DartType nodeType;
|
||||
|
||||
|
@ -239,6 +246,7 @@ class AssignmentExpressionResolver {
|
|||
node.writeType!,
|
||||
node.rightHandSide,
|
||||
assignedType,
|
||||
whyNotPromoted: operator == TokenType.EQ ? whyNotPromoted : null,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -52,10 +52,12 @@ class VariableDeclarationResolver {
|
|||
}
|
||||
|
||||
initializer.accept(_resolver);
|
||||
initializer = node.initializer;
|
||||
initializer = node.initializer!;
|
||||
var whyNotPromoted =
|
||||
_resolver.flowAnalysis?.flow?.whyNotPromoted(initializer);
|
||||
|
||||
if (parent.type == null) {
|
||||
_setInferredType(element, initializer!.typeOrThrow);
|
||||
_setInferredType(element, initializer.typeOrThrow);
|
||||
}
|
||||
|
||||
if (isTopLevel) {
|
||||
|
@ -72,6 +74,8 @@ class VariableDeclarationResolver {
|
|||
(element as ConstVariableElement).constantInitializer =
|
||||
ConstantAstCloner().cloneNullableNode(initializer);
|
||||
}
|
||||
_resolver.checkForInvalidAssignment(node.name, initializer,
|
||||
whyNotPromoted: whyNotPromoted);
|
||||
}
|
||||
|
||||
void _setInferredType(VariableElement element, DartType initializerType) {
|
||||
|
|
|
@ -41,7 +41,7 @@ mixin ErrorDetectionHelpers {
|
|||
|
||||
_checkForAssignableExpressionAtType(
|
||||
expression, actualStaticType, expectedStaticType, errorCode,
|
||||
whyNotPromotedInfo: whyNotPromotedInfo);
|
||||
whyNotPromoted: whyNotPromotedInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,8 @@ mixin ErrorDetectionHelpers {
|
|||
/// [StaticWarningCode.FIELD_INITIALIZER_NOT_ASSIGNABLE].
|
||||
void checkForFieldInitializerNotAssignable(
|
||||
ConstructorFieldInitializer initializer, FieldElement fieldElement,
|
||||
{required bool isConstConstructor}) {
|
||||
{required bool isConstConstructor,
|
||||
required Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
|
||||
// prepare field type
|
||||
DartType fieldType = fieldElement.type;
|
||||
// prepare expression type
|
||||
|
@ -102,6 +103,8 @@ mixin ErrorDetectionHelpers {
|
|||
}
|
||||
return;
|
||||
}
|
||||
var messages = computeWhyNotPromotedMessages(
|
||||
expression, expression, whyNotPromoted?.call());
|
||||
// report problem
|
||||
if (isConstConstructor) {
|
||||
// TODO(paulberry): this error should be based on the actual type of the
|
||||
|
@ -109,12 +112,14 @@ mixin ErrorDetectionHelpers {
|
|||
errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.CONST_FIELD_INITIALIZER_NOT_ASSIGNABLE,
|
||||
expression,
|
||||
[staticType, fieldType]);
|
||||
[staticType, fieldType],
|
||||
messages);
|
||||
}
|
||||
errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.FIELD_INITIALIZER_NOT_ASSIGNABLE,
|
||||
expression,
|
||||
[staticType, fieldType]);
|
||||
[staticType, fieldType],
|
||||
messages);
|
||||
// TODO(brianwilkerson) Define a hint corresponding to these errors and
|
||||
// report it if appropriate.
|
||||
// // test the propagated type of the expression
|
||||
|
@ -143,7 +148,8 @@ mixin ErrorDetectionHelpers {
|
|||
/// represent a valid assignment.
|
||||
///
|
||||
/// See [CompileTimeErrorCode.INVALID_ASSIGNMENT].
|
||||
void checkForInvalidAssignment(Expression? lhs, Expression? rhs) {
|
||||
void checkForInvalidAssignment(Expression? lhs, Expression? rhs,
|
||||
{Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
|
||||
if (lhs == null || rhs == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -173,7 +179,8 @@ mixin ErrorDetectionHelpers {
|
|||
}
|
||||
|
||||
_checkForAssignableExpression(
|
||||
rhs, leftType, CompileTimeErrorCode.INVALID_ASSIGNMENT);
|
||||
rhs, leftType, CompileTimeErrorCode.INVALID_ASSIGNMENT,
|
||||
whyNotPromoted: whyNotPromoted);
|
||||
}
|
||||
|
||||
/// Check for situations where the result of a method or function is used,
|
||||
|
@ -247,10 +254,12 @@ mixin ErrorDetectionHelpers {
|
|||
}
|
||||
|
||||
bool _checkForAssignableExpression(
|
||||
Expression expression, DartType expectedStaticType, ErrorCode errorCode) {
|
||||
Expression expression, DartType expectedStaticType, ErrorCode errorCode,
|
||||
{required Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
|
||||
DartType actualStaticType = expression.typeOrThrow;
|
||||
return _checkForAssignableExpressionAtType(
|
||||
expression, actualStaticType, expectedStaticType, errorCode);
|
||||
expression, actualStaticType, expectedStaticType, errorCode,
|
||||
whyNotPromoted: whyNotPromoted);
|
||||
}
|
||||
|
||||
bool _checkForAssignableExpressionAtType(
|
||||
|
@ -258,7 +267,7 @@ mixin ErrorDetectionHelpers {
|
|||
DartType actualStaticType,
|
||||
DartType expectedStaticType,
|
||||
ErrorCode errorCode,
|
||||
{Map<DartType, NonPromotionReason> Function()? whyNotPromotedInfo}) {
|
||||
{Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
|
||||
if (!typeSystem.isAssignableTo(actualStaticType, expectedStaticType)) {
|
||||
AstNode getErrorNode(AstNode node) {
|
||||
if (node is CascadeExpression) {
|
||||
|
@ -275,7 +284,7 @@ mixin ErrorDetectionHelpers {
|
|||
getErrorNode(expression),
|
||||
[actualStaticType, expectedStaticType],
|
||||
computeWhyNotPromotedMessages(
|
||||
expression, expression, whyNotPromotedInfo?.call()),
|
||||
expression, expression, whyNotPromoted?.call()),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1297,12 +1297,14 @@ class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
|
|||
var fieldElement = enclosingClass!.getField(node.fieldName.name);
|
||||
InferenceContext.setType(node.expression, fieldElement?.type);
|
||||
node.expression.accept(this);
|
||||
var whyNotPromoted = flowAnalysis?.flow?.whyNotPromoted(node.expression);
|
||||
node.accept(elementResolver);
|
||||
node.accept(typeAnalyzer);
|
||||
var enclosingConstructor = enclosingFunction as ConstructorElement;
|
||||
if (fieldElement != null) {
|
||||
checkForFieldInitializerNotAssignable(node, fieldElement,
|
||||
isConstConstructor: enclosingConstructor.isConst);
|
||||
isConstConstructor: enclosingConstructor.isConst,
|
||||
whyNotPromoted: whyNotPromoted);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2160,7 +2162,6 @@ class ResolverVisitor extends ScopedVisitor with ErrorDetectionHelpers {
|
|||
isFinal: parent.isFinal, isLate: parent.isLate);
|
||||
}
|
||||
}
|
||||
checkForInvalidAssignment(node.name, node.initializer);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
Loading…
Reference in a new issue