Migration: Add support for generic function types.
Fixes #37212.
Change-Id: I3dfe0afa32564b9e4762f3410f5c44ea42d6d004
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105467
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/nnbd_migration/lib/src/decorated_type.dart b/pkg/nnbd_migration/lib/src/decorated_type.dart
index 8e42f80..a740c1c 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type.dart
@@ -164,8 +164,15 @@
DecoratedTypeAnnotation(
DartType type, NullabilityNode nullabilityNode, this._offset,
- {List<DecoratedType> typeArguments = const []})
- : super(type, nullabilityNode, typeArguments: typeArguments);
+ {List<DecoratedType> typeArguments = const [],
+ DecoratedType returnType,
+ List<DecoratedType> positionalParameters = const [],
+ Map<String, DecoratedType> namedParameters = const {}})
+ : super(type, nullabilityNode,
+ typeArguments: typeArguments,
+ returnType: returnType,
+ positionalParameters: positionalParameters,
+ namedParameters: namedParameters);
@override
bool get isEmpty => !node.isNullable;
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart
index 316ab10..e7c5778 100644
--- a/pkg/nnbd_migration/lib/src/node_builder.dart
+++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -130,7 +130,6 @@
@override
DecoratedType visitTypeAnnotation(TypeAnnotation node) {
assert(node != null); // TODO(paulberry)
- assert(node is NamedType); // TODO(paulberry)
var type = node.type;
if (type.isVoid || type.isDynamic) {
var nullabilityNode = NullabilityNode.forTypeAnnotation(node.end);
@@ -141,9 +140,10 @@
potentialModification: false);
return decoratedType;
}
- assert(
- type is InterfaceType || type is TypeParameterType); // TODO(paulberry)
var typeArguments = const <DecoratedType>[];
+ DecoratedType returnType;
+ var positionalParameters = const <DecoratedType>[];
+ var namedParameters = const <String, DecoratedType>{};
if (type is InterfaceType && type.typeParameters.isNotEmpty) {
if (node is TypeName) {
assert(node.typeArguments != null);
@@ -153,10 +153,30 @@
assert(false); // TODO(paulberry): is this possible?
}
}
+ if (node is GenericFunctionType) {
+ returnType = decorateType(node.returnType, node);
+ if (node.typeParameters != null) {
+ throw UnimplementedError('TODO(paulberry)');
+ }
+ positionalParameters = <DecoratedType>[];
+ namedParameters = <String, DecoratedType>{};
+ }
var decoratedType = DecoratedTypeAnnotation(
type, NullabilityNode.forTypeAnnotation(node.end), node.end,
- typeArguments: typeArguments);
+ typeArguments: typeArguments,
+ returnType: returnType,
+ positionalParameters: positionalParameters,
+ namedParameters: namedParameters);
_variables.recordDecoratedTypeAnnotation(_source, node, decoratedType);
+ if (node is GenericFunctionType) {
+ var previousFunctionType = _currentFunctionType;
+ try {
+ _currentFunctionType = decoratedType;
+ node.parameters.accept(this);
+ } finally {
+ _currentFunctionType = previousFunctionType;
+ }
+ }
switch (_classifyComment(node.endToken.next.precedingComments)) {
case _NullabilityComment.bang:
_graph.connect(decoratedType.node, NullabilityNode.never, hard: true);
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 8a15fbf..ca633bf 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -920,6 +920,20 @@
await _checkSingleFileChanges(content, expected);
}
+ test_parameter_genericFunctionType() async {
+ var content = '''
+int f(int x, int Function(int i) g) {
+ return g(x);
+}
+''';
+ var expected = '''
+int f(int x, int Function(int i) g) {
+ return g(x);
+}
+''';
+ await _checkSingleFileChanges(content, expected);
+ }
+
test_single_file_multiple_changes() async {
var content = '''
int f() => null;
diff --git a/pkg/nnbd_migration/test/migration_visitor_test.dart b/pkg/nnbd_migration/test/migration_visitor_test.dart
index 04e624f..27969b5 100644
--- a/pkg/nnbd_migration/test/migration_visitor_test.dart
+++ b/pkg/nnbd_migration/test/migration_visitor_test.dart
@@ -1227,6 +1227,13 @@
}
}
+ /// Gets the [DecoratedType] associated with the generic function type
+ /// annotation whose text is [text].
+ DecoratedType decoratedGenericFunctionTypeAnnotation(String text) {
+ return _variables.decoratedTypeAnnotation(
+ testSource, findNode.genericFunctionType(text));
+ }
+
/// Gets the [DecoratedType] associated with the type annotation whose text
/// is [text].
DecoratedType decoratedTypeAnnotation(String text) {
@@ -1287,6 +1294,51 @@
same(decoratedType));
}
+ test_genericFunctionType_namedParameterType() async {
+ await analyze('''
+void f(void Function({int y}) x) {}
+''');
+ var decoratedType =
+ decoratedGenericFunctionTypeAnnotation('void Function({int y})');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedType));
+ expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+ var decoratedIntType = decoratedTypeAnnotation('int');
+ expect(decoratedType.namedParameters['y'], same(decoratedIntType));
+ expect(decoratedIntType.node, isNotNull);
+ expect(decoratedIntType.node, isNot(NullabilityNode.never));
+ }
+
+ test_genericFunctionType_returnType() async {
+ await analyze('''
+void f(int Function() x) {}
+''');
+ var decoratedType =
+ decoratedGenericFunctionTypeAnnotation('int Function()');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedType));
+ expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+ var decoratedIntType = decoratedTypeAnnotation('int');
+ expect(decoratedType.returnType, same(decoratedIntType));
+ expect(decoratedIntType.node, isNotNull);
+ expect(decoratedIntType.node, isNot(NullabilityNode.never));
+ }
+
+ test_genericFunctionType_unnamedParameterType() async {
+ await analyze('''
+void f(void Function(int) x) {}
+''');
+ var decoratedType =
+ decoratedGenericFunctionTypeAnnotation('void Function(int)');
+ expect(decoratedFunctionType('f').positionalParameters[0],
+ same(decoratedType));
+ expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+ var decoratedIntType = decoratedTypeAnnotation('int');
+ expect(decoratedType.positionalParameters[0], same(decoratedIntType));
+ expect(decoratedIntType.node, isNotNull);
+ expect(decoratedIntType.node, isNot(NullabilityNode.never));
+ }
+
test_interfaceType_typeParameter() async {
await analyze('''
void f(List<int> x) {}