1
1
import SwiftDiagnostics
2
2
import SwiftSyntax
3
-
4
- /// The replacement of a parameter.
5
- @_spi ( Testing)
6
- public struct ParameterReplacement {
7
- /// A reference to a parameter as it occurs in the macro expansion expression.
8
- public let reference : IdentifierExprSyntax
9
-
10
- /// The index of the parameter
11
- public let parameterIndex : Int
12
- }
3
+ import SwiftSyntaxBuilder
13
4
14
5
extension FunctionParameterSyntax {
15
6
/// Retrieve the name of the parameter as it is used in source.
@@ -44,6 +35,7 @@ extension FunctionParameterSyntax {
44
35
45
36
enum MacroExpanderError : DiagnosticMessage {
46
37
case undefined
38
+ case definitionNotMacroExpansion
47
39
case nonParameterReference( TokenSyntax )
48
40
case nonLiteralOrParameter( ExprSyntax )
49
41
@@ -52,6 +44,9 @@ enum MacroExpanderError: DiagnosticMessage {
52
44
case . undefined:
53
45
return " macro expansion requires a definition "
54
46
47
+ case . definitionNotMacroExpansion:
48
+ return " macro definition must itself by a macro expansion expression (starting with '#') "
49
+
55
50
case . nonParameterReference( let name) :
56
51
return " reference to value ' \( name. text) ' that is not a macro parameter in expansion "
57
52
@@ -69,9 +64,36 @@ enum MacroExpanderError: DiagnosticMessage {
69
64
}
70
65
}
71
66
67
+ /// Provide the definition of a macro
68
+ public enum MacroDefinition {
69
+ /// An externally-defined macro, known by its type name and the module in
70
+ /// which that type resides, which uses the deprecated syntax `A.B`.
71
+ case deprecatedExternal( node: Syntax , module: String , type: String )
72
+
73
+ /// A macro that is defined by expansion of another macro.
74
+ ///
75
+ /// The definition has the macro expansion expression itself, along with
76
+ /// sequence of replacements for subtrees that refer to parameters of the
77
+ /// defining macro. These subtrees will need to be replaced with the text of
78
+ /// the corresponding argument to the macro, which can be accomplished with
79
+ /// `MacroDeclSyntax.expandDefinition`.
80
+ case expansion( MacroExpansionExprSyntax , replacements: [ Replacement ] )
81
+ }
82
+
83
+ extension MacroDefinition {
84
+ /// A replacement that occurs as part of an expanded macro definition.
85
+ public struct Replacement {
86
+ /// A reference to a parameter as it occurs in the macro expansion expression.
87
+ public let reference : IdentifierExprSyntax
88
+
89
+ /// The index of the parameter in the defining macro.
90
+ public let parameterIndex : Int
91
+ }
92
+ }
93
+
72
94
fileprivate class ParameterReplacementVisitor : SyntaxAnyVisitor {
73
95
let macro : MacroDeclSyntax
74
- var replacements : [ ParameterReplacement ] = [ ]
96
+ var replacements : [ MacroDefinition . Replacement ] = [ ]
75
97
var diagnostics : [ Diagnostic ] = [ ]
76
98
77
99
init ( macro: MacroDeclSyntax ) {
@@ -159,7 +181,8 @@ fileprivate class ParameterReplacementVisitor: SyntaxAnyVisitor {
159
181
160
182
override func visitAny( _ node: Syntax ) -> SyntaxVisitorContinueKind {
161
183
if let expr = node. as ( ExprSyntax . self) {
162
- // We have an expression that is not one of the allowed forms,
184
+ // We have an expression that is not one of the allowed forms, so
185
+ // diagnose it.
163
186
diagnostics. append (
164
187
Diagnostic (
165
188
node: node,
@@ -176,24 +199,61 @@ fileprivate class ParameterReplacementVisitor: SyntaxAnyVisitor {
176
199
}
177
200
178
201
extension MacroDeclSyntax {
202
+ /// Check the definition of the given macro.
203
+ ///
204
+ /// Macros are defined by an expression, which must itself be a macro
205
+ /// expansion. Check the definition and produce a semantic representation of
206
+ /// it or one of the "builtin"
207
+ ///
179
208
/// Compute the sequence of parameter replacements required when expanding
180
209
/// the definition of a non-external macro.
181
- @_spi ( Testing)
182
- public func expansionParameterReplacements( ) -> ( replacements: [ ParameterReplacement ] , diagnostics: [ Diagnostic ] ) {
210
+ ///
211
+ /// If there are an errors that prevent expansion, the diagnostics will be
212
+ /// wrapped into a an error that prevents expansion, that error is thrown.
213
+ public func checkDefinition( ) throws -> MacroDefinition {
183
214
// Cannot compute replacements for an undefined macro.
184
- guard let definition = definition? . value else {
215
+ guard let originalDefinition = definition? . value else {
185
216
let undefinedDiag = Diagnostic (
186
217
node: Syntax ( self ) ,
187
218
message: MacroExpanderError . undefined
188
219
)
189
220
190
- return ( replacements: [ ] , diagnostics: [ undefinedDiag] )
221
+ throw DiagnosticsError ( diagnostics: [ undefinedDiag] )
222
+ }
223
+
224
+ /// Recognize the deprecated syntax A.B. Clients will need to
225
+ /// handle this themselves.
226
+ if let memberAccess = originalDefinition. as ( MemberAccessExprSyntax . self) ,
227
+ let base = memberAccess. base,
228
+ let baseName = base. as ( IdentifierExprSyntax . self) ? . identifier
229
+ {
230
+ let memberName = memberAccess. name
231
+ return . deprecatedExternal(
232
+ node: Syntax ( memberAccess) ,
233
+ module: baseName. trimmedDescription,
234
+ type: memberName. trimmedDescription
235
+ )
236
+ }
237
+
238
+ // Make sure we have a macro expansion expression.
239
+ guard let definition = originalDefinition. as ( MacroExpansionExprSyntax . self) else {
240
+ let badDefinitionDiag =
241
+ Diagnostic (
242
+ node: Syntax ( originalDefinition) ,
243
+ message: MacroExpanderError . definitionNotMacroExpansion
244
+ )
245
+
246
+ throw DiagnosticsError ( diagnostics: [ badDefinitionDiag] )
191
247
}
192
248
193
249
let visitor = ParameterReplacementVisitor ( macro: self )
194
250
visitor. walk ( definition)
195
251
196
- return ( replacements: visitor. replacements, diagnostics: visitor. diagnostics)
252
+ if !visitor. diagnostics. isEmpty {
253
+ throw DiagnosticsError ( diagnostics: visitor. diagnostics)
254
+ }
255
+
256
+ return . expansion( definition, replacements: visitor. replacements)
197
257
}
198
258
}
199
259
@@ -219,27 +279,18 @@ private final class MacroExpansionRewriter: SyntaxRewriter {
219
279
}
220
280
221
281
extension MacroDeclSyntax {
222
- /// Given a freestanding macro expansion syntax node that references this
223
- /// macro declaration, expand the macro by substituting the arguments from
224
- /// the macro expansion into the parameters that are used in the definition.
225
- ///
226
- /// If there are any errors, the function will throw with all diagnostics
227
- /// placed in a `DiagnosticsError`.
228
- public func expandDefinition(
229
- _ node: some FreestandingMacroExpansionSyntax
230
- ) throws -> ExprSyntax {
231
- let ( replacements, diagnostics) = expansionParameterReplacements ( )
232
-
233
- // If there were any diagnostics, don't allow replacement.
234
- if !diagnostics. isEmpty {
235
- throw DiagnosticsError ( diagnostics: diagnostics)
236
- }
237
-
282
+ /// Expand the definition of this macro when provided with the given
283
+ /// argument list.
284
+ private func expand(
285
+ argumentList: TupleExprElementListSyntax ? ,
286
+ definition: MacroExpansionExprSyntax ,
287
+ replacements: [ MacroDefinition . Replacement ]
288
+ ) -> ExprSyntax {
238
289
// FIXME: Do real call-argument matching between the argument list and the
239
290
// macro parameter list, porting over from the compiler.
240
- let arguments : [ ExprSyntax ] = node . argumentList. map { element in
291
+ let arguments : [ ExprSyntax ] = argumentList? . map { element in
241
292
element. expression
242
- }
293
+ } ?? [ ]
243
294
244
295
return MacroExpansionRewriter (
245
296
parameterReplacements: Dictionary (
@@ -248,6 +299,44 @@ extension MacroDeclSyntax {
248
299
}
249
300
) ,
250
301
arguments: arguments
251
- ) . visit ( definition!. value)
302
+ ) . visit ( definition)
303
+ }
304
+
305
+ /// Given a freestanding macro expansion syntax node that references this
306
+ /// macro declaration, expand the macro by substituting the arguments from
307
+ /// the macro expansion into the parameters that are used in the definition.
308
+ public func expand(
309
+ _ node: some FreestandingMacroExpansionSyntax ,
310
+ definition: MacroExpansionExprSyntax ,
311
+ replacements: [ MacroDefinition . Replacement ]
312
+ ) -> ExprSyntax {
313
+ return try expand (
314
+ argumentList: node. argumentList,
315
+ definition: definition,
316
+ replacements: replacements
317
+ )
318
+ }
319
+
320
+ /// Given an attached macro expansion syntax node that references this
321
+ /// macro declaration, expand the macro by substituting the arguments from
322
+ /// the expansion into the parameters that are used in the definition.
323
+ public func expand(
324
+ _ node: AttributeSyntax ,
325
+ definition: MacroExpansionExprSyntax ,
326
+ replacements: [ MacroDefinition . Replacement ]
327
+ ) -> ExprSyntax {
328
+ // Dig out the argument list.
329
+ let argumentList : TupleExprElementListSyntax ?
330
+ if case let . argumentList( argList) = node. argument {
331
+ argumentList = argList
332
+ } else {
333
+ argumentList = nil
334
+ }
335
+
336
+ return try expand (
337
+ argumentList: argumentList,
338
+ definition: definition,
339
+ replacements: replacements
340
+ )
252
341
}
253
342
}
0 commit comments