Skip to content
This repository has been archived by the owner on Feb 4, 2025. It is now read-only.

Make AugmentResponse more like MacroExecutionResult #106

Merged
merged 4 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading