Deprecate getReadType() and remove getExpressionType().

Change-Id: Ibc32afd7867f5b0f940affdaea97904c1afb194c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164361
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
index 489aa70..e16e7da 100644
--- a/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/binary_expression_resolver.dart
@@ -18,7 +18,6 @@
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/type_promotion_manager.dart';
-import 'package:analyzer/src/task/strong/checker.dart';
 import 'package:meta/meta.dart';
 
 /// Helper for resolving [BinaryExpression]s.
@@ -107,26 +106,12 @@
     );
   }
 
-  /// Gets the definite type of expression, which can be used in cases where
-  /// the most precise type is desired, for example computing the least upper
-  /// bound.
-  ///
-  /// See [getExpressionType] for more information. Without strong mode, this is
-  /// equivalent to [_getStaticType].
-  ///
-  /// TODO(scheglov) this is duplicate
-  DartType _getExpressionType(Expression expr, {bool read = false}) =>
-      getExpressionType(expr, _typeSystem, _typeProvider, read: read);
-
   /// Return the static type of the given [expression] that is to be used for
   /// type analysis.
   ///
   /// TODO(scheglov) this is duplicate
-  DartType _getStaticType(Expression expression, {bool read = false}) {
-    if (expression is NullLiteral) {
-      return _typeProvider.nullType;
-    }
-    DartType type = read ? getReadType(expression) : expression.staticType;
+  DartType _getStaticType(Expression expression) {
+    DartType type = expression.staticType;
     return _resolveTypeParameter(type);
   }
 
@@ -170,7 +155,7 @@
 
     left.accept(_resolver);
     left = node.leftOperand;
-    var leftType = _getExpressionType(left, read: false);
+    var leftType = left.staticType;
 
     var rightContextType = InferenceContext.getContext(node);
     if (rightContextType == null || rightContextType.isDynamic) {
@@ -183,9 +168,7 @@
     right = node.rightOperand;
     flow?.ifNullExpression_end();
 
-    // TODO(scheglov) This (and above) is absolutely wrong, and convoluted.
-    // This is just the status quo, until we can make types straight.
-    var rightType = _getExpressionType(right, read: false);
+    var rightType = right.staticType;
     if (_isNonNullableByDefault) {
       var promotedLeftType = _typeSystem.promoteToNonNull(leftType);
       _analyzeLeastUpperBoundTypes(node, promotedLeftType, rightType);
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 75d46e2..30df9bf 100644
--- a/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/postfix_expression_resolver.dart
@@ -17,7 +17,6 @@
 import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/resolver.dart';
-import 'package:analyzer/src/task/strong/checker.dart';
 import 'package:meta/meta.dart';
 
 /// Helper for resolving [PostfixExpression]s.
@@ -228,7 +227,7 @@
     operand.accept(_resolver);
     operand = node.operand;
 
-    var operandType = getReadType(operand);
+    var operandType = operand.staticType;
 
     var type = _typeSystem.promoteToNonNull(operandType);
     _inferenceHelper.recordStaticType(node, type);
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 55a6235e..9955b67 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -40,7 +40,6 @@
 import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
 import 'package:analyzer/src/generated/sdk.dart' show DartSdk, SdkLibrary;
 import 'package:analyzer/src/generated/this_access_tracker.dart';
-import 'package:analyzer/src/task/strong/checker.dart';
 import 'package:meta/meta.dart';
 
 class EnclosingExecutableContext {
@@ -319,8 +318,7 @@
       _checkForArgumentTypeNotAssignableForArgument(rhs);
     }
     if (operatorType == TokenType.QUESTION_QUESTION_EQ) {
-      _checkForDeadNullCoalesce(
-          getReadType(node.leftHandSide), node.rightHandSide);
+      _checkForDeadNullCoalesce(node.readType, node.rightHandSide);
     }
     _checkForAssignmentToFinal(lhs);
     super.visitAssignmentExpression(node);
@@ -355,8 +353,7 @@
     }
 
     if (type == TokenType.QUESTION_QUESTION) {
-      _checkForDeadNullCoalesce(
-          getReadType(node.leftOperand), node.rightOperand);
+      _checkForDeadNullCoalesce(node.leftOperand.staticType, node.rightOperand);
     }
 
     _checkForUseOfVoidResult(node.leftOperand);
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index ecee766..a06752f 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -16,8 +16,6 @@
 import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
 import 'package:analyzer/src/generated/migration.dart';
 import 'package:analyzer/src/generated/resolver.dart';
-import 'package:analyzer/src/task/strong/checker.dart'
-    show getExpressionType, getReadType;
 
 /// Instances of the class `StaticTypeAnalyzer` perform two type-related tasks. First, they
 /// compute the static type of every expression. Second, they look for any static type errors or
@@ -344,10 +342,9 @@
   /// Set the static type of [node] to be the least upper bound of the static
   /// types of subexpressions [expr1] and [expr2].
   void _analyzeLeastUpperBound(
-      Expression node, Expression expr1, Expression expr2,
-      {bool read = false}) {
-    DartType staticType1 = _getExpressionType(expr1, read: read);
-    DartType staticType2 = _getExpressionType(expr2, read: read);
+      Expression node, Expression expr1, Expression expr2) {
+    DartType staticType1 = expr1.staticType;
+    DartType staticType2 = expr2.staticType;
 
     _analyzeLeastUpperBoundTypes(node, staticType1, staticType2);
   }
@@ -371,20 +368,11 @@
     recordStaticType(node, staticType);
   }
 
-  /// Gets the definite type of expression, which can be used in cases where
-  /// the most precise type is desired, for example computing the least upper
-  /// bound.
-  ///
-  /// See [getExpressionType] for more information. Without strong mode, this is
-  /// equivalent to [_getStaticType].
-  DartType _getExpressionType(Expression expr, {bool read = false}) =>
-      getExpressionType(expr, _typeSystem, _typeProvider, read: read);
-
   /// Return the static type of the given [expression].
   DartType _getStaticType(Expression expression, {bool read = false}) {
     DartType type;
     if (read) {
-      type = getReadType(expression);
+      type = expression.staticType;
     } else {
       if (expression is SimpleIdentifier && expression.inSetterContext()) {
         var element = expression.staticElement;
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index 4e67d44..e3049ad 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -20,22 +20,9 @@
 import 'package:analyzer/src/error/codes.dart'
     show CompileTimeErrorCode, StrongModeCode;
 import 'package:analyzer/src/summary/idl.dart';
+import 'package:meta/meta.dart';
 
-/// Given an [expression] and a corresponding [typeSystem] and [typeProvider],
-/// gets the known static type of the expression.
-DartType getExpressionType(
-    Expression expression, TypeSystemImpl typeSystem, TypeProvider typeProvider,
-    {bool read = false}) {
-  DartType type;
-  if (read) {
-    type = getReadType(expression);
-  } else {
-    type = expression.staticType;
-  }
-  type ??= DynamicTypeImpl.instance;
-  return type;
-}
-
+@Deprecated('Use CompoundAssignmentExpression.readType')
 DartType getReadType(Expression expression) {
   if (expression is IndexExpression) {
     var aux = expression.auxiliaryElements;
@@ -148,8 +135,8 @@
     }
   }
 
-  void checkAssignment(Expression expr, DartType type) {
-    checkForCast(expr, type);
+  void checkAssignment(Expression expr, DartType to) {
+    checkForCast(expr, from: expr.staticType, to: to);
   }
 
   /// Analyzer checks boolean conversions, but we need to check too, because
@@ -176,20 +163,26 @@
 
       var exprType = element.expression.staticType;
       var asIterableType = exprType.asInstanceOf(typeProvider.iterableElement);
-      var elementType =
-          asIterableType == null ? null : asIterableType.typeArguments[0];
-      // Items in the spread will then potentially be downcast to the expected
-      // type.
-      _checkImplicitCast(element.expression, expectedType,
-          from: elementType, forSpread: true);
+
+      if (asIterableType != null) {
+        var elementType = asIterableType.typeArguments[0];
+        // Items in the spread will then potentially be downcast to the expected
+        // type.
+        _checkImplicitCast(element.expression,
+            to: expectedType, from: elementType, forSpread: true);
+      }
     }
   }
 
-  void checkForCast(Expression expr, DartType type) {
+  void checkForCast(
+    Expression expr, {
+    @required DartType from,
+    @required DartType to,
+  }) {
     if (expr is ParenthesizedExpression) {
-      checkForCast(expr.expression, type);
+      checkForCast(expr.expression, from: from, to: to);
     } else {
-      _checkImplicitCast(expr, type);
+      _checkImplicitCast(expr, from: from, to: to);
     }
   }
 
@@ -214,16 +207,18 @@
       var exprType = element.expression.staticType;
       var asMapType = exprType.asInstanceOf(typeProvider.mapElement);
 
-      var elementKeyType =
-          asMapType == null ? null : asMapType.typeArguments[0];
-      var elementValueType =
-          asMapType == null ? null : asMapType.typeArguments[1];
-      // Keys and values in the spread will then potentially be downcast to
-      // the expected types.
-      _checkImplicitCast(element.expression, expectedKeyType,
-          from: elementKeyType, forSpreadKey: true);
-      _checkImplicitCast(element.expression, expectedValueType,
-          from: elementValueType, forSpreadValue: true);
+      if (asMapType != null) {
+        var elementKeyType = asMapType.typeArguments[0];
+        var elementValueType = asMapType.typeArguments[1];
+        // Keys and values in the spread will then potentially be downcast to
+        // the expected types.
+        _checkImplicitCast(element.expression,
+            to: expectedKeyType, from: elementKeyType, forSpreadKey: true);
+        _checkImplicitCast(element.expression,
+            to: expectedValueType,
+            from: elementValueType,
+            forSpreadValue: true);
+      }
     }
   }
 
@@ -241,16 +236,17 @@
 
   @override
   void visitAssignmentExpression(AssignmentExpression node) {
+    var left = node.leftHandSide;
+    var right = node.rightHandSide;
     Token operator = node.operator;
     TokenType operatorType = operator.type;
     if (operatorType == TokenType.EQ ||
         operatorType == TokenType.QUESTION_QUESTION_EQ) {
-      DartType staticType = _getExpressionType(node.leftHandSide);
-      checkAssignment(node.rightHandSide, staticType);
+      checkForCast(right, from: right.staticType, to: node.writeType);
     } else if (operatorType == TokenType.AMPERSAND_AMPERSAND_EQ ||
         operatorType == TokenType.BAR_BAR_EQ) {
-      checkAssignment(node.leftHandSide, typeProvider.boolType);
-      checkAssignment(node.rightHandSide, typeProvider.boolType);
+      checkBoolean(left);
+      checkBoolean(right);
     } else {
       _checkCompoundAssignment(node);
     }
@@ -457,7 +453,8 @@
 
   @override
   void visitPostfixExpression(PostfixExpression node) {
-    _checkUnary(node.operand, node.operator, node.staticElement);
+    _checkUnary(node.operand, node.operator, node.staticElement,
+        readType: node.readType, writeType: node.writeType);
     node.visitChildren(this);
   }
 
@@ -466,7 +463,8 @@
     if (node.operator.type == TokenType.BANG) {
       checkBoolean(node.operand);
     } else {
-      _checkUnary(node.operand, node.operator, node.staticElement);
+      _checkUnary(node.operand, node.operator, node.staticElement,
+          readType: node.readType, writeType: node.writeType);
     }
     node.visitChildren(this);
   }
@@ -588,7 +586,8 @@
       for (VariableDeclaration variable in node.variables) {
         var initializer = variable.initializer;
         if (initializer != null) {
-          checkForCast(initializer, type.type);
+          checkForCast(initializer,
+              from: initializer.staticType, to: type.type);
         }
       }
     }
@@ -622,10 +621,9 @@
       assert(functionType.optionalParameterTypes.isEmpty);
 
       // Refine the return type.
-      var rhsType = _getExpressionType(expr.rightHandSide);
-      var lhsType = _getExpressionType(expr.leftHandSide);
+      var rhsType = expr.rightHandSide.staticType;
       var returnType = rules.refineBinaryExpressionType(
-        lhsType,
+        expr.readType,
         op,
         rhsType,
         functionType.returnType,
@@ -633,7 +631,7 @@
       );
 
       // Check the argument for an implicit cast.
-      _checkImplicitCast(expr.rightHandSide, paramTypes[0], from: rhsType);
+      _checkImplicitCast(expr.rightHandSide, to: paramTypes[0], from: rhsType);
 
       // Check the return type for an implicit cast.
       //
@@ -643,8 +641,8 @@
       //     y = /*implicit cast*/(y + 42);
       //     /*implicit assignment cast*/y += 42;
       //
-      _checkImplicitCast(expr.leftHandSide, lhsType,
-          from: returnType, opAssign: true);
+      _checkImplicitCast(expr.leftHandSide,
+          to: expr.writeType, from: returnType, opAssign: true);
     }
   }
 
