Stop using getReadType() in nnbd_migration.

The packages 'analyzer' and 'nnbd_migration' tightly depend on each
other via MigrationResolutionHooks. I will publish analyzer 0.40.4
shortly after this CL lands.

Change-Id: I6f5e51f88e0020a1674ffb251712658e896170e7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164900
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
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 59d39ff..6fd621d 100644
--- a/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/assignment_expression_resolver.dart
@@ -67,6 +67,8 @@
       _resolver.setReadElement(left, readElement);
     }
     _resolver.setWriteElement(left, writeElement);
+    _resolver.migrationResolutionHooks
+        ?.setCompoundAssignmentExpressionTypes(node);
 
     _resolver.setAssignmentBackwardCompatibility(
       assignment: node,
diff --git a/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
index c5a5ea2..dbc01c0 100644
--- a/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
@@ -62,6 +62,8 @@
     var operand = node.operand;
     _resolver.setReadElement(operand, readElement);
     _resolver.setWriteElement(operand, writeElement);
+    _resolver.migrationResolutionHooks
+        ?.setCompoundAssignmentExpressionTypes(node);
 
     _resolver.setAssignmentBackwardCompatibility(
       assignment: node,
@@ -82,8 +84,8 @@
   ///
   /// TODO(scheglov) this is duplicate
   void _checkForInvalidAssignmentIncDec(
-      AstNode node, Expression operand, DartType type) {
-    var operandWriteType = _getWriteType(operand);
+      PostfixExpression node, Expression operand, DartType type) {
+    var operandWriteType = node.writeType;
     if (!_typeSystem.isAssignableTo2(type, operandWriteType)) {
       _resolver.errorReporter.reportErrorForNode(
         CompileTimeErrorCode.INVALID_ASSIGNMENT,
@@ -129,16 +131,6 @@
     }
   }
 
-  DartType _getWriteType(Expression node) {
-    if (node is SimpleIdentifier) {
-      var element = node.staticElement;
-      if (element is PromotableElement) {
-        return element.type;
-      }
-    }
-    return node.staticType;
-  }
-
   void _resolve1(PostfixExpression node, DartType receiverType) {
     Expression operand = node.operand;
 
diff --git a/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
index d16861a..c3c161a 100644
--- a/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/prefix_expression_resolver.dart
@@ -65,6 +65,8 @@
       var operand = node.operand;
       _resolver.setReadElement(operand, readElement);
       _resolver.setWriteElement(operand, writeElement);
+      _resolver.migrationResolutionHooks
+          ?.setCompoundAssignmentExpressionTypes(node);
 
       _resolver.setAssignmentBackwardCompatibility(
         assignment: node,
diff --git a/pkg/analyzer/lib/src/generated/migration.dart b/pkg/analyzer/lib/src/generated/migration.dart
index 8ea9925..93d82cc 100644
--- a/pkg/analyzer/lib/src/generated/migration.dart
+++ b/pkg/analyzer/lib/src/generated/migration.dart
@@ -31,6 +31,10 @@
   DartType modifyInferredParameterType(
       ParameterElement parameter, DartType type);
 
+  /// Called after the resolver has determined the read and write types
+  /// of the assignment expression.
+  void setCompoundAssignmentExpressionTypes(CompoundAssignmentExpression node);
+
   /// Called when the resolver starts or stops making use of a [FlowAnalysis]
   /// instance.
   void setFlowAnalysis(
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index ff2d412..9e5e486 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -26,7 +26,6 @@
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
-import 'package:analyzer/src/task/strong/checker.dart';
 import 'package:nnbd_migration/fix_reason_target.dart';
 import 'package:nnbd_migration/instrumentation.dart';
 import 'package:nnbd_migration/nnbd_migration.dart';
@@ -471,20 +470,18 @@
       _wrapExceptions(node, () => type, () {
         var parent = node.parent;
         if (parent is AssignmentExpression) {
-          return (_assignmentLikeExpressionHandlers[parent] ??=
-                  _AssignmentExpressionHandler(parent))
-              .modifySubexpressionType(this, node, type);
+          if (parent.leftHandSide == node) {
+            return type;
+          }
+          return _assignmentLikeExpressionHandlers[parent]
+              .modifyAssignmentRhs(this, node, type);
         } else if (parent is PrefixExpression) {
           if (_isIncrementOrDecrementOperator(parent.operator.type)) {
-            return (_assignmentLikeExpressionHandlers[parent] ??=
-                    _PrefixExpressionHandler(parent))
-                .modifySubexpressionType(this, node, type);
+            return type;
           }
         } else if (parent is PostfixExpression) {
           if (_isIncrementOrDecrementOperator(parent.operator.type)) {
-            return (_assignmentLikeExpressionHandlers[parent] ??=
-                    _PostfixExpressionHandler(parent))
-                .modifySubexpressionType(this, node, type);
+            return type;
           }
         }
         return _modifyRValueType(node, type);
@@ -503,6 +500,30 @@
   }
 
   @override
+  void setCompoundAssignmentExpressionTypes(CompoundAssignmentExpression node) {
+    assert(_assignmentLikeExpressionHandlers[node] == null);
+    if (node is AssignmentExpression) {
+      var handler = _AssignmentExpressionHandler(node);
+      _assignmentLikeExpressionHandlers[node] = handler;
+      handler.handleLValueType(this, node.readType, node.writeType);
+    } else if (node is PrefixExpression) {
+      assert(_isIncrementOrDecrementOperator(node.operator.type));
+      var handler = _PrefixExpressionHandler(node);
+      _assignmentLikeExpressionHandlers[node] = handler;
+      handler.handleLValueType(this, node.readType, node.writeType);
+      handler.handleAssignmentRhs(this, _fixBuilder.typeProvider.intType);
+    } else if (node is PostfixExpression) {
+      assert(_isIncrementOrDecrementOperator(node.operator.type));
+      var handler = _PostfixExpressionHandler(node);
+      _assignmentLikeExpressionHandlers[node] = handler;
+      handler.handleLValueType(this, node.readType, node.writeType);
+      handler.handleAssignmentRhs(this, _fixBuilder.typeProvider.intType);
+    } else {
+      throw StateError('(${node.runtimeType}) $node');
+    }
+  }
+
+  @override
   void setFlowAnalysis(
       FlowAnalysis<AstNode, Statement, Expression, PromotableElement, DartType>
           flowAnalysis) {
@@ -765,37 +786,21 @@
   /// [writeType], and [rhsContextType].  Also verifies that for compound
   /// assignments, the [readType] is non-nullable, and that for null-aware
   /// assignments, the [readType] is nullable.
-  void handleLValueType(
-      MigrationResolutionHooksImpl hooks, DartType resolvedType) {
-    assert(resolvedType.nullabilitySuffix != NullabilitySuffix.star);
-    // Provisionally store the resolved type as the type of the target, so that
-    // getReadType can fall back on it if necessary.
-    var target = this.target;
-    target.staticType = resolvedType;
-    // The type passed in by the resolver for the LHS of an assignment is the
-    // "write type".
-    var writeType = resolvedType;
-    if (target is SimpleIdentifier) {
-      var element = target.staticElement;
-      if (element is PromotableElement) {
-        // However, if the LHS is a reference to a local variable that has
-        // been promoted, the resolver passes in the promoted type.  We
-        // want to use the variable element's type, so that we consider it
-        // ok to assign a value to the variable that un-does the
-        // promotion.  See https://github.com/dart-lang/sdk/issues/41411.
-        writeType = element.type;
-      }
-    }
-    assert(writeType.nullabilitySuffix != NullabilitySuffix.star);
-    this.writeType = writeType;
+  void handleLValueType(MigrationResolutionHooksImpl hooks,
+      DartType readTypeToSet, DartType writeTypeToSet) {
+    assert(writeTypeToSet.nullabilitySuffix != NullabilitySuffix.star);
+    writeType = writeTypeToSet;
+    // TODO(scheglov) Remove this after the analyzer breaking change that
+    // will top setting types for LHS.
+    target.staticType = writeTypeToSet;
     var fixBuilder = hooks._fixBuilder;
     if (combinerType == TokenType.EQ) {
-      rhsContextType = writeType;
+      rhsContextType = writeTypeToSet;
     } else {
-      readType = getReadType(target);
+      readType = readTypeToSet;
       assert(readType.nullabilitySuffix != NullabilitySuffix.star);
       if (combinerType == TokenType.QUESTION_QUESTION_EQ) {
-        rhsContextType = writeType;
+        rhsContextType = writeTypeToSet;
         if (fixBuilder._typeSystem.isNonNullable(readType)) {
           (fixBuilder._getChange(node) as NodeChangeForAssignment)
               .isWeakNullAware = true;
@@ -810,26 +815,13 @@
     }
   }
 
-  /// Called after visiting the LHS or the RHS of the assignment.
-  DartType modifySubexpressionType(MigrationResolutionHooksImpl hooks,
+  /// Called after visiting the RHS of the assignment.
+  DartType modifyAssignmentRhs(MigrationResolutionHooksImpl hooks,
       Expression subexpression, DartType type) {
-    if (identical(subexpression, target)) {
-      handleLValueType(hooks, type);
-      if (node is! AssignmentExpression) {
-        // Must be a pre or post increment/decrement, so the "RHS" is implicitly
-        // the integer 1.
-        handleAssignmentRhs(hooks, hooks._fixBuilder.typeProvider.intType);
-      }
-      return type;
-    } else {
-      var node = this.node;
-      assert(node is AssignmentExpression &&
-          identical(subexpression, node.rightHandSide));
-      type =
-          hooks._modifyRValueType(subexpression, type, context: rhsContextType);
-      handleAssignmentRhs(hooks, type);
-      return type;
-    }
+    type =
+        hooks._modifyRValueType(subexpression, type, context: rhsContextType);
+    handleAssignmentRhs(hooks, type);
+    return type;
   }
 }
 
diff --git a/pkg/nnbd_migration/pubspec.yaml b/pkg/nnbd_migration/pubspec.yaml
index 0ee45b0..ac8b395 100644
--- a/pkg/nnbd_migration/pubspec.yaml
+++ b/pkg/nnbd_migration/pubspec.yaml
@@ -6,7 +6,7 @@
   sdk: '>=2.6.0 <3.0.0'
 dependencies:
   _fe_analyzer_shared: ^4.0.0
-  analyzer: ^0.40.1
+  analyzer: ^0.40.4
   analyzer_plugin: ^0.2.4
   args: ^1.4.4
   charcode: ^1.1.2
diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart
index 010f005..4e3e6e9 100644
--- a/pkg/nnbd_migration/test/fix_builder_test.dart
+++ b/pkg/nnbd_migration/test/fix_builder_test.dart
@@ -3,13 +3,11 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'package:analyzer/dart/ast/ast.dart';
-import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/error/hint_codes.dart';
 import 'package:analyzer/src/generated/element_type_provider.dart';
-import 'package:analyzer/src/task/strong/checker.dart';
 import 'package:nnbd_migration/fix_reason_target.dart';
 import 'package:nnbd_migration/nnbd_migration.dart';
 import 'package:nnbd_migration/src/edit_plan.dart';
@@ -3364,10 +3362,8 @@
           identical(ElementTypeProvider.current, const ElementTypeProvider()));
       ElementTypeProvider.current = fixBuilder.migrationResolutionHooks;
       var assignment = node.thisOrAncestorOfType<AssignmentExpression>();
-      var isReadWrite = assignment.operator.type != TokenType.EQ;
-      var readType =
-          isReadWrite ? getReadType(node) ?? typeProvider.dynamicType : null;
-      var writeType = node.staticType;
+      var readType = assignment.readType;
+      var writeType = assignment.writeType;
       return AssignmentTargetInfo(readType, writeType);
     } finally {
       ElementTypeProvider.current = const ElementTypeProvider();