@@ -21,7 +21,9 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
21
21
providingPeersOf declaration: some DeclSyntaxProtocol ,
22
22
in context: some MacroExpansionContext
23
23
) throws -> [ DeclSyntax ] {
24
- _diagnoseIssues ( with: declaration, testAttribute: node, in: context)
24
+ guard _diagnoseIssues ( with: declaration, testAttribute: node, in: context) else {
25
+ return [ ]
26
+ }
25
27
26
28
guard let function = declaration. as ( FunctionDeclSyntax . self) else {
27
29
return [ ]
@@ -45,6 +47,17 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
45
47
testAttribute: AttributeSyntax ,
46
48
in context: some MacroExpansionContext
47
49
) -> TypeSyntax ? {
50
+ #if canImport(SwiftSyntax600)
51
+ let types = context. lexicalContext
52
+ . compactMap { $0. asProtocol ( ( any DeclGroupSyntax ) . self) }
53
+ . map ( \. type)
54
+ . reversed ( )
55
+ if types. isEmpty {
56
+ return nil
57
+ }
58
+ let typeName = types. map ( \. trimmedDescription) . joined ( separator: " . " )
59
+ return " \( raw: typeName) "
60
+ #else
48
61
// Find the beginning of the first attribute on the declaration, including
49
62
// those embedded in #if statements, to account for patterns like
50
63
// `@MainActor @Test func` where there's a space ahead of @Test, but the
@@ -79,6 +92,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
79
92
return TypeSyntax ( IdentifierTypeSyntax ( name: . keyword( . Self) ) )
80
93
}
81
94
return nil
95
+ #endif
82
96
}
83
97
84
98
/// Diagnose issues with a `@Test` declaration.
@@ -87,22 +101,30 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
87
101
/// - declaration: The function declaration to diagnose.
88
102
/// - testAttribute: The `@Test` attribute applied to `declaration`.
89
103
/// - context: The macro context in which the expression is being parsed.
104
+ ///
105
+ /// - Returns: Whether or not macro expansion should continue (i.e. stopping
106
+ /// if a fatal error was diagnosed.)
90
107
private static func _diagnoseIssues(
91
108
with declaration: some DeclSyntaxProtocol ,
92
109
testAttribute: AttributeSyntax ,
93
110
in context: some MacroExpansionContext
94
- ) {
111
+ ) -> Bool {
95
112
var diagnostics = [ DiagnosticMessage] ( )
96
113
defer {
97
- diagnostics . forEach ( context. diagnose)
114
+ context. diagnose ( diagnostics )
98
115
}
99
116
100
117
// The @Test attribute is only supported on function declarations.
101
118
guard let function = declaration. as ( FunctionDeclSyntax . self) else {
102
119
diagnostics. append ( . attributeNotSupported( testAttribute, on: declaration) )
103
- return
120
+ return false
104
121
}
105
122
123
+ #if canImport(SwiftSyntax600)
124
+ // Check if the lexical context is appropriate for a suite or test.
125
+ diagnostics += diagnoseIssuesWithLexicalContext ( containing: declaration, attribute: testAttribute, in: context)
126
+ #endif
127
+
106
128
// Only one @Test attribute is supported.
107
129
let suiteAttributes = function. attributes ( named: " Test " , in: context)
108
130
if suiteAttributes. count > 1 {
@@ -113,13 +135,22 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
113
135
114
136
// We don't support inout, isolated, or _const parameters on test functions.
115
137
for parameter in parameterList {
116
- if let specifier = parameter. type. as ( AttributedTypeSyntax . self) ? . specifier {
117
- switch specifier. tokenKind {
118
- case . keyword( . inout) , . keyword( . isolated) , . keyword( . _const) :
138
+ let invalidSpecifierKeywords : [ TokenKind ] = [ . keyword( . inout) , . keyword( . isolated) , . keyword( . _const) , ]
139
+ if let parameterType = parameter. type. as ( AttributedTypeSyntax . self) {
140
+ #if canImport(SwiftSyntax600)
141
+ for specifier in parameterType. specifiers {
142
+ guard case let . simpleTypeSpecifier( specifier) = specifier else {
143
+ continue
144
+ }
145
+ if invalidSpecifierKeywords. contains ( specifier. specifier. tokenKind) {
146
+ diagnostics. append ( . specifierNotSupported( specifier. specifier, on: parameter, whenUsing: testAttribute) )
147
+ }
148
+ }
149
+ #else
150
+ if let specifier = parameterType. specifier, invalidSpecifierKeywords. contains ( specifier. tokenKind) {
119
151
diagnostics. append ( . specifierNotSupported( specifier, on: parameter, whenUsing: testAttribute) )
120
- default :
121
- break
122
152
}
153
+ #endif
123
154
}
124
155
}
125
156
@@ -144,6 +175,8 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
144
175
}
145
176
}
146
177
}
178
+
179
+ return !diagnostics. lazy. map ( \. severity) . contains ( . error)
147
180
}
148
181
149
182
/// Create a function call parameter list used to call a function from its
@@ -220,21 +253,41 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
220
253
private static func _createCaptureListExpr(
221
254
from parametersWithLabels: some Sequence < ( DeclReferenceExprSyntax , FunctionParameterSyntax ) >
222
255
) -> ClosureCaptureClauseSyntax {
223
- ClosureCaptureClauseSyntax {
224
- for ( label, parameter) in parametersWithLabels {
225
- if case let . keyword ( specifierKeyword ) = parameter . type . as ( AttributedTypeSyntax . self ) ? . specifier ? . tokenKind ,
226
- specifierKeyword == . borrowing || specifierKeyword == . consuming {
227
- ClosureCaptureSyntax (
228
- name : label . baseName ,
229
- equal : . equalToken ( ) ,
230
- expression : CopyExprSyntax (
231
- copyKeyword : . keyword ( . copy ) . with ( \ . trailingTrivia , . space ) ,
232
- expression : label
233
- )
234
- )
235
- } else {
236
- ClosureCaptureSyntax ( expression : label )
256
+ let specifierKeywordsNeedingCopy : [ TokenKind ] = [ . keyword ( . borrowing ) , . keyword ( . consuming ) , ]
257
+ let closureCaptures = parametersWithLabels . lazy . map { label, parameter in
258
+ var needsCopy = false
259
+ if let parameterType = parameter . type . as ( AttributedTypeSyntax . self ) {
260
+ #if canImport(SwiftSyntax600)
261
+ needsCopy = parameterType . specifiers . contains { specifier in
262
+ guard case let . simpleTypeSpecifier ( specifier ) = specifier else {
263
+ return false
264
+ }
265
+ return specifierKeywordsNeedingCopy . contains ( specifier . specifier . tokenKind )
266
+ }
267
+ #else
268
+ if let specifier = parameterType . specifier {
269
+ needsCopy = specifierKeywordsNeedingCopy . contains ( specifier . tokenKind )
237
270
}
271
+ #endif
272
+ }
273
+
274
+ if needsCopy {
275
+ return ClosureCaptureSyntax (
276
+ name: label. baseName,
277
+ equal: . equalToken( ) ,
278
+ expression: CopyExprSyntax (
279
+ copyKeyword: . keyword( . copy) . with ( \. trailingTrivia, . space) ,
280
+ expression: label
281
+ )
282
+ )
283
+ } else {
284
+ return ClosureCaptureSyntax ( expression: label)
285
+ }
286
+ }
287
+
288
+ return ClosureCaptureClauseSyntax {
289
+ for closureCapture in closureCaptures {
290
+ closureCapture
238
291
}
239
292
}
240
293
}
@@ -406,6 +459,11 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
406
459
) -> [ DeclSyntax ] {
407
460
var result = [ DeclSyntax] ( )
408
461
462
+ #if canImport(SwiftSyntax600)
463
+ // Get the name of the type containing the function for passing to the test
464
+ // factory function later.
465
+ let typealiasExpr : ExprSyntax = typeName. map { " \( $0) .self " } ?? " nil "
466
+ #else
409
467
// We cannot directly refer to Self here because it will end up being
410
468
// resolved as the __TestContainer type we generate. Create a uniquely-named
411
469
// reference to Self outside the context of the generated type, and use it
@@ -415,7 +473,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
415
473
// inside a static computed property instead of a typealias (where covariant
416
474
// Self is disallowed.)
417
475
//
418
- // This "typealias" will not be necessary when rdar://105470382 is resolved .
476
+ // This "typealias" is not necessary when swift-syntax-6.0.0 is available .
419
477
var typealiasExpr : ExprSyntax = " nil "
420
478
if let typeName {
421
479
let typealiasName = context. makeUniqueName ( thunking: functionDecl)
@@ -430,6 +488,7 @@ public struct TestDeclarationMacro: PeerMacro, Sendable {
430
488
431
489
typealiasExpr = " \( typealiasName) "
432
490
}
491
+ #endif
433
492
434
493
// Parse the @Test attribute.
435
494
let attributeInfo = AttributeInfo ( byParsing: testAttribute, on: functionDecl, in: context)
0 commit comments