Skip to content

Commit

Permalink
Make AugmentResponse more like MacroExecutionResult in existing apis.
Browse files Browse the repository at this point in the history
In preparation for adding builder types, and making AugmentResponse not visible in the macro APIs any more.

Will do that after #104 is also merged, as we really need both.
  • Loading branch information
jakemac53 committed Oct 17, 2024
1 parent a482288 commit e5c685e
Show file tree
Hide file tree
Showing 11 changed files with 233 additions and 41 deletions.
30 changes: 27 additions & 3 deletions pkgs/_analyzer_macros/lib/macro_implementation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,34 @@ class AnalyzerMacroExecutionResult
static Future<AnalyzerMacroExecutionResult> dartModelToInjected(
macros_api_v1.MacroTarget target, AugmentResponse augmentResponse) async {
final declarations = <macros_api_v1.DeclarationCode>[];
for (final augmentation in augmentResponse.augmentations) {
declarations.add(macros_api_v1.DeclarationCode.fromParts(
await _resolveNames(augmentation.code)));
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)));
}
}

if (augmentResponse.enumValueAugmentations?.isNotEmpty == true) {
throw UnimplementedError('Enum value augmentations are not implemented');
}
if (augmentResponse.extendsTypeAugmentations?.isNotEmpty == true ||
augmentResponse.interfaceAugmentations?.isNotEmpty == true ||
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);
}

Expand Down
30 changes: 27 additions & 3 deletions pkgs/_cfe_macros/lib/macro_implementation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,34 @@ class CfeMacroExecutionResult implements macros_api_v1.MacroExecutionResult {
static Future<CfeMacroExecutionResult> dartModelToInjected(
macros_api_v1.MacroTarget target, AugmentResponse augmentResponse) async {
final declarations = <macros_api_v1.DeclarationCode>[];
for (final augmentation in augmentResponse.augmentations) {
declarations.add(macros_api_v1.DeclarationCode.fromParts(
await _resolveNames(augmentation.code)));
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)));
}
}

if (augmentResponse.enumValueAugmentations?.isNotEmpty == true) {
throw UnimplementedError('Enum value augmentations are not implemented');
}
if (augmentResponse.extendsTypeAugmentations?.isNotEmpty == true ||
augmentResponse.interfaceAugmentations?.isNotEmpty == true ||
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 CfeMacroExecutionResult(target, declarations);
}

Expand Down
2 changes: 1 addition & 1 deletion pkgs/_macro_client/lib/macro_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class MacroClient {
'Unexpected phase ${augmentRequest.phase}, '
'expected 1, 2, or 3.')
} ??
AugmentResponse(augmentations: []),
AugmentResponse(),
requestId: hostRequest.id)));
}
default:
Expand Down
22 changes: 12 additions & 10 deletions pkgs/_macro_host/test/macro_host_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ void main() {
phase: 2,
target: QualifiedName(
name: 'Foo', uri: 'package:foo/foo.dart'))),
Scope.macro.run(() => AugmentResponse(augmentations: [
Augmentation(code: [Code.string('int get x => 3;')])
])));
Scope.macro.run(() => AugmentResponse()
..typeAugmentations!['Foo'] = [
Augmentation(code: [Code.string('int get x => 3;')])
]));
});

test('hosts a macro, responds to queries', () async {
Expand All @@ -67,13 +68,14 @@ void main() {
phase: 3,
target: QualifiedName(
uri: 'package:foo/foo.dart', name: 'Foo'))),
Scope.macro.run(() => AugmentResponse(augmentations: [
Augmentation(code: [
Code.string(
'// {"uris":{"package:foo/foo.dart":{"scopes":{"Foo":{'
'"members":{},"properties":{"isClass":true}}}}}}')
])
])));
Scope.macro.run(() => AugmentResponse()
..typeAugmentations!['Foo'] = [
Augmentation(code: [
Code.string(
'// {"uris":{"package:foo/foo.dart":{"scopes":{"Foo":{'
'"members":{},"properties":{"isClass":true}}}}}}')
])
]));
});

