@@ -136,6 +136,95 @@ final class ExecuteCommandTests: XCTestCase {
136
136
)
137
137
}
138
138
139
+ func testFreestandingMacroExpansion( ) async throws {
140
+ try await SkipUnless . canBuildMacroUsingSwiftSyntaxFromSourceKitLSPBuild ( )
141
+
142
+ let project = try await SwiftPMTestProject (
143
+ files: [
144
+ " MyMacros/MyMacros.swift " : #"""
145
+ import SwiftCompilerPlugin
146
+ import SwiftSyntax
147
+ import SwiftSyntaxBuilder
148
+ import SwiftSyntaxMacros
149
+
150
+ public struct StringifyMacro: ExpressionMacro {
151
+ public static func expansion(
152
+ of node: some FreestandingMacroExpansionSyntax,
153
+ in context: some MacroExpansionContext
154
+ ) -> ExprSyntax {
155
+ guard let argument = node.argumentList.first?.expression else {
156
+ fatalError("compiler bug: the macro does not have any arguments")
157
+ }
158
+
159
+ return "(\(argument), \(literal: argument.description))"
160
+ }
161
+ }
162
+
163
+ @main
164
+ struct MyMacroPlugin: CompilerPlugin {
165
+ let providingMacros: [Macro.Type] = [
166
+ StringifyMacro.self,
167
+ ]
168
+ }
169
+ """# ,
170
+ " MyMacroClient/MyMacroClient.swift " : """
171
+ @freestanding(expression)
172
+ public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: " MyMacros " , type: " StringifyMacro " )
173
+
174
+ func test() {
175
+ 1️⃣#stringify2️⃣(1 + 2)
176
+ }
177
+ """ ,
178
+ ] ,
179
+ manifest: SwiftPMTestProject . macroPackageManifest
180
+ )
181
+ try await SwiftPMTestProject . build ( at: project. scratchDirectory)
182
+
183
+ let ( uri, positions) = try project. openDocument ( " MyMacroClient.swift " )
184
+
185
+ let args = ExpandMacroCommand (
186
+ positionRange: positions [ " 1️⃣ " ] ..< positions [ " 2️⃣ " ] ,
187
+ textDocument: TextDocumentIdentifier ( uri)
188
+ )
189
+
190
+ let metadata = SourceKitLSPCommandMetadata ( textDocument: TextDocumentIdentifier ( uri) )
191
+
192
+ var command = try args. asCommand ( )
193
+ command. arguments? . append ( metadata. encodeToLSPAny ( ) )
194
+
195
+ let request = ExecuteCommandRequest ( command: command. command, arguments: command. arguments)
196
+
197
+ project. testClient. handleSingleRequest { ( req: ShowDocumentRequest ) -> ShowDocumentResponse in
198
+ return ShowDocumentResponse ( success: true )
199
+ }
200
+
201
+ let result = try await project. testClient. send ( request)
202
+
203
+ guard let resultArray: [ MacroExpansionEdit ] = Array ( fromLSPArray: result ?? . null) else {
204
+ XCTFail ( " Result is not an array. " )
205
+ return
206
+ }
207
+
208
+ XCTAssertEqual ( resultArray. count, 1 )
209
+ XCTAssertEqual ( resultArray [ 0 ] . newText, " (1 + 2, \" 1 + 2 \" ) " )
210
+
211
+ let generatedMacroExpansionsPath = SourceKitLSPServer . Options. testDefault. generatedMacroExpansionsPath
212
+
213
+ XCTAssert (
214
+ ( try ? generatedMacroExpansionsPath. appending ( component: resultArray [ 0 ] . bufferName) . asURL
215
+ . checkResourceIsReachable ( ) ) ?? false
216
+ )
217
+
218
+ let fileContents = try XCTUnwrap (
219
+ try ? String (
220
+ contentsOf: generatedMacroExpansionsPath. appending ( component: resultArray [ 0 ] . bufferName) . asURL,
221
+ encoding: . utf8
222
+ )
223
+ )
224
+
225
+ XCTAssertTrue ( fileContents. contains ( " (1 + 2, \" 1 + 2 \" ) " ) )
226
+ }
227
+
139
228
func testLSPCommandMetadataRetrieval( ) {
140
229
var req = ExecuteCommandRequest ( command: " " , arguments: nil )
141
230
XCTAssertNil ( req. metadata)
0 commit comments