Support the remaining user-defined binary operators

Change-Id: I796495489605a903330169ff964a3ed296b2783a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106722
Reviewed-by: Dan Rubel <danrubel@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/nnbd_migration/lib/src/graph_builder.dart b/pkg/nnbd_migration/lib/src/graph_builder.dart
index 0bd79a9..30029c3 100644
--- a/pkg/nnbd_migration/lib/src/graph_builder.dart
+++ b/pkg/nnbd_migration/lib/src/graph_builder.dart
@@ -195,43 +195,41 @@
 
   @override
   DecoratedType visitBinaryExpression(BinaryExpression node) {
-    switch (node.operator.type) {
-      case TokenType.EQ_EQ:
-      case TokenType.BANG_EQ:
-        assert(node.leftOperand is! NullLiteral); // TODO(paulberry)
-        var leftType = node.leftOperand.accept(this);
-        node.rightOperand.accept(this);
-        if (node.rightOperand is NullLiteral) {
-          // TODO(paulberry): figure out what the rules for isPure should be.
-          // TODO(paulberry): only set falseChecksNonNull in unconditional
-          // control flow
-          bool isPure = node.leftOperand is SimpleIdentifier;
-          var conditionInfo = _ConditionInfo(node,
-              isPure: isPure,
-              trueGuard: leftType.node,
-              falseDemonstratesNonNullIntent: leftType.node);
-          _conditionInfo = node.operator.type == TokenType.EQ_EQ
-              ? conditionInfo
-              : conditionInfo.not(node);
-        }
-        return _nonNullableBoolType;
-      case TokenType.PLUS:
-        _handleAssignment(_notNullType, node.leftOperand);
-        var callee = node.staticElement;
-        assert(!(callee is ClassMemberElement &&
-            callee.enclosingElement.typeParameters
-                .isNotEmpty)); // TODO(paulberry)
-        assert(callee != null); // TODO(paulberry)
-        var calleeType = getOrComputeElementType(callee);
-        // TODO(paulberry): substitute if necessary
-        assert(calleeType.positionalParameters.length > 0); // TODO(paulberry)
-        _handleAssignment(
-            calleeType.positionalParameters[0], node.rightOperand);
-        return calleeType.returnType;
-      default:
-        // TODO(paulberry)
-        _unimplemented(
-            node, 'Binary expression with operator ${node.operator.lexeme}');
+    var operatorType = node.operator.type;
+    if (operatorType == TokenType.EQ_EQ || operatorType == TokenType.BANG_EQ) {
+      assert(node.leftOperand is! NullLiteral); // TODO(paulberry)
+      var leftType = node.leftOperand.accept(this);
+      node.rightOperand.accept(this);
+      if (node.rightOperand is NullLiteral) {
+        // TODO(paulberry): figure out what the rules for isPure should be.
+        // TODO(paulberry): only set falseChecksNonNull in unconditional
+        // control flow
+        bool isPure = node.leftOperand is SimpleIdentifier;
+        var conditionInfo = _ConditionInfo(node,
+            isPure: isPure,
+            trueGuard: leftType.node,
+            falseDemonstratesNonNullIntent: leftType.node);
+        _conditionInfo = operatorType == TokenType.EQ_EQ
+            ? conditionInfo
+            : conditionInfo.not(node);
+      }
+      return _nonNullableBoolType;
+    } else if (operatorType.isUserDefinableOperator) {
+      _handleAssignment(_notNullType, node.leftOperand);
+      var callee = node.staticElement;
+      assert(!(callee is ClassMemberElement &&
+          callee
+              .enclosingElement.typeParameters.isNotEmpty)); // TODO(paulberry)
+      assert(callee != null); // TODO(paulberry)
+      var calleeType = getOrComputeElementType(callee);
+      // TODO(paulberry): substitute if necessary
+      assert(calleeType.positionalParameters.length > 0); // TODO(paulberry)
+      _handleAssignment(calleeType.positionalParameters[0], node.rightOperand);
+      return calleeType.returnType;
+    } else {
+      // TODO(paulberry)
+      _unimplemented(
+          node, 'Binary expression with operator ${node.operator.lexeme}');
     }
   }
 
diff --git a/pkg/nnbd_migration/test/graph_builder_test.dart b/pkg/nnbd_migration/test/graph_builder_test.dart
index 71cd99b..0bc9ba1 100644
--- a/pkg/nnbd_migration/test/graph_builder_test.dart
+++ b/pkg/nnbd_migration/test/graph_builder_test.dart
@@ -286,7 +286,111 @@
     assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
   }
 
-  test_binaryExpression_add_left_check() async {
+  test_binaryExpression_ampersand_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i & j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_bar_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i | j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_caret_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i ^ j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_equal() async {
+    await analyze('''
+bool f(int i, int j) => i == j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_gt_result_not_null() async {
+    await analyze('''
+bool f(int i, int j) => i > j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_gtEq_result_not_null() async {
+    await analyze('''
+bool f(int i, int j) => i >= j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_gtGt_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i >> j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_lt_result_not_null() async {
+    await analyze('''
+bool f(int i, int j) => i < j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_ltEq_result_not_null() async {
+    await analyze('''
+bool f(int i, int j) => i <= j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_ltLt_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i << j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_minus_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i - j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_notEqual() async {
+    await analyze('''
+bool f(int i, int j) => i != j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+  }
+
+  test_binaryExpression_percent_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i % j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_plus_left_check() async {
     await analyze('''
 int f(int i, int j) => i + j;
 ''');
@@ -295,7 +399,7 @@
         assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true));
   }
 
-  test_binaryExpression_add_left_check_custom() async {
+  test_binaryExpression_plus_left_check_custom() async {
     await analyze('''
 class Int {
   Int operator+(Int other) => this;
@@ -307,7 +411,7 @@
         assertEdge(decoratedTypeAnnotation('Int i').node, never, hard: true));
   }
 
-  test_binaryExpression_add_result_custom() async {
+  test_binaryExpression_plus_result_custom() async {
     await analyze('''
 class Int {
   Int operator+(Int other) => this;
@@ -322,7 +426,7 @@
             hard: false));
   }
 
-  test_binaryExpression_add_result_not_null() async {
+  test_binaryExpression_plus_result_not_null() async {
     await analyze('''
 int f(int i, int j) => i + j;
 ''');
@@ -330,7 +434,7 @@
     assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
   }
 
-  test_binaryExpression_add_right_check() async {
+  test_binaryExpression_plus_right_check() async {
     await analyze('''
 int f(int i, int j) => i + j;
 ''');
@@ -339,7 +443,7 @@
         assertEdge(decoratedTypeAnnotation('int j').node, never, hard: true));
   }
 
-  test_binaryExpression_add_right_check_custom() async {
+  test_binaryExpression_plus_right_check_custom() async {
     await analyze('''
 class Int {
   Int operator+(Int other) => this;
@@ -354,12 +458,28 @@
             hard: true));
   }
 
-  test_binaryExpression_equal() async {
+  test_binaryExpression_slash_result_not_null() async {
     await analyze('''
-bool f(int i, int j) => i == j;
+double f(int i, int j) => i / j;
 ''');
 
-    assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
+    assertNoUpstreamNullability(decoratedTypeAnnotation('double f').node);
+  }
+
+  test_binaryExpression_star_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i * j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
+  }
+
+  test_binaryExpression_tildeSlash_result_not_null() async {
+    await analyze('''
+int f(int i, int j) => i ~/ j;
+''');
+
+    assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
   }
 
   test_boolLiteral() async {