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>
This commit is contained in:
Konstantin Shcheglov 2022-04-20 17:02:48 +00:00 committed by Commit Bot
parent c440a0d2bc
commit dd26a0eae1
4 changed files with 254 additions and 36 deletions

View file

@ -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),

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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<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 @@ 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');
}
}