Version 2.17.0-135.0.dev
Merge commit '77a4d88c06128a2023fec06f1b5b5641d96ac703' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
index 1009c21..16e5f20 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -225,27 +225,19 @@
void _resolveTypes(AssignmentExpressionImpl node,
{required Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
DartType assignedType;
- DartType nodeType;
+ DartType? implicitCallContext;
var rightHandSide = node.rightHandSide;
var operator = node.operator.type;
if (operator == TokenType.EQ) {
assignedType = rightHandSide.typeOrThrow;
- nodeType = assignedType;
+ implicitCallContext = node.writeType;
} else if (operator == TokenType.QUESTION_QUESTION_EQ) {
- var leftType = node.readType!;
-
- // The LHS value will be used only if it is non-null.
- if (_isNonNullableByDefault) {
- leftType = _typeSystem.promoteToNonNull(leftType);
- }
-
assignedType = rightHandSide.typeOrThrow;
- nodeType = _typeSystem.getLeastUpperBound(leftType, assignedType);
+ implicitCallContext = node.writeType;
} else if (operator == TokenType.AMPERSAND_AMPERSAND_EQ ||
operator == TokenType.BAR_BAR_EQ) {
assignedType = _typeProvider.boolType;
- nodeType = assignedType;
} else {
var operatorElement = node.staticElement;
if (operatorElement != null) {
@@ -261,14 +253,27 @@
} else {
assignedType = DynamicTypeImpl.instance;
}
- nodeType = assignedType;
}
- _inferenceHelper.recordStaticType(node, nodeType);
- var callReference = _resolver.insertImplicitCallReference(rightHandSide);
- if (callReference != rightHandSide) {
- assignedType = callReference.typeOrThrow;
+ var callInsertion = _resolver.insertImplicitCallReference(rightHandSide,
+ context: implicitCallContext);
+ if (callInsertion != null) {
+ assignedType = callInsertion.staticType;
}
+ DartType nodeType;
+ if (operator == TokenType.QUESTION_QUESTION_EQ) {
+ var leftType = node.readType!;
+
+ // The LHS value will be used only if it is non-null.
+ if (_isNonNullableByDefault) {
+ leftType = _typeSystem.promoteToNonNull(leftType);
+ }
+
+ nodeType = _typeSystem.getLeastUpperBound(leftType, assignedType);
+ } else {
+ nodeType = assignedType;
+ }
+ _inferenceHelper.recordStaticType(node, nodeType);
// TODO(scheglov) Remove from ErrorVerifier?
_checkForInvalidAssignment(
diff --git a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
index 3dd455d..0aa6a8f 100644
--- a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
@@ -64,7 +64,13 @@
_resolver.flowAnalysis.flow?.lateInitializer_end();
}
- initializer = _resolver.insertImplicitCallReference(initializer);
+ var callInsertion = _resolver.insertImplicitCallReference(initializer);
+ if (callInsertion != null) {
+ var insertedExpression = callInsertion.expression;
+ if (insertedExpression != null) {
+ initializer = callInsertion.expression;
+ }
+ }
// Initializers of top-level variables and fields are already included
// into elements during linking.
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index d07e3e9..d42e16a 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -78,6 +78,24 @@
/// promoted.
typedef WhyNotPromotedGetter = Map<DartType, NonPromotionReason> Function();
+/// Data structure describing the result of inserting an implicit call reference
+/// into the AST.
+class ImplicitCallInsertionResult {
+ /// The expression that was inserted, or `null`, if no expression was
+ /// inserted.
+ ///
+ /// The only reason this might be `null` is that, at the moment, we only
+ /// insert implicit call reference expressions if the 'constructor-tearoffs'
+ /// feature is enabled (to avoid breaking clients).
+ /// TODO(paulberry): make this non-nullable when we change this behavior.
+ final ImplicitCallReferenceImpl? expression;
+
+ /// The type of the implicit call tear-off.
+ final FunctionType staticType;
+
+ ImplicitCallInsertionResult(this.expression, this.staticType);
+}
+
/// Maintains and manages contextual type information used for
/// inferring types.
class InferenceContext {
@@ -798,32 +816,27 @@
}
/// If `expression` should be treated as `expression.call`, inserts an
- /// [ImplicitCallReferece] node which wraps [expression].
+ /// [ImplicitCallReference] node which wraps [expression].
///
- /// If an [ImplicitCallReferece] is inserted, returns it; otherwise, returns
- /// [expression].
- ExpressionImpl insertImplicitCallReference(Expression expression) {
+ /// If an [ImplicitCallReference] is inserted, returns an
+ /// [ImplicitCallInsertionResult] describing what was changed; otherwise,
+ /// returns `null`.
+ ImplicitCallInsertionResult? insertImplicitCallReference(
+ Expression expression,
+ {DartType? context}) {
expression as ExpressionImpl;
- if (!isConstructorTearoffsEnabled) {
- // Temporarily, only create [ImplicitCallReference] nodes under the
- // 'constructor-tearoffs' feature.
- // TODO(srawlins): When we are ready to make a breaking change release to
- // the analyzer package, remove this exception.
- return expression;
- }
-
var parent = expression.parent;
if (parent is CascadeExpression && parent.target == expression) {
// Do not perform an "implicit tear-off conversion" here. It should only
// be performed on [parent]. See
// https://github.com/dart-lang/language/issues/1873.
- return expression;
+ return null;
}
- var context = InferenceContext.getContext(expression);
+ context ??= InferenceContext.getContext(expression);
var callMethod =
getImplicitCallMethod(expression.typeOrThrow, context, expression);
if (callMethod == null || context == null) {
- return expression;
+ return null;
}
// `expression` is to be treated as `expression.call`.
@@ -848,6 +861,15 @@
} else {
typeArgumentTypes = [];
}
+
+ if (!isConstructorTearoffsEnabled) {
+ // Temporarily, only create [ImplicitCallReference] nodes under the
+ // 'constructor-tearoffs' feature.
+ // TODO(srawlins, paulberry): When we are ready to make a breaking change
+ // release to the analyzer package, remove this exception.
+ return ImplicitCallInsertionResult(null, callMethodType);
+ }
+
var callReference = astFactory.implicitCallReference(
expression: expression,
staticElement: callMethod,
@@ -858,7 +880,7 @@
callReference.staticType = callMethodType;
- return callReference;
+ return ImplicitCallInsertionResult(callReference, callMethodType);
}
/// If we reached a null-shorting termination, and the [node] has null
@@ -1440,8 +1462,8 @@
node as ConstructorFieldInitializerImpl);
if (fieldElement != null) {
if (fieldType != null && expression.staticType != null) {
- var callReference = insertImplicitCallReference(expression);
- if (expression != callReference) {
+ var callReference = insertImplicitCallReference(expression)?.expression;
+ if (callReference != null) {
checkForInvalidAssignment(node.fieldName, callReference,
whyNotPromoted: whyNotPromoted);
}
diff --git a/pkg/analyzer/test/generated/non_error_resolver_test.dart b/pkg/analyzer/test/generated/non_error_resolver_test.dart
index 73ea37b..66c764c 100644
--- a/pkg/analyzer/test/generated/non_error_resolver_test.dart
+++ b/pkg/analyzer/test/generated/non_error_resolver_test.dart
@@ -105,6 +105,23 @@
''');
}
+ test_no_call_tearoff_on_promoted_var() async {
+ await assertNoErrorsInCode('''
+class B {
+ Object call() => '';
+}
+void test(Object x) {
+ x as Object Function();
+ x; // promoted
+ x = B(); // No implicit tearoff of `.call`, demotes x
+ x; // demoted
+}
+''');
+ assertType(findNode.simple('x; // promoted'), 'Object Function()');
+ assertType(findNode.assignment('x = B()'), 'B');
+ assertType(findNode.simple('x; // demoted'), 'Object');
+ }
+
test_typedef_not_function() async {
newFile('$testPackageLibPath/a.dart', content: '''
typedef F = int;
@@ -1482,6 +1499,20 @@
''');
}
+ test_implicit_call_tearoff_assignment_rhs() async {
+ await assertNoErrorsInCode('''
+class C {
+ void call() {}
+}
+test() {
+ void Function() f;
+ f = C();
+ return f;
+}
+''');
+ assertType(findNode.assignment('f = C()'), 'void Function()');
+ }
+
test_importDuplicatedLibraryName() async {
newFile("$testPackageLibPath/lib.dart", content: "library lib;");
await assertErrorsInCode(r'''
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 7c41343..cc5d497 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -276,10 +276,7 @@
typedefType: null);
ExpressionInferenceResult inferredResult =
inferrer.instantiateTearOff(resultType, typeContext, node);
- Expression ensuredResultExpression =
- inferrer.ensureAssignableResult(typeContext, inferredResult);
- return new ExpressionInferenceResult(
- inferredResult.inferredType, ensuredResultExpression);
+ return inferrer.ensureAssignableResult(typeContext, inferredResult);
}
@override
@@ -505,8 +502,9 @@
node.condition, expectedType, !inferrer.isTopLevel,
isVoidAllowed: true);
- Expression condition =
- inferrer.ensureAssignableResult(expectedType, conditionResult);
+ Expression condition = inferrer
+ .ensureAssignableResult(expectedType, conditionResult)
+ .expression;
node.condition = condition..parent = node;
inferrer.flowAnalysis.assert_afterCondition(node.condition);
if (node.message != null) {
@@ -662,8 +660,9 @@
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
node.condition, expectedType, !inferrer.isTopLevel,
isVoidAllowed: true);
- Expression condition =
- inferrer.ensureAssignableResult(expectedType, conditionResult);
+ Expression condition = inferrer
+ .ensureAssignableResult(expectedType, conditionResult)
+ .expression;
node.condition = condition..parent = node;
inferrer.flowAnalysis.conditional_thenBegin(node.condition, node);
bool isThenReachable = inferrer.flowAnalysis.isReachable;
@@ -755,8 +754,9 @@
DartType receiverType = inferrer.getExtensionReceiverType(
node.extension, extensionTypeArguments);
- Expression receiver =
- inferrer.ensureAssignableResult(receiverType, receiverResult);
+ Expression receiver = inferrer
+ .ensureAssignableResult(receiverType, receiverResult)
+ .expression;
ObjectAccessTarget target = new ExtensionAccessTarget(
node.target, null, ProcedureKind.Setter, extensionTypeArguments);
@@ -767,7 +767,8 @@
ExpressionInferenceResult valueResult = inferrer.inferExpression(
node.value, const UnknownType(), true,
isVoidAllowed: false);
- Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+ valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+ Expression value = valueResult.expression;
VariableDeclaration? valueVariable;
if (node.forEffect) {
@@ -823,8 +824,9 @@
DartType receiverType = inferrer.getExtensionReceiverType(
node.extension, extensionTypeArguments);
- Expression receiver =
- inferrer.ensureAssignableResult(receiverType, receiverResult);
+ Expression receiver = inferrer
+ .ensureAssignableResult(receiverType, receiverResult)
+ .expression;
VariableDeclaration? receiverVariable;
Expression readReceiver;
@@ -876,11 +878,9 @@
node.rhs,
null);
- Expression binary = binaryResult.expression;
- DartType binaryType = binaryResult.inferredType;
-
- Expression value = inferrer.ensureAssignable(valueType, binaryType, binary,
+ binaryResult = inferrer.ensureAssignableResult(valueType, binaryResult,
isVoidAllowed: true);
+ Expression value = binaryResult.expression;
VariableDeclaration? valueVariable;
if (node.forEffect) {
@@ -952,7 +952,7 @@
node.condition, boolType, !inferrer.isTopLevel,
isVoidAllowed: true);
Expression condition =
- inferrer.ensureAssignableResult(boolType, conditionResult);
+ inferrer.ensureAssignableResult(boolType, conditionResult).expression;
node.condition = condition..parent = node;
inferrer.flowAnalysis.doStatement_end(condition);
return const StatementInferenceResult();
@@ -1063,9 +1063,10 @@
InitializerInferenceResult visitFieldInitializer(FieldInitializer node) {
ExpressionInferenceResult initializerResult =
inferrer.inferExpression(node.value, node.field.type, true);
- Expression initializer = inferrer.ensureAssignableResult(
- node.field.type, initializerResult,
- fileOffset: node.fileOffset);
+ Expression initializer = inferrer
+ .ensureAssignableResult(node.field.type, initializerResult,
+ fileOffset: node.fileOffset)
+ .expression;
node.value = initializer..parent = node;
return const SuccessfulInitializerInferenceResult();
}
@@ -1345,8 +1346,9 @@
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
node.condition!, expectedType, !inferrer.isTopLevel,
isVoidAllowed: true);
- Expression condition =
- inferrer.ensureAssignableResult(expectedType, conditionResult);
+ Expression condition = inferrer
+ .ensureAssignableResult(expectedType, conditionResult)
+ .expression;
node.condition = condition..parent = node;
}
@@ -1498,8 +1500,9 @@
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
node.condition, expectedType, !inferrer.isTopLevel,
isVoidAllowed: true);
- Expression condition =
- inferrer.ensureAssignableResult(expectedType, conditionResult);
+ Expression condition = inferrer
+ .ensureAssignableResult(expectedType, conditionResult)
+ .expression;
node.condition = condition..parent = node;
inferrer.flowAnalysis.ifStatement_thenBegin(condition, node);
StatementInferenceResult thenResult = inferrer.inferStatement(node.then);
@@ -1783,7 +1786,7 @@
element.condition, boolType, typeChecksNeeded,
isVoidAllowed: false);
Expression condition =
- inferrer.ensureAssignableResult(boolType, conditionResult);
+ inferrer.ensureAssignableResult(boolType, conditionResult).expression;
element.condition = condition..parent = element;
inferrer.flowAnalysis.ifStatement_thenBegin(condition, element);
ExpressionInferenceResult thenResult = inferElement(
@@ -1935,15 +1938,11 @@
ExpressionInferenceResult result = inferrer.inferExpression(
element, inferredTypeArgument, inferenceNeeded || typeChecksNeeded,
isVoidAllowed: true);
- Expression replacement;
if (inferredTypeArgument is! UnknownType) {
- replacement = inferrer.ensureAssignableResult(
- inferredTypeArgument, result,
+ result = inferrer.ensureAssignableResult(inferredTypeArgument, result,
isVoidAllowed: inferredTypeArgument is VoidType);
- } else {
- replacement = result.expression;
}
- return new ExpressionInferenceResult(result.inferredType, replacement);
+ return result;
}
}
@@ -2101,14 +2100,16 @@
ExpressionInferenceResult leftResult = inferrer.inferExpression(
node.left, boolType, !inferrer.isTopLevel,
isVoidAllowed: false);
- Expression left = inferrer.ensureAssignableResult(boolType, leftResult);
+ Expression left =
+ inferrer.ensureAssignableResult(boolType, leftResult).expression;
node.left = left..parent = node;
inferrer.flowAnalysis.logicalBinaryOp_rightBegin(node.left, node,
isAnd: node.operatorEnum == LogicalExpressionOperator.AND);
ExpressionInferenceResult rightResult = inferrer.inferExpression(
node.right, boolType, !inferrer.isTopLevel,
isVoidAllowed: false);
- Expression right = inferrer.ensureAssignableResult(boolType, rightResult);
+ Expression right =
+ inferrer.ensureAssignableResult(boolType, rightResult).expression;
node.right = right..parent = node;
inferrer.flowAnalysis.logicalBinaryOp_end(node, node.right,
isAnd: node.operatorEnum == LogicalExpressionOperator.AND);
@@ -2390,7 +2391,7 @@
entry.condition, boolType, typeChecksNeeded,
isVoidAllowed: false);
Expression condition =
- inferrer.ensureAssignableResult(boolType, conditionResult);
+ inferrer.ensureAssignableResult(boolType, conditionResult).expression;
entry.condition = condition..parent = entry;
inferrer.flowAnalysis.ifStatement_thenBegin(condition, entry);
// Note that this recursive invocation of inferMapEntry will add two types
@@ -2570,16 +2571,18 @@
ExpressionInferenceResult keyResult = inferrer.inferExpression(
entry.key, inferredKeyType, true,
isVoidAllowed: true);
- Expression key = inferrer.ensureAssignableResult(
- inferredKeyType, keyResult,
- isVoidAllowed: inferredKeyType is VoidType);
+ Expression key = inferrer
+ .ensureAssignableResult(inferredKeyType, keyResult,
+ isVoidAllowed: inferredKeyType is VoidType)
+ .expression;
entry.key = key..parent = entry;
ExpressionInferenceResult valueResult = inferrer.inferExpression(
entry.value, inferredValueType, true,
isVoidAllowed: true);
- Expression value = inferrer.ensureAssignableResult(
- inferredValueType, valueResult,
- isVoidAllowed: inferredValueType is VoidType);
+ Expression value = inferrer
+ .ensureAssignableResult(inferredValueType, valueResult,
+ isVoidAllowed: inferredValueType is VoidType)
+ .expression;
entry.value = value..parent = entry;
actualTypes.add(keyResult.inferredType);
actualTypes.add(valueResult.inferredType);
@@ -2938,9 +2941,10 @@
inferrer.coreTypes.boolRawType(inferrer.library.nonNullable);
ExpressionInferenceResult operandResult =
inferrer.inferExpression(node.operand, boolType, !inferrer.isTopLevel);
- Expression operand = inferrer.ensureAssignableResult(
- boolType, operandResult,
- fileOffset: node.fileOffset);
+ Expression operand = inferrer
+ .ensureAssignableResult(boolType, operandResult,
+ fileOffset: node.fileOffset)
+ .expression;
node.operand = operand..parent = node;
inferrer.flowAnalysis.logicalNot_end(node, node.operand);
return new ExpressionInferenceResult(boolType, node);
@@ -3138,10 +3142,10 @@
node.binaryName,
node.rhs,
null);
- DartType binaryType = binaryResult.inferredType;
- Expression binary =
- inferrer.ensureAssignableResult(writeType, binaryResult);
+ binaryResult = inferrer.ensureAssignableResult(writeType, binaryResult);
+ DartType binaryType = binaryResult.inferredType;
+ Expression binary = binaryResult.expression;
Expression write = _computePropertySet(node.writeOffset, writeReceiver,
receiverType, node.propertyName, writeTarget, binary,
@@ -3198,7 +3202,8 @@
.inferExpression(node.rhs, writeContext, true, isVoidAllowed: true);
inferrer.flowAnalysis.ifNullExpression_end();
- Expression rhs = inferrer.ensureAssignableResult(writeContext, rhsResult);
+ rhsResult = inferrer.ensureAssignableResult(writeContext, rhsResult);
+ Expression rhs = rhsResult.expression;
DartType writeType = rhsResult.inferredType;
Expression write = _computePropertySet(node.writeOffset, writeReceiver,
@@ -3329,7 +3334,8 @@
ExpressionInferenceResult indexResult = inferrer
.inferExpression(node.index, indexType, true, isVoidAllowed: true);
- Expression index = inferrer.ensureAssignableResult(indexType, indexResult);
+ Expression index =
+ inferrer.ensureAssignableResult(indexType, indexResult).expression;
ExpressionInferenceResult replacement = _computeIndexGet(
node.fileOffset,
@@ -3370,7 +3376,8 @@
ExpressionInferenceResult indexResult = inferrer
.inferExpression(node.index, indexType, true, isVoidAllowed: true);
- Expression index = inferrer.ensureAssignableResult(indexType, indexResult);
+ Expression index =
+ inferrer.ensureAssignableResult(indexType, indexResult).expression;
VariableDeclaration? indexVariable;
if (!node.forEffect && !isPureExpression(index)) {
@@ -3380,7 +3387,8 @@
ExpressionInferenceResult valueResult = inferrer
.inferExpression(node.value, valueType, true, isVoidAllowed: true);
- Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+ valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+ Expression value = valueResult.expression;
VariableDeclaration? valueVariable;
Expression? returnedValue;
@@ -3437,7 +3445,8 @@
ExpressionInferenceResult indexResult = inferrer
.inferExpression(node.index, indexType, true, isVoidAllowed: true);
- Expression index = inferrer.ensureAssignableResult(indexType, indexResult);
+ Expression index =
+ inferrer.ensureAssignableResult(indexType, indexResult).expression;
VariableDeclaration? indexVariable;
if (!isPureExpression(index)) {
@@ -3447,7 +3456,8 @@
ExpressionInferenceResult valueResult = inferrer
.inferExpression(node.value, valueType, true, isVoidAllowed: true);
- Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+ valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+ Expression value = valueResult.expression;
VariableDeclaration? valueVariable;
Expression returnedValue;
@@ -3506,8 +3516,9 @@
DartType receiverType = inferrer.getExtensionReceiverType(
node.extension, extensionTypeArguments);
- Expression receiver =
- inferrer.ensureAssignableResult(receiverType, receiverResult);
+ Expression receiver = inferrer
+ .ensureAssignableResult(receiverType, receiverResult)
+ .expression;
VariableDeclaration? receiverVariable;
if (!isPureExpression(receiver)) {
@@ -3524,11 +3535,13 @@
ExpressionInferenceResult indexResult = inferrer
.inferExpression(node.index, indexType, true, isVoidAllowed: true);
- Expression index = inferrer.ensureAssignableResult(indexType, indexResult);
+ Expression index =
+ inferrer.ensureAssignableResult(indexType, indexResult).expression;
ExpressionInferenceResult valueResult = inferrer
.inferExpression(node.value, valueType, true, isVoidAllowed: true);
- Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+ valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+ Expression value = valueResult.expression;
VariableDeclaration? valueVariable;
Expression returnedValue;
@@ -3643,7 +3656,8 @@
ExpressionInferenceResult valueResult = inferrer
.inferExpression(node.value, valueType, true, isVoidAllowed: true);
- Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+ valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+ Expression value = valueResult.expression;
inferrer.flowAnalysis.ifNullExpression_end();
DartType nonNullableReadType = readType.toNonNull();
@@ -3819,7 +3833,8 @@
inferrer.flowAnalysis.ifNullExpression_rightBegin(read, readType);
ExpressionInferenceResult valueResult = inferrer
.inferExpression(node.value, valueType, true, isVoidAllowed: true);
- Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+ valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+ Expression value = valueResult.expression;
inferrer.flowAnalysis.ifNullExpression_end();
DartType nonNullableReadType = readType.toNonNull();
@@ -3921,8 +3936,9 @@
DartType receiverType = inferrer.getExtensionReceiverType(
node.extension, extensionTypeArguments);
- Expression receiver =
- inferrer.ensureAssignableResult(receiverType, receiverResult);
+ Expression receiver = inferrer
+ .ensureAssignableResult(receiverType, receiverResult)
+ .expression;
VariableDeclaration? receiverVariable;
Expression readReceiver;
@@ -3989,7 +4005,8 @@
ExpressionInferenceResult valueResult = inferrer
.inferExpression(node.value, valueType, true, isVoidAllowed: true);
- Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+ valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+ Expression value = valueResult.expression;
inferrer.flowAnalysis.ifNullExpression_end();
DartType nonNullableReadType = readType.toNonNull();
@@ -4135,7 +4152,7 @@
}
DartType rightType =
inferrer.getPositionalParameterTypeForTarget(equalsTarget, leftType, 0);
- right = inferrer.ensureAssignableResult(
+ rightResult = inferrer.ensureAssignableResult(
rightType.withDeclaredNullability(inferrer.library.nullable),
rightResult,
errorTemplate: templateArgumentTypeNotAssignable,
@@ -4146,6 +4163,7 @@
templateArgumentTypeNotAssignableNullabilityNull,
nullabilityNullTypeErrorTemplate:
templateArgumentTypeNotAssignableNullabilityNullType);
+ right = rightResult.expression;
if (equalsTarget.isInstanceMember || equalsTarget.isObjectMember) {
FunctionType functionType =
@@ -4239,7 +4257,8 @@
.createBinary(fileOffset, left, binaryName, right));
}
- right = inferrer.ensureAssignableResult(rightType, rightResult);
+ rightResult = inferrer.ensureAssignableResult(rightType, rightResult);
+ right = rightResult.expression;
if (isSpecialCasedBinaryOperator) {
binaryType = inferrer.typeSchemaEnvironment
@@ -5106,15 +5125,15 @@
node.binaryName,
node.rhs,
null);
- Expression binary = binaryResult.expression;
- DartType binaryType = binaryResult.inferredType;
writeIndex = inferrer.ensureAssignable(
writeIndexType, indexResult.inferredType, writeIndex,
whyNotPromoted: whyNotPromotedIndex);
- binary = inferrer.ensureAssignable(valueType, binaryType, binary,
+ binaryResult = inferrer.ensureAssignableResult(valueType, binaryResult,
fileOffset: node.fileOffset);
+ Expression binary = binaryResult.expression;
+ DartType binaryType = binaryResult.inferredType;
VariableDeclaration? valueVariable;
Expression valueExpression;
@@ -5250,12 +5269,12 @@
node.binaryName,
node.rhs,
null);
+
+ binaryResult = inferrer.ensureAssignableResult(valueType, binaryResult,
+ fileOffset: node.fileOffset);
Expression binary = binaryResult.expression;
DartType binaryType = binaryResult.inferredType;
- binary = inferrer.ensureAssignable(valueType, binaryType, binary,
- fileOffset: node.fileOffset);
-
VariableDeclaration? valueVariable;
Expression valueExpression;
if (node.forEffect || node.forPostIncDec) {
@@ -5405,20 +5424,15 @@
node.binaryName,
node.rhs,
null);
+
+ binaryResult = inferrer.ensureAssignableResult(valueType, binaryResult,
+ fileOffset: node.fileOffset);
Expression binary = binaryResult.expression;
DartType binaryType = binaryResult.inferredType;
writeIndex = inferrer.ensureAssignable(
writeIndexType, indexResult.inferredType, writeIndex);
- Expression binaryReplacement = inferrer.ensureAssignable(
- valueType, binaryType, binary,
- fileOffset: node.fileOffset);
- // ignore: unnecessary_null_comparison
- if (binaryReplacement != null) {
- binary = binaryReplacement;
- }
-
VariableDeclaration? valueVariable;
Expression valueExpression;
if (node.forEffect || node.forPostIncDec) {
@@ -5511,8 +5525,9 @@
DartType receiverType = inferrer.getExtensionReceiverType(
node.extension, extensionTypeArguments);
- Expression receiver =
- inferrer.ensureAssignableResult(receiverType, receiverResult);
+ Expression receiver = inferrer
+ .ensureAssignableResult(receiverType, receiverResult)
+ .expression;
VariableDeclaration? receiverVariable;
Expression readReceiver;
@@ -5587,13 +5602,12 @@
node.rhs,
null);
- Expression binary = binaryResult.expression;
- DartType binaryType = binaryResult.inferredType;
-
writeIndex = inferrer.ensureAssignable(
writeIndexType, indexResult.inferredType, writeIndex);
- binary = inferrer.ensureAssignable(valueType, binaryType, binary,
+ binaryResult = inferrer.ensureAssignableResult(valueType, binaryResult,
fileOffset: node.fileOffset);
+ Expression binary = binaryResult.expression;
+ DartType binaryType = binaryResult.inferredType;
VariableDeclaration? valueVariable;
Expression valueExpression;
@@ -5721,9 +5735,10 @@
DartType writeContext = inferrer.getSetterType(target, receiverType);
ExpressionInferenceResult rhsResult = inferrer
.inferExpression(node.value, writeContext, true, isVoidAllowed: true);
- DartType rhsType = rhsResult.inferredType;
- Expression rhs = inferrer.ensureAssignableResult(writeContext, rhsResult,
+ rhsResult = inferrer.ensureAssignableResult(writeContext, rhsResult,
fileOffset: node.fileOffset, isVoidAllowed: writeContext is VoidType);
+ Expression rhs = rhsResult.expression;
+ DartType rhsType = rhsResult.inferredType;
Expression replacement = _computePropertySet(
node.fileOffset, receiver, receiverType, node.name, target, rhs,
@@ -5777,7 +5792,8 @@
ExpressionInferenceResult valueResult = inferrer
.inferExpression(node.value, valueType, true, isVoidAllowed: true);
- Expression value = inferrer.ensureAssignableResult(valueType, valueResult);
+ valueResult = inferrer.ensureAssignableResult(valueType, valueResult);
+ Expression value = valueResult.expression;
Expression write = _computePropertySet(node.writeOffset, writeReceiver,
nonNullReceiverType, node.name, writeTarget, value,
@@ -6023,8 +6039,9 @@
TypeInferenceEngine.resolveInferenceNode(writeMember);
ExpressionInferenceResult rhsResult = inferrer
.inferExpression(node.value, writeContext, true, isVoidAllowed: true);
- Expression rhs = inferrer.ensureAssignableResult(writeContext, rhsResult,
+ rhsResult = inferrer.ensureAssignableResult(writeContext, rhsResult,
fileOffset: node.fileOffset, isVoidAllowed: writeContext is VoidType);
+ Expression rhs = rhsResult.expression;
node.value = rhs..parent = node;
DartType rhsType = rhsResult.inferredType;
return new ExpressionInferenceResult(rhsType, node);
@@ -6166,8 +6183,9 @@
}
ExpressionInferenceResult rhsResult = inferrer
.inferExpression(node.value, writeContext, true, isVoidAllowed: true);
- Expression rhs = inferrer.ensureAssignableResult(writeContext, rhsResult,
+ rhsResult = inferrer.ensureAssignableResult(writeContext, rhsResult,
fileOffset: node.fileOffset, isVoidAllowed: writeContext is VoidType);
+ Expression rhs = rhsResult.expression;
node.value = rhs..parent = node;
return new ExpressionInferenceResult(rhsResult.inferredType, node);
}
@@ -6447,10 +6465,11 @@
ExpressionInferenceResult rhsResult = inferrer.inferExpression(
node.value, promotedType ?? declaredOrInferredType, true,
isVoidAllowed: true);
- Expression rhs = inferrer.ensureAssignableResult(
+ rhsResult = inferrer.ensureAssignableResult(
declaredOrInferredType, rhsResult,
fileOffset: node.fileOffset,
isVoidAllowed: declaredOrInferredType is VoidType);
+ Expression rhs = rhsResult.expression;
inferrer.flowAnalysis
.write(node, variable, rhsResult.inferredType, rhsResult.expression);
DartType resultType = rhsResult.inferredType;
@@ -6551,9 +6570,10 @@
isLate: node.isLate,
isImplicitlyTyped: node.isImplicitlyTyped);
}
- Expression initializer = inferrer.ensureAssignableResult(
+ initializerResult = inferrer.ensureAssignableResult(
node.type, initializerResult,
fileOffset: node.fileOffset, isVoidAllowed: node.type is VoidType);
+ Expression initializer = initializerResult.expression;
node.initializer = initializer..parent = node;
}
if (!inferrer.isTopLevel) {
@@ -6821,8 +6841,9 @@
ExpressionInferenceResult conditionResult = inferrer.inferExpression(
node.condition, expectedType, !inferrer.isTopLevel,
isVoidAllowed: false);
- Expression condition =
- inferrer.ensureAssignableResult(expectedType, conditionResult);
+ Expression condition = inferrer
+ .ensureAssignableResult(expectedType, conditionResult)
+ .expression;
node.condition = condition..parent = node;
inferrer.flowAnalysis.whileStatement_bodyBegin(node, node.condition);
StatementInferenceResult bodyResult = inferrer.inferStatement(node.body);
@@ -6992,11 +7013,8 @@
ExpressionInferenceResult visitParenthesized(
ParenthesizedExpression node, DartType typeContext) {
- ExpressionInferenceResult result = inferrer.inferExpression(
- node.expression, typeContext, true,
+ return inferrer.inferExpression(node.expression, typeContext, true,
isVoidAllowed: true);
- return new ExpressionInferenceResult(
- result.inferredType, result.expression);
}
void reportNonNullableInNullAwareWarningIfNeeded(
diff --git a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
index 6904eb1..5ccc48d 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart
@@ -925,9 +925,10 @@
? inferrer.wrapType(_yieldElementContext,
inferrer.coreTypes.iterableClass, inferrer.library.nonNullable)
: _yieldElementContext;
- Expression expression = inferrer.ensureAssignableResult(
- expectedType, expressionResult,
- fileOffset: node.fileOffset);
+ Expression expression = inferrer
+ .ensureAssignableResult(expectedType, expressionResult,
+ fileOffset: node.fileOffset)
+ .expression;
node.expression = expression..parent = node;
DartType type = expressionResult.inferredType;
if (!identical(expressionResult.expression, expression)) {
@@ -1057,9 +1058,10 @@
inferrer.coreTypes.streamClass, inferrer.library.nonNullable)
: _yieldElementContext;
- Expression expression = inferrer.ensureAssignableResult(
- expectedType, expressionResult,
- fileOffset: node.fileOffset);
+ Expression expression = inferrer
+ .ensureAssignableResult(expectedType, expressionResult,
+ fileOffset: node.fileOffset)
+ .expression;
node.expression = expression..parent = node;
DartType type = expressionResult.inferredType;
if (!identical(expressionResult.expression, expression)) {
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 14e18d1..e5b0ec9 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -464,29 +464,6 @@
.isSubtypeWhenIgnoringNullabilities();
}
- Expression ensureAssignableResult(
- DartType expectedType, ExpressionInferenceResult result,
- {int? fileOffset,
- bool isVoidAllowed: false,
- Template<Message Function(DartType, DartType, bool)>? errorTemplate,
- Template<Message Function(DartType, DartType, bool)>?
- nullabilityErrorTemplate,
- Template<Message Function(DartType, bool)>? nullabilityNullErrorTemplate,
- Template<Message Function(DartType, DartType, bool)>?
- nullabilityNullTypeErrorTemplate,
- Template<Message Function(DartType, DartType, DartType, DartType, bool)>?
- nullabilityPartErrorTemplate}) {
- return ensureAssignable(
- expectedType, result.inferredType, result.expression,
- fileOffset: fileOffset,
- isVoidAllowed: isVoidAllowed,
- errorTemplate: errorTemplate,
- nullabilityErrorTemplate: nullabilityErrorTemplate,
- nullabilityNullErrorTemplate: nullabilityNullErrorTemplate,
- nullabilityNullTypeErrorTemplate: nullabilityNullTypeErrorTemplate,
- nullabilityPartErrorTemplate: nullabilityPartErrorTemplate);
- }
-
/// Ensures that [expressionType] is assignable to [contextType].
///
/// Checks whether [expressionType] can be assigned to the greatest closure of
@@ -506,7 +483,40 @@
/// before returned and therefore shouldn't be checked to be a `Future`
/// directly.
Expression ensureAssignable(
- DartType contextType, DartType expressionType, Expression expression,
+ DartType expectedType, DartType expressionType, Expression expression,
+ {int? fileOffset,
+ DartType? declaredContextType,
+ DartType? runtimeCheckedType,
+ bool isVoidAllowed: false,
+ Template<Message Function(DartType, DartType, bool)>? errorTemplate,
+ Template<Message Function(DartType, DartType, bool)>?
+ nullabilityErrorTemplate,
+ Template<Message Function(DartType, bool)>? nullabilityNullErrorTemplate,
+ Template<Message Function(DartType, DartType, bool)>?
+ nullabilityNullTypeErrorTemplate,
+ Template<Message Function(DartType, DartType, DartType, DartType, bool)>?
+ nullabilityPartErrorTemplate,
+ Map<DartType, NonPromotionReason> Function()? whyNotPromoted}) {
+ return ensureAssignableResult(expectedType,
+ new ExpressionInferenceResult(expressionType, expression),
+ fileOffset: fileOffset,
+ declaredContextType: declaredContextType,
+ runtimeCheckedType: runtimeCheckedType,
+ isVoidAllowed: isVoidAllowed,
+ errorTemplate: errorTemplate,
+ nullabilityErrorTemplate: nullabilityErrorTemplate,
+ nullabilityNullErrorTemplate: nullabilityNullErrorTemplate,
+ nullabilityNullTypeErrorTemplate: nullabilityNullTypeErrorTemplate,
+ nullabilityPartErrorTemplate: nullabilityPartErrorTemplate,
+ whyNotPromoted: whyNotPromoted)
+ .expression;
+ }
+
+ /// Same as [ensureAssignable], but accepts an [ExpressionInferenceResult]
+ /// rather than an expression and a type separately. If no change is made,
+ /// [inferenceResult] is returned unchanged.
+ ExpressionInferenceResult ensureAssignableResult(
+ DartType contextType, ExpressionInferenceResult inferenceResult,
{int? fileOffset,
DartType? declaredContextType,
DartType? runtimeCheckedType,
@@ -548,35 +558,37 @@
// We don't need to insert assignability checks when doing top level type
// inference since top level type inference only cares about the type that
// is inferred (the kernel code is discarded).
- if (isTopLevel) return expression;
+ if (isTopLevel) return inferenceResult;
- fileOffset ??= expression.fileOffset;
+ fileOffset ??= inferenceResult.expression.fileOffset;
contextType = computeGreatestClosure(contextType);
DartType initialContextType = runtimeCheckedType ?? contextType;
Template<Message Function(DartType, DartType, bool)>?
- preciseTypeErrorTemplate = _getPreciseTypeErrorTemplate(expression);
+ preciseTypeErrorTemplate =
+ _getPreciseTypeErrorTemplate(inferenceResult.expression);
AssignabilityResult assignabilityResult = _computeAssignabilityKind(
- contextType, expressionType,
+ contextType, inferenceResult.inferredType,
isNonNullableByDefault: isNonNullableByDefault,
isVoidAllowed: isVoidAllowed,
isExpressionTypePrecise: preciseTypeErrorTemplate != null);
if (assignabilityResult.needsTearOff) {
- TypedTearoff typedTearoff =
- _tearOffCall(expression, expressionType as InterfaceType, fileOffset);
- expression = typedTearoff.tearoff;
- expressionType = typedTearoff.tearoffType;
+ TypedTearoff typedTearoff = _tearOffCall(inferenceResult.expression,
+ inferenceResult.inferredType as InterfaceType, fileOffset);
+ inferenceResult = new ExpressionInferenceResult(
+ typedTearoff.tearoffType, typedTearoff.tearoff);
}
if (assignabilityResult.implicitInstantiation != null) {
- ExpressionInferenceResult instantiationResult =
- _applyImplicitInstantiation(assignabilityResult.implicitInstantiation,
- expressionType, expression);
- expression = instantiationResult.expression;
- expressionType = instantiationResult.inferredType;
+ inferenceResult = _applyImplicitInstantiation(
+ assignabilityResult.implicitInstantiation,
+ inferenceResult.inferredType,
+ inferenceResult.expression);
}
+ DartType expressionType = inferenceResult.inferredType;
+ Expression expression = inferenceResult.expression;
Expression result;
switch (assignabilityResult.kind) {
case AssignabilityKind.assignable:
@@ -673,8 +685,10 @@
if (!identical(result, expression)) {
flowAnalysis.forwardExpression(result, expression);
+ return new ExpressionInferenceResult(expressionType, result);
+ } else {
+ return inferenceResult;
}
- return result;
}
Expression _wrapTearoffErrorExpression(Expression expression,
@@ -2058,11 +2072,10 @@
this.helper = helper;
ExpressionInferenceResult initializerResult =
inferExpression(initializer, declaredType, true, isVoidAllowed: true);
- initializer = ensureAssignableResult(declaredType, initializerResult,
+ initializerResult = ensureAssignableResult(declaredType, initializerResult,
isVoidAllowed: declaredType is VoidType);
this.helper = null;
- return new ExpressionInferenceResult(
- initializerResult.inferredType, initializer);
+ return initializerResult;
}
@override
@@ -4003,7 +4016,7 @@
ExpressionInferenceResult result =
inferExpression(initializer, declaredType, true);
if (hasDeclaredInitializer) {
- initializer = ensureAssignableResult(declaredType, result);
+ initializer = ensureAssignableResult(declaredType, result).expression;
}
this.helper = null;
return initializer;
diff --git a/tests/language/call/implicit_tearoff_assignment_test.dart b/tests/language/call/implicit_tearoff_assignment_test.dart
new file mode 100644
index 0000000..c3d4d58
--- /dev/null
+++ b/tests/language/call/implicit_tearoff_assignment_test.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2022, 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 ensures that when a `.call` tearoff occurs on the right hand side
+// of an assignment, the resulting expression has an appropriate type.
+
+import "package:expect/expect.dart";
+
+import '../static_type_helper.dart';
+
+dynamic _topLevelPropertySetValue;
+
+set topLevelProperty(void Function() value) {
+ Expect.isNull(_topLevelPropertySetValue);
+ _topLevelPropertySetValue = value;
+}
+
+class C {
+ static dynamic _staticPropertySetValue;
+
+ void call() {}
+
+ static set staticProperty(void Function() value) {
+ Expect.isNull(_staticPropertySetValue);
+ _staticPropertySetValue = value;
+ }
+}
+
+class Base {
+ late final dynamic _basePropertySetValue;
+
+ set baseProperty(void Function() value) {
+ _basePropertySetValue = value;
+ }
+}
+
+class Derived extends Base {
+ late final dynamic _indexSetValue;
+ late final dynamic _instanceSetValue;
+
+ operator []=(int index, void Function() value) {
+ _indexSetValue = value;
+ }
+
+ set instanceProperty(void Function() value) {
+ _instanceSetValue = value;
+ }
+
+ void testSuperPropertySet() {
+ Expect.type<void Function()>((super.baseProperty = C())
+ ..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(super._basePropertySetValue);
+ }
+}
+
+class Extended {
+ late final dynamic _extensionIndexSetValue;
+ late final dynamic _extensionPropertySetValue;
+}
+
+extension on Extended {
+ operator []=(int index, void Function() value) {
+ _extensionIndexSetValue = value;
+ }
+
+ set extensionProperty(void Function() value) {
+ _extensionPropertySetValue = value;
+ }
+}
+
+void testExtensionIndexSet() {
+ Extended e = Extended();
+ Expect.type<void Function()>(
+ (e[0] = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(e._extensionIndexSetValue);
+}
+
+void testExtensionSet() {
+ Extended e = Extended();
+ Expect.type<void Function()>((e.extensionProperty = C())
+ ..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(e._extensionPropertySetValue);
+}
+
+void testIndexSet() {
+ Derived d = Derived();
+ Expect.type<void Function()>(
+ (d[0] = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(d._indexSetValue);
+}
+
+void testInstanceSet() {
+ Derived d = Derived();
+ Expect.type<void Function()>(
+ (d.instanceProperty = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(d._instanceSetValue);
+}
+
+void testNullAwarePropertySet() {
+ Derived? d = Derived() as Derived?; // ignore: unnecessary_cast
+ Expect.type<void Function()>((d?.instanceProperty = C())
+ ..expectStaticType<Exactly<void Function()?>>());
+ Expect.type<void Function()>(d!._instanceSetValue);
+}
+
+void testStaticSet() {
+ C._staticPropertySetValue = null;
+ Expect.type<void Function()>(
+ (C.staticProperty = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(C._staticPropertySetValue);
+}
+
+void testTopLevelSet() {
+ _topLevelPropertySetValue = null;
+ Expect.type<void Function()>(
+ (topLevelProperty = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(_topLevelPropertySetValue);
+}
+
+void testVariableSet() {
+ void Function() f;
+ Expect.type<void Function()>(
+ (f = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(f);
+}
+
+main() {
+ testExtensionIndexSet();
+ testExtensionSet();
+ testIndexSet();
+ testInstanceSet();
+ testStaticSet();
+ Derived().testSuperPropertySet();
+ testTopLevelSet();
+ testVariableSet();
+}
diff --git a/tests/language_2/call/implicit_tearoff_assignment_test.dart b/tests/language_2/call/implicit_tearoff_assignment_test.dart
new file mode 100644
index 0000000..87ccd15
--- /dev/null
+++ b/tests/language_2/call/implicit_tearoff_assignment_test.dart
@@ -0,0 +1,144 @@
+// Copyright (c) 2022, 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 ensures that when a `.call` tearoff occurs on the right hand side
+// of an assignment, the resulting expression has an appropriate type.
+
+// @dart = 2.9
+
+import "package:expect/expect.dart";
+
+import '../static_type_helper.dart';
+
+dynamic _topLevelPropertySetValue;
+
+set topLevelProperty(void Function() value) {
+ Expect.isNull(_topLevelPropertySetValue);
+ _topLevelPropertySetValue = value;
+}
+
+class C {
+ static dynamic _staticPropertySetValue;
+
+ void call() {}
+
+ static set staticProperty(void Function() value) {
+ Expect.isNull(_staticPropertySetValue);
+ _staticPropertySetValue = value;
+ }
+}
+
+class Base {
+ dynamic _basePropertySetValue;
+
+ set baseProperty(void Function() value) {
+ Expect.isNull(_basePropertySetValue);
+ _basePropertySetValue = value;
+ }
+}
+
+class Derived extends Base {
+ dynamic _indexSetValue;
+ dynamic _instanceSetValue;
+
+ operator []=(int index, void Function() value) {
+ Expect.isNull(_indexSetValue);
+ _indexSetValue = value;
+ }
+
+ set instanceProperty(void Function() value) {
+ Expect.isNull(_instanceSetValue);
+ _instanceSetValue = value;
+ }
+
+ void testSuperPropertySet() {
+ Expect.type<void Function()>((super.baseProperty = C())
+ ..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(super._basePropertySetValue);
+ }
+}
+
+class Extended {
+ dynamic _extensionIndexSetValue;
+ dynamic _extensionPropertySetValue;
+}
+
+extension on Extended {
+ operator []=(int index, void Function() value) {
+ Expect.isNull(_extensionIndexSetValue);
+ _extensionIndexSetValue = value;
+ }
+
+ set extensionProperty(void Function() value) {
+ Expect.isNull(_extensionPropertySetValue);
+ _extensionPropertySetValue = value;
+ }
+}
+
+void testExtensionIndexSet() {
+ Extended e = Extended();
+ Expect.type<void Function()>(
+ (e[0] = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(e._extensionIndexSetValue);
+}
+
+void testExtensionSet() {
+ Extended e = Extended();
+ Expect.type<void Function()>((e.extensionProperty = C())
+ ..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(e._extensionPropertySetValue);
+}
+
+void testIndexSet() {
+ Derived d = Derived();
+ Expect.type<void Function()>(
+ (d[0] = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(d._indexSetValue);
+}
+
+void testInstanceSet() {
+ Derived d = Derived();
+ Expect.type<void Function()>(
+ (d.instanceProperty = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(d._instanceSetValue);
+}
+
+void testNullAwarePropertySet() {
+ Derived d = Derived();
+ Expect.type<void Function()>((d?.instanceProperty = C())
+ ..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(d._instanceSetValue);
+}
+
+void testStaticSet() {
+ C._staticPropertySetValue = null;
+ Expect.type<void Function()>(
+ (C.staticProperty = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(C._staticPropertySetValue);
+}
+
+void testTopLevelSet() {
+ _topLevelPropertySetValue = null;
+ Expect.type<void Function()>(
+ (topLevelProperty = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(_topLevelPropertySetValue);
+}
+
+void testVariableSet() {
+ void Function() f;
+ Expect.type<void Function()>(
+ (f = C())..expectStaticType<Exactly<void Function()>>());
+ Expect.type<void Function()>(f);
+}
+
+main() {
+ testExtensionIndexSet();
+ testExtensionSet();
+ testIndexSet();
+ testInstanceSet();
+ testStaticSet();
+ Derived().testSuperPropertySet();
+ testTopLevelSet();
+ testVariableSet();
+}
diff --git a/tools/VERSION b/tools/VERSION
index 43c10b9..39320fd 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 134
+PRERELEASE 135
PRERELEASE_PATCH 0
\ No newline at end of file