diff --git a/goldens/foo/lib/json_codable_test.analyzer.augmentations b/goldens/foo/lib/json_codable_test.analyzer.augmentations index d85bbd3c..1811b50b 100644 --- a/goldens/foo/lib/json_codable_test.analyzer.augmentations +++ b/goldens/foo/lib/json_codable_test.analyzer.augmentations @@ -6,19 +6,20 @@ import 'package:foo/json_codable_test.dart' as prefix1; augment class A { // TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external A.fromJson(prefix0.Map json); + +// TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external prefix0.Map toJson(); -augment A.fromJson(prefix0.Map json) : -boolField = json[r'boolField'] as prefix0.bool, -stringField = json[r'stringField'] as prefix0.String, -intField = json[r'intField'] as prefix0.int, -doubleField = json[r'doubleField'] as prefix0.double, -numField = json[r'numField'] as prefix0.num, -listOfSerializableField = [for (final item in json[r'listOfSerializableField'] as prefix0.List) prefix1.C.fromJson(item as prefix0.Map)], -setOfSerializableField = {for (final item in json[r'setOfSerializableField'] as prefix0.List) prefix1.C.fromJson(item as prefix0.Map)}, -mapOfSerializableField = {for (final prefix0.MapEntry(:key, :value) in (json[r'mapOfSerializableField'] as prefix0.Map).entries) key: prefix1.C.fromJson(value as prefix0.Map)}; - -augment prefix0.Map toJson() { +augment A.fromJson(json) + : boolField = json[r'boolField'] as prefix0.bool, + stringField = json[r'stringField'] as prefix0.String, + intField = json[r'intField'] as prefix0.int, + doubleField = json[r'doubleField'] as prefix0.double, + numField = json[r'numField'] as prefix0.num, + listOfSerializableField = [for (final item in json[r'listOfSerializableField'] as prefix0.List) prefix1.C.fromJson(item as prefix0.Map)], + setOfSerializableField = {for (final item in json[r'setOfSerializableField'] as prefix0.List) prefix1.C.fromJson(item as prefix0.Map)}, + mapOfSerializableField = {for (final prefix0.MapEntry(:key, :value) in (json[r'mapOfSerializableField'] as prefix0.Map).entries) key: prefix1.C.fromJson(value as prefix0.Map)}; + augment toJson() { final json = {}; json[r'boolField'] = boolField; json[r'stringField'] = stringField; @@ -36,19 +37,20 @@ json[r'mapOfSerializableField'] = {for (final prefix0.MapEntry(:key, :value) in augment class B { // TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external B.fromJson(prefix0.Map json); + +// TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external prefix0.Map toJson(); -augment B.fromJson(prefix0.Map json) : -nullableBoolField = json[r'nullableBoolField'] as prefix0.bool?, -nullableStringField = json[r'nullableStringField'] as prefix0.String?, -nullableIntField = json[r'nullableIntField'] as prefix0.int?, -nullableDoubleField = json[r'nullableDoubleField'] as prefix0.double?, -nullableNumField = json[r'nullableNumField'] as prefix0.num?, -nullableListOfSerializableField = json[r'nullableListOfSerializableField'] == null ? null : [for (final item in json[r'nullableListOfSerializableField'] as prefix0.List) prefix1.C.fromJson(item as prefix0.Map)], -nullableSetOfSerializableField = json[r'nullableSetOfSerializableField'] == null ? null : {for (final item in json[r'nullableSetOfSerializableField'] as prefix0.List) prefix1.C.fromJson(item as prefix0.Map)}, -nullableMapOfSerializableField = json[r'nullableMapOfSerializableField'] == null ? null : {for (final prefix0.MapEntry(:key, :value) in (json[r'nullableMapOfSerializableField'] as prefix0.Map).entries) key: prefix1.C.fromJson(value as prefix0.Map)}; - -augment prefix0.Map toJson() { +augment B.fromJson(json) + : nullableBoolField = json[r'nullableBoolField'] as prefix0.bool?, + nullableStringField = json[r'nullableStringField'] as prefix0.String?, + nullableIntField = json[r'nullableIntField'] as prefix0.int?, + nullableDoubleField = json[r'nullableDoubleField'] as prefix0.double?, + nullableNumField = json[r'nullableNumField'] as prefix0.num?, + nullableListOfSerializableField = json[r'nullableListOfSerializableField'] == null ? null : [for (final item in json[r'nullableListOfSerializableField'] as prefix0.List) prefix1.C.fromJson(item as prefix0.Map)], + nullableSetOfSerializableField = json[r'nullableSetOfSerializableField'] == null ? null : {for (final item in json[r'nullableSetOfSerializableField'] as prefix0.List) prefix1.C.fromJson(item as prefix0.Map)}, + nullableMapOfSerializableField = json[r'nullableMapOfSerializableField'] == null ? null : {for (final prefix0.MapEntry(:key, :value) in (json[r'nullableMapOfSerializableField'] as prefix0.Map).entries) key: prefix1.C.fromJson(value as prefix0.Map)}; + augment toJson() { final json = {}; if (nullableBoolField != null) { json[r'nullableBoolField'] = nullableBoolField; @@ -82,12 +84,13 @@ json[r'nullableMapOfSerializableField'] = nullableMapOfSerializableField == null augment class C { // TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external C.fromJson(prefix0.Map json); + +// TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external prefix0.Map toJson(); -augment C.fromJson(prefix0.Map json) : -x = json[r'x'] as prefix0.int; - -augment prefix0.Map toJson() { +augment C.fromJson(json) + : x = json[r'x'] as prefix0.int; + augment toJson() { final json = {}; json[r'x'] = x; @@ -98,13 +101,14 @@ json[r'x'] = x; augment class D { // TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external D.fromJson(prefix0.Map json); + +// TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external prefix0.Map toJson(); -augment D.fromJson(prefix0.Map json) : -y = json[r'y'] as prefix0.String, -super.fromJson(json); - -augment prefix0.Map toJson() { +augment D.fromJson(json) + : y = json[r'y'] as prefix0.String, + super.fromJson(json); + augment toJson() { final json = super.toJson(); json[r'y'] = y; @@ -115,20 +119,21 @@ json[r'y'] = y; augment class E { // TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external E.fromJson(prefix0.Map json); + +// TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external prefix0.Map toJson(); -augment E.fromJson(prefix0.Map json) : -listOfNullableInts = [for (final item in json[r'listOfNullableInts'] as prefix0.List) item as prefix0.int?], -listOfNullableSerializables = [for (final item in json[r'listOfNullableSerializables'] as prefix0.List) item == null ? null : prefix1.C.fromJson(item as prefix0.Map)], -listOfNullableMapsOfNullableInts = [for (final item in json[r'listOfNullableMapsOfNullableInts'] as prefix0.List) item == null ? null : {for (final prefix0.MapEntry(:key, :value) in (item as prefix0.Map).entries) key: value as prefix0.int?}], -setOfNullableInts = {for (final item in json[r'setOfNullableInts'] as prefix0.List) item as prefix0.int?}, -setOfNullableSerializables = {for (final item in json[r'setOfNullableSerializables'] as prefix0.List) item == null ? null : prefix1.C.fromJson(item as prefix0.Map)}, -setOfNullableMapsOfNullableInts = {for (final item in json[r'setOfNullableMapsOfNullableInts'] as prefix0.List) item == null ? null : {for (final prefix0.MapEntry(:key, :value) in (item as prefix0.Map).entries) key: value as prefix0.int?}}, -mapOfNullableInts = {for (final prefix0.MapEntry(:key, :value) in (json[r'mapOfNullableInts'] as prefix0.Map).entries) key: value as prefix0.int?}, -mapOfNullableSerializables = {for (final prefix0.MapEntry(:key, :value) in (json[r'mapOfNullableSerializables'] as prefix0.Map).entries) key: value == null ? null : prefix1.C.fromJson(value as prefix0.Map)}, -mapOfNullableSetsOfNullableInts = {for (final prefix0.MapEntry(:key, :value) in (json[r'mapOfNullableSetsOfNullableInts'] as prefix0.Map).entries) key: value == null ? null : {for (final item in value as prefix0.List) item as prefix0.int?}}; - -augment prefix0.Map toJson() { +augment E.fromJson(json) + : listOfNullableInts = [for (final item in json[r'listOfNullableInts'] as prefix0.List) item as prefix0.int?], + listOfNullableSerializables = [for (final item in json[r'listOfNullableSerializables'] as prefix0.List) item == null ? null : prefix1.C.fromJson(item as prefix0.Map)], + listOfNullableMapsOfNullableInts = [for (final item in json[r'listOfNullableMapsOfNullableInts'] as prefix0.List) item == null ? null : {for (final prefix0.MapEntry(:key, :value) in (item as prefix0.Map).entries) key: value as prefix0.int?}], + setOfNullableInts = {for (final item in json[r'setOfNullableInts'] as prefix0.List) item as prefix0.int?}, + setOfNullableSerializables = {for (final item in json[r'setOfNullableSerializables'] as prefix0.List) item == null ? null : prefix1.C.fromJson(item as prefix0.Map)}, + setOfNullableMapsOfNullableInts = {for (final item in json[r'setOfNullableMapsOfNullableInts'] as prefix0.List) item == null ? null : {for (final prefix0.MapEntry(:key, :value) in (item as prefix0.Map).entries) key: value as prefix0.int?}}, + mapOfNullableInts = {for (final prefix0.MapEntry(:key, :value) in (json[r'mapOfNullableInts'] as prefix0.Map).entries) key: value as prefix0.int?}, + mapOfNullableSerializables = {for (final prefix0.MapEntry(:key, :value) in (json[r'mapOfNullableSerializables'] as prefix0.Map).entries) key: value == null ? null : prefix1.C.fromJson(value as prefix0.Map)}, + mapOfNullableSetsOfNullableInts = {for (final prefix0.MapEntry(:key, :value) in (json[r'mapOfNullableSetsOfNullableInts'] as prefix0.Map).entries) key: value == null ? null : {for (final item in value as prefix0.List) item as prefix0.int?}}; + augment toJson() { final json = {}; json[r'listOfNullableInts'] = [for (final item in listOfNullableInts) item]; json[r'listOfNullableSerializables'] = [for (final item in listOfNullableSerializables) item == null ? null : item.toJson()]; @@ -147,12 +152,13 @@ json[r'mapOfNullableSetsOfNullableInts'] = {for (final prefix0.MapEntry(:key, : augment class F { // TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external F.fromJson(prefix0.Map json); + +// TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. // external prefix0.Map toJson(); -augment F.fromJson(prefix0.Map json) : -fieldWithDollarSign$ = json[r'fieldWithDollarSign$'] as prefix0.int; - -augment prefix0.Map toJson() { +augment F.fromJson(json) + : fieldWithDollarSign$ = json[r'fieldWithDollarSign$'] as prefix0.int; + augment toJson() { final json = {}; json[r'fieldWithDollarSign$'] = fieldWithDollarSign$; diff --git a/pkgs/_analyzer_macros/lib/macro_implementation.dart b/pkgs/_analyzer_macros/lib/macro_implementation.dart index 0cd89501..6e55136d 100644 --- a/pkgs/_analyzer_macros/lib/macro_implementation.dart +++ b/pkgs/_analyzer_macros/lib/macro_implementation.dart @@ -168,20 +168,28 @@ class AnalyzerMacroExecutionResult macros_api_v1.MacroTarget target, AugmentResponse augmentResponse) async { final declarations = []; if (augmentResponse.typeAugmentations?.isNotEmpty == true) { - // TODO: Handle multiple type augmentations, or augmentations where the - // target is itself a member of a type and not the type. - final entry = augmentResponse.typeAugmentations!.entries.single; - if (entry.key != target.qualifiedName.name) { - throw UnimplementedError( - 'Type augmentations are only implemented when the type is the ' - 'target of the augmentation.'); - } - for (final augmentation in entry.value) { - declarations.add(macros_api_v1.DeclarationCode.fromParts( - await _resolveNames(augmentation.code))); + // TODO: Handle targets that are not interfaces, or test that this works + // already. + for (final entry in augmentResponse.typeAugmentations!.entries) { + if (entry.key != target.qualifiedName.name) { + throw UnimplementedError( + 'Type augmentations are only implemented when the type is the ' + 'target of the augmentation, expected ' + '${target.qualifiedName.name} but got ${entry.key}'); + } + for (final augmentation in entry.value) { + declarations.add(macros_api_v1.DeclarationCode.fromParts( + await _resolveNames(augmentation.code))); + } } } + for (final augmentation + in augmentResponse.libraryAugmentations ?? const []) { + declarations.add(macros_api_v1.DeclarationCode.fromParts( + await _resolveNames(augmentation.code))); + } + if (augmentResponse.enumValueAugmentations?.isNotEmpty == true) { throw UnimplementedError('Enum value augmentations are not implemented'); } @@ -190,10 +198,6 @@ class AnalyzerMacroExecutionResult augmentResponse.mixinAugmentations?.isNotEmpty == true) { throw UnimplementedError('Type augmentations are not implemented'); } - if (augmentResponse.libraryAugmentations?.isNotEmpty == true || - augmentResponse.newTypeNames?.isNotEmpty == true) { - throw UnimplementedError('Library augmentations are not implemented'); - } return AnalyzerMacroExecutionResult(target, declarations); } diff --git a/pkgs/_macro_client/lib/macro_client.dart b/pkgs/_macro_client/lib/macro_client.dart index 3e8ff5d7..b036a2f7 100644 --- a/pkgs/_macro_client/lib/macro_client.dart +++ b/pkgs/_macro_client/lib/macro_client.dart @@ -155,6 +155,11 @@ class MacroClient { } } +/// Interface for the remote Host API for macros to query against. +abstract class Host { + Future query(Query query); +} + /// [Host] that is connected to a remote macro host. /// /// Wraps `MacroClient` exposing just what should be available to the macro. diff --git a/pkgs/_macro_client/lib/src/builder_impls.dart b/pkgs/_macro_client/lib/src/builder_impls.dart new file mode 100644 index 00000000..cbe59021 --- /dev/null +++ b/pkgs/_macro_client/lib/src/builder_impls.dart @@ -0,0 +1,344 @@ +// Copyright (c) 2024, 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 'package:dart_model/dart_model.dart'; +import 'package:macro/macro.dart'; +import 'package:macro_service/macro_service.dart'; + +import '../macro_client.dart'; + +abstract base class BuilderBase implements Builder { + @override + final T target; + + final Host host; + + final AugmentResponse response; + + @override + Model get model => MacroScope.current.model; + + BuilderBase(this.target, this.host) + : response = AugmentResponse(libraryAugmentations: [], newTypeNames: []); + + BuilderBase.nested(this.target, this.host, this.response); + + @override + Future query(Query query) => host.query(query); +} + +abstract base class TypesBuilderBase extends BuilderBase + implements TypesBuilder { + TypesBuilderBase(super.target, super.host); + + TypesBuilderBase.nested(super.target, super.host, super.response) + : super.nested(); + + @override + void declareType(String name, Augmentation typeDeclaration) { + response.newTypeNames!.add(name); + response.libraryAugmentations!.add(typeDeclaration); + } +} + +base mixin ExtendsClauseBuilderImpl on TypesBuilderBase + implements ExtendsClauseBuilder { + /// Sets the `extends` clause to [superclass]. + /// + /// The type must not already have an `extends` clause. + @override + void extendsType(Augmentation superclass) { + response.extendsTypeAugmentations!.update( + model.qualifiedNameOf(target.node)!.name, + // TODO: This should only ever have one thing, but we don't have setters + // so a list is a hack for lazily adding this field. We should probably + // throw though if we see more than one. + (value) => value..add(superclass), + ifAbsent: () => [superclass], + ); + } +} + +base mixin ImplementsClauseBuilderImpl + on TypesBuilderBase implements ImplementsClauseBuilder { + /// Appends [interfaces] to the list of interfaces for this type. + @override + void appendInterfaces(Iterable interfaces) { + response.interfaceAugmentations!.update( + model.qualifiedNameOf(target.node)!.name, + (value) => value..addAll(interfaces), + ifAbsent: () => [...interfaces], + ); + } +} + +base mixin WithClauseBuilderImpl on TypesBuilderBase + implements WithClauseBuilder { + /// Appends [mixins] to the list of mixins for this type. + @override + void appendMixins(Iterable mixins) { + response.mixinAugmentations!.update( + model.qualifiedNameOf(target.node)!.name, + (value) => value..addAll(mixins), + ifAbsent: () => [...mixins], + ); + } +} + +final class ClassTypesBuilderImpl + extends TypesBuilderBase + with + WithClauseBuilderImpl, + ImplementsClauseBuilderImpl, + ExtendsClauseBuilderImpl + implements ClassTypesBuilder { + ClassTypesBuilderImpl(super.target, super.host); + + ClassTypesBuilderImpl.nested(super.target, super.host, super.response) + : super.nested(); +} + +abstract base class DeclarationsBuilderBase + extends BuilderBase implements DeclarationsBuilder { + DeclarationsBuilderBase(super.target, super.host); + + DeclarationsBuilderBase.nested(super.target, super.host, super.response) + : super.nested(); + + @override + void declareInLibrary(Augmentation declaration) { + response.libraryAugmentations!.add(declaration); + } +} + +abstract base class MemberDeclarationsBuilderBase + extends DeclarationsBuilderBase implements MemberDeclarationsBuilder { + MemberDeclarationsBuilderBase(super.target, super.host); + + MemberDeclarationsBuilderBase.nested(super.target, super.host, super.response) + : super.nested(); + + @override + void declareInType(Augmentation declaration) { + response.typeAugmentations!.update( + model.qualifiedNameOf(target.node)!.name, + (value) => value..add(declaration), + ifAbsent: () => [declaration], + ); + } +} + +final class ClassDeclarationsBuilderImpl + extends MemberDeclarationsBuilderBase + implements ClassDeclarationsBuilder { + ClassDeclarationsBuilderImpl(super.target, super.host); + + ClassDeclarationsBuilderImpl.nested(super.target, super.host, super.response) + : super.nested(); +} + +final class MethodDefinitionsBuilderImpl + extends BuilderBase implements MethodDefinitionsBuilder { + MethodDefinitionsBuilderImpl(super.target, super.host); + + MethodDefinitionsBuilderImpl.nested(super.target, super.host, super.response) + : super.nested(); + + @override + void augment({Augmentation? body, Augmentation? docCommentsAndMetadata}) { + final augmentation = _buildFunctionAugmentation(body, target, model, + docComments: docCommentsAndMetadata); + response.typeAugmentations!.update( + target.parentInterface.name, (value) => value..add(augmentation), + ifAbsent: () => [augmentation]); + } +} + +final class ConstructorDefinitionsBuilderImpl + extends BuilderBase implements ConstructorDefinitionsBuilder { + ConstructorDefinitionsBuilderImpl(super.target, super.host); + + ConstructorDefinitionsBuilderImpl.nested( + super.target, super.host, super.response) + : super.nested(); + + @override + void augment( + {Augmentation? body, + List? initializers, + Augmentation? docCommentsAndMetadata}) { + final augmentation = _buildFunctionAugmentation(body, target, model, + initializers: initializers, docComments: docCommentsAndMetadata); + response.typeAugmentations!.update( + target.parentInterface.name, (value) => value..add(augmentation), + ifAbsent: () => [augmentation]); + } +} + +abstract base class InterfaceDefinitionsBuilderBase + extends BuilderBase implements InterfaceDefinitionsBuilder { + InterfaceDefinitionsBuilderBase(super.target, super.host); + + InterfaceDefinitionsBuilderBase.nested( + super.target, super.host, super.response) + : super.nested(); + + /// Retrieve a [FieldDefinitionsBuilder] for a field with [name] in + /// [target]. + /// + /// Throws if [name] does not refer to a field in the [target] interface + /// declaration. + @override + FieldDefinitionsBuilder buildField(QualifiedName name) => + throw UnimplementedError(); + + /// Retrieve a [MethodDefinitionsBuilder] for a method with [name] in + /// [target]. + /// + /// Throws if [name] does not refer to a method in the [target] interface + /// declaration. + @override + MethodDefinitionsBuilder buildMethod(QualifiedName name) => + MethodDefinitionsBuilderImpl.nested( + Member.fromJson(model.lookup(name)!), host, response); + + /// Retrieve a [ConstructorDefinitionsBuilder] for a constructor with [name] + /// in [target]. + /// + /// Throws if [name] does not refer to a constructor in the [target] + /// interface declaration. + @override + ConstructorDefinitionsBuilder buildConstructor(QualifiedName name) => + ConstructorDefinitionsBuilderImpl.nested( + Member.fromJson(model.lookup(name)!), host, response); +} + +final class ClassDefinitionsBuilderImpl + extends InterfaceDefinitionsBuilderBase + implements ClassDefinitionsBuilder { + ClassDefinitionsBuilderImpl(super.target, super.host); + + ClassDefinitionsBuilderImpl.nested(super.target, super.host, super.response) + : super.nested(); + + @override + void augment({Augmentation? docCommentsAndMetadata}) { + if (docCommentsAndMetadata == null) return; + response.typeAugmentations!.update( + model.qualifiedNameOf(target.node)!.name, + (value) => value..add(docCommentsAndMetadata), + ifAbsent: () => [docCommentsAndMetadata], + ); + } +} + +/// Builds the code to augment a function, method, or constructor with a new +/// body. +/// +/// The [initializers] parameter can only be used if [declaration] is a +/// constructor. +Augmentation _buildFunctionAugmentation( + Augmentation? body, Member declaration, Model model, + {List? initializers, Augmentation? docComments}) { + assert(initializers == null || declaration.properties.isConstructor); + final properties = declaration.properties; + final qualifiedName = model.qualifiedNameOf(declaration.node)!; + final parts = [ + if (docComments != null) ...[docComments, '\n'], + if (declaration.properties.isMethod) ' ', + 'augment ', + if (properties.isConstructor) ...[ + // TODO: Uncomment once we have `isConst`. + // if (properties.isConst) 'const ', + // TODO: Uncomment once we have `isFactory`. + // if (properties.isFactory) 'factory ', + declaration.parentInterface.name, + if (qualifiedName.name.isNotEmpty) '.', + ] else ...[ + if (properties.isMethod && properties.isStatic) 'static ', + // TODO: Uncomment once we have support for converting types into code. + // declaration.returnType.code, + ' ', + // TODO: Uncomment once we have `isOperator`. + // if (properties.isOperator) 'operator ', + ], + if (properties.isGetter) 'get ', + // TODO: Uncomment once we have `isSetter`. + if (properties.isGetter) 'set ', + qualifiedName.name, + if (!properties.isGetter) ...[ + // TODO: Uncomment once we have `typeParameters`. + // if (declaration.typeParameters.isNotEmpty) ...[ + // '<', + // for (TypeParameterDeclaration typeParam + // in declaration.typeParameters) ...[ + // typeParam.identifier.name, + // if (typeParam.bound != null) ...[ + // ' extends ', + // typeParam.bound!.code, + // ], + // if (typeParam != declaration.typeParameters.last) ', ', + // ], + // '>', + // ], + '(', + // TODO: Extreme hack alert!!! Parameters only expose their types today + // which isn't enough, this assumes we are a fromJson method :D. + if (declaration.requiredPositionalParameters.length == 1) + 'json' + else if (declaration.requiredPositionalParameters.isNotEmpty) + throw UnimplementedError(), + // for (var positionalRequired + // in declaration.requiredPositionalParameters) ...[ + // positionalRequired.code, + // ', ', + // ], + if (declaration.optionalPositionalParameters.isNotEmpty) ...[ + // TODO: Implement this. + throw UnimplementedError(), + // '[', + // for (var positionalOptional + // in declaration.optionalPositionalParameters) ...[ + // positionalOptional.code, + // ', ', + // ], + // ']', + ], + if (declaration.namedParameters.isNotEmpty) ...[ + // TODO: Implement this. + throw UnimplementedError(), + // '{', + // for (var named in declaration.namedParameters) ...[ + // named.code, + // ', ', + // ], + // '}', + ], + ')', + ], + if (initializers != null && initializers.isNotEmpty) ...[ + '\n : ', + ...initializers.first.code, + for (var initializer in initializers.skip(1)) ...[ + ',\n ', + ...initializer.code, + ], + ], + if (body == null) + ';' + else ...[ + ' ', + ...body.code, + ] + ]; + return Augmentation(code: [ + for (var part in parts) + switch (part) { + String() => Code.string(part), + // TODO: All maps will pass this check... + Code() => part, + _ => throw StateError('Unexpected code kind $part'), + }, + ]); +} diff --git a/pkgs/_macro_client/lib/src/execute_macro.dart b/pkgs/_macro_client/lib/src/execute_macro.dart index 7fbc6656..5f496e23 100644 --- a/pkgs/_macro_client/lib/src/execute_macro.dart +++ b/pkgs/_macro_client/lib/src/execute_macro.dart @@ -8,6 +8,9 @@ import 'package:dart_model/dart_model.dart'; import 'package:macro/macro.dart'; import 'package:macro_service/macro_service.dart'; +import '../macro_client.dart'; +import 'builder_impls.dart'; + /// Runs [macro] in the types phase and returns an [AugmentResponse]. Future executeTypesMacro( Macro macro, Host host, AugmentRequest request) async { @@ -17,7 +20,9 @@ Future executeTypesMacro( switch ((interface.properties, macro)) { case (Properties(isClass: true), ClassTypesMacro macro): - return await macro.buildTypesForClass(interface, model, host); + final builder = ClassTypesBuilderImpl(interface, host); + await macro.buildTypesForClass(builder); + return builder.response; case (_, LibraryTypesMacro()): case (_, ConstructorTypesMacro()): case (_, MethodTypesMacro()): @@ -46,7 +51,9 @@ Future executeDeclarationsMacro( switch ((interface.properties, macro)) { case (Properties(isClass: true), ClassDeclarationsMacro macro): - return await macro.buildDeclarationsForClass(interface, model, host); + final builder = ClassDeclarationsBuilderImpl(interface, host); + await macro.buildDeclarationsForClass(builder); + return builder.response; case (_, LibraryDeclarationsMacro()): case (_, EnumDeclarationsMacro()): case (_, ExtensionDeclarationsMacro()): @@ -75,7 +82,9 @@ Future executeDefinitionsMacro( switch ((interface.properties, macro)) { case (Properties(isClass: true), ClassDefinitionsMacro macro): - return await macro.buildDefinitionsForClass(interface, model, host); + final builder = ClassDefinitionsBuilderImpl(interface, host); + await macro.buildDefinitionsForClass(builder); + return builder.response; case (_, LibraryDefinitionsMacro()): case (_, EnumDefinitionsMacro()): case (_, ExtensionDefinitionsMacro()): diff --git a/pkgs/_macro_client/test/macro_client_test.dart b/pkgs/_macro_client/test/macro_client_test.dart index 23e64923..91d9bcbf 100644 --- a/pkgs/_macro_client/test/macro_client_test.dart +++ b/pkgs/_macro_client/test/macro_client_test.dart @@ -139,6 +139,8 @@ void main() { 'extendsTypeAugmentations': {}, 'interfaceAugmentations': {}, 'mixinAugmentations': {}, + 'libraryAugmentations': [], + 'newTypeNames': [], 'typeAugmentations': { 'Foo': [ { @@ -155,7 +157,7 @@ void main() { }); }); - test('sends query requests to host, sends reponse', () async { + test('sends query requests to host, sends response', () async { final serverSocket = await ServerSocket.bind('localhost', 0); unawaited(MacroClient.run( @@ -221,6 +223,8 @@ void main() { 'extendsTypeAugmentations': {}, 'interfaceAugmentations': {}, 'mixinAugmentations': {}, + 'libraryAugmentations': [], + 'newTypeNames': [], 'typeAugmentations': { 'Foo': [ { @@ -301,6 +305,8 @@ void main() { 'extendsTypeAugmentations': {}, 'interfaceAugmentations': {}, 'mixinAugmentations': {}, + 'libraryAugmentations': [], + 'newTypeNames': [], 'typeAugmentations': { 'Foo': [ { @@ -328,6 +334,8 @@ void main() { 'extendsTypeAugmentations': {}, 'interfaceAugmentations': {}, 'mixinAugmentations': {}, + 'libraryAugmentations': [], + 'newTypeNames': [], 'typeAugmentations': { 'Foo': [ { diff --git a/pkgs/_macro_host/test/macro_host_test.dart b/pkgs/_macro_host/test/macro_host_test.dart index d8d6c1fe..67c45a45 100644 --- a/pkgs/_macro_host/test/macro_host_test.dart +++ b/pkgs/_macro_host/test/macro_host_test.dart @@ -40,12 +40,13 @@ void main() { expect( await host.augment(macroAnnotation, AugmentRequest(phase: 2, target: fooTarget, model: fooModel)), - Scope.macro.run(() => AugmentResponse() - ..typeAugmentations!['Foo'] = [ - Augmentation(code: [ - Code.string('int get x => 3;'), - ]), - ])); + Scope.macro.run(() => + AugmentResponse(libraryAugmentations: [], newTypeNames: []) + ..typeAugmentations!['Foo'] = [ + Augmentation(code: [ + Code.string('int get x => 3;'), + ]), + ])); }); test('hosts a macro, responds to queries', () async { @@ -67,14 +68,15 @@ void main() { expect( await host.augment(macroAnnotation, AugmentRequest(phase: 3, target: fooTarget, model: fooModel)), - Scope.macro.run(() => AugmentResponse() - ..typeAugmentations!['Foo'] = [ - Augmentation(code: [ - Code.string( - '// {"uris":{"package:foo/foo.dart":{"scopes":{"Foo":{' - '"members":{},"properties":{"isClass":true}}}}}}'), - ]), - ])); + Scope.macro.run(() => + AugmentResponse(libraryAugmentations: [], newTypeNames: []) + ..typeAugmentations!['Foo'] = [ + Augmentation(code: [ + Code.string( + '// {"uris":{"package:foo/foo.dart":{"scopes":{"Foo":{' + '"members":{},"properties":{"isClass":true}}}}}}'), + ]), + ])); }); test('hosts two macros', () async { diff --git a/pkgs/_test_macros/lib/declare_x_macro.dart b/pkgs/_test_macros/lib/declare_x_macro.dart index fe256bae..e3b414ad 100644 --- a/pkgs/_test_macros/lib/declare_x_macro.dart +++ b/pkgs/_test_macros/lib/declare_x_macro.dart @@ -22,11 +22,8 @@ class DeclareXImplementation implements ClassDeclarationsMacro { runsInPhases: [2]); @override - Future buildDeclarationsForClass( - Interface target, Model model, Host host) async { - return AugmentResponse() - ..typeAugmentations![model.qualifiedNameOf(target.node)!.name] = [ - Augmentation(code: expandTemplate('int get x => 3;')) - ]; + void buildDeclarationsForClass(ClassDeclarationsBuilder builder) { + builder + .declareInType(Augmentation(code: expandTemplate('int get x => 3;'))); } } diff --git a/pkgs/_test_macros/lib/json_codable.dart b/pkgs/_test_macros/lib/json_codable.dart index 237aa6d7..5f886b79 100644 --- a/pkgs/_test_macros/lib/json_codable.dart +++ b/pkgs/_test_macros/lib/json_codable.dart @@ -27,48 +27,40 @@ class JsonCodableImplementation runsInPhases: [2, 3]); @override - Future buildDeclarationsForClass( - Interface target, Model model, Host host) async { - final qualifiedName = model.qualifiedNameOf(target.node)!; - return AugmentResponse() - ..typeAugmentations![qualifiedName.name] = [ - Augmentation(code: expandTemplate(''' + void buildDeclarationsForClass(ClassDeclarationsBuilder builder) { + final name = builder.model.qualifiedNameOf(builder.target.node)!.name; + builder + ..declareInType(Augmentation(code: expandTemplate(''' +// TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. +// external $name.fromJson($_jsonMapType json); + '''))) + ..declareInType(Augmentation(code: expandTemplate(''' // TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80. -// external ${qualifiedName.name}.fromJson($_jsonMapType json); // external $_jsonMapType toJson(); - ''')) - ]; + '''))); } @override - Future buildDefinitionsForClass( - Interface target, Model model, Host host) async { - final qualifiedName = model.qualifiedNameOf(target.node)!; + void buildDefinitionsForClass(ClassDefinitionsBuilder builder) async { + final qualifiedName = builder.model.qualifiedNameOf(builder.target.node)!; // TODO(davidmorgan): put `extends` information directly in `Interface`. final superclassName = MacroScope.current.typeSystem.supertypeOf(qualifiedName); - return AugmentResponse() - ..typeAugmentations![qualifiedName.name] = [ - await _generateFromJson( - host, model, qualifiedName, superclassName, target), - await _generateToJson( - host, model, qualifiedName, superclassName, target), - ]; + await _generateFromJson(builder, qualifiedName, superclassName); + await _generateToJson(builder, qualifiedName, superclassName); } - Future _generateFromJson( - Host host, - Model model, + Future _generateFromJson( + InterfaceDefinitionsBuilder builder, QualifiedName target, QualifiedName superclassName, - Interface clazz, ) async { var superclassHasFromJson = false; // TODO(davidmorgan): add recommended way to check for core types. if (superclassName.asString != 'dart:core#Object') { // TODO(davidmorgan): first query could already fetch the super class. - final supermodel = await host.query(Query(target: superclassName)); + final supermodel = await builder.query(Query(target: superclassName)); final superclass = supermodel.uris[superclassName.uri]!.scopes[superclassName.name]!; final constructor = superclass.members['fromJson']; @@ -84,8 +76,8 @@ class JsonCodableImplementation } final initializers = []; - for (final field - in clazz.members.entries.where((m) => m.value.properties.isField)) { + for (final field in builder.target.members.entries + .where((m) => m.value.properties.isField)) { final name = field.key; final type = field.value.returnType; initializers @@ -96,25 +88,24 @@ class JsonCodableImplementation initializers.add('super.fromJson(json)'); } - // TODO(davidmorgan): helper for augmenting initializers. - // See: https://github.com/dart-lang/sdk/blob/main/pkg/_macros/lib/src/executor/builder_impls.dart#L500 - return Augmentation(code: expandTemplate(''' -augment ${target.name}.fromJson($_jsonMapType json) : -${initializers.join(',\n')}; -''')); + builder + .buildConstructor(builder.model + .qualifiedNameOf(builder.target.members['fromJson']!.node)!) + .augment(initializers: [ + for (var initializer in initializers) + Augmentation(code: expandTemplate(initializer)), + ]); } - Future _generateToJson( - Host host, - Model model, + Future _generateToJson( + InterfaceDefinitionsBuilder builder, QualifiedName target, QualifiedName superclassName, - Interface clazz, ) async { var superclassHasToJson = false; if (superclassName.asString != 'dart:core#Object') { // TODO(davidmorgan): first query could already fetch the super class. - final supermodel = await host.query(Query(target: superclassName)); + final supermodel = await builder.query(Query(target: superclassName)); final superclass = supermodel.uris[superclassName.uri]!.scopes[superclassName.name]!; final method = superclass.members['toJson']; @@ -130,8 +121,8 @@ ${initializers.join(',\n')}; } final serializers = []; - for (final field - in clazz.members.entries.where((m) => m.value.properties.isField)) { + for (final field in builder.target.members.entries + .where((m) => m.value.properties.isField)) { final name = field.key; final type = field.value.returnType; var serializer = "json[r'$name'] = ${_convertTypeToJson(name, type)};\n"; @@ -145,13 +136,16 @@ ${initializers.join(',\n')}; // See: https://github.com/dart-lang/sdk/blob/main/pkg/_macros/lib/src/executor/builder_impls.dart#L500 final jsonInitializer = superclassHasToJson ? 'super.toJson()' : '$_jsonMapTypeForLiteral{}'; - return Augmentation(code: expandTemplate(''' -augment $_jsonMapType toJson() { + builder + .buildMethod(builder.model + .qualifiedNameOf(builder.target.members['toJson']!.node)!) + .augment(body: Augmentation(code: expandTemplate(''' +{ final json = $jsonInitializer; ${serializers.join('')} return json; } -''')); +'''))); } /// Returns whether [constructor] is a constructor diff --git a/pkgs/_test_macros/lib/query_class.dart b/pkgs/_test_macros/lib/query_class.dart index 3ddd6f04..92b01070 100644 --- a/pkgs/_test_macros/lib/query_class.dart +++ b/pkgs/_test_macros/lib/query_class.dart @@ -25,15 +25,11 @@ class QueryClassImplementation implements ClassDefinitionsMacro { runsInPhases: [3]); @override - Future buildDefinitionsForClass( - Interface target, Model model, Host host) async { - final qualifiedName = model.qualifiedNameOf(target.node)!; - final result = await host.query(Query( - target: model.qualifiedNameOf(target.node), - )); - return AugmentResponse() - ..typeAugmentations![qualifiedName.name] = [ - Augmentation(code: expandTemplate('// ${json.encode(result)}')) - ]; + Future buildDefinitionsForClass(ClassDefinitionsBuilder builder) async { + final qualifiedName = builder.model.qualifiedNameOf(builder.target.node)!; + final result = await builder.query(Query(target: qualifiedName)); + builder.augment( + docCommentsAndMetadata: + Augmentation(code: expandTemplate('// ${json.encode(result)}'))); } } diff --git a/pkgs/dart_model/lib/src/dart_model.dart b/pkgs/dart_model/lib/src/dart_model.dart index 02f7d7cd..f2959cdd 100644 --- a/pkgs/dart_model/lib/src/dart_model.dart +++ b/pkgs/dart_model/lib/src/dart_model.dart @@ -5,6 +5,7 @@ import 'dart_model.g.dart'; import 'json_buffer/json_buffer_builder.dart'; import 'lazy_merged_map.dart'; +import 'scopes.dart'; export 'dart_model.g.dart'; @@ -19,7 +20,32 @@ extension QualifiedNameExtension on QualifiedName { other.isStatic == isStatic; } +extension ParentInterface on Member { + QualifiedName get parentInterface { + final self = MacroScope.current.model.qualifiedNameOf(node)!; + return QualifiedName(uri: self.uri, name: self.scope); + } +} + extension ModelExtension on Model { + /// Looks up [name] in `this`. + /// + /// It is on the user to cast this to a proper extension type. + /// + /// Returns null if it is not present. + // TODO: return a `Declaration` interface? + Map? lookup(QualifiedName name) { + final library = uris[name.uri]; + if (library == null) return null; + if (name.scope == null) { + throw UnsupportedError( + 'Can only look up names in nested scopes for now.'); + } + final scope = library.scopes[name.scope!]; + if (scope == null) return null; + return scope.members[name.name]?.node; + } + /// Returns the path in the model to [node], or `null` if /// [node] is not in this [Model]. /// @@ -76,7 +102,7 @@ extension ModelExtension on Model { /// /// Throws if [value] is not in [map]. String _keyOf(Object value, Map map) { - for (final entry in map.entries) { + for (final entry in map.expandWithMerged.expand((map) => map.entries)) { if (entry.value == value) return entry.key; } throw ArgumentError('Value not in map: $value, $map'); @@ -114,11 +140,9 @@ extension ModelExtension on Model { static void _buildParentsMap(Map parent, Map, Map> result) { for (final child in parent.values.whereType>()) { - if (result.containsKey(child)) { - throw StateError( - 'Same node found twice.\n\nChild:\n$child\n\nParent:\n$parent'); - } else { - result[child] = parent; + for (final map in child.expandWithMerged) { + if (result.containsKey(map)) continue; + result[map] = parent; _buildParentsMap(child, result); } } diff --git a/pkgs/dart_model/lib/src/lazy_merged_map.dart b/pkgs/dart_model/lib/src/lazy_merged_map.dart index 2b6e2198..4e1815c3 100644 --- a/pkgs/dart_model/lib/src/lazy_merged_map.dart +++ b/pkgs/dart_model/lib/src/lazy_merged_map.dart @@ -39,7 +39,14 @@ class LazyMergedMapView extends MapBase { rightValue is Map) { return LazyMergedMapView(leftValue, rightValue); } - if (leftValue != rightValue) { + if (leftValue is List && rightValue is List) { + if (leftValue.length != rightValue.length) { + throw StateError('Cannot merge lists of different lengths, ' + 'got $leftValue and $rightValue'); + } + // TODO: Something better for lists, it isn't clear how to merge them. + return leftValue; + } else if (leftValue != rightValue) { throw StateError('Cannot merge maps with different values, and ' '$leftValue != $rightValue'); } @@ -56,6 +63,13 @@ class LazyMergedMapView extends MapBase { void operator []=(String key, Object? value) => throw UnsupportedError('Merged maps are read only'); + @override + bool operator ==(Object other) => + other is LazyMergedMapView && other.left == left && other.right == right; + + @override + int get hashCode => Object.hash(left, right); + @override void clear() => throw UnsupportedError('Merged maps are read only'); @@ -82,6 +96,18 @@ extension AllMaps on Map { yield this; } } + + /// All the maps merged into this map, recursively expanded, including the + /// merged map objects themselves. + Iterable> get expandWithMerged sync* { + if (this case final LazyMergedMapView self) { + yield self; + yield* self.left.expand; + yield* self.right.expand; + } else { + yield this; + } + } } extension MergeModels on Model { diff --git a/pkgs/dart_model/test/model_test.dart b/pkgs/dart_model/test/model_test.dart index f9cc4a30..7c3386fe 100644 --- a/pkgs/dart_model/test/model_test.dart +++ b/pkgs/dart_model/test/model_test.dart @@ -169,23 +169,37 @@ void main() { }); }); - test('path to Members throws on cycle', () { + test('path to Members works for cycles', () { final copiedModel = Model.fromJson(_copyMap(model.node)); // Add an invalid link creating a loop in the map structure. (copiedModel.node['uris'] as Map)['loop'] = copiedModel; final member = copiedModel.uris['package:dart_model/dart_model.dart']! .scopes['JsonData']!.members['_root']!; - expect(() => copiedModel.qualifiedNameOf(member.node), throwsStateError); + expect( + copiedModel.qualifiedNameOf(member.node), + QualifiedName( + uri: 'package:dart_model/dart_model.dart', + scope: 'JsonData', + name: '_root', + isStatic: false, + )); }); - test('path to Members throws on reused node', () { + test('path to Members works for reused node', () { final copiedModel = Model.fromJson(_copyMap(model.node)); // Reuse a node. copiedModel.uris['duplicate'] = copiedModel.uris['package:dart_model/dart_model.dart']!; final member = copiedModel.uris['package:dart_model/dart_model.dart']! .scopes['JsonData']!.members['_root']!; - expect(() => copiedModel.qualifiedNameOf(member.node), throwsStateError); + expect( + copiedModel.qualifiedNameOf(member.node), + QualifiedName( + uri: 'package:dart_model/dart_model.dart', + scope: 'JsonData', + name: '_root', + isStatic: false, + )); }); test('path to Member returns null for Member in wrong Map', () { diff --git a/pkgs/macro/lib/macro.dart b/pkgs/macro/lib/macro.dart index 4b4322e1..5efc9ab5 100644 --- a/pkgs/macro/lib/macro.dart +++ b/pkgs/macro/lib/macro.dart @@ -2,403 +2,5 @@ // 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:dart_model/dart_model.dart'; -import 'package:macro_service/macro_service.dart'; - -/// The macro host. -abstract interface class Host { - Future query(Query query); - // TODO(davidmorgan): introspection methods go here. -} - -/// The marker interface for all types of macros. -abstract interface class Macro { - /// Description of the macro. - /// - /// TODO(davidmorgan): where possible the macro information should be - /// determined by `macro_builder` and injected in the bootstrap script rather - /// than relying on the user-written macro code to return it. - /// - MacroDescription get description; -} - -/// The interface for [Macro]s that can be applied to a library directive, and -/// want to contribute new type declarations to the library. -abstract interface class LibraryTypesMacro implements Macro { - FutureOr buildTypesForLibrary( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to a library directive, and -/// want to contribute new non-type declarations to the library. -abstract interface class LibraryDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForLibrary( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to a library directive, and -/// want to provide definitions for declarations in the library. -abstract interface class LibraryDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForLibrary( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any top level function, -/// instance method, or static method, and want to contribute new type -/// declarations to the program. -abstract interface class FunctionTypesMacro implements Macro { - FutureOr buildTypesForFunction( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any top level function, -/// instance method, or static method, and want to contribute new non-type -/// declarations to the program. -abstract interface class FunctionDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForFunction( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any top level function, -/// instance method, or static method, and want to augment the function -/// definition. -abstract interface class FunctionDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForFunction( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any top level variable or -/// instance field, and want to contribute new type declarations to the -/// program. -abstract interface class VariableTypesMacro implements Macro { - FutureOr buildTypesForVariable( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any top level variable or -/// instance field and want to contribute new non-type declarations to the -/// program. -abstract interface class VariableDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForVariable( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any top level variable -/// or instance field, and want to augment the variable definition. -abstract interface class VariableDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForVariable( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any class, and want to -/// contribute new type declarations to the program. -abstract interface class ClassTypesMacro implements Macro { - FutureOr buildTypesForClass( - Interface target, Model model, Host host); -} - -/// The interface for [Macro]s that can be applied to any class, and want to -/// contribute new non-type declarations to the program. -abstract interface class ClassDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForClass( - Interface target, Model model, Host host); -} - -/// The interface for [Macro]s that can be applied to any class, and want to -/// augment the definitions of the members of that class. -abstract interface class ClassDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForClass( - Interface target, Model model, Host host); -} - -/// The interface for [Macro]s that can be applied to any enum, and want to -/// contribute new type declarations to the program. -abstract interface class EnumTypesMacro implements Macro { - FutureOr buildTypesForEnum( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any enum, and want to -/// contribute new non-type declarations to the program. -abstract interface class EnumDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForEnum( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any enum, and want to -/// augment the definitions of members or values of that enum. -abstract interface class EnumDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForEnum( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any enum, and want to -/// contribute new type declarations to the program. -abstract interface class EnumValueTypesMacro implements Macro { - FutureOr buildTypesForEnumValue( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any enum, and want to -/// contribute new non-type declarations to the program. -abstract interface class EnumValueDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForEnumValue( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any enum, and want to -/// augment the definitions of members or values of that enum. -abstract interface class EnumValueDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForEnumValue( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any field, and want to -/// contribute new type declarations to the program. -abstract interface class FieldTypesMacro implements Macro { - FutureOr buildTypesForField( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any field, and want to -/// contribute new type declarations to the program. -abstract interface class FieldDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForField( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any field, and want to -/// augment the field definition. -abstract interface class FieldDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForField( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any method, and want to -/// contribute new type declarations to the program. -abstract interface class MethodTypesMacro implements Macro { - FutureOr buildTypesForMethod( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any method, and want to -/// contribute new non-type declarations to the program. -abstract interface class MethodDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForMethod( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any method, and want to -/// augment the function definition. -abstract interface class MethodDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForMethod( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any constructor, and want -/// to contribute new type declarations to the program. -abstract interface class ConstructorTypesMacro implements Macro { - FutureOr buildTypesForConstructor( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any constructors, and -/// want to contribute new non-type declarations to the program. -abstract interface class ConstructorDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForConstructor( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any constructor, and want -/// to augment the function definition. -abstract interface class ConstructorDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForConstructor( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any mixin declaration, and -/// want to contribute new type declarations to the program. -abstract interface class MixinTypesMacro implements Macro { - FutureOr buildTypesForMixin( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any mixin declaration, and -/// want to contribute new non-type declarations to the program. -abstract interface class MixinDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForMixin( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any mixin declaration, and -/// want to augment the definitions of the members of that mixin. -abstract interface class MixinDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForMixin( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any extension declaration, -/// and want to contribute new type declarations to the program. -abstract interface class ExtensionTypesMacro implements Macro { - FutureOr buildTypesForExtension( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any extension declaration, -/// and want to contribute new non-type declarations to the program. -abstract interface class ExtensionDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForExtension( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any extension declaration, -/// and want to augment the definitions of the members of that extension. -abstract interface class ExtensionDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForExtension( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any extension type -/// declaration, and want to contribute new type declarations to the program. -abstract interface class ExtensionTypeTypesMacro implements Macro { - FutureOr buildTypesForExtensionType( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any extension type -/// declaration, and want to contribute new non-type declarations to the -/// program. -abstract interface class ExtensionTypeDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForExtensionType( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any extension type -/// declaration, and want to augment the definitions of the members of that -/// extension. -abstract interface class ExtensionTypeDefinitionsMacro implements Macro { - FutureOr buildDefinitionsForExtensionType( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any type alias -/// declaration, and want to contribute new type declarations to the program. -abstract interface class TypeAliasTypesMacro implements Macro { - FutureOr buildTypesForTypeAlias( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} - -/// The interface for [Macro]s that can be applied to any type alias -/// declaration, and want to contribute new non-type declarations to the -/// program. -abstract interface class TypeAliasDeclarationsMacro implements Macro { - FutureOr buildDeclarationsForTypeAlias( - // TODO: Fill in a real type once we have it - Object target, - Model model, - Host host); -} +export 'src/builders.dart'; +export 'src/macro.dart'; diff --git a/pkgs/macro/lib/src/builders.dart b/pkgs/macro/lib/src/builders.dart new file mode 100644 index 00000000..dbe8cc0c --- /dev/null +++ b/pkgs/macro/lib/src/builders.dart @@ -0,0 +1,440 @@ +// Copyright (c) 2024, 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 'package:dart_model/dart_model.dart'; + +import 'macro.dart'; + +/// The base interface used to add declarations to the program as well +/// as augment existing ones. +abstract interface class Builder< + // TODO: Change to Declaration https://github.com/dart-lang/macros/issues/10 + T extends Object> { + /// The target declaration of this macro application. + T get target; + + /// The current accumulated [Model] for this macro application. + /// + /// This will contain all data including the [target] as well as the results + /// of any queries which have been ran. + Model get model; + + /// TODO: We eventually want phase specific queries here. + Future query(Query query); +} + +/// The API used by [Macro]s to contribute new type declarations to the +/// current library. +abstract interface class TypesBuilder< + // TODO: Change to Declaration https://github.com/dart-lang/macros/issues/10 + T extends Object> implements Builder { + /// Adds a new type declaration to the surrounding library. + /// + /// The [name] must match the name of the new [typeDeclaration] (this does + /// not include any type parameters, just the name). + void declareType( + String name, + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation typeDeclaration); +} + +/// The API used by macros in the type phase to add interfaces to [target]s +/// `implements` clause. +abstract interface class ImplementsClauseBuilder + implements TypesBuilder { + /// Appends [interfaces] to the `implements` clause on [target]. + void appendInterfaces( + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Iterable interfaces); +} + +/// The API used by macros in the type phase to add mixins to the [target]s +/// `with` clause. +abstract interface class WithClauseBuilder + implements TypesBuilder { + /// Appends [mixins] to the `with` clause on [target]. + void appendMixins( + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Iterable mixins); +} + +/// The API used by macros in the type phase to set the `extends` clause +/// on the [target] type. +abstract interface class ExtendsClauseBuilder + implements TypesBuilder { + /// Sets the `extends` clause on [target] to [supertype]. + /// + /// The [target] type must not already have an `extends` clause. + void extendsType( + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation supertype); +} + +/// The builder API for [LibraryTypesMacro]s. +abstract interface class LibraryTypesBuilder + implements TypesBuilder {} + +/// The builder API for [FunctionTypesMacro]s. +abstract interface class FunctionTypesBuilder< + // TODO: Change to Function https://github.com/dart-lang/macros/issues/10 + T extends Object> implements TypesBuilder {} + +/// The builder API for [MethodTypesMacro]s. +abstract interface class MethodTypesBuilder< + // TODO: Change to Method https://github.com/dart-lang/macros/issues/10 + T extends Member> implements FunctionTypesBuilder {} + +/// The builder API for [ConstructorTypesMacro]s. +abstract interface class ConstructorTypesBuilder< + // TODO: Change to Constructor https://github.com/dart-lang/macros/issues/10 + T extends Member> implements MethodTypesBuilder {} + +/// The builder API for [VariableTypesMacro]s. +abstract interface class VariableTypesBuilder< + // TODO: Change to Variable https://github.com/dart-lang/macros/issues/10 + T extends Object> implements TypesBuilder {} + +/// The builder API for [FieldTypesMacro]s. +abstract interface class FieldTypesBuilder< + // TODO: Change to Field https://github.com/dart-lang/macros/issues/10 + T extends Member> implements VariableTypesBuilder {} + +/// The builder API for [ClassTypesMacro]s. +abstract interface class ClassTypesBuilder< + // TODO: Change to Class https://github.com/dart-lang/macros/issues/10 + T extends Interface> + implements + TypesBuilder, + ExtendsClauseBuilder, + ImplementsClauseBuilder, + WithClauseBuilder {} + +/// The builder API for [EnumTypesMacro]s. +abstract interface class EnumTypesBuilder< + // TODO: Change to Enum https://github.com/dart-lang/macros/issues/10 + T extends Interface> + implements + TypesBuilder, + ImplementsClauseBuilder, + WithClauseBuilder {} + +/// The builder API for [EnumValueTypesMacro]s. +abstract interface class EnumValueTypesBuilder< + // TODO: Change to EnumValue https://github.com/dart-lang/macros/issues/10 + T extends Member> implements TypesBuilder {} + +/// The builder API for [ExtensionTypesMacro]s. +/// +/// Note that augmentations do not allow altering the `on` clause. +abstract interface class ExtensionTypesBuilder< + // TODO: Change to Extension https://github.com/dart-lang/macros/issues/10 + // ignore: lines_longer_than_80_chars + T extends Interface> implements TypesBuilder, ImplementsClauseBuilder {} + +/// The builder API for [ExtensionTypeTypesMacro]s. +/// +// TODO: Should this implement `WithClauseBuilder`, the spec is unclear? +abstract interface class ExtensionTypeTypesBuilder< + // TODO: Change to ExtensionType https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements TypesBuilder {} + +/// The builder API for [MixinTypesMacro]s. +/// +/// Note that mixins don't support mixins, only interfaces. +abstract interface class MixinTypesBuilder< + // TODO: Change to Mixin https://github.com/dart-lang/macros/issues/10 + T extends Interface> + implements TypesBuilder, ImplementsClauseBuilder {} + +/// The builder API for [TypeAliasTypesMacro]s. +abstract interface class TypeAliasTypesBuilder< + // TODO: Change to TypeAlias https://github.com/dart-lang/macros/issues/10 + T extends Object> implements TypesBuilder {} + +/// The base API used by all [Macro]s in the declarations phase. +abstract interface class DeclarationsBuilder< + // TODO: Change to Declaration https://github.com/dart-lang/macros/issues/10 + T extends Object> implements Builder { + /// Adds a new regular declaration to the library containing (or equal to) the + /// [target]. + /// + /// Note that type declarations are not supported. + void declareInLibrary( + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation declaration); +} + +/// The builder API for [LibraryDeclarationsMacro]s. +/// +// TODO: Should theses builders be able to get `DeclarationsBuilder`s for the +// nested scopes defined in `target`? What would the ordering behavior of that +// be? +abstract interface class LibraryDeclarationsBuilder + implements DeclarationsBuilder {} + +/// The builder API for [FunctionDeclarationsMacro]s. +abstract interface class FunctionDeclarationsBuilder< + // TODO: Change to Function https://github.com/dart-lang/macros/issues/10 + T extends Object> implements DeclarationsBuilder {} + +/// The builder API for [MethodDeclarationsMacro]s. +abstract interface class MethodDeclarationsBuilder< + // TODO: Change to Method https://github.com/dart-lang/macros/issues/10 + T extends Member> implements FunctionDeclarationsBuilder {} + +/// The builder API for [ConstructorDeclarationsMacro]s. +abstract interface class ConstructorDeclarationsBuilder< + // TODO: Change to Constructor https://github.com/dart-lang/macros/issues/10 + T extends Member> implements MethodDeclarationsBuilder {} + +/// The builder API for [VariableDeclarationsMacro]s. +abstract interface class VariableDeclarationsBuilder< + // TODO: Change to Variable https://github.com/dart-lang/macros/issues/10 + T extends Object> implements DeclarationsBuilder {} + +/// The builder API for [FieldDeclarationsMacro]s. +abstract interface class FieldDeclarationsBuilder< + // TODO: Change to Field https://github.com/dart-lang/macros/issues/10 + T extends Member> implements VariableDeclarationsBuilder {} + +/// The base builder API for [Macro]s which run on any [Interface] in the +/// delarations phase. +abstract interface class MemberDeclarationsBuilder + implements DeclarationsBuilder { + /// Adds a new declaration to the [target] interface. + void declareInType( + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation declaration); +} + +/// The builder API for [ClassDeclarationsMacro]s. +abstract interface class ClassDeclarationsBuilder< + // TODO: Change to Class https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements MemberDeclarationsBuilder {} + +/// The builder API for [EnumDeclarationsMacro]s. +abstract interface class EnumDeclarationsBuilder< + // TODO: Change to Enum https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements MemberDeclarationsBuilder { + /// Adds a new enum value declaration to the [target] enum. + void declareEnumValue( + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation declaration); +} + +/// The builder API for [EnumValueDeclarationsMacro]s. +abstract interface class EnumValueDeclarationsBuilder< + // TODO: Change to EnumValue https://github.com/dart-lang/macros/issues/10 + T extends Member> implements DeclarationsBuilder {} + +/// The builder API for [ExtensionDeclarationsMacro]s. +abstract interface class ExtensionDeclarationsBuilder< + // TODO: Change to Extension https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements MemberDeclarationsBuilder {} + +/// The builder API for [ExtensionTypeDeclarationsMacro]s. +abstract interface class ExtensionTypeDeclarationsBuilder< + // TODO: Change to ExtensionType https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements MemberDeclarationsBuilder {} + +/// The builder API for [MixinDeclarationsMacro]s. +abstract interface class MixinDeclarationsBuilder< + // TODO: Change to Mixin https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements MemberDeclarationsBuilder {} + +/// The builder API for [TypeAliasDeclarationsMacro]s. +abstract interface class TypeAliasDeclarationsBuilder< + // TODO: Change to TypeAlias https://github.com/dart-lang/macros/issues/10 + T extends Object> implements DeclarationsBuilder {} + +/// The base builder API for [Macro]s which run in the definitions phase. +abstract interface class DefinitionsBuilder< + // TODO: Change to Declaration https://github.com/dart-lang/macros/issues/10 + T extends Object> implements Builder { + /// Augments an existing declaration. + /// + /// If [docCommentsAndMetadata] are supplied, they will be added above this + /// augment declaration. + void augment({ + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation? docCommentsAndMetadata, + }); +} + +/// The builder API for [LibraryDefinitionsMacro]s. +abstract interface class LibraryDefinitionsBuilder + implements DefinitionsBuilder { + /// Retrieve a [InterfaceDefinitionsBuilder] for the interface declaration + /// with [name] in [target]. + /// + /// Throws if [name] does not refer to an interface declaration in the + /// [target] library. + InterfaceDefinitionsBuilder buildInterface(QualifiedName name); + + /// Retrieve a [FunctionDefinitionsBuilder] for a function declaration with + /// [name] in [target]. + /// + /// Throws if [name] does not refer to a top level function declaration in the + /// [target] library. + FunctionDefinitionsBuilder buildFunction(QualifiedName name); + + /// Retrieve a [VariableDefinitionsBuilder] for a variable declaration with + /// [name] in [target]. + /// + /// Throws if [name] does not refer to a top level variable declaration in the + /// [target] library. + VariableDefinitionsBuilder buildVariable(QualifiedName name); +} + +/// The builder API for [FunctionDefinitionsMacro]s. +abstract interface class FunctionDefinitionsBuilder< + // TODO: Change to Function https://github.com/dart-lang/macros/issues/10 + T extends Object> implements DefinitionsBuilder { + /// Augments an existing function. + /// + /// If [body] is supplied it will be used as the body for the augmenting + /// declaration, otherwise no body will be provided (which will leave the + /// existing body in tact). + /// + /// TODO: Link the library augmentations proposal to describe the semantics. + @override + void augment({ + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation? body, + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation? docCommentsAndMetadata, + }); +} + +/// The builder API for [MethodDefinitionsMacro]s. +abstract interface class MethodDefinitionsBuilder< + // TODO: Change to Method https://github.com/dart-lang/macros/issues/10 + T extends Member> implements FunctionDefinitionsBuilder {} + +/// The builder API for [ConstructorDefinitionsMacro]s. +abstract interface class ConstructorDefinitionsBuilder< + // TODO: Change to Constructor https://github.com/dart-lang/macros/issues/10 + T extends Member> implements DefinitionsBuilder { + /// Augments an existing constructor body. + /// + /// If [body] is supplied it will be used as the body for the augmenting + /// declaration, otherwise no body will be provided. + /// + /// The [initializers] should not contain trailing or preceding commas, but + /// may include doc comments. + /// + /// TODO: Link the library augmentations proposal to describe the semantics. + @override + void augment({ + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation? body, + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + List? initializers, + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation? docCommentsAndMetadata, + }); +} + +/// The builder API for [VariableDefinitionsMacro]s. +abstract interface class VariableDefinitionsBuilder< + // TODO: Change to Variable https://github.com/dart-lang/macros/issues/10 + T extends Object> implements DefinitionsBuilder { + /// Augments an existing variable. + /// + /// For [getter] and [setter] the full function declaration should be + /// provided, minus the `augment` keyword (which will be implicitly added). + /// + /// To provide doc comments or metadata for [getter] or [setter], just include + /// them in the [Augmentation] object for those. + /// + /// If [docCommentsAndMetadata] is provided but [initializer] is not, there + /// will be no augmenting initializer (which leaves the existing initializer + /// in tact). + /// + /// TODO: Link the library augmentations proposal to describe the semantics. + @override + void augment({ + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation? getter, + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation? setter, + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation? initializer, + // TODO: Tighten this type https://github.com/dart-lang/macros/issues/111 + Augmentation? docCommentsAndMetadata, + }); +} + +/// The builder API for [FieldDefinitionsMacro]s. +abstract interface class FieldDefinitionsBuilder< + // TODO: Change to Field https://github.com/dart-lang/macros/issues/10 + T extends Member> implements VariableDefinitionsBuilder {} + +/// The base builder API for [Macro]s which run on any [Interface] in the +/// definitions phase. +abstract interface class InterfaceDefinitionsBuilder + implements DefinitionsBuilder { + /// Retrieve a [FieldDefinitionsBuilder] for a field with [name] in + /// [target]. + /// + /// Throws if [name] does not refer to a field in the [target] interface + /// declaration. + FieldDefinitionsBuilder buildField(QualifiedName name); + + /// Retrieve a [MethodDefinitionsBuilder] for a method with [name] in + /// [target]. + /// + /// Throws if [name] does not refer to a method in the [target] interface + /// declaration. + MethodDefinitionsBuilder buildMethod(QualifiedName name); + + /// Retrieve a [ConstructorDefinitionsBuilder] for a constructor with [name] + /// in [target]. + /// + /// Throws if [name] does not refer to a constructor in the [target] + /// interface declaration. + ConstructorDefinitionsBuilder buildConstructor(QualifiedName name); +} + +/// The builder API for [ClassDefinitionsMacro]s. +abstract interface class ClassDefinitionsBuilder< + // TODO: Change to Class https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements InterfaceDefinitionsBuilder {} + +/// The builder API for [EnumDefinitionsMacro]s. +abstract interface class EnumDefinitionsBuilder< + // TODO: Change to Enum https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements InterfaceDefinitionsBuilder { + /// Retrieve an [EnumValueDefinitionsBuilder] for an entry with [name] in + /// [target]. + /// + /// Throws if [name] does not refer to an entry on the [target] enum + /// declaration. + EnumValueDefinitionsBuilder buildEnumValue(QualifiedName name); +} + +/// The builder API for [EnumValueDefinitionsMacro]s. +abstract interface class EnumValueDefinitionsBuilder< + // TODO: Change to EnumValue https://github.com/dart-lang/macros/issues/10 + T extends Member> implements DefinitionsBuilder {} + +/// The builder API for [ExtensionDefinitionsMacro]s. +abstract interface class ExtensionDefinitionsBuilder< + // TODO: Change to Field https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements InterfaceDefinitionsBuilder {} + +/// The builder API for [ExtensionTypeDefinitionsMacro]s. +abstract interface class ExtensionTypeDefinitionsBuilder< + // TODO: Change to ExtensionType https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements InterfaceDefinitionsBuilder {} + +/// The builder API for [MixinDefinitionsMacro]s. +abstract interface class MixinDefinitionsBuilder< + // TODO: Change to Mixin https://github.com/dart-lang/macros/issues/10 + T extends Interface> implements InterfaceDefinitionsBuilder {} + +/// The builder API for [TypeAliasDefinitionsMacro]s. +abstract interface class TypeAliasDefinitionsBuilder< + // TODO: Change to TypeAlias https://github.com/dart-lang/macros/issues/10 + T extends Object> implements DefinitionsBuilder {} diff --git a/pkgs/macro/lib/src/macro.dart b/pkgs/macro/lib/src/macro.dart new file mode 100644 index 00000000..db90c627 --- /dev/null +++ b/pkgs/macro/lib/src/macro.dart @@ -0,0 +1,278 @@ +// Copyright (c) 2024, 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:macro_service/macro_service.dart'; + +import 'builders.dart'; + +/// The marker interface for all types of macros. +abstract interface class Macro { + /// Description of the macro. + /// + /// TODO(davidmorgan): where possible the macro information should be + /// determined by `macro_builder` and injected in the bootstrap script rather + /// than relying on the user-written macro code to return it. + /// + MacroDescription get description; +} + +/// The interface for [Macro]s that can be applied to a library directive, and +/// want to contribute new type declarations to the library. +abstract interface class LibraryTypesMacro implements Macro { + FutureOr buildTypesForLibrary(LibraryTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to a library directive, and +/// want to contribute new non-type declarations to the library. +abstract interface class LibraryDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForLibrary( + LibraryDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to a library directive, and +/// want to provide definitions for declarations in the library. +abstract interface class LibraryDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForLibrary(LibraryDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any top level function, +/// instance method, or static method, and want to contribute new type +/// declarations to the program. +abstract interface class FunctionTypesMacro implements Macro { + FutureOr buildTypesForFunction(FunctionTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any top level function, +/// instance method, or static method, and want to contribute new non-type +/// declarations to the program. +abstract interface class FunctionDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForFunction( + FunctionDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any top level function, +/// instance method, or static method, and want to augment the function +/// definition. +abstract interface class FunctionDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForFunction( + FunctionDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any top level variable or +/// instance field, and want to contribute new type declarations to the +/// program. +abstract interface class VariableTypesMacro implements Macro { + FutureOr buildTypesForVariable(VariableTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any top level variable or +/// instance field and want to contribute new non-type declarations to the +/// program. +abstract interface class VariableDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForVariable( + VariableDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any top level variable +/// or instance field, and want to augment the variable definition. +abstract interface class VariableDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForVariable( + VariableDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any class, and want to +/// contribute new type declarations to the program. +abstract interface class ClassTypesMacro implements Macro { + FutureOr buildTypesForClass(ClassTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any class, and want to +/// contribute new non-type declarations to the program. +abstract interface class ClassDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForClass(ClassDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any class, and want to +/// augment the definitions of the members of that class. +abstract interface class ClassDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForClass(ClassDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// contribute new type declarations to the program. +abstract interface class EnumTypesMacro implements Macro { + FutureOr buildTypesForEnum(EnumTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// contribute new non-type declarations to the program. +abstract interface class EnumDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForEnum(EnumDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// augment the definitions of members or values of that enum. +abstract interface class EnumDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForEnum(EnumTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// contribute new type declarations to the program. +abstract interface class EnumValueTypesMacro implements Macro { + FutureOr buildTypesForEnumValue(EnumValueTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// contribute new non-type declarations to the program. +abstract interface class EnumValueDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForEnumValue( + EnumValueDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any enum, and want to +/// augment the definitions of members or values of that enum. +abstract interface class EnumValueDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForEnumValue( + EnumValueDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any field, and want to +/// contribute new type declarations to the program. +abstract interface class FieldTypesMacro implements Macro { + FutureOr buildTypesForField(FieldTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any field, and want to +/// contribute new type declarations to the program. +abstract interface class FieldDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForField(FieldDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any field, and want to +/// augment the field definition. +abstract interface class FieldDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForField(FieldDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any method, and want to +/// contribute new type declarations to the program. +abstract interface class MethodTypesMacro implements Macro { + FutureOr buildTypesForMethod(MethodTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any method, and want to +/// contribute new non-type declarations to the program. +abstract interface class MethodDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForMethod(MethodDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any method, and want to +/// augment the function definition. +abstract interface class MethodDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForMethod(MethodDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any constructor, and want +/// to contribute new type declarations to the program. +abstract interface class ConstructorTypesMacro implements Macro { + FutureOr buildTypesForConstructor(ConstructorTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any constructors, and +/// want to contribute new non-type declarations to the program. +abstract interface class ConstructorDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForConstructor( + ConstructorDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any constructor, and want +/// to augment the function definition. +abstract interface class ConstructorDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForConstructor( + ConstructorDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any mixin declaration, and +/// want to contribute new type declarations to the program. +abstract interface class MixinTypesMacro implements Macro { + FutureOr buildTypesForMixin(MixinTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any mixin declaration, and +/// want to contribute new non-type declarations to the program. +abstract interface class MixinDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForMixin(MixinDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any mixin declaration, and +/// want to augment the definitions of the members of that mixin. +abstract interface class MixinDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForMixin(MixinDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any extension declaration, +/// and want to contribute new type declarations to the program. +abstract interface class ExtensionTypesMacro implements Macro { + FutureOr buildTypesForExtension(ExtensionTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any extension declaration, +/// and want to contribute new non-type declarations to the program. +abstract interface class ExtensionDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForExtension( + ExtensionDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any extension declaration, +/// and want to augment the definitions of the members of that extension. +abstract interface class ExtensionDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForExtension( + ExtensionDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any extension type +/// declaration, and want to contribute new type declarations to the program. +abstract interface class ExtensionTypeTypesMacro implements Macro { + FutureOr buildTypesForExtensionType(ExtensionTypeTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any extension type +/// declaration, and want to contribute new non-type declarations to the +/// program. +abstract interface class ExtensionTypeDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForExtensionType( + ExtensionTypeDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any extension type +/// declaration, and want to augment the definitions of the members of that +/// extension. +abstract interface class ExtensionTypeDefinitionsMacro implements Macro { + FutureOr buildDefinitionsForExtensionType( + ExtensionTypeDefinitionsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any type alias +/// declaration, and want to contribute new type declarations to the program. +abstract interface class TypeAliasTypesMacro implements Macro { + FutureOr buildTypesForTypeAlias(TypeAliasTypesBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any type alias +/// declaration, and want to contribute new non-type declarations to the +/// program. +abstract interface class TypeAliasDeclarationsMacro implements Macro { + FutureOr buildDeclarationsForTypeAlias( + TypeAliasDeclarationsBuilder builder); +} + +/// The interface for [Macro]s that can be applied to any type alias +/// declaration, and want to contribute new non-type declarations to the +/// program. +abstract interface class TypeAliasDefinitionsMacro implements Macro { + FutureOr buildDeclarationsForTypeAlias( + TypeAliasDeclarationsBuilder builder); +}