blob: c59570723905453de6b4be9856e13aec42ff0fa9 [file] [log] [blame]
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'package:_macros/src/api.dart';
/// A macro for testing diagnostics reporting, including error handling.
class DiagnosticMacro implements ClassTypesMacro {
@override
FutureOr<void> buildTypesForClass(
ClassDeclaration clazz, ClassTypeBuilder builder) {
builder.report(Diagnostic(
DiagnosticMessage('superclass',
target: clazz.superclass!.asDiagnosticTarget),
Severity.info,
contextMessages: [
DiagnosticMessage(
'interface',
target: clazz.interfaces.single.asDiagnosticTarget,
),
],
correctionMessage: 'correct me!'));
// Test general error handling also
throw 'I threw an error!';
}
}
/// A very simple macro that augments any declaration it is given, usually
/// adding print statements and inlining values from the declaration object
/// for comparison with expected values in tests.
///
/// When applied to [MethodDeclaration]s there is some extra work that happens
/// to validate the introspection APIs work as expected.
class SimpleMacro
implements
ClassTypesMacro,
ClassDeclarationsMacro,
ClassDefinitionMacro,
ConstructorTypesMacro,
ConstructorDeclarationsMacro,
ConstructorDefinitionMacro,
EnumTypesMacro,
EnumDeclarationsMacro,
EnumDefinitionMacro,
EnumValueTypesMacro,
EnumValueDeclarationsMacro,
EnumValueDefinitionMacro,
ExtensionTypesMacro,
ExtensionDeclarationsMacro,
ExtensionDefinitionMacro,
ExtensionTypeTypesMacro,
ExtensionTypeDeclarationsMacro,
ExtensionTypeDefinitionMacro,
FieldTypesMacro,
FieldDeclarationsMacro,
FieldDefinitionMacro,
FunctionTypesMacro,
FunctionDeclarationsMacro,
FunctionDefinitionMacro,
LibraryTypesMacro,
LibraryDeclarationsMacro,
LibraryDefinitionMacro,
MethodTypesMacro,
MethodDeclarationsMacro,
MethodDefinitionMacro,
MixinTypesMacro,
MixinDeclarationsMacro,
MixinDefinitionMacro,
TypeAliasTypesMacro,
TypeAliasDeclarationsMacro,
VariableTypesMacro,
VariableDeclarationsMacro,
VariableDefinitionMacro {
final bool? myBool;
final int? myInt;
final double? myDouble;
final Set? mySet;
final List? myList;
final Map? myMap;
final String? myString;
SimpleMacro([this.myInt])
: myBool = null,
myDouble = null,
mySet = null,
myList = null,
myMap = null,
myString = null;
SimpleMacro.named(
{required this.myBool,
required this.myDouble,
required this.myInt,
required this.mySet,
required this.myList,
required this.myMap,
required this.myString});
@override
FutureOr<void> buildDeclarationsForClass(
ClassDeclaration clazz, MemberDeclarationBuilder builder) async {
var fields = await builder.fieldsOf(clazz);
builder.declareInType(DeclarationCode.fromParts([
'static const List<String> fieldNames = [',
for (var field in fields) "'${field.identifier.name}',",
'];',
]));
}
@override
FutureOr<void> buildDeclarationsForConstructor(
ConstructorDeclaration constructor, MemberDeclarationBuilder builder) {
var className = constructor.definingType.name;
var constructorName = constructor.identifier.name;
builder.declareInType(DeclarationCode.fromString(
'factory $className.${constructorName}Delegate() => '
'$className.$constructorName();'));
}
@override
FutureOr<void> buildDeclarationsForEnum(
EnumDeclaration enuum, EnumDeclarationBuilder builder) async {
var values = await builder.valuesOf(enuum);
builder.declareInType(DeclarationCode.fromParts([
'static const List<String> valuesByName = {',
for (var value in values) ...[
"'${value.identifier.name}': ",
value.identifier
],
'};',
]));
}
@override
FutureOr<void> buildDeclarationsForEnumValue(
EnumValueDeclaration value, EnumDeclarationBuilder builder) async {
final parent = await builder.typeDeclarationOf(value.definingEnum);
builder.declareInType(DeclarationCode.fromParts([
parent.identifier,
' ${value.identifier.name}ToString() => ',
value.identifier,
'.toString();',
]));
}
@override
FutureOr<void> buildDeclarationsForFunction(
FunctionDeclaration function, DeclarationBuilder builder) {
var functionName = function.identifier.name;
builder.declareInLibrary(DeclarationCode.fromParts([
function.returnType.code,
if (function.isGetter) ' get' else if (function.isSetter) ' set ',
' delegate${functionName.capitalize()}',
if (!function.isGetter) ...[
'(',
if (function.isSetter) ...[
function.positionalParameters.first.type.code,
' value',
],
')',
],
' => $functionName',
function.isGetter
? ''
: function.isSetter
? ' = value'
: '()',
';',
]));
}
@override
FutureOr<void> buildDeclarationsForMethod(
MethodDeclaration method, MemberDeclarationBuilder builder) {
if (method.positionalParameters.isNotEmpty ||
method.namedParameters.isNotEmpty) {
throw UnsupportedError('Can only run on method with no parameters!');
}
var methodName = method.identifier.name;
builder.declareInLibrary(DeclarationCode.fromParts([
method.returnType.code,
' delegateMember${methodName.capitalize()}() => $methodName();',
]));
}
@override
FutureOr<void> buildDeclarationsForMixin(
MixinDeclaration mixin, MemberDeclarationBuilder builder) async {
var methods = await builder.methodsOf(mixin);
builder.declareInType(DeclarationCode.fromParts([
'static const List<String> methodNames = [',
for (var method in methods) "'${method.identifier.name}',",
'];',
]));
}
@override
FutureOr<void> buildDeclarationsForVariable(
VariableDeclaration variable, DeclarationBuilder builder) {
var variableName = variable.identifier.name;
builder.declareInLibrary(DeclarationCode.fromParts([
variable.type.code,
' get delegate${variableName.capitalize()} => $variableName;',
]));
}
@override
FutureOr<void> buildDeclarationsForField(
FieldDeclaration field, MemberDeclarationBuilder builder) {
var fieldName = field.identifier.name;
builder.declareInType(DeclarationCode.fromParts([
field.type.code,
' get delegate${fieldName.capitalize()} => $fieldName;',
]));
}
@override
Future<void> buildDefinitionForClass(
ClassDeclaration clazz, TypeDefinitionBuilder builder) async {
// Apply ourself to all our members
var fields = (await builder.fieldsOf(clazz));
for (var field in fields) {
await buildDefinitionForField(
field, await builder.buildField(field.identifier));
}
var methods = (await builder.methodsOf(clazz));
for (var method in methods) {
await buildDefinitionForMethod(
method, await builder.buildMethod(method.identifier));
}
var constructors = (await builder.constructorsOf(clazz));
for (var constructor in constructors) {
await buildDefinitionForConstructor(
constructor, await builder.buildConstructor(constructor.identifier));
}
}
@override
Future<void> buildDefinitionForConstructor(ConstructorDeclaration constructor,
ConstructorDefinitionBuilder builder) async {
var clazz = await builder.declarationOf(constructor.definingType)
as TypeDeclaration;
var fields = (await builder.fieldsOf(clazz));
builder.augment(
body: await _buildFunctionAugmentation(constructor, builder),
initializers: [
for (var field in fields)
// TODO: Compare against actual `int` type.
if (field.hasFinal &&
(field.type as NamedTypeAnnotation).identifier.name == 'int')
RawCode.fromParts([field.identifier, ' = ${myInt!}']),
],
);
}
@override
Future<void> buildDefinitionForEnum(
EnumDeclaration enuum, EnumDefinitionBuilder builder) async {
// Apply ourself to all our members
var values = (await builder.valuesOf(enuum));
for (var value in values) {
await buildDefinitionForEnumValue(
value, await builder.buildEnumValue(value.identifier));
}
var fields = (await builder.fieldsOf(enuum));
for (var field in fields) {
await buildDefinitionForField(
field, await builder.buildField(field.identifier));
}
var methods = (await builder.methodsOf(enuum));
for (var method in methods) {
await buildDefinitionForMethod(
method, await builder.buildMethod(method.identifier));
}
var constructors = (await builder.constructorsOf(enuum));
for (var constructor in constructors) {
await buildDefinitionForConstructor(
constructor, await builder.buildConstructor(constructor.identifier));
}
}
@override
FutureOr<void> buildDefinitionForEnumValue(
EnumValueDeclaration value, EnumValueDefinitionBuilder builder) async {
final parent =
await builder.typeDeclarationOf(value.definingEnum) as EnumDeclaration;
final constructor = (await builder.constructorsOf(parent)).first;
final parts = [
value.identifier,
'(',
];
final stringType = await builder.resolve(NamedTypeAnnotationCode(
name:
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'String')));
for (var positional in constructor.positionalParameters) {
final resolvedType = await builder.resolve(positional.type.code);
if (!(await resolvedType.isExactly(stringType))) {
throw StateError('Expected only string parameters');
}
parts.add("'${positional.identifier.name}', ");
}
for (var named in constructor.namedParameters) {
final resolvedType = await builder.resolve(named.type.code);
if (!(await resolvedType.isExactly(stringType))) {
throw StateError('Expected only string parameters');
}
parts.add("${named.identifier.name}: '${named.identifier.name}', ");
}
parts.add('),');
builder.augment(DeclarationCode.fromParts(parts));
}
@override
Future<void> buildDefinitionForField(
FieldDeclaration field, VariableDefinitionBuilder builder) async =>
buildDefinitionForVariable(field, builder);
@override
Future<void> buildDefinitionForFunction(
FunctionDeclaration function, FunctionDefinitionBuilder builder) async {
builder.augment(await _buildFunctionAugmentation(function, builder),
docComments: CommentCode.fromString('// A comment!'));
}
@override
Future<void> buildDefinitionForMethod(
MethodDeclaration method, FunctionDefinitionBuilder builder) async {
await buildDefinitionForFunction(method, builder);
// Test the type declaration resolver
var parentClass = await builder.typeDeclarationOf(method.definingType);
// Should be able to find ourself in the methods of the parent class.
(await builder.methodsOf(parentClass))
.singleWhere((m) => m.identifier == method.identifier);
TypeDeclaration? superClass;
final interfaces = <TypeDeclaration>[];
final mixins = <TypeDeclaration>[];
final superclassConstraints = <TypeDeclaration>[];
// Test the class introspector
if (parentClass is ClassDeclaration) {
superClass =
(await builder.typeDeclarationOf(parentClass.superclass!.identifier));
interfaces.addAll(await Future.wait(parentClass.interfaces.map(
(interface) => builder.typeDeclarationOf(interface.identifier))));
mixins.addAll(await Future.wait(parentClass.mixins
.map((mixins) => builder.typeDeclarationOf(mixins.identifier))));
} else if (parentClass is MixinDeclaration) {
superclassConstraints.addAll(await Future.wait(
parentClass.superclassConstraints.map(
(interface) => builder.typeDeclarationOf(interface.identifier))));
interfaces.addAll(await Future.wait(parentClass.interfaces.map(
(interface) => builder.typeDeclarationOf(interface.identifier))));
} else if (parentClass is EnumDeclaration) {
interfaces.addAll(await Future.wait(parentClass.interfaces.map(
(interface) => builder.typeDeclarationOf(interface.identifier))));
mixins.addAll(await Future.wait(parentClass.mixins
.map((mixins) => builder.typeDeclarationOf(mixins.identifier))));
}
var fields = (await builder.fieldsOf(parentClass));
var methods = (await builder.methodsOf(parentClass));
var constructors = (await builder.constructorsOf(parentClass));
// Test the type resolver and static type interfaces
var methodReturnType = method.returnType as RecordTypeAnnotation;
var staticReturnType = await builder
.resolve(methodReturnType.positionalFields.first.type.code);
if (!(await staticReturnType.isExactly(staticReturnType))) {
throw StateError('The return type should be exactly equal to itself!');
}
if (!(await staticReturnType.isSubtypeOf(staticReturnType))) {
throw StateError('The return type should be a subtype of itself!');
}
// TODO: Use `builder.instantiateCode` instead once implemented.
if (constructors.isNotEmpty) {
var classType = await builder.resolve(constructors.first.returnType.code);
if (await staticReturnType.isExactly(classType)) {
throw StateError(
'The return type should not be exactly equal to the class type');
}
if (await staticReturnType.isSubtypeOf(classType)) {
throw StateError(
'The return type should not be a subtype of the class type!');
}
}
builder.augment(FunctionBodyCode.fromParts([
'''{
print('myBool: $myBool');
print('myDouble: $myDouble');
print('myInt: $myInt');
print('myList: $myList');
print('mySet: $mySet');
print('myMap: $myMap');
print('myString: $myString');
print('parentClass: ${parentClass.identifier.name}');
print('superClass: ${superClass?.identifier.name}');''',
for (var interface in interfaces)
"\n print('interface: ${interface.identifier.name}');",
for (var mixin in mixins)
"\n print('mixin: ${mixin.identifier.name}');",
for (var field in fields)
"\n print('field: ${field.identifier.name}');",
for (var method in methods)
"\n print('method: ${method.identifier.name}');",
for (var constructor in constructors)
"\n print('constructor: ${constructor.identifier.name}');",
'''
\n return augmented();
}''',
]));
}
@override
Future<void> buildDefinitionForMixin(
MixinDeclaration mixin, TypeDefinitionBuilder builder) async {
// Apply ourself to all our members
var fields = (await builder.fieldsOf(mixin));
for (var field in fields) {
await buildDefinitionForField(
field, await builder.buildField(field.identifier));
}
var methods = (await builder.methodsOf(mixin));
for (var method in methods) {
await buildDefinitionForMethod(
method, await builder.buildMethod(method.identifier));
}
}
@override
Future<void> buildDefinitionForVariable(
VariableDeclaration variable, VariableDefinitionBuilder builder) async {
var definingClass =
variable is FieldDeclaration ? variable.definingType.name : '';
builder.augment(
getter: DeclarationCode.fromParts([
variable.type.code,
' get ',
variable.identifier.name,
''' {
print('parentClass: $definingClass');
''',
if (variable is FieldDeclaration)
"print('isAbstract: ${variable.hasAbstract}');\n",
'''print('isExternal: ${variable.hasExternal}');
print('isFinal: ${variable.hasFinal}');
print('isLate: ${variable.hasLate}');
return augmented;
}''',
]),
setter: DeclarationCode.fromParts([
'set ',
variable.identifier.name,
'(',
variable.type.code,
' value) { augmented = value; }'
]),
initializer: ExpressionCode.fromString("'new initial value' + augmented"),
);
}
@override
FutureOr<void> buildTypesForClass(
ClassDeclaration clazz, ClassTypeBuilder builder) async {
List<Object> buildTypeParam(
TypeParameterDeclaration typeParam, bool isFirst) {
return [
if (!isFirst) ', ',
typeParam.identifier.name,
if (typeParam.bound != null) ...[
' extends ',
typeParam.bound!.code,
]
];
}
var name = '${clazz.identifier.name}Builder';
builder.declareType(
name,
DeclarationCode.fromParts([
'class $name',
if (clazz.typeParameters.isNotEmpty) ...[
'<',
...buildTypeParam(clazz.typeParameters.first, true),
for (var typeParam in clazz.typeParameters.skip(1))
...buildTypeParam(typeParam, false),
'>',
],
' implements Builder<',
clazz.identifier,
if (clazz.typeParameters.isNotEmpty) ...[
'<',
clazz.typeParameters.first.identifier.name,
for (var typeParam in clazz.typeParameters)
', ${typeParam.identifier.name}',
'>',
],
'> {}'
]));
final interfaceName = 'HasX';
builder.declareType(interfaceName, DeclarationCode.fromString('''
abstract interface class $interfaceName {
int get x;
}'''));
final mixinName = 'GetX';
builder.declareType(mixinName, DeclarationCode.fromString('''
mixin $mixinName implements $interfaceName {
int get x => 1;
}'''));
// ignore: deprecated_member_use_from_same_package
final mySuperClass = await builder.resolveIdentifier(
Uri.parse('package:foo/bar.dart'), 'MySuperclass');
builder.extendsType(NamedTypeAnnotationCode(name: mySuperClass));
builder.appendInterfaces([RawTypeAnnotationCode.fromString(interfaceName)]);
builder.appendMixins([RawTypeAnnotationCode.fromString(mixinName)]);
}
@override
FutureOr<void> buildTypesForConstructor(
ConstructorDeclaration constructor, TypeBuilder builder) {
var name = 'GeneratedBy${constructor.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForEnum(EnumDeclaration enuum, TypeBuilder builder) {
final name = 'GeneratedBy${enuum.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForEnumValue(
EnumValueDeclaration value, TypeBuilder builder) {
final name = 'GeneratedBy${value.definingEnum.name}_'
'${value.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForField(
FieldDeclaration field, TypeBuilder builder) {
var name = 'GeneratedBy${field.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForFunction(
FunctionDeclaration function, TypeBuilder builder) {
var suffix = function.isGetter
? 'Getter'
: function.isSetter
? 'Setter'
: '';
var name = 'GeneratedBy${function.identifier.name.capitalize()}$suffix';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForMethod(
MethodDeclaration method, TypeBuilder builder) {
var name = 'GeneratedBy${method.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForMixin(
MixinDeclaration mixin, TypeBuilder builder) {
final onNames = mixin.superclassConstraints
.map((type) => type.identifier.name.capitalize())
.join('');
final name = 'GeneratedBy${mixin.identifier.name.capitalize()}On$onNames';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildTypesForVariable(
VariableDeclaration variable, TypeBuilder builder) {
var name = 'GeneratedBy${variable.identifier.name.capitalize()}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
void buildDeclarationsForLibrary(
Library library, DeclarationBuilder builder) {
builder.declareInLibrary(
DeclarationCode.fromString("final LibraryInfo library;"));
}
@override
Future<void> buildDefinitionForLibrary(
Library library, LibraryDefinitionBuilder builder) async {
var languageVersion = library.languageVersion;
var allDeclarations = await builder.topLevelDeclarationsOf(library);
var variableDeclaration =
allDeclarations.singleWhere((d) => d.identifier.name == 'library');
var variableBuilder =
await builder.buildVariable(variableDeclaration.identifier);
variableBuilder.augment(
initializer: ExpressionCode.fromParts([
'LibraryInfo(',
"Uri.parse('${library.uri}'), ",
"'${languageVersion.major}.${languageVersion.minor}', ",
"[",
for (var type in allDeclarations)
if (type is TypeDeclaration) ...[type.identifier, ', '],
"])",
]));
}
@override
void buildTypesForLibrary(Library library, TypeBuilder builder) {
builder.declareType('LibraryInfo', DeclarationCode.fromString('''
class LibraryInfo {
final Uri uri;
final String languageVersion;
final List<Type> definedTypes;
const LibraryInfo(this.uri, this.languageVersion, this.definedTypes);
}'''));
}
@override
FutureOr<void> buildTypesForExtension(
ExtensionDeclaration extension, TypeBuilder builder) {
final onType = extension.onType as NamedTypeAnnotation;
final name = '${extension.identifier.name}On${onType.identifier.name}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildDeclarationsForExtension(
ExtensionDeclaration extension, MemberDeclarationBuilder builder) async {
final dartCoreList =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'List');
final dartCoreString =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'String');
builder.declareInType(DeclarationCode.fromParts([
NamedTypeAnnotationCode(name: dartCoreList, typeArguments: [
NamedTypeAnnotationCode(name: dartCoreString),
]),
' get onTypeFieldNames;',
]));
}
@override
FutureOr<void> buildDefinitionForExtension(
ExtensionDeclaration extension, TypeDefinitionBuilder builder) async {
// Get a builder for the getter we added earlier.
final extensionMethods = await builder.methodsOf(extension);
final getterBuilder = await builder.buildMethod(extensionMethods
.singleWhere((m) => m.identifier.name == 'onTypeFieldNames')
.identifier);
// Introspect on our `on` type.
final onType = (await builder.typeDeclarationOf(
(extension.onType as NamedTypeAnnotation).identifier));
final onTypeFields = await builder.fieldsOf(onType);
getterBuilder.augment(FunctionBodyCode.fromParts([
'=> [',
for (var field in onTypeFields) "'${field.identifier.name}',",
'];',
]));
}
@override
FutureOr<void> buildTypesForExtensionType(
ExtensionTypeDeclaration extensionType, TypeBuilder builder) {
final representationType =
extensionType.representationType as NamedTypeAnnotation;
final name = '${extensionType.identifier.name}On'
'${representationType.identifier.name}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildDeclarationsForExtensionType(
ExtensionTypeDeclaration extensionType,
MemberDeclarationBuilder builder) async {
final dartCoreList =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'List');
final dartCoreString =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'String');
builder.declareInType(DeclarationCode.fromParts([
NamedTypeAnnotationCode(name: dartCoreList, typeArguments: [
NamedTypeAnnotationCode(name: dartCoreString),
]),
' get onTypeFieldNames;',
]));
}
@override
FutureOr<void> buildDefinitionForExtensionType(
ExtensionTypeDeclaration extensionType,
TypeDefinitionBuilder builder) async {
// Get a builder for the getter we added earlier.
final extensionTypeMethods = await builder.methodsOf(extensionType);
final getterBuilder = await builder.buildMethod(extensionTypeMethods
.singleWhere((m) => m.identifier.name == 'onTypeFieldNames')
.identifier);
// Introspect on our "representation" type.
final onType = (await builder.typeDeclarationOf(
(extensionType.representationType as NamedTypeAnnotation).identifier));
final onTypeFields = await builder.fieldsOf(onType);
getterBuilder.augment(FunctionBodyCode.fromParts([
'=> [',
for (var field in onTypeFields) "'${field.identifier.name}',",
'];',
]));
}
@override
FutureOr<void> buildTypesForTypeAlias(
TypeAliasDeclaration extensionType, TypeBuilder builder) {
final representationType = extensionType.aliasedType as NamedTypeAnnotation;
final name = '${extensionType.identifier.name}AliasedType'
'${representationType.identifier.name}';
builder.declareType(name, DeclarationCode.fromString('class $name {}'));
}
@override
FutureOr<void> buildDeclarationsForTypeAlias(
TypeAliasDeclaration extensionType, DeclarationBuilder builder) async {
final dartCoreList =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'List');
final dartCoreString =
// ignore: deprecated_member_use_from_same_package
await builder.resolveIdentifier(Uri.parse('dart:core'), 'String');
builder.declareInLibrary(DeclarationCode.fromParts([
NamedTypeAnnotationCode(name: dartCoreList, typeArguments: [
NamedTypeAnnotationCode(name: dartCoreString),
]),
' get aliasedTypeFieldNames;',
]));
}
}
Future<FunctionBodyCode> _buildFunctionAugmentation(
FunctionDeclaration function,
DefinitionPhaseIntrospector introspector) async {
Future<List<Object>> typeParts(TypeAnnotation annotation) async {
if (annotation is OmittedTypeAnnotation) {
var inferred = await introspector.inferType(annotation);
return [inferred.code, ' (inferred)'];
}
return [annotation.code];
}
return FunctionBodyCode.fromParts([
'{\n',
if (function is MethodDeclaration)
"print('definingClass: ${function.definingType.name}');\n",
if (function is ConstructorDeclaration)
"print('isFactory: ${function.isFactory}');\n",
'''
print('isExternal: ${function.hasExternal}');
print('isGetter: ${function.isGetter}');
print('isSetter: ${function.isSetter}');
print('returnType: ''',
function.returnType.code,
"');\n",
for (var param in function.positionalParameters) ...[
"print('positionalParam: ",
...await typeParts(param.type),
' ${param.identifier.name}',
"');\n",
],
for (var param in function.namedParameters) ...[
"print('namedParam: ",
...await typeParts(param.type),
' ${param.identifier.name}',
"');\n",
],
for (var param in function.typeParameters) ...[
"print('typeParam: ${param.identifier.name} ",
if (param.bound != null) param.bound!.code,
"');\n",
],
'return augmented',
if (function.isSetter) ...[
' = ',
function.positionalParameters.first.identifier,
],
if (!function.isGetter && !function.isSetter) '()',
''';
}''',
]);
}
class DanglingTaskMacro
implements
LibraryTypesMacro,
LibraryDeclarationsMacro,
LibraryDefinitionMacro {
@override
void buildTypesForLibrary(Library library, TypeBuilder builder) {
Future.value('hello').then((_) => Future.value('world'));
}
@override
void buildDeclarationsForLibrary(
Library library, DeclarationBuilder builder) {
Future.value('hello').then((_) => Future.value('world'));
}
@override
FutureOr<void> buildDefinitionForLibrary(
Library library, LibraryDefinitionBuilder builder) {
Future.value('hello').then((_) => Future.value('world'));
}
}
class DanglingTimerMacro
implements
LibraryTypesMacro,
LibraryDeclarationsMacro,
LibraryDefinitionMacro {
@override
void buildTypesForLibrary(Library library, TypeBuilder builder) {
Timer.run(() {});
}
@override
void buildDeclarationsForLibrary(
Library library, DeclarationBuilder builder) {
Timer.run(() {});
}
@override
FutureOr<void> buildDefinitionForLibrary(
Library library, LibraryDefinitionBuilder builder) {
Timer.run(() {});
}
}
class DanglingPeriodicTimerMacro
implements
LibraryTypesMacro,
LibraryDeclarationsMacro,
LibraryDefinitionMacro {
@override
void buildTypesForLibrary(Library library, TypeBuilder builder) {
Timer.periodic(Duration(seconds: 1), (_) {});
}
@override
void buildDeclarationsForLibrary(
Library library, DeclarationBuilder builder) {
Timer.periodic(Duration(seconds: 1), (_) {});
}
@override
FutureOr<void> buildDefinitionForLibrary(
Library library, LibraryDefinitionBuilder builder) {
Timer.periodic(Duration(seconds: 1), (_) {});
}
}
extension on String {
String capitalize() => '${this[0].toUpperCase()}${substring(1)}';
}