|
| 1 | +import SwiftSyntax |
| 2 | +import SwiftSyntaxBuilder |
| 3 | +import SwiftSyntaxMacros |
| 4 | +import SwiftDiagnostics |
| 5 | + |
| 6 | +public enum JSFunctionMacro {} |
| 7 | + |
| 8 | +extension JSFunctionMacro: BodyMacro { |
| 9 | + public static func expansion( |
| 10 | + of node: AttributeSyntax, |
| 11 | + providingBodyFor declaration: some DeclSyntaxProtocol & WithOptionalCodeBlockSyntax, |
| 12 | + in context: some MacroExpansionContext |
| 13 | + ) throws -> [CodeBlockItemSyntax] { |
| 14 | + if let functionDecl = declaration.as(FunctionDeclSyntax.self) { |
| 15 | + let enclosingTypeName = JSMacroHelper.enclosingTypeName(from: context) |
| 16 | + let isStatic = JSMacroHelper.isStatic(functionDecl.modifiers) |
| 17 | + let isInstanceMember = enclosingTypeName != nil && !isStatic |
| 18 | + |
| 19 | + let name = functionDecl.name.text |
| 20 | + let glueName = JSMacroHelper.glueName(baseName: name, enclosingTypeName: enclosingTypeName) |
| 21 | + |
| 22 | + var arguments: [String] = [] |
| 23 | + if isInstanceMember { |
| 24 | + arguments.append("self.jsObject") |
| 25 | + } |
| 26 | + arguments.append( |
| 27 | + contentsOf: JSMacroHelper.parameterNames(functionDecl.signature.parameterClause.parameters) |
| 28 | + ) |
| 29 | + |
| 30 | + let argsJoined = arguments.joined(separator: ", ") |
| 31 | + let call = "\(glueName)(\(argsJoined))" |
| 32 | + |
| 33 | + let effects = functionDecl.signature.effectSpecifiers |
| 34 | + let isAsync = effects?.asyncSpecifier != nil |
| 35 | + let isThrows = effects?.throwsClause != nil |
| 36 | + let prefix = JSMacroHelper.tryAwaitPrefix(isAsync: isAsync, isThrows: isThrows) |
| 37 | + |
| 38 | + let isVoid = JSMacroHelper.isVoidReturn(functionDecl.signature.returnClause?.type) |
| 39 | + let line = isVoid ? "\(prefix)\(call)" : "return \(prefix)\(call)" |
| 40 | + return [CodeBlockItemSyntax(stringLiteral: line)] |
| 41 | + } |
| 42 | + |
| 43 | + if let initializerDecl = declaration.as(InitializerDeclSyntax.self) { |
| 44 | + guard let enclosingTypeName = JSMacroHelper.enclosingTypeName(from: context) else { |
| 45 | + context.diagnose( |
| 46 | + Diagnostic(node: Syntax(declaration), message: JSMacroMessage.unsupportedDeclaration) |
| 47 | + ) |
| 48 | + return [] |
| 49 | + } |
| 50 | + |
| 51 | + let glueName = JSMacroHelper.glueName(baseName: "init", enclosingTypeName: enclosingTypeName) |
| 52 | + let parameters = initializerDecl.signature.parameterClause.parameters |
| 53 | + let arguments = JSMacroHelper.parameterNames(parameters) |
| 54 | + let call = "\(glueName)(\(arguments.joined(separator: ", ")))" |
| 55 | + |
| 56 | + let effects = initializerDecl.signature.effectSpecifiers |
| 57 | + let isAsync = effects?.asyncSpecifier != nil |
| 58 | + let isThrows = effects?.throwsClause != nil |
| 59 | + let prefix = JSMacroHelper.tryAwaitPrefix(isAsync: isAsync, isThrows: isThrows) |
| 60 | + |
| 61 | + return [ |
| 62 | + CodeBlockItemSyntax(stringLiteral: "let jsObject = \(prefix)\(call)"), |
| 63 | + CodeBlockItemSyntax(stringLiteral: "self.init(unsafelyWrapping: jsObject)"), |
| 64 | + ] |
| 65 | + } |
| 66 | + |
| 67 | + context.diagnose(Diagnostic(node: Syntax(declaration), message: JSMacroMessage.unsupportedDeclaration)) |
| 68 | + return [] |
| 69 | + } |
| 70 | +} |
0 commit comments