Migration: propagate nullability inference through override relationships: parameter types.

Change-Id: Ib41ffa388af9688efc59aea2c196d8fdcf713ad8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107101
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Dan Rubel <danrubel@google.com>
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index 6688d6c..d690038 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -860,13 +860,22 @@
           destination.typeArguments.isNotEmpty) {
         throw UnimplementedError('TODO(paulberry)');
       }
-      if (source.positionalParameters.isNotEmpty ||
-          destination.positionalParameters.isNotEmpty) {
-        throw UnimplementedError('TODO(paulberry)');
+      for (int i = 0;
+          i < source.positionalParameters.length &&
+              i < destination.positionalParameters.length;
+          i++) {
+        // Note: source and destination are swapped due to contravariance.
+        _checkAssignment(expressionChecks,
+            source: destination.positionalParameters[i],
+            destination: source.positionalParameters[i],
+            hard: hard);
       }
-      if (source.namedParameters.isNotEmpty ||
-          destination.namedParameters.isNotEmpty) {
-        throw UnimplementedError('TODO(paulberry)');
+      for (var entry in destination.namedParameters.entries) {
+        // Note: source and destination are swapped due to contravariance.
+        _checkAssignment(expressionChecks,
+            source: entry.value,
+            destination: source.namedParameters[entry.key],
+            hard: hard);
       }
     } else if (destination.type.isDynamic || source.type.isDynamic) {
       // ok; nothing further to do.
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 90f1633..27d1287 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -1043,6 +1043,72 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  test_override_parameter_type_non_nullable() async {
+    var content = '''
+abstract class Base {
+  void f(int i);
+}
+class Derived extends Base {
+  void f(int i) {
+    assert(i != null);
+  }
+}
+void g(int i, bool b, Base base) {
+  if (b) {
+    base.f(i);
+  }
+}
+void h(Base base) {
+  g(null, false, base);
+}
+''';
+    var expected = '''
+abstract class Base {
+  void f(int i);
+}
+class Derived extends Base {
+  void f(int i) {
+    assert(i != null);
+  }
+}
+void g(int? i, bool b, Base base) {
+  if (b) {
+    base.f(i!);
+  }
+}
+void h(Base base) {
+  g(null, false, base);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
+  test_override_parameter_type_nullable() async {
+    var content = '''
+abstract class Base {
+  void f(int i);
+}
+class Derived extends Base {
+  void f(int i) {}
+}
+void g(int i, Base base) {
+  base.f(null);
+}
+''';
+    var expected = '''
+abstract class Base {
+  void f(int? i);
+}
+class Derived extends Base {
+  void f(int? i) {}
+}
+void g(int i, Base base) {
+  base.f(null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   test_override_return_type_non_nullable() async {
     var content = '''
 abstract class Base {
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index 6580d6d..72cbff6 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -1274,6 +1274,114 @@
     expect(never.isNullable, isFalse);
   }
 
+  test_override_parameter_type_named() async {
+    await analyze('''
+abstract class Base {
+  void f({int/*1*/ i});
+}
+class Derived extends Base {
+  void f({int/*2*/ i}) {}
+}
+''');
+    var int1 = decoratedTypeAnnotation('int/*1*/');
+    var int2 = decoratedTypeAnnotation('int/*2*/');
+    assertEdge(int1.node, int2.node, hard: true);
+  }
+
+  test_override_parameter_type_named_over_none() async {
+    await analyze('''
+abstract class Base {
+  void f();
+}
+class Derived extends Base {
+  void f({int i}) {}
+}
+''');
+    // No assertions; just checking that it doesn't crash.
+  }
+
+  test_override_parameter_type_operator() async {
+    await analyze('''
+abstract class Base {
+  Base operator+(Base/*1*/ b);
+}
+class Derived extends Base {
+  Base operator+(Base/*2*/ b) => this;
+}
+''');
+    var base1 = decoratedTypeAnnotation('Base/*1*/');
+    var base2 = decoratedTypeAnnotation('Base/*2*/');
+    assertEdge(base1.node, base2.node, hard: true);
+  }
+
+  test_override_parameter_type_optional() async {
+    await analyze('''
+abstract class Base {
+  void f([int/*1*/ i]);
+}
+class Derived extends Base {
+  void f([int/*2*/ i]) {}
+}
+''');
+    var int1 = decoratedTypeAnnotation('int/*1*/');
+    var int2 = decoratedTypeAnnotation('int/*2*/');
+    assertEdge(int1.node, int2.node, hard: true);
+  }
+
+  test_override_parameter_type_optional_over_none() async {
+    await analyze('''
+abstract class Base {
+  void f();
+}
+class Derived extends Base {
+  void f([int i]) {}
+}
+''');
+    // No assertions; just checking that it doesn't crash.
+  }
+
+  test_override_parameter_type_optional_over_required() async {
+    await analyze('''
+abstract class Base {
+  void f(int/*1*/ i);
+}
+class Derived extends Base {
+  void f([int/*2*/ i]) {}
+}
+''');
+    var int1 = decoratedTypeAnnotation('int/*1*/');
+    var int2 = decoratedTypeAnnotation('int/*2*/');
+    assertEdge(int1.node, int2.node, hard: true);
+  }
+
+  test_override_parameter_type_required() async {
+    await analyze('''
+abstract class Base {
+  void f(int/*1*/ i);
+}
+class Derived extends Base {
+  void f(int/*2*/ i) {}
+}
+''');
+    var int1 = decoratedTypeAnnotation('int/*1*/');
+    var int2 = decoratedTypeAnnotation('int/*2*/');
+    assertEdge(int1.node, int2.node, hard: true);
+  }
+
+  test_override_parameter_type_setter() async {
+    await analyze('''
+abstract class Base {
+  void set x(int/*1*/ value);
+}
+class Derived extends Base {
+  void set x(int/*2*/ value) {}
+}
+''');
+    var int1 = decoratedTypeAnnotation('int/*1*/');
+    var int2 = decoratedTypeAnnotation('int/*2*/');
+    assertEdge(int1.node, int2.node, hard: true);
+  }
+
   test_override_return_type_getter() async {
     await analyze('''
 abstract class Base {