Don't force target type equality for null-aware extension method calls
Null-aware calls, like `v?.extMethod()` don't require `v` to be
non-nullable, even if `extMethod` was declared on a non-nullable type.
Tested: added a new test.
Change-Id: I113ecde21e480bcd467367d0954b721c2ad9f7e0
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/301982
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index a4f4b8e..18d0d99 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -281,7 +281,9 @@
/// [targetExpression], if provided, is the expression for the target
/// (receiver) for a method, getter, or setter invocation.
DecoratedType getOrComputeElementType(AstNode node, Element element,
- {DecoratedType? targetType, Expression? targetExpression}) {
+ {DecoratedType? targetType,
+ Expression? targetExpression,
+ bool isNullAware = false}) {
Map<TypeParameterElement, DecoratedType>? substitution;
Element? baseElement = element.declaration;
if (targetType != null) {
@@ -347,8 +349,9 @@
FixReasonTarget.root,
source: targetType,
destination: onType,
- hard: targetExpression == null ||
- _isReferenceInScope(targetExpression));
+ hard: !isNullAware &&
+ (targetExpression == null ||
+ _isReferenceInScope(targetExpression)));
// (3) substitute those decorated types into the declared type of the
// extension method or extension property, so that the caller will match
// up parameter types and the return type appropriately.
@@ -1444,7 +1447,9 @@
calleeType = targetType;
} else if (callee != null) {
calleeType = getOrComputeElementType(node, callee,
- targetType: targetType, targetExpression: target);
+ targetType: targetType,
+ targetExpression: target,
+ isNullAware: isNullAware);
if (callee is PropertyAccessorElement) {
calleeType = calleeType.returnType;
}
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 736f517..d3e3738 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -1183,6 +1183,32 @@
});
}
+ Future<void> test_call_already_migrated_extension_null_aware() async {
+ var content = '''
+import 'already_migrated.dart';
+class C {
+ X m(V v) => v?.toX();
+}
+''';
+ var alreadyMigrated = '''
+// @dart=2.12
+class X {}
+class V {}
+extension Ext on V {
+ X toX() => X();
+}
+''';
+ var expected = '''
+import 'already_migrated.dart';
+class C {
+ X? m(V? v) => v?.toX();
+}
+''';
+ await _checkSingleFileChanges(content, expected, migratedInput: {
+ '$projectPath/lib/already_migrated.dart': alreadyMigrated
+ });
+ }
+
Future<void> test_call_generic_function_returns_generic_class() async {
var content = '''
class B<E> implements List<E/*?*/> {