test('hosts two macros', () async {
Expand Down
8 changes: 5 additions & 3 deletions pkgs/_test_macros/lib/declare_x_macro.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ class DeclareXImplementation implements ClassDeclarationsMacro {
Host host, AugmentRequest request) async {
// TODO(davidmorgan): make the host only run in the phases requested so
// that this is not needed.
if (request.phase != 2) return AugmentResponse(augmentations: []);
if (request.phase != 2) return AugmentResponse();

// TODO(davidmorgan): still need to pass through the augment target.
return AugmentResponse(
augmentations: [Augmentation(code: expandTemplate('int get x => 3;'))]);
return AugmentResponse()
..typeAugmentations![request.target.name] = [
Augmentation(code: expandTemplate('int get x => 3;'))
];
}
}
16 changes: 9 additions & 7 deletions pkgs/_test_macros/lib/json_codable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@ class JsonCodableImplementation
Future<AugmentResponse> buildDeclarationsForClass(
Host host, AugmentRequest request) async {
final target = request.target;
return AugmentResponse(augmentations: [
Augmentation(code: expandTemplate('''
return AugmentResponse()
..typeAugmentations![request.target.name] = [
Augmentation(code: expandTemplate('''
// TODO(davidmorgan): see https://github.com/dart-lang/macros/issues/80.
// external ${target.name}.fromJson($_jsonMapType json);
// external $_jsonMapType toJson();
'''))
]);
];
}

@override
Expand All @@ -49,10 +50,11 @@ class JsonCodableImplementation
// TODO(davidmorgan): put `extends` information directly in `Interface`.
final superclassName = MacroScope.current.typeSystem.supertypeOf(target);

return AugmentResponse(augmentations: [
await _generateFromJson(host, model, target, superclassName, clazz),
await _generateToJson(host, model, target, superclassName, clazz)
]);
return AugmentResponse()
..typeAugmentations![request.target.name] = [
await _generateFromJson(host, model, target, superclassName, clazz),
await _generateToJson(host, model, target, superclassName, clazz)
];
}

Future<Augmentation> _generateFromJson(
Expand Down
7 changes: 4 additions & 3 deletions pkgs/_test_macros/lib/query_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ class QueryClassImplementation implements ClassDefinitionsMacro {
final model = await host.query(Query(
target: request.target,
));
return AugmentResponse(augmentations: [
Augmentation(code: expandTemplate('// ${json.encode(model)}'))
]);
return AugmentResponse()
..typeAugmentations![request.target.name] = [
Augmentation(code: expandTemplate('// ${json.encode(model)}'))
];
}
}
46 changes: 41 additions & 5 deletions pkgs/macro_service/lib/src/macro_service.g.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,50 @@ extension type AugmentRequest.fromJson(Map<String, Object?> node)
extension type AugmentResponse.fromJson(Map<String, Object?> node)
implements Object {
AugmentResponse({
List<Augmentation>? augmentations,
List<Augmentation>? libraryAugmentations,
List<String>? newTypeNames,
}) : this.fromJson({
if (augmentations != null) 'augmentations': augmentations,
'enumValueAugmentations': <String, Object?>{},
'extendsTypeAugmentations': <String, Object?>{},
'interfaceAugmentations': <String, Object?>{},
if (libraryAugmentations != null)
'libraryAugmentations': libraryAugmentations,
'mixinAugmentations': <String, Object?>{},
if (newTypeNames != null) 'newTypeNames': newTypeNames,
'typeAugmentations': <String, Object?>{},
});

/// The augmentations.
List<Augmentation> get augmentations =>
(node['augmentations'] as List).cast();
/// Any augmentations to enum values that should be applied to an enum as a result of executing a macro, indexed by the name of the enum.
Map<String, List<Augmentation>>? get enumValueAugmentations =>
(node['enumValueAugmentations'] as Map?)
?.deepCast<String, List<Augmentation>>((v) => (v as List).cast());

/// Any extends clauses that should be added to types as a result of executing a macro, indexed by the name of the augmented type declaration.
Map<String, List<Augmentation>>? get extendsTypeAugmentations =>
(node['extendsTypeAugmentations'] as Map?)
?.deepCast<String, List<Augmentation>>((v) => (v as List).cast());

/// Any interfaces that should be added to types as a result of executing a macro, indexed by the name of the augmented type declaration.
Map<String, List<Augmentation>>? get interfaceAugmentations =>
(node['interfaceAugmentations'] as Map?)
?.deepCast<String, List<Augmentation>>((v) => (v as List).cast());

/// Any augmentations that should be applied to the library as a result of executing a macro.
List<Augmentation>? get libraryAugmentations =>
(node['libraryAugmentations'] as List?)?.cast();

/// Any mixins that should be added to types as a result of executing a macro, indexed by the name of the augmented type declaration.
Map<String, List<Augmentation>>? get mixinAugmentations =>
(node['mixinAugmentations'] as Map?)
?.deepCast<String, List<Augmentation>>((v) => (v as List).cast());

/// The names of any new types declared in [libraryAugmentations].
List<String>? get newTypeNames => (node['newTypeNames'] as List?)?.cast();

/// Any augmentations that should be applied to a class as a result of executing a macro, indexed by the name of the class.
Map<String, List<Augmentation>>? get typeAugmentations =>
(node['typeAugmentations'] as Map?)
?.deepCast<String, List<Augmentation>>((v) => (v as List).cast());
}

/// Request could not be handled.
Expand Down
61 changes: 59 additions & 2 deletions schemas/macro_service.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,69 @@
"type": "object",
"description": "Macro's response to an [AugmentRequest]: the resulting augmentations.",
"properties": {
"augmentations": {
"enumValueAugmentations": {
"type": "object",
"description": "Any augmentations to enum values that should be applied to an enum as a result of executing a macro, indexed by the name of the enum.",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "file:dart_model.schema.json#/$defs/Augmentation"
}
}
},
"extendsTypeAugmentations": {
"type": "object",
"description": "Any extends clauses that should be added to types as a result of executing a macro, indexed by the name of the augmented type declaration.",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "file:dart_model.schema.json#/$defs/Augmentation"
}
}
},
"interfaceAugmentations": {
"type": "object",
"description": "Any interfaces that should be added to types as a result of executing a macro, indexed by the name of the augmented type declaration.",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "file:dart_model.schema.json#/$defs/Augmentation"
}
}
},
"libraryAugmentations": {
"type": "array",
"description": "The augmentations.",
"description": "Any augmentations that should be applied to the library as a result of executing a macro.",
"items": {
"$ref": "file:dart_model.schema.json#/$defs/Augmentation"
}
},
"mixinAugmentations": {
"type": "object",
"description": "Any mixins that should be added to types as a result of executing a macro, indexed by the name of the augmented type declaration.",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "file:dart_model.schema.json#/$defs/Augmentation"
}
}
},
"newTypeNames": {
"type": "array",
"description": "The names of any new types declared in [libraryAugmentations].",
"items": {
"type": "string"
}
},
"typeAugmentations": {
"type": "object",
"description": "Any augmentations that should be applied to a class as a result of executing a macro, indexed by the name of the class.",
"additionalProperties": {
"type": "array",
"items": {
"$ref": "file:dart_model.schema.json#/$defs/Augmentation"
}
}
}
}
},
Expand Down
47 changes: 45 additions & 2 deletions tool/dart_model_generator/lib/definitions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -360,9 +360,52 @@ static QualifiedName parse(String string) {
"Macro's response to an [AugmentRequest]: the resulting "
'augmentations.',
properties: [
Property('augmentations',
Property('enumValueAugmentations',
type: 'Map<List<Augmentation>>',
description:
'Any augmentations to enum values that should be applied '
'to an enum as a result of executing a macro, indexed by '
'the name of the enum.',
nullable: true),
Property('extendsTypeAugmentations',
type: 'Map<List<Augmentation>>',
description:
'Any extends clauses that should be added to types as a '
'result of executing a macro, indexed by the name '
'of the augmented type declaration.',
nullable: true),
Property('interfaceAugmentations',
type: 'Map<List<Augmentation>>',
description:
'Any interfaces that should be added to types as a '
'result of executing a macro, indexed by the name '
'of the augmented type declaration.',
nullable: true),
Property('libraryAugmentations',
type: 'List<Augmentation>',
description: 'The augmentations.'),
description:
'Any augmentations that should be applied to the library '
'as a result of executing a macro.',
nullable: true),
Property('mixinAugmentations',
type: 'Map<List<Augmentation>>',
description:
'Any mixins that should be added to types as a result of '
'executing a macro, indexed by the name of the '
'augmented type declaration.',
nullable: true),
Property('newTypeNames',
type: 'List<String>',
description: 'The names of any new types declared in '
'[libraryAugmentations].',
nullable: true),
Property('typeAugmentations',
type: 'Map<List<Augmentation>>',
description:
'Any augmentations that should be applied to a class as '
'a result of executing a macro, indexed by the '
'name of the class.',
nullable: true),
]),
Definition.clazz('ErrorResponse',
description: 'Request could not be handled.',
Expand Down
5 changes: 3 additions & 2 deletions tool/dart_model_generator/lib/generate_dart_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ class TypeReference {
return '($rawCast)$q.deepCast<String, ${elementType!.dartType}>('
'(v) => ${elementType!.castExpression('v')})';
} else if (isList) {
if (elementType!.elementType == null) return '($rawCast).cast()';
if (elementType!.elementType == null) return '($rawCast)$q.cast()';
throw UnsupportedError('Deep casting for lists isn\'t yet supported.');
} else {
return rawCast;
Expand Down Expand Up @@ -493,6 +493,7 @@ class ClassTypeDefinition implements Definition {
result.writeln(' $name() : ');
} else {
result.writeln(' $name({');
// TODO: Why are we excluding Map properties?
for (final property in propertiesExceptMap) {
result.writeln(property.parameterCode);
}
Expand All @@ -514,7 +515,7 @@ class ClassTypeDefinition implements Definition {
result.writeln('{');
for (final property in properties) {
if (property.type.isMap) {
result.writeln("'${property.name}': {},");
result.writeln("'${property.name}': <String, Object?>{},");
} else {
result.writeln(property.namedArgumentCode);
}
Expand Down

0 comments on commit e5c685e

Please sign in to comment.