Skip to content

Commit 178c59f

Browse files
authored
Add LiteralParams macro. (dart-archive#128)
* Add LiteralParams macro. Fix converter for lists of union types. * Address review comments.
1 parent 65bf11d commit 178c59f

File tree

7 files changed

+202
-13
lines changed

7 files changed

+202
-13
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
part of 'package:foo/literal_params.dart';
2+
3+
augment class Foo {
4+
// anInt: 7, int
5+
// aNum: 8.0, double
6+
// aDouble: 9.0, double
7+
// aString: 10, String
8+
// anObject: {type: {reference: {type: ClassReference, value: {}}, typeArguments: []}, constructor: {type: ConstructorReference, value: {}}, arguments: [{type: NamedArgument, value: {name: a, expression: {type: BooleanLiteral, value: {text: true}}}}, {type: NamedArgument, value: {name: b, expression: {type: BooleanLiteral, value: {text: false}}}}]}, String
9+
// ints: [11, 12], List<Object>
10+
// nums: [13.0, 14], List<Object>
11+
// doubles: [15.0, 16], List<Object>
12+
// strings: [17, eighteen], List<Object>
13+
// objects: [19, {type: {reference: {type: ClassReference, value: {}}, typeArguments: []}, constructor: {type: ConstructorReference, value: {}}, arguments: [{type: NamedArgument, value: {name: a, expression: {type: BooleanLiteral, value: {text: true}}}}, {type: NamedArgument, value: {name: b, expression: {type: BooleanLiteral, value: {text: false}}}}]}], List<Object>
14+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:_test_macros/literal_params.dart';
6+
7+
@LiteralParams(
8+
anInt: 7,
9+
aNum: 8.0,
10+
aDouble: 9.0,
11+
aString: '10',
12+
anObject: Bar(a: true, b: false),
13+
ints: [11, 12],
14+
nums: [13.0, 14],
15+
doubles: [15.0, 16],
16+
strings: ['17', 'eighteen'],
17+
objects: [
18+
19,
19+
Bar(a: true, b: false),
20+
],
21+
)
22+
class Foo {}
23+
24+
class Bar {
25+
final bool? a;
26+
final bool? b;
27+
28+
const Bar({this.a, this.b});
29+
}
30+
31+
void main() {}

goldens/foo/lib/metadata.analyzer.json

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,26 @@
5555
},
5656
"arguments": [
5757
{
58-
"name": "aBool",
59-
"expression": {
60-
"type": "BooleanLiteral",
61-
"value": {
62-
"text": "true"
58+
"type": "NamedArgument",
59+
"value": {
60+
"name": "aBool",
61+
"expression": {
62+
"type": "BooleanLiteral",
63+
"value": {
64+
"text": "true"
65+
}
6366
}
6467
}
6568
},
6669
{
67-
"name": "anInt",
68-
"expression": {
69-
"type": "IntegerLiteral",
70-
"value": {
71-
"text": "23"
70+
"type": "NamedArgument",
71+
"value": {
72+
"name": "anInt",
73+
"expression": {
74+
"type": "IntegerLiteral",
75+
"value": {
76+
"text": "23"
77+
}
7278
}
7379
}
7480
}

pkgs/_analyzer_cfe_macros/lib/metadata_converter.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,19 @@ T? convert<T>(Object? object) => switch (object) {
407407
int o => o as T,
408408
bool o => o as T,
409409
double o => o as T,
410+
// Manually added converters for lists of union types.
411+
List<front_end.Argument> o =>
412+
o.map((i) => convertToArgument(i)!).toList() as T,
413+
List<front_end.Element> o =>
414+
o.map((i) => convertToElement(i)!).toList() as T,
415+
List<front_end.Expression> o =>
416+
o.map((i) => convertToExpression(i)!).toList() as T,
417+
List<front_end.RecordField> o =>
418+
o.map((i) => convertToRecordField(i)!).toList() as T,
419+
List<front_end.StringLiteralPart> o =>
420+
o.map((i) => convertToStringLiteralPart(i)!).toList() as T,
421+
List<front_end.TypeAnnotation> o =>
422+
o.map((i) => convertToTypeAnnotation(i)!).toList() as T,
410423
List o => o.map((i) => convert<Map<String, Object?>>(i)!).toList() as T,
411424
null => null,
412425
_ => throw ArgumentError(object),

pkgs/_analyzer_cfe_macros/test/metadata_converter_test.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,12 @@ void main() {
5757
'typeArguments': [],
5858
'arguments': [
5959
{
60-
'expression': {
61-
'type': 'IntegerLiteral',
62-
'value': {'text': '4'}
60+
'type': 'PositionalArgument',
61+
'value': {
62+
'expression': {
63+
'type': 'IntegerLiteral',
64+
'value': {'text': '4'}
65+
}
6366
}
6467
}
6568
]
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:dart_model/dart_model.dart';
6+
// ignore: implementation_imports
7+
import 'package:dart_model/src/macro_metadata.g.dart';
8+
import 'package:macro/macro.dart';
9+
import 'package:macro_service/macro_service.dart';
10+
11+
import 'templating.dart';
12+
13+
/// Covers macro metadata cases where the params will always be written as
14+
/// literals in the annotation.
15+
///
16+
/// Outputs comments with evaluation results.
17+
///
18+
/// Throws if the annotation has something other than supported literals.
19+
/// TODO(davidmorgan): support diagnostics, make failures diagnostics.
20+
class LiteralParams {
21+
final int? anInt;
22+
final num? aNum;
23+
final double? aDouble;
24+
final String? aString;
25+
final Object? anObject;
26+
final List<int>? ints;
27+
final List<num>? nums;
28+
final List<double>? doubles;
29+
final List<String>? strings;
30+
final List<Object>? objects;
31+
32+
const LiteralParams(
33+
{required this.anInt,
34+
this.aNum,
35+
this.aDouble,
36+
this.aString,
37+
this.anObject,
38+
this.ints,
39+
this.nums,
40+
this.doubles,
41+
this.strings,
42+
this.objects});
43+
}
44+
45+
class LiteralParamsImplementation implements ClassDeclarationsMacro {
46+
// TODO(davidmorgan): this should be injected by the bootstrap script.
47+
@override
48+
MacroDescription get description => MacroDescription(
49+
annotation: QualifiedName(
50+
uri: 'package:_test_macros/literal_params.dart',
51+
name: 'LiteralParams'),
52+
runsInPhases: [2]);
53+
54+
@override
55+
Future<void> buildDeclarationsForClass(
56+
ClassDeclarationsBuilder builder) async {
57+
// TODO(davidmorgan): need a way to find the correct annotation, this just
58+
// uses the first.
59+
final annotation = builder
60+
.target.metadataAnnotations.first.expression.asConstructorInvocation;
61+
62+
final namedArguments = {
63+
for (final argument in annotation.arguments)
64+
if (argument.type == ArgumentType.namedArgument)
65+
argument.asNamedArgument.name:
66+
argument.asNamedArgument.expression.evaluate
67+
};
68+
69+
builder.declareInType(Augmentation(
70+
code: expandTemplate([
71+
for (final entry in namedArguments.entries)
72+
' // ${entry.key}: ${entry.value}, ${entry.value.runtimeType}',
73+
].join('\n'))));
74+
}
75+
}
76+
77+
// TODO(davidmorgan): common code for this in `dart_model` so macros don't
78+
// all have to write expression evaluation code.
79+
extension ExpressionExtension on Expression {
80+
Object get evaluate => switch (type) {
81+
ExpressionType.integerLiteral => int.parse(asIntegerLiteral.text),
82+
ExpressionType.doubleLiteral => double.parse(asDoubleLiteral.text),
83+
ExpressionType.stringLiteral => asStringLiteral.evaluate,
84+
ExpressionType.booleanLiteral => bool.parse(asBooleanLiteral.text),
85+
ExpressionType.listLiteral =>
86+
asListLiteral.elements.map((e) => e.evaluate).toList(),
87+
// TODO(davidmorgan): need the type name to do something useful here,
88+
// for now just return the JSON.
89+
ExpressionType.constructorInvocation =>
90+
asConstructorInvocation.toString(),
91+
// TODO(davidmorgan): need to follow references to do something useful
92+
// here, for now just return the JSON.
93+
ExpressionType.staticGet => asStaticGet.toString(),
94+
_ => throw UnsupportedError(
95+
'Not supported in @LiteralParams annotation: $this'),
96+
};
97+
}
98+
99+
extension ElementExtension on Element {
100+
Object get evaluate => switch (type) {
101+
ElementType.expressionElement =>
102+
asExpressionElement.expression.evaluate,
103+
_ => throw UnsupportedError(
104+
'Not supported in @LiteralParams annotation: $this'),
105+
};
106+
}
107+
108+
extension StringLiteralExtension on StringLiteral {
109+
Object get evaluate {
110+
if (parts.length != 1) {
111+
throw UnsupportedError(
112+
'Not supported in @LiteralParams annotation: $this');
113+
}
114+
final part = parts.single;
115+
return switch (part.type) {
116+
StringLiteralPartType.stringPart => part.asStringPart.text,
117+
_ => throw UnsupportedError(
118+
'Not supported in @LiteralParams annotation: $this'),
119+
};
120+
}
121+
}

pkgs/_test_macros/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ dev_dependencies:
2121
# macro lib/declare_x_macro.dart#DeclareX package:_test_macros/declare_x_macro.dart#DeclareXImplementation
2222
# macro lib/json_codable.dart#JsonCodable package:_test_macros/json_codable.dart#JsonCodableImplementation
2323
# macro lib/query_class.dart#QueryClass package:_test_macros/query_class.dart#QueryClassImplementation
24+
# macro lib/literal_params.dart#LiteralParams package:_test_macros/literal_params.dart#LiteralParamsImplementation

0 commit comments

Comments
 (0)