Fix the special casing of binary operators with primitive types when NNBD is enabled

Change-Id: I36f7f63b26eb3ea87efbe7af96d2134ea6402158
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102020
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index 14f6958..8eff525 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -41,6 +41,11 @@
   final ResolverVisitor _resolver;
 
   /**
+   * The feature set that should be used to resolve types.
+   */
+  final FeatureSet _featureSet;
+
+  /**
    * The object providing access to the types defined by the language.
    */
   TypeProvider _typeProvider;
@@ -72,16 +77,12 @@
   TypePromotionManager _promoteManager;
 
   /**
-   * Whether NNBD is enabled for this compilation unit.
-   */
-  bool _nonNullableEnabled;
-
-  /**
-   * Initialize a newly created type analyzer.
+   * Initialize a newly created static type analyzer to analyze types for the
+   * [_resolver] based on the
    *
    * @param resolver the resolver driving this participant
    */
-  StaticTypeAnalyzer(this._resolver, FeatureSet featureSet) {
+  StaticTypeAnalyzer(this._resolver, this._featureSet) {
     _typeProvider = _resolver.typeProvider;
     _typeSystem = _resolver.typeSystem;
     _dynamicType = _typeProvider.dynamicType;
@@ -89,10 +90,14 @@
     AnalysisOptionsImpl analysisOptions =
         _resolver.definingLibrary.context.analysisOptions;
     _strictInference = analysisOptions.strictInference;
-    _nonNullableEnabled = featureSet.isEnabled(Feature.non_nullable);
   }
 
   /**
+   * Return `true` if NNBD is enabled for this compilation unit.
+   */
+  bool get _nonNullableEnabled => _featureSet.isEnabled(Feature.non_nullable);
+
+  /**
    * Given a constructor name [node] and a type [type], record an inferred type
    * for the constructor if in strong mode. This is used to fill in any
    * inferred type parameters found by the resolver.
@@ -318,7 +323,8 @@
           _getStaticType(node.leftHandSide, read: true),
           operator,
           node.rightHandSide.staticType,
-          staticType);
+          staticType,
+          _featureSet);
       _recordStaticType(node, staticType);
     }
   }
@@ -409,7 +415,8 @@
         node.leftOperand.staticType,
         node.operator.type,
         node.rightOperand.staticType,
-        staticType);
+        staticType,
+        _featureSet);
     _recordStaticType(node, staticType);
   }
 
diff --git a/pkg/analyzer/lib/src/generated/type_system.dart b/pkg/analyzer/lib/src/generated/type_system.dart
index a774f85..4ef1a61 100644
--- a/pkg/analyzer/lib/src/generated/type_system.dart
+++ b/pkg/analyzer/lib/src/generated/type_system.dart
@@ -5,6 +5,7 @@
 import 'dart:collection';
 import 'dart:math' as math;
 
+import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart' show AstNode;
 import 'package:analyzer/dart/ast/token.dart' show Keyword, TokenType;
 import 'package:analyzer/dart/element/element.dart';
@@ -648,7 +649,7 @@
 
   @override
   DartType refineBinaryExpressionType(DartType leftType, TokenType operator,
-      DartType rightType, DartType currentType) {
+      DartType rightType, DartType currentType, FeatureSet featureSet) {
     if (leftType is TypeParameterType &&
         leftType.element.bound == typeProvider.numType) {
       if (rightType == leftType || rightType == typeProvider.intType) {
@@ -658,6 +659,9 @@
             operator == TokenType.PLUS_EQ ||
             operator == TokenType.MINUS_EQ ||
             operator == TokenType.STAR_EQ) {
+          if (featureSet.isEnabled(Feature.non_nullable)) {
+            return promoteToNonNull(leftType as TypeImpl);
+          }
           return leftType;
         }
       }
@@ -666,13 +670,16 @@
             operator == TokenType.MINUS ||
             operator == TokenType.STAR ||
             operator == TokenType.SLASH) {
+          if (featureSet.isEnabled(Feature.non_nullable)) {
+            return promoteToNonNull(typeProvider.doubleType as TypeImpl);
+          }
           return typeProvider.doubleType;
         }
       }
       return currentType;
     }
-    return super
-        .refineBinaryExpressionType(leftType, operator, rightType, currentType);
+    return super.refineBinaryExpressionType(
+        leftType, operator, rightType, currentType, featureSet);
   }
 
   @override
@@ -2088,16 +2095,22 @@
   }
 
   /**
-   * Attempts to make a better guess for the type of a binary with the given
-   * [operator], given that resolution has so far produced the [currentType].
+   * Determine the type of a binary expression with the given [operator] whose
+   * left operand has the type [leftType] and whose right operand has the type
+   * [rightType], given that resolution has so far produced the [currentType].
+   * The [featureSet] is used to determine whether any features that effect the
+   * computation have been enabled.
    */
   DartType refineBinaryExpressionType(DartType leftType, TokenType operator,
-      DartType rightType, DartType currentType) {
+      DartType rightType, DartType currentType, FeatureSet featureSet) {
     // bool
     if (operator == TokenType.AMPERSAND_AMPERSAND ||
         operator == TokenType.BAR_BAR ||
         operator == TokenType.EQ_EQ ||
         operator == TokenType.BANG_EQ) {
+      if (featureSet.isEnabled(Feature.non_nullable)) {
+        return promoteToNonNull(typeProvider.boolType as TypeImpl);
+      }
       return typeProvider.boolType;
     }
     DartType intType = typeProvider.intType;
@@ -2113,6 +2126,9 @@
           operator == TokenType.STAR_EQ) {
         DartType doubleType = typeProvider.doubleType;
         if (rightType == doubleType) {
+          if (featureSet.isEnabled(Feature.non_nullable)) {
+            return promoteToNonNull(doubleType as TypeImpl);
+          }
           return doubleType;
         }
       }
@@ -2128,6 +2144,9 @@
           operator == TokenType.STAR_EQ ||
           operator == TokenType.TILDE_SLASH_EQ) {
         if (rightType == intType) {
+          if (featureSet.isEnabled(Feature.non_nullable)) {
+            return promoteToNonNull(intType as TypeImpl);
+          }
           return intType;
         }
       }
