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 {