Migration: add the ability to union decorated function types.

Change-Id: Ife92ab951ee0a60edea12c0d1fe4f242135a8ae3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107440
Reviewed-by: Konstantin Shcheglov <scheglov@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 5656737..e3f31e6 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type.dart
@@ -119,7 +119,7 @@
             DecoratedType.forImplicitType(parameter.type, graph);
       }
     }
-    return DecoratedType(type, graph.never,
+    return DecoratedType(type, node,
         returnType:
             returnType ?? DecoratedType.forImplicitType(type.returnType, graph),
         namedParameters: namedParameters,
@@ -137,6 +137,9 @@
           typeArguments: type.typeArguments
               .map((t) => DecoratedType.forImplicitType(t, graph))
               .toList());
+    } else if (type is FunctionType) {
+      return DecoratedType.forImplicitFunction(
+          type, NullabilityNode.forInferredType(), graph);
     }
     // TODO(paulberry)
     throw UnimplementedError(
diff --git a/pkg/nnbd_migration/lib/src/edge_builder.dart b/pkg/nnbd_migration/lib/src/edge_builder.dart
index cf7d441..f72b3c5 100644
--- a/pkg/nnbd_migration/lib/src/edge_builder.dart
+++ b/pkg/nnbd_migration/lib/src/edge_builder.dart
@@ -1101,9 +1101,8 @@
         i++) {
       _unionDecoratedTypes(x.typeArguments[i], y.typeArguments[i], origin);
     }
-    if (x.returnType != null || y.returnType != null) {
-      // TODO(paulberry)
-      throw UnimplementedError('_unionDecoratedTypes($x, $y, $origin)');
+    if (x.returnType != null && y.returnType != null) {
+      _unionDecoratedTypes(x.returnType, y.returnType, origin);
     }
   }
 }
diff --git a/pkg/nnbd_migration/test/edge_builder_test.dart b/pkg/nnbd_migration/test/edge_builder_test.dart
index 046dcef..a664f07 100644
--- a/pkg/nnbd_migration/test/edge_builder_test.dart
+++ b/pkg/nnbd_migration/test/edge_builder_test.dart
@@ -2056,6 +2056,17 @@
     assertUnion(cType.typeArguments[0].node, cBound.node);
   }
 
+  test_typeName_union_with_bound_function_type() async {
+    await analyze('''
+class C<T extends int Function()> {}
+void f(C c) {}
+''');
+    var cType = decoratedTypeAnnotation('C c');
+    var cBound = decoratedGenericFunctionTypeAnnotation('int Function()');
+    assertUnion(cType.typeArguments[0].node, cBound.node);
+    assertUnion(cType.typeArguments[0].returnType.node, cBound.returnType.node);
+  }
+
   test_typeName_union_with_bounds() async {
     await analyze('''
 class C<T extends Object, U extends Object> {}
diff --git a/pkg/nnbd_migration/test/node_builder_test.dart b/pkg/nnbd_migration/test/node_builder_test.dart
index 055aee1..abca42e 100644
--- a/pkg/nnbd_migration/test/node_builder_test.dart
+++ b/pkg/nnbd_migration/test/node_builder_test.dart
@@ -403,6 +403,24 @@
     expect(decoratedArgType.node, same(always));
   }
 
+  test_interfaceType_generic_instantiate_to_function_type() async {
+    await analyze('''
+class C<T extends int Function()> {}
+void f(C x) {}
+''');
+    var decoratedCType = decoratedTypeAnnotation('C x');
+    expect(decoratedFunctionType('f').positionalParameters[0],
+        same(decoratedCType));
+    expect(decoratedCType.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedCType.typeArguments, hasLength(1));
+    var decoratedArgType = decoratedCType.typeArguments[0];
+    expect(decoratedArgType.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArgType.typeArguments, isEmpty);
+    var decoratedArgReturnType = decoratedArgType.returnType;
+    expect(decoratedArgReturnType.node, TypeMatcher<NullabilityNodeMutable>());
+    expect(decoratedArgReturnType.typeArguments, isEmpty);
+  }
+
   test_interfaceType_generic_instantiate_to_generic_type() async {
     await analyze('''
 class C<T> {}