mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 16:41:07 +00:00
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:
parent
c440a0d2bc
commit
dd26a0eae1
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue