@@ -18,6 +18,7 @@ enum MacroExpanderError: DiagnosticMessage {
18
18
case undefined
19
19
case definitionNotMacroExpansion
20
20
case nonParameterReference( TokenSyntax )
21
+ case nonTypeReference( TokenSyntax )
21
22
case nonLiteralOrParameter( ExprSyntax )
22
23
23
24
var message : String {
@@ -31,6 +32,9 @@ enum MacroExpanderError: DiagnosticMessage {
31
32
case . nonParameterReference( let name) :
32
33
return " reference to value ' \( name. text) ' that is not a macro parameter in expansion "
33
34
35
+ case . nonTypeReference( let name) :
36
+ return " reference to type ' \( name) ' that is not a macro type parameter in expansion "
37
+
34
38
case . nonLiteralOrParameter:
35
39
return " only literals and macro parameters are permitted in expansion "
36
40
}
@@ -58,7 +62,15 @@ public enum MacroDefinition {
58
62
/// defining macro. These subtrees will need to be replaced with the text of
59
63
/// the corresponding argument to the macro, which can be accomplished with
60
64
/// `MacroDeclSyntax.expandDefinition`.
61
- case expansion( MacroExpansionExprSyntax , replacements: [ Replacement ] )
65
+ case expansion( MacroExpansionExprSyntax , replacements: [ Replacement ] , genericReplacements: [ GenericArgumentReplacement ] )
66
+ }
67
+
68
+ extension MacroDefinition {
69
+ /// Best effort compatibility shim, the case has gained additional parameters.
70
+ @available ( * , deprecated, message: " Use the expansion case with three associated values instead " )
71
+ public func expansion( _ node: MacroExpansionExprSyntax , replacements: [ Replacement ] ) -> Self {
72
+ . expansion( node, replacements: replacements, genericReplacements: [ ] )
73
+ }
62
74
}
63
75
64
76
extension MacroDefinition {
@@ -70,11 +82,21 @@ extension MacroDefinition {
70
82
/// The index of the parameter in the defining macro.
71
83
public let parameterIndex : Int
72
84
}
85
+
86
+ /// A replacement that occurs as part of an expanded macro definition.
87
+ public struct GenericArgumentReplacement {
88
+ /// A reference to a parameter as it occurs in the macro expansion expression.
89
+ public let reference : GenericArgumentSyntax
90
+
91
+ /// The index of the parameter in the defining macro.
92
+ public let parameterIndex : Int
93
+ }
73
94
}
74
95
75
96
fileprivate class ParameterReplacementVisitor : SyntaxAnyVisitor {
76
97
let macro : MacroDeclSyntax
77
98
var replacements : [ MacroDefinition . Replacement ] = [ ]
99
+ var genericReplacements : [ MacroDefinition . GenericArgumentReplacement ] = [ ]
78
100
var diagnostics : [ Diagnostic ] = [ ]
79
101
80
102
init ( macro: MacroDeclSyntax ) {
@@ -156,6 +178,44 @@ fileprivate class ParameterReplacementVisitor: SyntaxAnyVisitor {
156
178
return . visitChildren
157
179
}
158
180
181
+ override func visit( _ node: GenericArgumentClauseSyntax ) -> SyntaxVisitorContinueKind {
182
+ return . visitChildren
183
+ }
184
+
185
+ override func visit( _ node: GenericArgumentListSyntax ) -> SyntaxVisitorContinueKind {
186
+ return . visitChildren
187
+ }
188
+
189
+ override func visit( _ node: GenericArgumentSyntax ) -> SyntaxVisitorContinueKind {
190
+ guard let baseName = node. argument. as ( IdentifierTypeSyntax . self) ? . name else {
191
+ return . skipChildren
192
+ }
193
+
194
+ guard let genericParameterClause = macro. genericParameterClause else {
195
+ return . skipChildren
196
+ }
197
+
198
+ let matchedParameter = genericParameterClause. parameters. enumerated ( ) . first { ( index, parameter) in
199
+ return parameter. name. text == baseName. text
200
+ }
201
+
202
+ guard let ( parameterIndex, _) = matchedParameter else {
203
+ // We have a reference to something that isn't a parameter of the macro.
204
+ diagnostics. append (
205
+ Diagnostic (
206
+ node: Syntax ( baseName) ,
207
+ message: MacroExpanderError . nonTypeReference ( baseName)
208
+ )
209
+ )
210
+
211
+ return . visitChildren
212
+ }
213
+
214
+ genericReplacements. append ( . init( reference: node, parameterIndex: parameterIndex) )
215
+
216
+ return . visitChildren
217
+ }
218
+
159
219
override func visitAny( _ node: Syntax ) -> SyntaxVisitorContinueKind {
160
220
if let expr = node. as ( ExprSyntax . self) {
161
221
// We have an expression that is not one of the allowed forms, so
@@ -230,7 +290,7 @@ extension MacroDeclSyntax {
230
290
throw DiagnosticsError ( diagnostics: visitor. diagnostics)
231
291
}
232
292
233
- return . expansion( definition, replacements: visitor. replacements)
293
+ return . expansion( definition, replacements: visitor. replacements, genericReplacements : visitor . genericReplacements )
234
294
}
235
295
}
236
296
@@ -239,10 +299,19 @@ extension MacroDeclSyntax {
239
299
private final class MacroExpansionRewriter : SyntaxRewriter {
240
300
let parameterReplacements : [ DeclReferenceExprSyntax : Int ]
241
301
let arguments : [ ExprSyntax ]
242
-
243
- init ( parameterReplacements: [ DeclReferenceExprSyntax : Int ] , arguments: [ ExprSyntax ] ) {
302
+ let genericParameterReplacements : [ GenericArgumentSyntax : Int ]
303
+ let genericArguments : [ TypeSyntax ]
304
+
305
+ init (
306
+ parameterReplacements: [ DeclReferenceExprSyntax : Int ] ,
307
+ arguments: [ ExprSyntax ] ,
308
+ genericReplacements: [ GenericArgumentSyntax : Int ] ,
309
+ genericArguments: [ TypeSyntax ]
310
+ ) {
244
311
self . parameterReplacements = parameterReplacements
245
312
self . arguments = arguments
313
+ self . genericParameterReplacements = genericReplacements
314
+ self . genericArguments = genericArguments
246
315
super. init ( viewMode: . sourceAccurate)
247
316
}
248
317
@@ -254,31 +323,62 @@ private final class MacroExpansionRewriter: SyntaxRewriter {
254
323
// Swap in the argument for this parameter
255
324
return arguments [ parameterIndex] . trimmed
256
325
}
326
+
327
+ override func visit( _ node: GenericArgumentSyntax ) -> GenericArgumentSyntax {
328
+ guard let parameterIndex = genericParameterReplacements [ node] else {
329
+ return super. visit ( node)
330
+ }
331
+
332
+ guard parameterIndex < genericArguments. count else {
333
+ return super. visit ( node)
334
+ }
335
+
336
+ // Swap in the argument for type parameter
337
+ var node = node
338
+ node. argument = genericArguments [ parameterIndex] . trimmed
339
+ return node
340
+ }
257
341
}
258
342
259
343
extension MacroDeclSyntax {
260
344
/// Expand the definition of this macro when provided with the given
261
345
/// argument list.
262
346
private func expand(
263
347
argumentList: LabeledExprListSyntax ? ,
348
+ genericArgumentList: GenericArgumentClauseSyntax ? ,
264
349
definition: MacroExpansionExprSyntax ,
265
- replacements: [ MacroDefinition . Replacement ]
350
+ replacements: [ MacroDefinition . Replacement ] ,
351
+ genericReplacements: [ MacroDefinition . GenericArgumentReplacement ] = [ ]
266
352
) -> ExprSyntax {
267
353
// FIXME: Do real call-argument matching between the argument list and the
268
354
// macro parameter list, porting over from the compiler.
355
+ let parameterReplacements = Dictionary (
356
+ replacements. map { replacement in
357
+ ( replacement. reference, replacement. parameterIndex)
358
+ } ,
359
+ uniquingKeysWith: { l, r in l }
360
+ )
269
361
let arguments : [ ExprSyntax ] =
270
362
argumentList? . map { element in
271
363
element. expression
272
364
} ?? [ ]
273
365
274
- return MacroExpansionRewriter (
275
- parameterReplacements: Dictionary (
276
- uniqueKeysWithValues: replacements. map { replacement in
277
- ( replacement. reference, replacement. parameterIndex)
278
- }
279
- ) ,
280
- arguments: arguments
281
- ) . visit ( definition)
366
+ let genericReplacements = Dictionary (
367
+ genericReplacements. map { replacement in
368
+ ( replacement. reference, replacement. parameterIndex)
369
+ } ,
370
+ uniquingKeysWith: { l, r in l }
371
+ )
372
+ let genericArguments : [ TypeSyntax ] =
373
+ genericArgumentList? . arguments. map { $0. argument } ?? [ ]
374
+
375
+ let rewriter = MacroExpansionRewriter (
376
+ parameterReplacements: parameterReplacements,
377
+ arguments: arguments,
378
+ genericReplacements: genericReplacements,
379
+ genericArguments: genericArguments
380
+ )
381
+ return rewriter. visit ( definition)
282
382
}
283
383
284
384
/// Given a freestanding macro expansion syntax node that references this
@@ -287,12 +387,15 @@ extension MacroDeclSyntax {
287
387
public func expand(
288
388
_ node: some FreestandingMacroExpansionSyntax ,
289
389
definition: MacroExpansionExprSyntax ,
290
- replacements: [ MacroDefinition . Replacement ]
390
+ replacements: [ MacroDefinition . Replacement ] ,
391
+ genericReplacements: [ MacroDefinition . GenericArgumentReplacement ] = [ ]
291
392
) -> ExprSyntax {
292
393
return expand (
293
394
argumentList: node. arguments,
395
+ genericArgumentList: node. genericArgumentClause,
294
396
definition: definition,
295
- replacements: replacements
397
+ replacements: replacements,
398
+ genericReplacements: genericReplacements
296
399
)
297
400
}
298
401
@@ -302,7 +405,8 @@ extension MacroDeclSyntax {
302
405
public func expand(
303
406
_ node: AttributeSyntax ,
304
407
definition: MacroExpansionExprSyntax ,
305
- replacements: [ MacroDefinition . Replacement ]
408
+ replacements: [ MacroDefinition . Replacement ] ,
409
+ genericReplacements: [ MacroDefinition . GenericArgumentReplacement ] = [ ]
306
410
) -> ExprSyntax {
307
411
// Dig out the argument list.
308
412
let argumentList : LabeledExprListSyntax ?
@@ -314,8 +418,10 @@ extension MacroDeclSyntax {
314
418
315
419
return expand (
316
420
argumentList: argumentList,
421
+ genericArgumentList: . init( arguments: [ ] ) ,
317
422
definition: definition,
318
- replacements: replacements
423
+ replacements: replacements,
424
+ genericReplacements: genericReplacements
319
425
)
320
426
}
321
427
}
0 commit comments