@@ -66,17 +66,19 @@ struct AttributeInfo {
66
66
/// The traits applied to the attribute, if any.
67
67
var traits = [ ExprSyntax] ( )
68
68
69
+ /// Test arguments passed to a parameterized test function, if any.
70
+ ///
71
+ /// When non-`nil`, the value of this property is an array beginning with the
72
+ /// argument passed to this attribute for the parameter labeled `arguments:`
73
+ /// followed by all of the remaining, unlabeled arguments.
74
+ var testFunctionArguments : [ Argument ] ?
75
+
69
76
/// Whether or not this attribute specifies arguments to the associated test
70
77
/// function.
71
78
var hasFunctionArguments : Bool {
72
- otherArguments. lazy
73
- . compactMap ( \. label? . tokenKind)
74
- . contains ( . identifier( " arguments " ) )
79
+ testFunctionArguments != nil
75
80
}
76
81
77
- /// Additional arguments passed to the attribute, if any.
78
- var otherArguments = [ Argument] ( )
79
-
80
82
/// The source location of the attribute.
81
83
///
82
84
/// When parsing, the testing library uses the start of the attribute's name
@@ -98,6 +100,7 @@ struct AttributeInfo {
98
100
init ( byParsing attribute: AttributeSyntax , on declaration: some SyntaxProtocol , in context: some MacroExpansionContext ) {
99
101
self . attribute = attribute
100
102
103
+ var nonDisplayNameArguments : [ Argument ] = [ ]
101
104
if let arguments = attribute. arguments, case let . argumentList( argumentList) = arguments {
102
105
// If the first argument is an unlabelled string literal, it's the display
103
106
// name of the test or suite. If it's anything else, including a nil
@@ -106,11 +109,11 @@ struct AttributeInfo {
106
109
let firstArgumentHasLabel = ( firstArgument. label != nil )
107
110
if !firstArgumentHasLabel, let stringLiteral = firstArgument. expression. as ( StringLiteralExprSyntax . self) {
108
111
displayName = stringLiteral
109
- otherArguments = argumentList. dropFirst ( ) . map ( Argument . init)
112
+ nonDisplayNameArguments = argumentList. dropFirst ( ) . map ( Argument . init)
110
113
} else if !firstArgumentHasLabel, firstArgument. expression. is ( NilLiteralExprSyntax . self) {
111
- otherArguments = argumentList. dropFirst ( ) . map ( Argument . init)
114
+ nonDisplayNameArguments = argumentList. dropFirst ( ) . map ( Argument . init)
112
115
} else {
113
- otherArguments = argumentList. map ( Argument . init)
116
+ nonDisplayNameArguments = argumentList. map ( Argument . init)
114
117
}
115
118
}
116
119
}
@@ -119,7 +122,7 @@ struct AttributeInfo {
119
122
// See _SelfRemover for more information. Rewriting a syntax tree discards
120
123
// location information from the copy, so only invoke the rewriter if the
121
124
// `Self` keyword is present somewhere.
122
- otherArguments = otherArguments . map { argument in
125
+ nonDisplayNameArguments = nonDisplayNameArguments . map { argument in
123
126
var expr = argument. expression
124
127
if argument. expression. tokens ( viewMode: . sourceAccurate) . map ( \. tokenKind) . contains ( . keyword( . Self) ) {
125
128
let selfRemover = _SelfRemover ( in: context)
@@ -131,15 +134,14 @@ struct AttributeInfo {
131
134
// Look for any traits in the remaining arguments and slice them off. Traits
132
135
// are the remaining unlabelled arguments. The first labelled argument (if
133
136
// present) is the start of subsequent context-specific arguments.
134
- if !otherArguments . isEmpty {
135
- if let labelledArgumentIndex = otherArguments . firstIndex ( where: { $0. label != nil } ) {
137
+ if !nonDisplayNameArguments . isEmpty {
138
+ if let labelledArgumentIndex = nonDisplayNameArguments . firstIndex ( where: { $0. label != nil } ) {
136
139
// There is an argument with a label, so splice there.
137
- traits = otherArguments [ otherArguments . startIndex ..< labelledArgumentIndex] . map ( \. expression)
138
- otherArguments = Array ( otherArguments [ labelledArgumentIndex... ] )
140
+ traits = nonDisplayNameArguments [ nonDisplayNameArguments . startIndex ..< labelledArgumentIndex] . map ( \. expression)
141
+ testFunctionArguments = Array ( nonDisplayNameArguments [ labelledArgumentIndex... ] )
139
142
} else {
140
143
// No argument has a label, so all the remaining arguments are traits.
141
- traits = otherArguments. map ( \. expression)
142
- otherArguments. removeAll ( keepingCapacity: false )
144
+ traits = nonDisplayNameArguments. map ( \. expression)
143
145
}
144
146
}
145
147
@@ -178,21 +180,16 @@ struct AttributeInfo {
178
180
}
179
181
} ) )
180
182
181
- // Any arguments of the test declaration macro which specify test arguments
182
- // need to be wrapped a closure so they may be evaluated lazily by the
183
- // testing library at runtime. If any such arguments are present, they will
184
- // begin with a labeled argument named `arguments:` and include all
185
- // subsequent unlabeled arguments.
186
- var otherArguments = self . otherArguments
187
- if let argumentsIndex = otherArguments. firstIndex ( where: { $0. label? . tokenKind == . identifier( " arguments " ) } ) {
188
- for index in argumentsIndex ..< otherArguments. endIndex {
189
- var argument = otherArguments [ index]
190
- argument. expression = . init( ClosureExprSyntax { argument. expression. trimmed } )
191
- otherArguments [ index] = argument
183
+ // If there are any parameterized test function arguments, wrap each in a
184
+ // closure so they may be evaluated lazily at runtime.
185
+ if let testFunctionArguments {
186
+ arguments += testFunctionArguments. map { argument in
187
+ var copy = argument
188
+ copy. expression = . init( ClosureExprSyntax { argument. expression. trimmed } )
189
+ return copy
192
190
}
193
191
}
194
192
195
- arguments += otherArguments
196
193
arguments. append ( Argument ( label: " sourceLocation " , expression: sourceLocation) )
197
194
198
195
return LabeledExprListSyntax ( arguments)
0 commit comments