Build FunctionTypeAnnotation(s) for macros.

Change-Id: Ia18c132cb05c4a9cd1df569bb65202a8f1379596
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241801
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart
index 9f618ae..3cfe128 100644
--- a/pkg/analyzer/lib/src/summary2/macro_application.dart
+++ b/pkg/analyzer/lib/src/summary2/macro_application.dart
@@ -172,6 +172,30 @@
     );
   }
 
+  static macro.ParameterDeclarationImpl _buildFormalParameter(
+    FormalParameter node,
+  ) {
+    if (node is DefaultFormalParameter) {
+      node = node.parameter;
+    }
+
+    final macro.TypeAnnotationImpl typeAnnotation;
+    if (node is SimpleFormalParameter) {
+      typeAnnotation = _buildTypeAnnotation(node.type);
+    } else {
+      throw UnimplementedError('(${node.runtimeType}) $node');
+    }
+
+    return macro.ParameterDeclarationImpl(
+      id: macro.RemoteInstance.uniqueId,
+      identifier:
+          _buildIdentifier(node.identifier!), // TODO(scheglov) might be null
+      isNamed: node.isNamed,
+      isRequired: node.isRequired,
+      type: typeAnnotation,
+    );
+  }
+
   static macro.IdentifierImpl _buildIdentifier(Identifier node) {
     final String name;
     if (node is SimpleIdentifier) {
@@ -185,8 +209,27 @@
     );
   }
 
