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