mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:37:53 +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) {
|
static macro.IdentifierImpl _buildIdentifier(Identifier node) {
|
||||||
final String name;
|
final String name;
|
||||||
if (node is SimpleIdentifier) {
|
if (node is SimpleIdentifier) {
|
||||||
|
@ -185,8 +209,27 @@ class LibraryMacroApplier {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static macro.TypeAnnotationImpl _buildTypeAnnotation(TypeAnnotation node) {
|
static macro.TypeAnnotationImpl _buildTypeAnnotation(TypeAnnotation? node) {
|
||||||
if (node is NamedType) {
|
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(
|
return macro.NamedTypeAnnotationImpl(
|
||||||
id: macro.RemoteInstance.uniqueId,
|
id: macro.RemoteInstance.uniqueId,
|
||||||
identifier: _buildIdentifier(node.name),
|
identifier: _buildIdentifier(node.name),
|
||||||
|
|
|
@ -33,7 +33,8 @@ class ArgumentMacroApplicationError extends MacroApplicationError {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toStringForTest() {
|
String toStringForTest() {
|
||||||
return 'Argument(annotation: $annotationIndex, argument: $argumentIndex)';
|
return 'Argument(annotation: $annotationIndex, '
|
||||||
|
'argument: $argumentIndex, message: $message)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -118,7 +119,7 @@ class UnknownMacroApplicationError extends MacroApplicationError {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toStringForTest() {
|
String toStringForTest() {
|
||||||
return 'Unknown(annotation: $annotationIndex)';
|
return 'Unknown(annotation: $annotationIndex, message: $message)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -131,15 +131,12 @@ class _TypeStringBuilder {
|
||||||
_TypeStringBuilder(this._sink);
|
_TypeStringBuilder(this._sink);
|
||||||
|
|
||||||
void write(TypeAnnotation type) {
|
void write(TypeAnnotation type) {
|
||||||
if (type is NamedTypeAnnotation) {
|
if (type is FunctionTypeAnnotation) {
|
||||||
_sink.write(type.identifier.name);
|
_writeFunctionTypeAnnotation(type);
|
||||||
_sink.writeList(
|
} else if (type is NamedTypeAnnotation) {
|
||||||
elements: type.typeArguments,
|
_writeNamedTypeAnnotation(type);
|
||||||
write: write,
|
} else if (type is OmittedTypeAnnotation) {
|
||||||
separator: ', ',
|
_sink.write('OmittedType');
|
||||||
open: '<',
|
|
||||||
close: '>',
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
throw UnimplementedError('(${type.runtimeType}) $type');
|
throw UnimplementedError('(${type.runtimeType}) $type');
|
||||||
}
|
}
|
||||||
|
@ -147,6 +144,80 @@ class _TypeStringBuilder {
|
||||||
_sink.write('?');
|
_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 {
|
extension on StringSink {
|
||||||
|
@ -158,22 +229,24 @@ extension on StringSink {
|
||||||
String? close,
|
String? close,
|
||||||
}) {
|
}) {
|
||||||
elements = elements.toList();
|
elements = elements.toList();
|
||||||
if (elements.isNotEmpty) {
|
if (elements.isEmpty) {
|
||||||
if (open != null) {
|
return;
|
||||||
this.write(open);
|
}
|
||||||
}
|
|
||||||
var isFirst = true;
|
if (open != null) {
|
||||||
for (var element in elements) {
|
this.write(open);
|
||||||
if (isFirst) {
|
}
|
||||||
isFirst = false;
|
var isFirst = true;
|
||||||
} else {
|
for (var element in elements) {
|
||||||
this.write(separator);
|
if (isFirst) {
|
||||||
}
|
isFirst = false;
|
||||||
write(element);
|
} else {
|
||||||
}
|
this.write(separator);
|
||||||
if (close != null) {
|
|
||||||
this.write(close);
|
|
||||||
}
|
}
|
||||||
|
write(element);
|
||||||
|
}
|
||||||
|
if (close != null) {
|
||||||
|
this.write(close);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
|
import 'package:_fe_analyzer_shared/src/macros/executor/multi_executor.dart'
|
||||||
as macro;
|
as macro;
|
||||||
import 'package:analyzer/dart/ast/ast.dart';
|
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/dart/element/element.dart';
|
||||||
import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
|
import 'package:analyzer/src/summary2/kernel_compilation_service.dart';
|
||||||
import 'package:analyzer/src/summary2/macro.dart';
|
import 'package:analyzer/src/summary2/macro.dart';
|
||||||
|
@ -83,7 +84,8 @@ class MacroElementsTest extends ElementsBaseTest {
|
||||||
},
|
},
|
||||||
constructorParametersCode: '(this.foo, this.bar)',
|
constructorParametersCode: '(this.foo, this.bar)',
|
||||||
argumentsCode: '(0, const Object())',
|
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 {
|
test_macroFlag_class() async {
|
||||||
var library = await buildLibrary(r'''
|
var library = await buildLibrary(r'''
|
||||||
macro class A {}
|
macro class A {}
|
||||||
|
@ -504,6 +588,14 @@ class A {}
|
||||||
{'package:test/arguments_text.dart'}
|
{'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) {
|
if (expected != null) {
|
||||||
final x = library.parts.single.topLevelVariables.single;
|
final x = library.parts.single.topLevelVariables.single;
|
||||||
expect(x.name, 'x');
|
expect(x.name, 'x');
|
||||||
|
@ -514,13 +606,6 @@ class A {}
|
||||||
print(actual);
|
print(actual);
|
||||||
}
|
}
|
||||||
expect(actual, expected);
|
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 {
|
} else {
|
||||||
fail("Either 'expected' or 'expectedErrors' must be provided.");
|
fail("Either 'expected' or 'expectedErrors' must be provided.");
|
||||||
}
|
}
|
||||||
|
@ -561,10 +646,26 @@ $declarationCode
|
||||||
{'package:test/declaration_text.dart'}
|
{'package:test/declaration_text.dart'}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
_assertNoErrorsForClassElement(
|
||||||
|
library.definingCompilationUnit.getType('A'),
|
||||||
|
);
|
||||||
|
|
||||||
var x = library.parts.single.topLevelVariables.single;
|
var x = library.parts.single.topLevelVariables.single;
|
||||||
expect(x.name, 'x');
|
expect(x.name, 'x');
|
||||||
x as ConstTopLevelVariableElementImpl;
|
x as ConstTopLevelVariableElementImpl;
|
||||||
var x_literal = x.constantInitializer as SimpleStringLiteral;
|
var x_literal = x.constantInitializer as SimpleStringLiteral;
|
||||||
return x_literal.value;
|
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