-  static macro.TypeAnnotationImpl _buildTypeAnnotation(TypeAnnotation node) {
-    if (node is NamedType) {
+  static macro.TypeAnnotationImpl _buildTypeAnnotation(TypeAnnotation? node) {
+    if (node == null) {
+      return macro.OmittedTypeAnnotationImpl(
+        id: macro.RemoteInstance.uniqueId,
+      );
+    } else if (node is GenericFunctionType) {
+      return macro.FunctionTypeAnnotationImpl(
+        id: macro.RemoteInstance.uniqueId,
+        isNullable: node.question != null,
+        namedParameters: node.parameters.parameters
+            .where((e) => e.isNamed)
+            .map(_buildFormalParameter)
+            .toList(),
+        positionalParameters: node.parameters.parameters
+            .where((e) => e.isPositional)
+            .map(_buildFormalParameter)
+            .toList(),
+        returnType: _buildTypeAnnotation(node.returnType),
+        typeParameters: _buildTypeParameters(node.typeParameters),
+      );
+    } else if (node is NamedType) {
       return macro.NamedTypeAnnotationImpl(
         id: macro.RemoteInstance.uniqueId,
         identifier: _buildIdentifier(node.name),
diff --git a/pkg/analyzer/lib/src/summary2/macro_application_error.dart b/pkg/analyzer/lib/src/summary2/macro_application_error.dart
index a7b9018..de56919 100644
--- a/pkg/analyzer/lib/src/summary2/macro_application_error.dart
+++ b/pkg/analyzer/lib/src/summary2/macro_application_error.dart
@@ -33,7 +33,8 @@
 
   @override
   String toStringForTest() {
-    return 'Argument(annotation: $annotationIndex, argument: $argumentIndex)';
+    return 'Argument(annotation: $annotationIndex, '
+        'argument: $argumentIndex, message: $message)';
   }
 
   @override
@@ -118,7 +119,7 @@
 
   @override
   String toStringForTest() {
-    return 'Unknown(annotation: $annotationIndex)';
+    return 'Unknown(annotation: $annotationIndex, message: $message)';
   }
 
   @override
diff --git a/pkg/analyzer/test/src/summary/macro/declaration_text.dart b/pkg/analyzer/test/src/summary/macro/declaration_text.dart
index 066c9b6..85ec1ed 100644
--- a/pkg/analyzer/test/src/summary/macro/declaration_text.dart
+++ b/pkg/analyzer/test/src/summary/macro/declaration_text.dart
@@ -131,15 +131,12 @@
   _TypeStringBuilder(this._sink);
 
   void write(TypeAnnotation type) {
-    if (type is NamedTypeAnnotation) {
-      _sink.write(type.identifier.name);
-      _sink.writeList(
-        elements: type.typeArguments,
-        write: write,
-        separator: ', ',
-        open: '<',
-        close: '>',
-      );
+    if (type is FunctionTypeAnnotation) {
+      _writeFunctionTypeAnnotation(type);
+    } else if (type is NamedTypeAnnotation) {
+      _writeNamedTypeAnnotation(type);
+    } else if (type is OmittedTypeAnnotation) {
+      _sink.write('OmittedType');
     } else {
       throw UnimplementedError('(${type.runtimeType}) $type');
     }
@@ -147,6 +144,80 @@
       _sink.write('?');
     }
   }
+
+  void _writeFormalParameter(ParameterDeclaration node) {
+    final String closeSeparator;
+    if (node.isNamed) {
+      _sink.write('{');
+      closeSeparator = '}';
+      if (node.isRequired) {
+        _sink.write('required ');
+      }
+    } else if (!node.isRequired) {
+      _sink.write('[');
+      closeSeparator = ']';
+    } else {
+      closeSeparator = '';
+    }
+
+    write(node.type);
+    _sink.write(' ');
+    _sink.write(node.identifier.name);
+
+    _sink.write(closeSeparator);
+  }
+
+  void _writeFunctionTypeAnnotation(FunctionTypeAnnotation type) {
+    write(type.returnType);
+    _sink.write(' Function');
+
+    _sink.writeList(
+      elements: type.typeParameters,
+      write: _writeTypeParameter,
+      separator: ', ',
+      open: '<',
+      close: '>',
+    );
+
+    _sink.write('(');
+    var hasFormalParameter = false;
+    for (final formalParameter in type.positionalParameters) {
+      if (hasFormalParameter) {
+        _sink.write(', ');
+      }
+      _writeFormalParameter(formalParameter);
+      hasFormalParameter = true;
+    }
+    for (final formalParameter in type.namedParameters) {
+      if (hasFormalParameter) {
+        _sink.write(', ');
+      }
+      _writeFormalParameter(formalParameter);
+      hasFormalParameter = true;
+    }
+    _sink.write(')');
+  }
+
+  void _writeNamedTypeAnnotation(NamedTypeAnnotation type) {
+    _sink.write(type.identifier.name);
+    _sink.writeList(
+      elements: type.typeArguments,
+      write: write,
+      separator: ', ',
+      open: '<',
+      close: '>',
+    );
+  }
+
+  void _writeTypeParameter(TypeParameterDeclaration node) {
+    _sink.write(node.identifier.name);
+
+    final bound = node.bound;
+    if (bound != null) {
+      _sink.write(' extends ');
+      write(bound);
+    }
+  }
 }
 
 extension on StringSink {
@@ -158,22 +229,24 @@
     String? close,
   }) {
     elements = elements.toList();
-    if (elements.isNotEmpty) {
-      if (open != null) {
-        this.write(open);
+    if (elements.isEmpty) {
+      return;
+    }
+
+    if (open != null) {
+      this.write(open);
+    }
+    var isFirst = true;
+    for (var element in elements) {
+      if (isFirst) {
+        isFirst = false;
+      } else {
+        this.write(separator);
       }
-      var isFirst = true;
-      for (var element in elements) {
-        if (isFirst) {
-          isFirst = false;
-        } else {
-          this.write(separator);
-        }
-        write(element);
-      }
-      if (close != null) {
-        this.write(close);
-      }
+      write(element);
+    }
+    if (close != null) {
+      this.write(close);
     }
   }
 }
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart
index 0c3f764..c861ab4 100644
--- a/pkg/analyzer/test/src/summary/macro_test.dart
+++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -5,6 +5,7 @@
 import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
     as macro;
 import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
 import 'package:analyzer/src/summary2/macro.dart';
@@ -83,7 +84,8 @@
       },
       constructorParametersCode: '(this.foo, this.bar)',
       argumentsCode: '(0, const Object())',
-      expectedErrors: 'Argument(annotation: 0, argument: 1)',
+      expectedErrors: 'Argument(annotation: 0, argument: 1, '
+          'message: Not supported: InstanceCreationExpressionImpl)',
     );
   }
 
@@ -385,6 +387,88 @@
 ''');
   }
 
+  test_introspect_types_functionTypeAnnotation_formalParameters_namedOptional_simpleFormalParameter() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function(int a, {int? b, int? c})> {}
+''', r'''
+class A
+  superclass: B<void Function(int a, {int? b}, {int? c})>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_formalParameters_namedRequired_simpleFormalParameter() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function(int a, {required int b, required int c})> {}
+''', r'''
+class A
+  superclass: B<void Function(int a, {required int b}, {required int c})>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_formalParameters_positionalOptional_simpleFormalParameter() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function(int a, [int b, int c])> {}
+''', r'''
+class A
+  superclass: B<void Function(int a, [int b], [int c])>
+''');
+  }
+
+  /// TODO(scheglov) Tests for unnamed positional formal parameters.
+  test_introspect_types_functionTypeAnnotation_formalParameters_positionalRequired_simpleFormalParameter() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function(int a, double b)> {}
+''', r'''
+class A
+  superclass: B<void Function(int a, double b)>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_nullable() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function()?> {}
+''', r'''
+class A
+  superclass: B<void Function()?>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_returnType() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function()> {}
+''', r'''
+class A
+  superclass: B<void Function()>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_returnType_omitted() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<Function()> {}
+''', r'''
+class A
+  superclass: B<OmittedType Function()>
+''');
+  }
+
+  test_introspect_types_functionTypeAnnotation_typeParameters() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends B<void Function<T, U extends num>()> {}
+''', r'''
+class A
+  superclass: B<void Function<T, U extends num>()>
+''');
+  }
+
+  test_introspect_types_namedTypeAnnotation_prefixed() async {
+    await _assertTypesPhaseIntrospectionText(r'''
+class A extends prefix.B {}
+''', r'''
+class A
+  superclass: B
+''');
+  }
+
   test_macroFlag_class() async {
     var library = await buildLibrary(r'''
 macro class A {}
@@ -504,6 +588,14 @@
       {'package:test/arguments_text.dart'}
     ]);
 
+    final A = library.definingCompilationUnit.getType('A');
+    if (expectedErrors != null) {
+      expect(_errorsStrForClassElement(A), expectedErrors);
+      return;
+    } else {
+      _assertNoErrorsForClassElement(A);
+    }
+
     if (expected != null) {
       final x = library.parts.single.topLevelVariables.single;
       expect(x.name, 'x');
@@ -514,13 +606,6 @@
         print(actual);
       }
       expect(actual, expected);
-    } else if (expectedErrors != null) {
-      var A = library.definingCompilationUnit.getType('A');
-      A as ClassElementImpl;
-      expect(
-        A.macroApplicationErrors.map((e) => e.toStringForTest()).join('\n'),
-        expectedErrors,
-      );
     } else {
       fail("Either 'expected' or 'expectedErrors' must be provided.");
     }
@@ -561,10 +646,26 @@
       {'package:test/declaration_text.dart'}
     ]);
 
+    _assertNoErrorsForClassElement(
+      library.definingCompilationUnit.getType('A'),
+    );
+
     var x = library.parts.single.topLevelVariables.single;
     expect(x.name, 'x');
     x as ConstTopLevelVariableElementImpl;
     var x_literal = x.constantInitializer as SimpleStringLiteral;
     return x_literal.value;
   }
+
+  static void _assertNoErrorsForClassElement(ClassElement? element) {
+    var actual = _errorsStrForClassElement(element);
+    expect(actual, isEmpty);
+  }
+
+  static String _errorsStrForClassElement(ClassElement? element) {
+    element as ClassElementImpl;
+    return element.macroApplicationErrors.map((e) {
+      return e.toStringForTest();
+    }).join('\n');
+  }
 }