Macro. Introspect top-level functions.

Change-Id: Ida8e7768efb3072870fd32bb48d0905d235ad343
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/340740
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2023-12-11 17:36:21 +00:00 committed by Commit Queue
parent c31dfad2df
commit 8371eb481f
5 changed files with 191 additions and 2 deletions

View file

@ -173,8 +173,14 @@ class LibraryMacroApplier {
members: declaration.members,
);
case ast.FunctionDeclarationImpl():
// TODO(scheglov): implement it
break;
await _addAnnotations(
libraryElement: libraryElement,
container: container,
declarationsPhaseElement: null,
targetNode: declaration,
targetDeclarationKind: macro.DeclarationKind.function,
annotations: declaration.metadata,
);
case ast.FunctionTypeAliasImpl():
// TODO(scheglov): implement it
break;

View file

@ -90,6 +90,8 @@ class DeclarationBuilder {
return fromNode.extensionDeclaration(node);
case ast.ExtensionTypeDeclarationImpl():
return fromNode.extensionTypeDeclaration(node);
case ast.FunctionDeclarationImpl():
return fromNode.functionDeclaration(node);
case ast.MethodDeclarationImpl():
return fromNode.methodDeclaration(node);
case ast.MixinDeclarationImpl():
@ -638,6 +640,30 @@ class DeclarationBuilderFromNode {
);
}
macro.FunctionDeclarationImpl functionDeclaration(
ast.FunctionDeclarationImpl node,
) {
final element = node.declaredElement!;
final function = node.functionExpression;
return FunctionDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
element: element,
identifier: _declaredIdentifier(node.name, element),
library: library(element),
metadata: _buildMetadata(element),
hasBody: function.body is! ast.EmptyFunctionBody,
hasExternal: node.externalKeyword != null,
isGetter: node.isGetter,
isOperator: false,
isSetter: node.isSetter,
namedParameters: _namedFormalParameters(function.parameters),
positionalParameters: _positionalFormalParameters(function.parameters),
returnType: _typeAnnotation(node.returnType),
typeParameters: _typeParameters(function.typeParameters),
);
}
macro.LibraryImpl library(Element element) {
final library = element.library!;
@ -1038,6 +1064,29 @@ class FieldDeclarationImpl extends macro.FieldDeclarationImpl
});
}
class FunctionDeclarationImpl extends macro.FunctionDeclarationImpl
implements HasElement {
@override
final ExecutableElementImpl element;
FunctionDeclarationImpl._({
required super.id,
required super.identifier,
required super.library,
required super.metadata,
required super.hasBody,
required super.hasExternal,
required super.isGetter,
required super.isOperator,
required super.isSetter,
required super.namedParameters,
required super.positionalParameters,
required super.returnType,
required super.typeParameters,
required this.element,
});
}
/// A macro declaration that has an [Element].
abstract interface class HasElement {
ElementImpl get element;

View file

@ -551,6 +551,7 @@ class _ElementWriter {
_writeTypeParameterElements(e.typeParameters);
_writeParameterElements(e.parameters);
_writeReturnType(e.returnType);
_writeMacroDiagnostics(e);
_writeAugmentationTarget(e);
_writeAugmentation(e);
});

View file

@ -13,6 +13,7 @@ import 'package:_fe_analyzer_shared/src/macros/api.dart';
ExtensionDeclarationsMacro,
ExtensionTypeDeclarationsMacro,
FieldDeclarationsMacro,
FunctionDeclarationsMacro,
MethodDeclarationsMacro,
MixinDeclarationsMacro {
final Set<Object?> withDetailsFor;
@ -72,6 +73,13 @@ import 'package:_fe_analyzer_shared/src/macros/api.dart';
});
}
@override
Future<void> buildDeclarationsForFunction(declaration, builder) async {
await _write(builder, declaration, (printer) async {
await printer.writeFunctionDeclaration(declaration);
});
}
@override
Future<void> buildDeclarationsForMethod(declaration, builder) async {
await _write(builder, declaration, (printer) async {
@ -330,6 +338,25 @@ class _Printer {
});
}
Future<void> writeFunctionDeclaration(FunctionDeclaration e) async {
sink.writelnWithIndent(e.identifier.name);
await sink.withIndent(() async {
await sink.writeFlags({
'hasBody': e.hasBody,
'hasExternal': e.hasExternal,
'isGetter': e.isGetter,
'isOperator': e.isOperator,
'isSetter': e.isSetter,
});
await _writeMetadata(e);
await _writeNamedFormalParameters(e.namedParameters);
await _writePositionalFormalParameters(e.positionalParameters);
await _writeNamedTypeAnnotation('returnType', e.returnType);
await _writeTypeParameters(e.typeParameters);
});
}
Future<void> writeMethodDeclaration(MethodDeclaration e) async {
_assertEnclosingClass(e);
sink.writelnWithIndent(e.identifier.name);

View file

@ -5751,6 +5751,112 @@ class A
''');
}
test_unit_function() async {
await _assertIntrospectText(r'''
@Introspect()
void foo() {}
''', r'''
foo
flags: hasBody
returnType: void
''');
}
test_unit_function_flags_hasExternal() async {
await _assertIntrospectText(r'''
@Introspect()
external void foo();
''', r'''
foo
flags: hasExternal
returnType: void
''');
}
test_unit_function_metadata() async {
await _assertIntrospectText(r'''
@Introspect(withMetadata: true)
@a1
@a2
void foo() {}
const a1 = 0;
const a2 = 0;
''', r'''
foo
flags: hasBody
metadata
ConstructorMetadataAnnotation
type: Introspect
IdentifierMetadataAnnotation
identifier: a1
IdentifierMetadataAnnotation
identifier: a2
returnType: void
''');
}
test_unit_function_namedParameters() async {
await _assertIntrospectText(r'''
@Introspect()
void foo({required int a, String? b}) {}
''', r'''
foo
flags: hasBody
namedParameters
a
flags: isNamed isRequired
type: int
b
flags: isNamed
type: String?
returnType: void
''');
}
test_unit_function_positionalParameters() async {
await _assertIntrospectText(r'''
@Introspect()
void foo(int a, [String? b]) {}
''', r'''
foo
flags: hasBody
positionalParameters
a
flags: isRequired
type: int
b
type: String?
returnType: void
''');
}
test_unit_getter() async {
await _assertIntrospectText(r'''
@Introspect()
int get foo => 0;
''', r'''
foo
flags: hasBody isGetter
returnType: int
''');
}
test_unit_setter() async {
await _assertIntrospectText(r'''
@Introspect()
set foo(int value) {}
''', r'''
foo
flags: hasBody isSetter
positionalParameters
value
flags: isRequired
type: int
returnType: OmittedType
''');
}
/// Assert that the textual dump of the introspection information produced
/// by `IntrospectTypesPhaseMacro` in [code], is the [expected].
Future<void> _assertIntrospectText(