Migration: start adding support for assignment of function types.

So far we only handle zero-parameter non-generic functions.  Support
for positional parameters, named parameters, and type parameters will
be added in future CLs.

Change-Id: I5a5907ba595da40352193e0193f908bca4d1bf1c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106040
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/nnbd_migration/lib/src/graph_builder.dart b/pkg/nnbd_migration/lib/src/graph_builder.dart
index 96097494..aee7dcf 100644
--- a/pkg/nnbd_migration/lib/src/graph_builder.dart
+++ b/pkg/nnbd_migration/lib/src/graph_builder.dart
@@ -686,7 +686,8 @@
   DecoratedType visitSimpleIdentifier(SimpleIdentifier node) {
     var staticElement = node.staticElement;
     if (staticElement is ParameterElement ||
-        staticElement is LocalVariableElement) {
+        staticElement is LocalVariableElement ||
+        staticElement is FunctionElement) {
       return getOrComputeElementType(staticElement);
     } else if (staticElement is PropertyAccessorElement) {
       var elementType = getOrComputeElementType(staticElement);
@@ -804,6 +805,23 @@
             sourceType.typeArguments[i], expressionChecks,
             hard: false);
       }
+    } else if (sourceType.type is FunctionType &&
+        destinationType.type is FunctionType) {
+      _checkAssignment(
+          destinationType.returnType, sourceType.returnType, expressionChecks,
+          hard: hard);
+      if (sourceType.typeArguments.isNotEmpty ||
+          destinationType.typeArguments.isNotEmpty) {
+        throw UnimplementedError('TODO(paulberry)');
+      }
+      if (sourceType.positionalParameters.isNotEmpty ||
+          destinationType.positionalParameters.isNotEmpty) {
+        throw UnimplementedError('TODO(paulberry)');
+      }
+      if (sourceType.namedParameters.isNotEmpty ||
+          destinationType.namedParameters.isNotEmpty) {
+        throw UnimplementedError('TODO(paulberry)');
+      }
     } else if (destinationType.type.isDynamic || sourceType.type.isDynamic) {
       // ok; nothing further to do.
     } else {
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 92d776d..41dd341 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -332,6 +332,24 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  test_data_flow_function_return_type() async {
+    var content = '''
+int Function() f(int Function() x) => x;
+int g() => null;
+main() {
+  f(g);
+}
+''';
+    var expected = '''
+int? Function() f(int? Function() x) => x;
+int? g() => null;
+main() {
+  f(g);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   test_data_flow_generic_contravariant_inward() async {
     var content = '''
 class C<T> {
diff --git a/pkg/nnbd_migration/test/graph_builder_test.dart b/pkg/nnbd_migration/test/graph_builder_test.dart
index 9d6d8b7..3bd2a82 100644
--- a/pkg/nnbd_migration/test/graph_builder_test.dart
+++ b/pkg/nnbd_migration/test/graph_builder_test.dart
@@ -1450,6 +1450,15 @@
         assertEdge(decoratedTypeAnnotation('C c').node, never, hard: true));
   }
 
+  test_return_function_type_simple() async {
+    await analyze('''
+int/*1*/ Function() f(int/*2*/ Function() x) => x;
+''');
+    var int1 = decoratedTypeAnnotation('int/*1*/');
+    var int2 = decoratedTypeAnnotation('int/*2*/');
+    assertEdge(int2.node, int1.node, hard: true);
+  }
+
   test_return_implicit_null() async {
     verifyNoTestUnitErrors = false;
     await analyze('''
@@ -1646,6 +1655,19 @@
     assertEdge(always, decoratedTypeAnnotation('String>{').node, hard: false);
   }
 
+  test_simpleIdentifier_function() async {
+    await analyze('''
+int f() => null;
+main() {
+  int Function() g = f;
+}
+''');
+
+    assertEdge(decoratedTypeAnnotation('int f').node,
+        decoratedTypeAnnotation('int Function').node,
+        hard: false);
+  }
+
   test_simpleIdentifier_local() async {
     await analyze('''
 main() {