| // 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'; |
| |
| /*macro*/ class Introspect |
| implements |
| ClassDeclarationsMacro, |
| ConstructorDeclarationsMacro, |
| EnumDeclarationsMacro, |
| EnumValueDeclarationsMacro, |
| ExtensionDeclarationsMacro, |
| ExtensionTypeDeclarationsMacro, |
| FieldDeclarationsMacro, |
| FunctionDeclarationsMacro, |
| LibraryDeclarationsMacro, |
| MethodDeclarationsMacro, |
| MixinDeclarationsMacro, |
| VariableDeclarationsMacro { |
| final Set<Object?> withDetailsFor; |
| final bool withMetadata; |
| final bool withUnnamedConstructor; |
| |
| const Introspect({ |
| this.withDetailsFor = const {}, |
| this.withMetadata = false, |
| this.withUnnamedConstructor = false, |
| }); |
| |
| @override |
| Future<void> buildDeclarationsForClass( |
| ClassDeclaration declaration, |
| MemberDeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeClassDeclaration(declaration); |
| }); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForConstructor( |
| ConstructorDeclaration declaration, |
| MemberDeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeConstructorDeclaration(declaration); |
| }); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForEnum( |
| EnumDeclaration declaration, |
| EnumDeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeEnumDeclaration(declaration); |
| }); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForEnumValue( |
| EnumValueDeclaration declaration, |
| EnumDeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeEnumValueDeclaration(declaration); |
| }); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForExtension( |
| ExtensionDeclaration declaration, |
| MemberDeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeExtensionDeclaration(declaration); |
| }); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForExtensionType( |
| ExtensionTypeDeclaration declaration, |
| MemberDeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeExtensionTypeDeclaration(declaration); |
| }); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForField( |
| FieldDeclaration declaration, |
| MemberDeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeField(declaration); |
| }); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForFunction( |
| FunctionDeclaration declaration, |
| DeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeFunctionDeclaration(declaration); |
| }); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForLibrary( |
| Library library, |
| DeclarationBuilder builder, |
| ) async { |
| final buffer = StringBuffer(); |
| final sink = TreeStringSink( |
| sink: buffer, |
| indent: '', |
| ); |
| |
| final types = await builder.typesOf(library); |
| final includedDeclarations = <Declaration>{}; |
| |
| final printer = _Printer( |
| sink: sink, |
| withMetadata: withMetadata, |
| withUnnamedConstructor: withUnnamedConstructor, |
| introspector: builder, |
| shouldWriteDetailsFor: (declaration) { |
| return includedDeclarations.remove(declaration); |
| }, |
| ); |
| |
| for (final type in types) { |
| includedDeclarations.add(type); |
| await printer.writeAnyDeclaration(type); |
| } |
| |
| final text = buffer.toString(); |
| _declareIntrospectResult(builder, text); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForMethod( |
| MethodDeclaration declaration, |
| MemberDeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeMethodDeclaration(declaration); |
| }); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForMixin( |
| MixinDeclaration declaration, |
| MemberDeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeMixinDeclaration(declaration); |
| }); |
| } |
| |
| @override |
| Future<void> buildDeclarationsForVariable( |
| VariableDeclaration declaration, |
| DeclarationBuilder builder, |
| ) async { |
| await _write(builder, declaration, (printer) async { |
| await printer.writeVariable(declaration); |
| }); |
| } |
| |
| void _declareIntrospectResult(DeclarationBuilder builder, String text) { |
| builder.declareInLibrary( |
| DeclarationCode.fromString( |
| 'const _introspect = r"""$text""";', |
| ), |
| ); |
| } |
| |
| Future<void> _write( |
| DeclarationBuilder builder, |
| Declaration declaration, |
| Future<void> Function(_Printer printer) withPrinter, |
| ) async { |
| final buffer = StringBuffer(); |
| final sink = TreeStringSink( |
| sink: buffer, |
| indent: '', |
| ); |
| |
| final includedNames = { |
| declaration.identifier.name, |
| ...withDetailsFor, |
| }; |
| |
| final printer = _Printer( |
| sink: sink, |
| withMetadata: withMetadata, |
| withUnnamedConstructor: withUnnamedConstructor, |
| introspector: builder, |
| shouldWriteDetailsFor: (declaration) { |
| final nameToCheck = declaration.identifier.name; |
| return includedNames.remove(nameToCheck); |
| }, |
| ); |
| await withPrinter(printer); |
| |
| final text = buffer.toString(); |
| _declareIntrospectResult(builder, text); |
| } |
| } |
| |
| /*macro*/ class IntrospectDeclaration implements FunctionDefinitionMacro { |
| final String uriStr; |
| final String name; |
| final bool withUnnamedConstructor; |
| |
| IntrospectDeclaration({ |
| required this.uriStr, |
| required this.name, |
| this.withUnnamedConstructor = false, |
| }); |
| |
| @override |
| Future<void> buildDefinitionForFunction( |
| FunctionDeclaration declaration, |
| FunctionDefinitionBuilder builder, |
| ) async { |
| final buffer = StringBuffer(); |
| final sink = TreeStringSink( |
| sink: buffer, |
| indent: '', |
| ); |
| |
| final includedNames = {name}; |
| |
| final printer = _Printer( |
| sink: sink, |
| withMetadata: true, |
| withUnnamedConstructor: withUnnamedConstructor, |
| introspector: builder, |
| shouldWriteDetailsFor: (declaration) { |
| final name = declaration.identifier.name; |
| return includedNames.remove(name); |
| }, |
| ); |
| |
| // ignore: deprecated_member_use |
| final identifier = await builder.resolveIdentifier( |
| Uri.parse(uriStr), |
| name, |
| ); |
| final declaration = await builder.declarationOf(identifier); |
| await printer.writeAnyDeclaration(declaration); |
| |
| final text = buffer.toString(); |
| |
| builder.augment( |
| FunctionBodyCode.fromString('=> r"""$text""";'), |
| ); |
| } |
| } |
| |
| /// We use [nameToFind] only because we cannot get [Library] by URI. |
| /*macro*/ class LibraryTopLevelDeclarations implements FunctionDefinitionMacro { |
| final String uriStr; |
| final String nameToFind; |
| |
| LibraryTopLevelDeclarations({ |
| required this.uriStr, |
| required this.nameToFind, |
| }); |
| |
| @override |
| Future<void> buildDefinitionForFunction( |
| FunctionDeclaration declaration, |
| FunctionDefinitionBuilder builder, |
| ) async { |
| final buffer = StringBuffer(); |
| final sink = TreeStringSink( |
| sink: buffer, |
| indent: '', |
| ); |
| |
| final includedNames = <String>{}; |
| |
| final printer = _Printer( |
| sink: sink, |
| withMetadata: true, |
| withUnnamedConstructor: false, |
| introspector: builder, |
| shouldWriteDetailsFor: (declaration) { |
| final name = declaration.identifier.name; |
| return includedNames.remove(name); |
| }, |
| ); |
| |
| // ignore: deprecated_member_use |
| final identifier = await builder.resolveIdentifier( |
| Uri.parse(uriStr), |
| nameToFind, |
| ); |
| final declaration = await builder.declarationOf(identifier); |
| final library = declaration.library; |
| |
| sink.writelnWithIndent('topLevelDeclarationsOf'); |
| await sink.withIndent(() async { |
| final topDeclarations = await builder.topLevelDeclarationsOf(library); |
| for (final declaration in topDeclarations) { |
| final name = declaration.identifier.name; |
| if (name != '_starter') { |
| includedNames.add(name); |
| await printer.writeAnyDeclaration(declaration); |
| } |
| } |
| }); |
| |
| final text = buffer.toString(); |
| builder.augment( |
| FunctionBodyCode.fromString('=> r"""$text""";'), |
| ); |
| } |
| } |
| |
| /// Wrapper around a [StringSink] for writing tree structures. |
| class TreeStringSink { |
| final StringSink _sink; |
| String _indent = ''; |
| |
| TreeStringSink({ |
| required StringSink sink, |
| required String indent, |
| }) : _sink = sink, |
| _indent = indent; |
| |
| Future<void> withIndent(Future<void> Function() f) async { |
| final indent = _indent; |
| _indent = '$indent '; |
| await f(); |
| _indent = indent; |
| } |
| |
| void write(Object object) { |
| _sink.write(object); |
| } |
| |
| Future<void> writeElements<T extends Object>( |
| String name, |
| Iterable<T> elements, |
| Future<void> Function(T) f, |
| ) async { |
| if (elements.isNotEmpty) { |
| writelnWithIndent(name); |
| await withIndent(() async { |
| for (final element in elements) { |
| await f(element); |
| } |
| }); |
| } |
| } |
| |
| Future<void> writeFlags(Map<String, bool> flags) async { |
| if (flags.values.any((flag) => flag)) { |
| await writeIndentedLine(() async { |
| write('flags:'); |
| for (final entry in flags.entries) { |
| if (entry.value) { |
| write(' ${entry.key}'); |
| } |
| } |
| }); |
| } |
| } |
| |
| void writeIf(bool flag, Object object) { |
| if (flag) { |
| write(object); |
| } |
| } |
| |
| void writeIndent() { |
| _sink.write(_indent); |
| } |
| |
| Future<void> writeIndentedLine(void Function() f) async { |
| writeIndent(); |
| f(); |
| writeln(); |
| } |
| |
| void writeln([Object? object = '']) { |
| _sink.writeln(object); |
| } |
| |
| void writelnWithIndent(Object object) { |
| _sink.write(_indent); |
| _sink.writeln(object); |
| } |
| |
| void writeWithIndent(Object object) { |
| _sink.write(_indent); |
| _sink.write(object); |
| } |
| } |
| |
| class _Printer { |
| final TreeStringSink sink; |
| final bool withMetadata; |
| final bool withUnnamedConstructor; |
| final DeclarationPhaseIntrospector introspector; |
| final bool Function(Declaration declaration) shouldWriteDetailsFor; |
| |
| Identifier? _enclosingDeclarationIdentifier; |
| |
| _Printer({ |
| required this.sink, |
| required this.withMetadata, |
| required this.withUnnamedConstructor, |
| required this.introspector, |
| required this.shouldWriteDetailsFor, |
| }); |
| |
| Future<void> writeAnyDeclaration(Declaration declaration) async { |
| switch (declaration) { |
| case ClassDeclaration(): |
| await writeClassDeclaration(declaration); |
| case EnumDeclaration(): |
| await writeEnumDeclaration(declaration); |
| case ExtensionDeclaration(): |
| await writeExtensionDeclaration(declaration); |
| case ExtensionTypeDeclaration(): |
| await writeExtensionTypeDeclaration(declaration); |
| case FunctionDeclaration(): |
| await writeFunctionDeclaration(declaration); |
| case MixinDeclaration(): |
| await writeMixinDeclaration(declaration); |
| case VariableDeclaration(): |
| await writeVariable(declaration); |
| default: |
| throw UnimplementedError('${declaration.runtimeType}'); |
| } |
| } |
| |
| Future<void> writeClassDeclaration(ClassDeclaration e) async { |
| if (!shouldWriteDetailsFor(e)) { |
| return; |
| } |
| |
| sink.writelnWithIndent('class ${e.identifier.name}'); |
| |
| await sink.withIndent(() async { |
| await sink.writeFlags({ |
| 'hasAbstract': e.hasAbstract, |
| 'hasBase': e.hasBase, |
| 'hasExternal': e.hasExternal, |
| 'hasFinal': e.hasFinal, |
| 'hasInterface': e.hasInterface, |
| 'hasMixin': e.hasMixin, |
| 'hasSealed': e.hasSealed, |
| }); |
| await _writeMetadata(e); |
| if (e.superclass case final superclass?) { |
| await _writeNamedTypeAnnotation('superclass', superclass); |
| } |
| await _writeTypeParameters(e.typeParameters); |
| await _writeTypeAnnotations('mixins', e.mixins); |
| await _writeTypeAnnotations('interfaces', e.interfaces); |
| await _writeTypeDeclarationMembers(e); |
| }); |
| } |
| |
| Future<void> writeConstructorDeclaration(ConstructorDeclaration e) async { |
| _assertEnclosingClass(e); |
| |
| sink.writelnWithIndent( |
| e.identifier.name.ifNotEmptyOrElse('<unnamed>'), |
| ); |
| |
| await sink.withIndent(() async { |
| await sink.writeFlags({ |
| 'hasBody': e.hasBody, |
| 'hasExternal': e.hasExternal, |
| 'isFactory': e.isFactory, |
| 'isGetter': e.isGetter, |
| 'isOperator': e.isOperator, |
| 'isSetter': e.isSetter, |
| 'isStatic': e.isStatic, |
| }); |
| await _writeMetadata(e); |
| await _writeNamedFormalParameters(e.namedParameters); |
| await _writePositionalFormalParameters(e.positionalParameters); |
| await _writeNamedTypeAnnotation('returnType', e.returnType); |
| await _writeTypeParameters(e.typeParameters); |
| }); |
| } |
| |
| Future<void> writeEnumDeclaration(EnumDeclaration e) async { |
| if (!shouldWriteDetailsFor(e)) { |
| return; |
| } |
| |
| sink.writelnWithIndent('enum ${e.identifier.name}'); |
| |
| await sink.withIndent(() async { |
| await _writeMetadata(e); |
| await _writeTypeParameters(e.typeParameters); |
| await _writeTypeAnnotations('mixins', e.mixins); |
| await _writeTypeAnnotations('interfaces', e.interfaces); |
| await sink.writeElements( |
| 'values', |
| await introspector.valuesOf(e), |
| writeEnumValueDeclaration, |
| ); |
| await _writeTypeDeclarationMembers(e); |
| }); |
| } |
| |
| Future<void> writeEnumValueDeclaration(EnumValueDeclaration e) async { |
| final enclosing = _enclosingDeclarationIdentifier; |
| if (enclosing != null && e.definingEnum != enclosing) { |
| throw StateError('Mismatch: definingEnum'); |
| } |
| |
| sink.writelnWithIndent(e.identifier.name); |
| |
| await sink.withIndent(() async { |
| await _writeMetadata(e); |
| // TODO(scheglov): Write, when added. |
| // await _writeNamedTypeAnnotation('type', e.type); |
| }); |
| } |
| |
| Future<void> writeExtensionDeclaration(ExtensionDeclaration e) async { |
| if (!shouldWriteDetailsFor(e)) { |
| return; |
| } |
| |
| sink.writelnWithIndent('extension ${e.identifier.name}'); |
| |
| await sink.withIndent(() async { |
| await _writeMetadata(e); |
| |
| await _writeTypeParameters(e.typeParameters); |
| await _writeNamedTypeAnnotation('onType', e.onType); |
| await _writeTypeDeclarationMembers(e); |
| }); |
| } |
| |
| Future<void> writeExtensionTypeDeclaration(ExtensionTypeDeclaration e) async { |
| if (!shouldWriteDetailsFor(e)) { |
| return; |
| } |
| |
| sink.writelnWithIndent('extension type ${e.identifier.name}'); |
| |
| await sink.withIndent(() async { |
| await _writeMetadata(e); |
| |
| await _writeTypeParameters(e.typeParameters); |
| await _writeNamedTypeAnnotation( |
| 'representationType', |
| e.representationType, |
| ); |
| await _writeTypeDeclarationMembers(e); |
| }); |
| } |
| |
| Future<void> writeField(FieldDeclaration e) async { |
| _assertEnclosingClass(e); |
| sink.writelnWithIndent(e.identifier.name); |
| |
| await sink.withIndent(() async { |
| await sink.writeFlags({ |
| 'hasAbstract': e.hasAbstract, |
| 'hasExternal': e.hasExternal, |
| 'hasFinal': e.hasFinal, |
| 'hasLate': e.hasLate, |
| 'isStatic': e.isStatic, |
| }); |
| await _writeMetadata(e); |
| await _writeNamedTypeAnnotation('type', e.type); |
| }); |
| } |
| |
| 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); |
| |
| await sink.withIndent(() async { |
| await sink.writeFlags({ |
| 'hasBody': e.hasBody, |
| 'hasExternal': e.hasExternal, |
| 'isGetter': e.isGetter, |
| 'isOperator': e.isOperator, |
| 'isSetter': e.isSetter, |
| 'isStatic': e.isStatic, |
| }); |
| await _writeMetadata(e); |
| await _writeNamedFormalParameters(e.namedParameters); |
| await _writePositionalFormalParameters(e.positionalParameters); |
| await _writeNamedTypeAnnotation('returnType', e.returnType); |
| await _writeTypeParameters(e.typeParameters); |
| }); |
| } |
| |
| Future<void> writeMixinDeclaration(MixinDeclaration e) async { |
| if (!shouldWriteDetailsFor(e)) { |
| return; |
| } |
| |
| sink.writelnWithIndent('mixin ${e.identifier.name}'); |
| |
| await sink.withIndent(() async { |
| await sink.writeFlags({ |
| 'hasBase': e.hasBase, |
| }); |
| |
| await _writeMetadata(e); |
| |
| await _writeTypeParameters(e.typeParameters); |
| await _writeTypeAnnotations( |
| 'superclassConstraints', |
| e.superclassConstraints, |
| ); |
| await _writeTypeAnnotations('interfaces', e.interfaces); |
| await _writeTypeDeclarationMembers(e); |
| }); |
| } |
| |
| Future<void> writeVariable(VariableDeclaration e) async { |
| sink.writelnWithIndent(e.identifier.name); |
| |
| await sink.withIndent(() async { |
| await sink.writeFlags({ |
| 'hasExternal': e.hasExternal, |
| 'hasFinal': e.hasFinal, |
| 'hasLate': e.hasLate, |
| }); |
| await _writeMetadata(e); |
| await _writeNamedTypeAnnotation('type', e.type); |
| }); |
| } |
| |
| void _assertEnclosingClass(MemberDeclaration e) { |
| final enclosing = _enclosingDeclarationIdentifier; |
| if (enclosing != null && e.definingType != enclosing) { |
| throw StateError('Mismatch: definingClass'); |
| } |
| } |
| |
| bool _shouldWriteArguments(ConstructorMetadataAnnotation annotation) { |
| return !const { |
| 'Introspect', |
| }.contains(annotation.type.name); |
| } |
| |
| Future<void> _writeExpressionCode( |
| ExpressionCode code, { |
| String? name, |
| }) async { |
| await sink.writeIndentedLine(() async { |
| if (name != null) { |
| sink.write('$name: '); |
| } |
| sink.write('${code.parts}'); |
| }); |
| } |
| |
| Future<void> _writeFormalParameterDeclaration( |
| FormalParameterDeclaration e) async { |
| sink.writelnWithIndent(e.identifier.name); |
| await sink.withIndent(() async { |
| await sink.writeFlags({ |
| 'isNamed': e.isNamed, |
| 'isRequired': e.isRequired, |
| }); |
| await _writeMetadata(e); |
| await _writeNamedTypeAnnotation('type', e.type); |
| }); |
| } |
| |
| /// If [type] is [OmittedTypeAnnotation], write the inferred type. |
| Future<void> _writeInferredTypeAnnotation(TypeAnnotation type) async { |
| if (type is OmittedTypeAnnotation) { |
| if (introspector case final DefinitionPhaseIntrospector introspector) { |
| final inferred = await introspector.inferType(type); |
| await sink.withIndent(() async { |
| sink.writelnWithIndent('inferred: ${inferred.asString}'); |
| }); |
| } |
| } |
| } |
| |
| Future<void> _writeMetadata(Annotatable e) async { |
| if (withMetadata) { |
| await sink.writeElements( |
| 'metadata', |
| e.metadata, |
| _writeMetadataAnnotation, |
| ); |
| } |
| } |
| |
| Future<void> _writeMetadataAnnotation(MetadataAnnotation e) async { |
| switch (e) { |
| case ConstructorMetadataAnnotation(): |
| sink.writelnWithIndent('ConstructorMetadataAnnotation'); |
| await sink.withIndent(() async { |
| sink.writelnWithIndent('type: ${e.type.name}'); |
| final constructorName = e.constructor.name; |
| if (constructorName.isNotEmpty) { |
| sink.writelnWithIndent('constructorName: $constructorName'); |
| } |
| if (_shouldWriteArguments(e)) { |
| await sink.writeElements( |
| 'positionalArguments', |
| e.positionalArguments, |
| (argument) async { |
| await _writeExpressionCode(argument); |
| }, |
| ); |
| await sink.writeElements( |
| 'namedArguments', |
| e.namedArguments.entries, |
| (entry) async { |
| await _writeExpressionCode(name: entry.key, entry.value); |
| }, |
| ); |
| } |
| }); |
| case IdentifierMetadataAnnotation(): |
| sink.writelnWithIndent('IdentifierMetadataAnnotation'); |
| await sink.withIndent(() async { |
| sink.writelnWithIndent('identifier: ${e.identifier.name}'); |
| }); |
| default: |
| } |
| } |
| |
| Future<void> _writeNamedFormalParameters( |
| Iterable<FormalParameterDeclaration> elements, |
| ) async { |
| await sink.writeElements( |
| 'namedParameters', |
| elements, |
| _writeFormalParameterDeclaration, |
| ); |
| } |
| |
| Future<void> _writeNamedTypeAnnotation( |
| String name, |
| TypeAnnotation? type, |
| ) async { |
| sink.writeWithIndent('$name: '); |
| await _writeTypeAnnotation(type); |
| } |
| |
| Future<void> _writePositionalFormalParameters( |
| Iterable<FormalParameterDeclaration> elements, |
| ) async { |
| await sink.writeElements( |
| 'positionalParameters', |
| elements, |
| _writeFormalParameterDeclaration, |
| ); |
| } |
| |
| Future<void> _writeTypeAnnotation(TypeAnnotation? type) async { |
| if (type != null) { |
| sink.writeln(type.asString); |
| await _writeInferredTypeAnnotation(type); |
| await _writeTypeAnnotationDeclaration(type); |
| } else { |
| sink.writeln('null'); |
| } |
| } |
| |
| Future<void> _writeTypeAnnotationDeclaration(TypeAnnotation type) async { |
| await sink.withIndent(() async { |
| switch (type) { |
| case FunctionTypeAnnotation(): |
| // No declaration. |
| break; |
| case NamedTypeAnnotation(): |
| final identifier = type.identifier; |
| if (identifier.name == 'void') { |
| return; |
| } |
| |
| TypeDeclaration declaration; |
| try { |
| declaration = await introspector.typeDeclarationOf(identifier); |
| } on MacroImplementationException { |
| sink.writelnWithIndent('noDeclaration'); |
| return; |
| } |
| |
| switch (declaration) { |
| case ClassDeclaration(): |
| await writeClassDeclaration(declaration); |
| case EnumDeclaration(): |
| await writeEnumDeclaration(declaration); |
| case MixinDeclaration(): |
| await writeMixinDeclaration(declaration); |
| default: |
| throw UnimplementedError('${declaration.runtimeType}'); |
| } |
| case OmittedTypeAnnotation(): |
| // No declaration, yet. |
| break; |
| default: |
| throw UnimplementedError('(${type.runtimeType}) $type'); |
| } |
| }); |
| } |
| |
| Future<void> _writeTypeAnnotations( |
| String name, |
| Iterable<TypeAnnotation> types, |
| ) async { |
| await sink.writeElements(name, types, (type) async { |
| sink.writeIndent(); |
| await _writeTypeAnnotation(type); |
| }); |
| } |
| |
| Future<void> _writeTypeDeclarationMembers(TypeDeclaration e) async { |
| _enclosingDeclarationIdentifier = e.identifier; |
| |
| final constructors = await introspector.constructorsOf(e); |
| await sink.writeElements( |
| 'constructors', |
| constructors.where((element) { |
| return element.identifier.name.isNotEmpty || withUnnamedConstructor; |
| }), |
| writeConstructorDeclaration, |
| ); |
| |
| await sink.writeElements( |
| 'fields', |
| await introspector.fieldsOf(e), |
| writeField, |
| ); |
| await sink.writeElements( |
| 'methods', |
| await introspector.methodsOf(e), |
| writeMethodDeclaration, |
| ); |
| |
| _enclosingDeclarationIdentifier = null; |
| } |
| |
| Future<void> _writeTypeParameter(TypeParameterDeclaration e) async { |
| sink.writelnWithIndent(e.identifier.name); |
| |
| await sink.withIndent(() async { |
| await _writeMetadata(e); |
| if (e.bound case final bound?) { |
| await _writeNamedTypeAnnotation('bound', bound); |
| } |
| }); |
| } |
| |
| Future<void> _writeTypeParameters( |
| Iterable<TypeParameterDeclaration> elements, |
| ) async { |
| await sink.writeElements('typeParameters', elements, _writeTypeParameter); |
| } |
| } |
| |
| class _TypeAnnotationStringBuilder { |
| final StringSink _sink; |
| |
| _TypeAnnotationStringBuilder(this._sink); |
| |
| void write(TypeAnnotation type) { |
| 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'); |
| } |
| if (type.isNullable) { |
| _sink.write('?'); |
| } |
| } |
| |
| void _writeFormalParameter(FormalParameter 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); |
| if (node.name != null) { |
| _sink.write(' '); |
| _sink.write(node.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(TypeParameter node) { |
| _sink.write(node.name); |
| |
| final bound = node.bound; |
| if (bound != null) { |
| _sink.write(' extends '); |
| write(bound); |
| } |
| } |
| } |
| |
| extension on StringSink { |
| void writeList<T>({ |
| required Iterable<T> elements, |
| required void Function(T element) write, |
| required String separator, |
| String? open, |
| String? close, |
| }) { |
| elements = elements.toList(); |
| 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); |
| } |
| } |
| } |
| |
| extension E on TypeAnnotation { |
| String get asString { |
| final buffer = StringBuffer(); |
| _TypeAnnotationStringBuilder(buffer).write(this); |
| return buffer.toString(); |
| } |
| } |
| |
| extension StringExtension on String { |
| String ifNotEmptyOrElse(String orElse) { |
| return isNotEmpty ? this : orElse; |
| } |
| } |