@@ -694,8 +692,9 @@
   ///
   /// If [expr] does not require an implicit cast because it is not related to
   /// [to] or is already a subtype of it, does nothing.
-  void _checkImplicitCast(Expression expr, DartType to,
-      {DartType from,
+  void _checkImplicitCast(Expression expr,
+      {@required DartType to,
+      @required DartType from,
       bool opAssign = false,
       bool forSpread = false,
       bool forSpreadKey = false,
@@ -704,9 +703,7 @@
       return;
     }
 
-    from ??= _getExpressionType(expr);
-
-    if (_needsImplicitCast(expr, to, from: from) == true) {
+    if (_needsImplicitCast(expr, to: to, from: from) == true) {
       _recordImplicitCast(expr, to,
           from: from,
           opAssign: opAssign,
@@ -730,7 +727,8 @@
     if (expression != null) checkAssignment(expression, type);
   }
 
-  void _checkUnary(Expression operand, Token op, MethodElement element) {
+  void _checkUnary(Expression operand, Token op, MethodElement element,
+      {@required DartType readType, @required DartType writeType}) {
     bool isIncrementAssign = op.type.isIncrementOperator;
     if (op.isUserDefinableOperator || isIncrementAssign) {
       if (element != null && isIncrementAssign) {
@@ -744,9 +742,8 @@
         // Refine the return type.
         var functionType = element.type;
         var rhsType = typeProvider.intType;
-        var lhsType = _getExpressionType(operand);
         var returnType = rules.refineBinaryExpressionType(
-          lhsType,
+          readType,
           TokenType.PLUS,
           rhsType,
           functionType.returnType,
@@ -763,7 +760,8 @@
         //     y = /*implicit cast*/(y + 1);
         //     /*implicit assignment cast*/y++;
         //
-        _checkImplicitCast(operand, lhsType, from: returnType, opAssign: true);
+        _checkImplicitCast(operand,
+            to: writeType, from: returnType, opAssign: true);
       }
     }
   }
@@ -827,9 +825,6 @@
     }
   }
 
-  DartType _getExpressionType(Expression expr) =>
-      getExpressionType(expr, rules, typeProvider);
-
   DartType _getInstanceTypeArgument(
       DartType expressionType, ClassElement instanceType) {
     var asInstanceType = expressionType.asInstanceOf(instanceType);
@@ -860,9 +855,8 @@
   /// downcast implicitly).
   ///
   /// If [from] is omitted, uses the static type of [expr]
-  bool _needsImplicitCast(Expression expr, DartType to, {DartType from}) {
-    from ??= _getExpressionType(expr);
-
+  bool _needsImplicitCast(Expression expr,
+      {@required DartType from, @required DartType to}) {
     // Void is considered Top, but may only be *explicitly* cast.
     if (from.isVoid) return null;
 
@@ -886,8 +880,8 @@
     if (to.isDartAsyncFutureOr) {
       var to1 = (to as InterfaceType).typeArguments[0];
       var to2 = typeProvider.futureType2(to1);
-      return _needsImplicitCast(expr, to1, from: from) == true ||
-          _needsImplicitCast(expr, to2, from: from) == true;
+      return _needsImplicitCast(expr, to: to1, from: from) == true ||
+          _needsImplicitCast(expr, to: to2, from: from) == true;
     }
 
     // Anything else is an illegal sideways cast.
@@ -1021,7 +1015,7 @@
     var sequenceElement = awaitKeyword != null
         ? typeProvider.streamElement
         : typeProvider.iterableElement;
-    var iterableType = _getExpressionType(node.iterable);
+    var iterableType = node.iterable.staticType;
     var elementType = _getInstanceTypeArgument(iterableType, sequenceElement);
 
     // If the sequence is not an Iterable (or Stream for await for) but is a
@@ -1043,8 +1037,8 @@
     if (elementType != null) {
       // Insert a cast from the sequence's element type to the loop variable's
       // if needed.
-      _checkImplicitCast(loopVariable, loopVariableElement.type,
-          from: elementType);
+      _checkImplicitCast(loopVariable,
+          to: loopVariableElement.type, from: elementType);
     }
   }
 }
diff --git a/pkg/analyzer/test/src/dart/resolution/assignment_test.dart b/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
index 0c06671..155270f 100644
--- a/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/assignment_test.dart
@@ -466,21 +466,14 @@
   }
 
   test_notLValue_postfixIncrement_compound_ifNull() async {
-    await assertErrorsInCode(
-      '''
+    await assertErrorsInCode('''
 void f(num x, int y) {
   x++ ??= y;
 }
-''',
-      expectedErrorsByNullability(nullable: [
-        error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 25, 3),
-        error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 25, 3),
-        error(StaticWarningCode.DEAD_NULL_AWARE_EXPRESSION, 33, 1),
-      ], legacy: [
-        error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 25, 3),
-        error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 25, 3),
-      ]),
-    );
+''', [
+      error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 25, 3),
+      error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 25, 3),
+    ]);
 
     assertAssignment(
       findNode.assignment('= y'),
@@ -557,21 +550,14 @@
   }
 
   test_notLValue_prefixIncrement_compound_ifNull() async {
-    await assertErrorsInCode(
-      '''
+    await assertErrorsInCode('''
 void f(num x, int y) {
   ++x ??= y;
 }
-''',
-      expectedErrorsByNullability(nullable: [
-        error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 25, 3),
-        error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 25, 3),
-        error(StaticWarningCode.DEAD_NULL_AWARE_EXPRESSION, 33, 1),
-      ], legacy: [
-        error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 25, 3),
-        error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 25, 3),
-      ]),
-    );
+''', [
+      error(ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE, 25, 3),
+      error(ParserErrorCode.MISSING_ASSIGNABLE_SELECTOR, 25, 3),
+    ]);
 
     assertAssignment(
       findNode.assignment('= y'),