@@ -14,7 +14,17 @@ extension JSFunctionMacro: BodyMacro {
1414 if let functionDecl = declaration. as ( FunctionDeclSyntax . self) {
1515 let enclosingTypeName = JSMacroHelper . enclosingTypeName ( from: context)
1616 let isStatic = JSMacroHelper . isStatic ( functionDecl. modifiers)
17- let isInstanceMember = enclosingTypeName != nil && !isStatic
17+ let isTopLevel = enclosingTypeName == nil
18+ let isInstanceMember = !isTopLevel && !isStatic
19+ if !isTopLevel {
20+ JSMacroHelper . diagnoseMissingJSClass ( node: node, for: " JSFunction " , in: context)
21+ }
22+
23+ JSMacroHelper . diagnoseThrowsRequiresJSException (
24+ signature: functionDecl. signature,
25+ on: Syntax ( functionDecl) ,
26+ in: context
27+ )
1828
1929 // Strip backticks from function name (e.g., "`prefix`" -> "prefix")
2030 // Backticks are only needed for Swift identifiers, not function names
@@ -34,8 +44,7 @@ extension JSFunctionMacro: BodyMacro {
3444
3545 let effects = functionDecl. signature. effectSpecifiers
3646 let isAsync = effects? . asyncSpecifier != nil
37- let isThrows = effects? . throwsClause != nil
38- let prefix = JSMacroHelper . tryAwaitPrefix ( isAsync: isAsync, isThrows: isThrows)
47+ let prefix = JSMacroHelper . tryAwaitPrefix ( isAsync: isAsync, isThrows: true )
3948
4049 let isVoid = JSMacroHelper . isVoidReturn ( functionDecl. signature. returnClause? . type)
4150 let line = isVoid ? " \( prefix) \( call) " : " return \( prefix) \( call) "
@@ -45,28 +54,58 @@ extension JSFunctionMacro: BodyMacro {
4554 if let initializerDecl = declaration. as ( InitializerDeclSyntax . self) {
4655 guard let enclosingTypeName = JSMacroHelper . enclosingTypeName ( from: context) else {
4756 context. diagnose (
48- Diagnostic ( node: Syntax ( declaration) , message: JSMacroMessage . unsupportedDeclaration)
57+ Diagnostic (
58+ node: Syntax ( declaration) ,
59+ message: JSMacroMessage . unsupportedDeclaration,
60+ notes: [
61+ Note (
62+ node: Syntax ( declaration) ,
63+ message: JSMacroNoteMessage (
64+ message: " Move this initializer inside a JS wrapper type annotated with @JSClass. "
65+ )
66+ )
67+ ]
68+ )
4969 )
5070 return [ CodeBlockItemSyntax ( stringLiteral: " fatalError( \" @JSFunction init must be inside a type \" ) " ) ]
5171 }
5272
73+ JSMacroHelper . diagnoseMissingJSClass ( node: node, for: " JSFunction " , in: context)
74+ JSMacroHelper . diagnoseThrowsRequiresJSException (
75+ signature: initializerDecl. signature,
76+ on: Syntax ( initializerDecl) ,
77+ in: context
78+ )
79+
5380 let glueName = JSMacroHelper . glueName ( baseName: " init " , enclosingTypeName: enclosingTypeName)
5481 let parameters = initializerDecl. signature. parameterClause. parameters
5582 let arguments = JSMacroHelper . parameterNames ( parameters)
5683 let call = " \( glueName) ( \( arguments. joined ( separator: " , " ) ) ) "
5784
5885 let effects = initializerDecl. signature. effectSpecifiers
5986 let isAsync = effects? . asyncSpecifier != nil
60- let isThrows = effects? . throwsClause != nil
61- let prefix = JSMacroHelper . tryAwaitPrefix ( isAsync: isAsync, isThrows: isThrows)
87+ let prefix = JSMacroHelper . tryAwaitPrefix ( isAsync: isAsync, isThrows: true )
6288
6389 return [
6490 CodeBlockItemSyntax ( stringLiteral: " let jsObject = \( prefix) \( call) " ) ,
6591 CodeBlockItemSyntax ( stringLiteral: " self.init(unsafelyWrapping: jsObject) " ) ,
6692 ]
6793 }
6894
69- context. diagnose ( Diagnostic ( node: Syntax ( declaration) , message: JSMacroMessage . unsupportedDeclaration) )
95+ context. diagnose (
96+ Diagnostic (
97+ node: Syntax ( declaration) ,
98+ message: JSMacroMessage . unsupportedDeclaration,
99+ notes: [
100+ Note (
101+ node: Syntax ( declaration) ,
102+ message: JSMacroNoteMessage (
103+ message: " Apply @JSFunction to a function or initializer on your @JSClass wrapper type. "
104+ )
105+ )
106+ ]
107+ )
108+ )
70109 return [ ]
71110 }
72111}
@@ -82,7 +121,21 @@ extension JSFunctionMacro: PeerMacro {
82121 ) throws -> [ DeclSyntax ] {
83122 if declaration. is ( FunctionDeclSyntax . self) { return [ ] }
84123 if declaration. is ( InitializerDeclSyntax . self) { return [ ] }
85- context. diagnose ( Diagnostic ( node: Syntax ( declaration) , message: JSMacroMessage . unsupportedDeclaration) )
124+ context. diagnose (
125+ Diagnostic (
126+ node: Syntax ( declaration) ,
127+ message: JSMacroMessage . unsupportedDeclaration,
128+ notes: [
129+ Note (
130+ node: Syntax ( declaration) ,
131+ message: JSMacroNoteMessage (
132+ message:
133+ " Place @JSFunction on a function or initializer; use @JSGetter/@JSSetter for properties. "
134+ )
135+ )
136+ ]
137+ )
138+ )
86139 return [ ]
87140 }
88141}
0 commit comments