| // Copyright (c) 2023, 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:_fe_analyzer_shared/src/macros/api.dart'; |
| |
| /// Does not do anything useful, just augments the target in the definitions |
| /// phase, so that any omitted types are written into the augmentation. |
| /*macro*/ class AugmentForOmittedTypes |
| implements FieldDefinitionMacro, FunctionDefinitionMacro { |
| const AugmentForOmittedTypes(); |
| |
| @override |
| FutureOr<void> buildDefinitionForField(declaration, builder) async { |
| builder.augment( |
| initializer: ExpressionCode.fromString('0'), |
| ); |
| } |
| |
| @override |
| FutureOr<void> buildDefinitionForFunction(declaration, builder) async { |
| builder.augment( |
| FunctionBodyCode.fromString('{}'), |
| ); |
| } |
| } |
| |
| /*macro*/ class DefineToStringAsTypeName |
| implements ClassDefinitionMacro, MethodDefinitionMacro { |
| const DefineToStringAsTypeName(); |
| |
| @override |
| FutureOr<void> buildDefinitionForClass( |
| ClassDeclaration clazz, |
| TypeDefinitionBuilder builder, |
| ) async { |
| final methods = await builder.methodsOf(clazz); |
| final toString = methods.firstWhereOrNull( |
| (e) => e.identifier.name == 'toString', |
| ); |
| if (toString == null) { |
| throw StateError('No toString() declaration'); |
| } |
| |
| final toStringBuilder = await builder.buildMethod( |
| toString.identifier, |
| ); |
| |
| toStringBuilder.augment( |
| FunctionBodyCode.fromParts([ |
| '{\n return \'${clazz.identifier.name}\';\n }', |
| ]), |
| ); |
| } |
| |
| @override |
| FutureOr<void> buildDefinitionForMethod( |
| MethodDeclaration method, |
| FunctionDefinitionBuilder builder, |
| ) async { |
| builder.augment( |
| FunctionBodyCode.fromString("=> '${method.definingType.name}';"), |
| ); |
| } |
| } |
| |
| /*macro*/ class ReferenceIdentifier implements ClassDeclarationsMacro { |
| final String uriStr; |
| final String topName; |
| final String? memberName; |
| final String parametersCode; |
| final String leadCode; |
| |
| const ReferenceIdentifier( |
| this.uriStr, |
| this.topName, { |
| this.memberName, |
| this.parametersCode = '', |
| this.leadCode = '', |
| }); |
| |
| @override |
| FutureOr<void> buildDeclarationsForClass( |
| ClassDeclaration declaration, |
| MemberDeclarationBuilder builder, |
| ) async { |
| final uri = Uri.parse(uriStr); |
| |
| // ignore: deprecated_member_use |
| var identifier = await builder.resolveIdentifier(uri, topName); |
| |
| if (memberName case final memberName?) { |
| final type = await builder.typeDeclarationOf(identifier); |
| identifier = [ |
| ...await builder.constructorsOf(type), |
| ...await builder.fieldsOf(type), |
| ...await builder.methodsOf(type), |
| ].map((e) => e.identifier).firstWhere((e) => e.name == memberName); |
| } |
| |
| builder.declareInType( |
| DeclarationCode.fromParts([ |
| ' void doReference($parametersCode) {\n ', |
| leadCode, |
| identifier, |
| ';\n }', |
| ]), |
| ); |
| } |
| } |
| |
| extension<T> on Iterable<T> { |
| T? firstWhereOrNull(bool Function(T element) test) { |
| for (final element in this) { |
| if (test(element)) return element; |
| } |
| return null; |
| } |
| } |