From 9c42e3f3591b763c4085bf50882b724f143ab3c2 Mon Sep 17 00:00:00 2001 From: Ryan Sheehan Date: Wed, 25 Feb 2026 17:09:19 -0500 Subject: [PATCH] BridgeJS: fix codegen for async + throws exported methods The render method handled isAsync and isThrows as mutually exclusive branches, so methods that were both async and throws only got the JSPromise.async wrapper without the explicit throws(JSException) type annotation on the closure. This caused the Swift compiler to infer throws(any Error) instead of throws(JSException), producing a build error. Add a new branch for the combined case that explicitly annotates the closure with `() async throws(JSException) [-> JSValue] in`. Made-with: Cursor --- .../BridgeJS/Sources/BridgeJSCore/ExportSwift.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 7311b24b..4c91ebd7 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -319,8 +319,19 @@ public class ExportSwift { func render(abiName: String) -> DeclSyntax { let body: CodeBlockItemListSyntax if effects.isAsync { + // Explicit closure type annotation needed when throws is present + // so Swift infers throws(JSException) instead of throws(any Error) + // See: https://github.com/swiftlang/swift/issues/76165 + let closureHead: String + if effects.isThrows { + let hasReturn = self.body.contains { $0.description.contains("return ") } + let ret = hasReturn ? " -> JSValue" : "" + closureHead = " () async throws(JSException)\(ret) in" + } else { + closureHead = "" + } body = """ - let ret = JSPromise.async { + let ret = JSPromise.async {\(raw: closureHead) \(CodeBlockItemListSyntax(self.body)) }.jsObject return ret.bridgeJSLowerReturn()