diff --git a/pkg/analyzer/lib/src/task/strong/checker.dart b/pkg/analyzer/lib/src/task/strong/checker.dart
index 0708ecf..e451ec6 100644
--- a/pkg/analyzer/lib/src/task/strong/checker.dart
+++ b/pkg/analyzer/lib/src/task/strong/checker.dart
@@ -6,6 +6,7 @@
 // refactored to fit into analyzer.
 import 'dart:collection';
 
+import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/standard_resolution_map.dart';
 import 'package:analyzer/dart/ast/token.dart' show TokenType;
@@ -123,6 +124,8 @@
   final AnalysisOptionsImpl _options;
   _OverrideChecker _overrideChecker;
 
+  FeatureSet _featureSet;
+
   bool _failure = false;
   bool _hasImplicitCasts;
   HashSet<ExecutableElement> _covariantPrivateMembers;
@@ -334,6 +337,7 @@
 
   @override
   void visitCompilationUnit(CompilationUnit node) {
+    _featureSet = node.featureSet;
     _hasImplicitCasts = false;
     _covariantPrivateMembers = new HashSet();
     node.visitChildren(this);
@@ -733,7 +737,7 @@
       var rhsType = _getExpressionType(expr.rightHandSide);
       var lhsType = _getExpressionType(expr.leftHandSide);
       var returnType = rules.refineBinaryExpressionType(
-          lhsType, op, rhsType, functionType.returnType);
+          lhsType, op, rhsType, functionType.returnType, _featureSet);
 
       // Check the argument for an implicit cast.
       _checkImplicitCast(expr.rightHandSide, paramTypes[0], from: rhsType);
@@ -958,8 +962,8 @@
         var functionType = element.type;
         var rhsType = typeProvider.intType;
         var lhsType = _getExpressionType(operand);
-        var returnType = rules.refineBinaryExpressionType(
-            lhsType, TokenType.PLUS, rhsType, functionType.returnType);
+        var returnType = rules.refineBinaryExpressionType(lhsType,
+            TokenType.PLUS, rhsType, functionType.returnType, _featureSet);
 
         // Skip the argument check - `int` cannot be downcast.
         //
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/equality_expressions_test.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/equality_expressions_test.dart
index c78c359..d0bf2ff 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_inference/equality_expressions_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/equality_expressions_test.dart
@@ -40,12 +40,6 @@
 
   @override
   bool get typeToStringWithNullability => true;
-
-  @failingTest
-  @override
-  test_simple() async {
-    await super.test_simple();
-  }
 }
 
 @reflectiveTest
@@ -71,10 +65,4 @@
 
   @override
   bool get typeToStringWithNullability => true;
-
-  @failingTest
-  @override
-  test_simple() async {
-    await super.test_simple();
-  }
 }
diff --git a/pkg/analyzer/test/src/dart/resolution/type_inference/logical_boolean_expressions_test.dart b/pkg/analyzer/test/src/dart/resolution/type_inference/logical_boolean_expressions_test.dart
index 3c2e6ae..7f26d21 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_inference/logical_boolean_expressions_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_inference/logical_boolean_expressions_test.dart
@@ -40,12 +40,6 @@
 
   @override
   bool get typeToStringWithNullability => true;
-
-  @failingTest
-  @override
-  test_simple() async {
-    await super.test_simple();
-  }
 }
 
 @reflectiveTest
@@ -71,10 +65,4 @@
 
   @override
   bool get typeToStringWithNullability => true;
-
-  @failingTest
-  @override
-  test_simple() async {
-    await super.test_simple();
-  }
 }