diff --git a/pkg/analyzer/lib/src/summary2/macro_application.dart b/pkg/analyzer/lib/src/summary2/macro_application.dart index 9f618ae13ea..3cfe128a560 100644 --- a/pkg/analyzer/lib/src/summary2/macro_application.dart +++ b/pkg/analyzer/lib/src/summary2/macro_application.dart @@ -172,6 +172,30 @@ class LibraryMacroApplier { ); } + 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 @@ class LibraryMacroApplier { ); } - 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 a7b90189e14..de56919f7ad 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 @@ class ArgumentMacroApplicationError extends MacroApplicationError { @override String toStringForTest() { - return 'Argument(annotation: $annotationIndex, argument: $argumentIndex)'; + return 'Argument(annotation: $annotationIndex, ' + 'argument: $argumentIndex, message: $message)'; } @override @@ -118,7 +119,7 @@ class UnknownMacroApplicationError extends MacroApplicationError { @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 066c9b6f38b..85ec1ed8451 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 @@ class _TypeStringBuilder { _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 @@ class _TypeStringBuilder { _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 @@ extension on StringSink { String? close, }) { elements = elements.toList(); - if (elements.isNotEmpty) { - if (open != null) { - this.write(open); - } - var isFirst = true; - for (var element in elements) { - if (isFirst) { - isFirst = false; - } else { - this.write(separator); - } - write(element); - } - if (close != null) { - this.write(close); + 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); } + 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 0c3f7641281..c861ab43799 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 @@ class MacroElementsTest extends ElementsBaseTest { }, 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 @@ class A '''); } + test_introspect_types_functionTypeAnnotation_formalParameters_namedOptional_simpleFormalParameter() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B {} +''', r''' +class A + superclass: B +'''); + } + + test_introspect_types_functionTypeAnnotation_formalParameters_namedRequired_simpleFormalParameter() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B {} +''', r''' +class A + superclass: B +'''); + } + + test_introspect_types_functionTypeAnnotation_formalParameters_positionalOptional_simpleFormalParameter() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B {} +''', r''' +class A + superclass: B +'''); + } + + /// TODO(scheglov) Tests for unnamed positional formal parameters. + test_introspect_types_functionTypeAnnotation_formalParameters_positionalRequired_simpleFormalParameter() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B {} +''', r''' +class A + superclass: B +'''); + } + + test_introspect_types_functionTypeAnnotation_nullable() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B {} +''', r''' +class A + superclass: B +'''); + } + + test_introspect_types_functionTypeAnnotation_returnType() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B {} +''', r''' +class A + superclass: B +'''); + } + + test_introspect_types_functionTypeAnnotation_returnType_omitted() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B {} +''', r''' +class A + superclass: B +'''); + } + + test_introspect_types_functionTypeAnnotation_typeParameters() async { + await _assertTypesPhaseIntrospectionText(r''' +class A extends B()> {} +''', r''' +class A + superclass: B()> +'''); + } + + 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 @@ class A {} {'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 @@ class A {} 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 @@ $declarationCode {'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'); + } }