mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:30:32 +00:00
Flow analysis: implement "why not promoted" for binary/unary operator target.
Bug: https://github.com/dart-lang/sdk/issues/44898 Change-Id: If464a54bdb63fc661db312df5dc10f108049286a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/196240 Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
6645753681
commit
e3443b9f53
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
// This test contains a test case for each condition that can lead to the front
|
||||
// end's `NullableOperatorCallError` error, for which we wish to report "why not
|
||||
// promoted" context information.
|
||||
|
||||
class C1 {
|
||||
int? bad;
|
||||
}
|
||||
|
||||
userDefinableBinaryOpLhs(C1 c) {
|
||||
if (c.bad == null) return;
|
||||
c.bad
|
||||
/*cfe.invoke: notPromoted(propertyNotPromoted(target: member:C1.bad, type: int?))*/
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C1.bad, type: int?))*/
|
||||
+
|
||||
1;
|
||||
}
|
||||
|
||||
class C2 {
|
||||
int? bad;
|
||||
}
|
||||
|
||||
userDefinableUnaryOp(C2 c) {
|
||||
if (c.bad == null) return;
|
||||
/*cfe.invoke: notPromoted(propertyNotPromoted(target: member:C2.bad, type: int?))*/
|
||||
-c.
|
||||
/*analyzer.notPromoted(propertyNotPromoted(target: member:C2.bad, type: int?))*/
|
||||
bad;
|
||||
}
|
|
@ -666,7 +666,8 @@ class InferenceVisitor
|
|||
read,
|
||||
readType,
|
||||
node.binaryName,
|
||||
node.rhs);
|
||||
node.rhs,
|
||||
null);
|
||||
|
||||
Expression binary = binaryResult.expression;
|
||||
DartType binaryType = binaryResult.inferredType;
|
||||
|
@ -2906,7 +2907,8 @@ class InferenceVisitor
|
|||
read,
|
||||
readType,
|
||||
node.binaryName,
|
||||
node.rhs);
|
||||
node.rhs,
|
||||
null);
|
||||
DartType binaryType = binaryResult.inferredType;
|
||||
|
||||
Expression binary =
|
||||
|
@ -3999,7 +4001,8 @@ class InferenceVisitor
|
|||
Expression left,
|
||||
DartType leftType,
|
||||
Name binaryName,
|
||||
Expression right) {
|
||||
Expression right,
|
||||
Map<DartType, NonPromotionReason> Function() whyNotPromoted) {
|
||||
assert(binaryName != equalsName);
|
||||
|
||||
ObjectAccessTarget binaryTarget = inferrer.findInterfaceMember(
|
||||
|
@ -4203,6 +4206,10 @@ class InferenceVisitor
|
|||
}
|
||||
|
||||
if (!inferrer.isTopLevel && binaryTarget.isNullable) {
|
||||
List<LocatedMessage> context = inferrer.getWhyNotPromotedContext(
|
||||
whyNotPromoted?.call(),
|
||||
binary,
|
||||
(type) => !type.isPotentiallyNullable);
|
||||
return new ExpressionInferenceResult(
|
||||
binaryType,
|
||||
inferrer.helper.wrapInProblem(
|
||||
|
@ -4210,7 +4217,8 @@ class InferenceVisitor
|
|||
templateNullableOperatorCallError.withArguments(
|
||||
binaryName.text, leftType, inferrer.isNonNullableByDefault),
|
||||
binary.fileOffset,
|
||||
binaryName.text.length));
|
||||
binaryName.text.length,
|
||||
context: context));
|
||||
}
|
||||
return new ExpressionInferenceResult(binaryType, binary);
|
||||
}
|
||||
|
@ -4220,8 +4228,12 @@ class InferenceVisitor
|
|||
///
|
||||
/// [fileOffset] is used as the file offset for created nodes.
|
||||
/// [expressionType] is the already inferred type of the [expression].
|
||||
ExpressionInferenceResult _computeUnaryExpression(int fileOffset,
|
||||
Expression expression, DartType expressionType, Name unaryName) {
|
||||
ExpressionInferenceResult _computeUnaryExpression(
|
||||
int fileOffset,
|
||||
Expression expression,
|
||||
DartType expressionType,
|
||||
Name unaryName,
|
||||
Map<DartType, NonPromotionReason> Function() whyNotPromoted) {
|
||||
ObjectAccessTarget unaryTarget = inferrer.findInterfaceMember(
|
||||
expressionType, unaryName, fileOffset,
|
||||
includeExtensionMethods: true);
|
||||
|
@ -4352,6 +4364,8 @@ class InferenceVisitor
|
|||
}
|
||||
|
||||
if (!inferrer.isTopLevel && unaryTarget.isNullable) {
|
||||
List<LocatedMessage> context = inferrer.getWhyNotPromotedContext(
|
||||
whyNotPromoted?.call(), unary, (type) => !type.isPotentiallyNullable);
|
||||
// TODO(johnniwinther): Special case 'unary-' in messages. It should
|
||||
// probably be referred to as "Unary operator '-' ...".
|
||||
return new ExpressionInferenceResult(
|
||||
|
@ -4361,7 +4375,8 @@ class InferenceVisitor
|
|||
templateNullableOperatorCallError.withArguments(unaryName.text,
|
||||
expressionType, inferrer.isNonNullableByDefault),
|
||||
unary.fileOffset,
|
||||
unaryName == unaryMinusName ? 1 : unaryName.text.length));
|
||||
unaryName == unaryMinusName ? 1 : unaryName.text.length,
|
||||
context: context));
|
||||
}
|
||||
return new ExpressionInferenceResult(unaryType, unary);
|
||||
}
|
||||
|
@ -5121,7 +5136,8 @@ class InferenceVisitor
|
|||
left,
|
||||
readType,
|
||||
node.binaryName,
|
||||
node.rhs);
|
||||
node.rhs,
|
||||
null);
|
||||
Expression binary = binaryResult.expression;
|
||||
DartType binaryType = binaryResult.inferredType;
|
||||
|
||||
|
@ -5263,7 +5279,8 @@ class InferenceVisitor
|
|||
left,
|
||||
readType,
|
||||
node.binaryName,
|
||||
node.rhs);
|
||||
node.rhs,
|
||||
null);
|
||||
Expression binary = binaryResult.expression;
|
||||
DartType binaryType = binaryResult.inferredType;
|
||||
|
||||
|
@ -5417,7 +5434,8 @@ class InferenceVisitor
|
|||
left,
|
||||
readType,
|
||||
node.binaryName,
|
||||
node.rhs);
|
||||
node.rhs,
|
||||
null);
|
||||
Expression binary = binaryResult.expression;
|
||||
DartType binaryType = binaryResult.inferredType;
|
||||
|
||||
|
@ -5596,7 +5614,8 @@ class InferenceVisitor
|
|||
left,
|
||||
readType,
|
||||
node.binaryName,
|
||||
node.rhs);
|
||||
node.rhs,
|
||||
null);
|
||||
|
||||
Expression binary = binaryResult.expression;
|
||||
DartType binaryType = binaryResult.inferredType;
|
||||
|
@ -6922,13 +6941,16 @@ class InferenceVisitor
|
|||
BinaryExpression node, DartType typeContext) {
|
||||
ExpressionInferenceResult leftResult =
|
||||
inferrer.inferExpression(node.left, const UnknownType(), true);
|
||||
Map<DartType, NonPromotionReason> Function() whyNotPromoted =
|
||||
inferrer.flowAnalysis?.whyNotPromoted(leftResult.expression);
|
||||
return _computeBinaryExpression(
|
||||
node.fileOffset,
|
||||
typeContext,
|
||||
leftResult.expression,
|
||||
leftResult.inferredType,
|
||||
node.binaryName,
|
||||
node.right);
|
||||
node.right,
|
||||
whyNotPromoted);
|
||||
}
|
||||
|
||||
ExpressionInferenceResult visitUnary(
|
||||
|
@ -7001,8 +7023,10 @@ class InferenceVisitor
|
|||
expressionResult =
|
||||
inferrer.inferExpression(node.expression, const UnknownType(), true);
|
||||
}
|
||||
Map<DartType, NonPromotionReason> Function() whyNotPromoted =
|
||||
inferrer.flowAnalysis?.whyNotPromoted(expressionResult.expression);
|
||||
return _computeUnaryExpression(node.fileOffset, expressionResult.expression,
|
||||
expressionResult.inferredType, node.unaryName);
|
||||
expressionResult.inferredType, node.unaryName, whyNotPromoted);
|
||||
}
|
||||
|
||||
ExpressionInferenceResult visitParenthesized(
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||
// 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.
|
||||
|
||||
// This test contains a test case for each condition that can lead to the front
|
||||
// end's `NullableOperatorCallError` error, for which we wish to report "why not
|
||||
// promoted" context information.
|
||||
|
||||
class C1 {
|
||||
int? bad;
|
||||
// ^^^
|
||||
// [context 2] 'bad' refers to a property so it couldn't be promoted. See http://dart.dev/go/non-promo-property
|
||||
// [context 3] 'bad' refers to a property so it couldn't be promoted.
|
||||
}
|
||||
|
||||
userDefinableBinaryOpLhs(C1 c) {
|
||||
if (c.bad == null) return;
|
||||
c.bad + 1;
|
||||
// ^
|
||||
// [analyzer 2] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
|
||||
// [cfe 3] Operator '+' cannot be called on 'int?' because it is potentially null.
|
||||
}
|
||||
|
||||
class C2 {
|
||||
int? bad;
|
||||
// ^^^
|
||||
// [context 1] 'bad' refers to a property so it couldn't be promoted. See http://dart.dev/go/non-promo-property
|
||||
// [context 4] 'bad' refers to a property so it couldn't be promoted.
|
||||
}
|
||||
|
||||
userDefinableUnaryOp(C2 c) {
|
||||
if (c.bad == null) return;
|
||||
-c.bad;
|
||||
//^
|
||||
// [analyzer 1] COMPILE_TIME_ERROR.UNCHECKED_USE_OF_NULLABLE_VALUE
|
||||
// [cfe 4] Operator 'unary-' cannot be called on 'int?' because it is potentially null.
|
||||
}
|
Loading…
Reference in a new issue