@@ -152,6 +152,240 @@ public class ExportSwift {
152
152
)
153
153
}
154
154
155
+ /// Detects whether given expression is supported as default parameter value
156
+ private func isSupportedDefaultValueExpression( _ initClause: InitializerClauseSyntax ) -> Bool {
157
+ let expression = initClause. value
158
+
159
+ // Function calls are checked later in extractDefaultValue (as constructors are allowed)
160
+ if expression. is ( ArrayExprSyntax . self) { return false }
161
+ if expression. is ( DictionaryExprSyntax . self) { return false }
162
+ if expression. is ( BinaryOperatorExprSyntax . self) { return false }
163
+ if expression. is ( ClosureExprSyntax . self) { return false }
164
+
165
+ // Method call chains (e.g., obj.foo())
166
+ if let memberExpression = expression. as ( MemberAccessExprSyntax . self) ,
167
+ memberExpression. base? . is ( FunctionCallExprSyntax . self) == true
168
+ {
169
+ return false
170
+ }
171
+
172
+ return true
173
+ }
174
+
175
+ /// Extract enum case value from member access expression
176
+ private func extractEnumCaseValue(
177
+ from memberExpr: MemberAccessExprSyntax ,
178
+ type: BridgeType
179
+ ) -> DefaultValue ? {
180
+ let caseName = memberExpr. declName. baseName. text
181
+
182
+ let enumName : String ?
183
+ switch type {
184
+ case . caseEnum( let name) , . rawValueEnum( let name, _) , . associatedValueEnum( let name) :
185
+ enumName = name
186
+ case . optional( let wrappedType) :
187
+ switch wrappedType {
188
+ case . caseEnum( let name) , . rawValueEnum( let name, _) , . associatedValueEnum( let name) :
189
+ enumName = name
190
+ default :
191
+ return nil
192
+ }
193
+ default :
194
+ return nil
195
+ }
196
+
197
+ guard let enumName = enumName else { return nil }
198
+
199
+ if memberExpr. base == nil {
200
+ return . enumCase( enumName, caseName)
201
+ }
202
+
203
+ if let baseExpr = memberExpr. base? . as ( DeclReferenceExprSyntax . self) {
204
+ let baseName = baseExpr. baseName. text
205
+ let lastComponent = enumName. split ( separator: " . " ) . last. map ( String . init) ?? enumName
206
+ if baseName == enumName || baseName == lastComponent {
207
+ return . enumCase( enumName, caseName)
208
+ }
209
+ }
210
+
211
+ return nil
212
+ }
213
+
214
+ /// Extracts default value from parameter's default value clause
215
+ private func extractDefaultValue(
216
+ from defaultClause: InitializerClauseSyntax ? ,
217
+ type: BridgeType
218
+ ) -> DefaultValue ? {
219
+ guard let defaultClause = defaultClause else {
220
+ return nil
221
+ }
222
+
223
+ if !isSupportedDefaultValueExpression( defaultClause) {
224
+ diagnose (
225
+ node: defaultClause,
226
+ message: " Complex default parameter expressions are not supported " ,
227
+ hint: " Use simple literal values (e.g., \" text \" , 42, true, nil) or simple constants "
228
+ )
229
+ return nil
230
+ }
231
+
232
+ let expr = defaultClause. value
233
+
234
+ if expr. is ( NilLiteralExprSyntax . self) {
235
+ guard case . optional( _) = type else {
236
+ diagnose (
237
+ node: expr,
238
+ message: " nil is only valid for optional parameters " ,
239
+ hint: " Make the parameter optional by adding ? to the type "
240
+ )
241
+ return nil
242
+ }
243
+ return . null
244
+ }
245
+
246
+ if let memberExpr = expr. as ( MemberAccessExprSyntax . self) ,
247
+ let enumValue = extractEnumCaseValue ( from: memberExpr, type: type)
248
+ {
249
+ return enumValue
250
+ }
251
+
252
+ if let funcCall = expr. as ( FunctionCallExprSyntax . self) {
253
+ return extractConstructorDefaultValue ( from: funcCall, type: type)
254
+ }
255
+
256
+ if let literalValue = extractLiteralValue ( from: expr, type: type) {
257
+ return literalValue
258
+ }
259
+
260
+ diagnose (
261
+ node: expr,
262
+ message: " Unsupported default parameter value expression " ,
263
+ hint: " Use simple literal values like \" text \" , 42, true, false, nil, or enum cases like .caseName "
264
+ )
265
+ return nil
266
+ }
267
+
268
+ /// Extracts default value from a constructor call expression
269
+ private func extractConstructorDefaultValue(
270
+ from funcCall: FunctionCallExprSyntax ,
271
+ type: BridgeType
272
+ ) -> DefaultValue ? {
273
+ guard let calledExpr = funcCall. calledExpression. as ( DeclReferenceExprSyntax . self) else {
274
+ diagnose (
275
+ node: funcCall,
276
+ message: " Complex constructor expressions are not supported " ,
277
+ hint: " Use a simple constructor call like ClassName() or ClassName(arg: value) "
278
+ )
279
+ return nil
280
+ }
281
+
282
+ let className = calledExpr. baseName. text
283
+ let expectedClassName : String ?
284
+ switch type {
285
+ case . swiftHeapObject( let name) :
286
+ expectedClassName = name. split ( separator: " . " ) . last. map ( String . init)
287
+ case . optional( . swiftHeapObject( let name) ) :
288
+ expectedClassName = name. split ( separator: " . " ) . last. map ( String . init)
289
+ default :
290
+ diagnose (
291
+ node: funcCall,
292
+ message: " Constructor calls are only supported for class types " ,
293
+ hint: " Parameter type should be a Swift class "
294
+ )
295
+ return nil
296
+ }
297
+
298
+ guard let expectedClassName = expectedClassName, className == expectedClassName else {
299
+ diagnose (
300
+ node: funcCall,
301
+ message: " Constructor class name ' \( className) ' doesn't match parameter type " ,
302
+ hint: " Ensure the constructor matches the parameter type "
303
+ )
304
+ return nil
305
+ }
306
+
307
+ if funcCall. arguments. isEmpty {
308
+ return . object( className)
309
+ }
310
+
311
+ var constructorArgs : [ DefaultValue ] = [ ]
312
+ for argument in funcCall. arguments {
313
+ guard let argValue = extractLiteralValue ( from: argument. expression) else {
314
+ diagnose (
315
+ node: argument. expression,
316
+ message: " Constructor argument must be a literal value " ,
317
+ hint: " Use simple literals like \" text \" , 42, true, false in constructor arguments "
318
+ )
319
+ return nil
320
+ }
321
+
322
+ constructorArgs. append ( argValue)
323
+ }
324
+
325
+ return . objectWithArguments( className, constructorArgs)
326
+ }
327
+
328
+ /// Extracts a literal value from an expression with optional type checking
329
+ private func extractLiteralValue( from expr: ExprSyntax , type: BridgeType ? = nil ) -> DefaultValue ? {
330
+ if expr. is ( NilLiteralExprSyntax . self) {
331
+ return . null
332
+ }
333
+
334
+ if let stringLiteral = expr. as ( StringLiteralExprSyntax . self) ,
335
+ let segment = stringLiteral. segments. first? . as ( StringSegmentSyntax . self)
336
+ {
337
+ let value = DefaultValue . string ( segment. content. text)
338
+ if let type = type, !type. isCompatibleWith ( . string) {
339
+ return nil
340
+ }
341
+ return value
342
+ }
343
+
344
+ if let boolLiteral = expr. as ( BooleanLiteralExprSyntax . self) {
345
+ let value = DefaultValue . bool ( boolLiteral. literal. text == " true " )
346
+ if let type = type, !type. isCompatibleWith ( . bool) {
347
+ return nil
348
+ }
349
+ return value
350
+ }
351
+
352
+ var numericExpr = expr
353
+ var isNegative = false
354
+ if let prefixExpr = expr. as ( PrefixOperatorExprSyntax . self) ,
355
+ prefixExpr. operator. text == " - "
356
+ {
357
+ numericExpr = prefixExpr. expression
358
+ isNegative = true
359
+ }
360
+
361
+ if let intLiteral = numericExpr. as ( IntegerLiteralExprSyntax . self) ,
362
+ let intValue = Int ( intLiteral. literal. text)
363
+ {
364
+ let value = DefaultValue . int ( isNegative ? - intValue : intValue)
365
+ if let type = type, !type. isCompatibleWith ( . int) {
366
+ return nil
367
+ }
368
+ return value
369
+ }
370
+
371
+ if let floatLiteral = numericExpr. as ( FloatLiteralExprSyntax . self) {
372
+ if let floatValue = Float ( floatLiteral. literal. text) {
373
+ let value = DefaultValue . float ( isNegative ? - floatValue : floatValue)
374
+ if type == nil || type? . isCompatibleWith ( . float) == true {
375
+ return value
376
+ }
377
+ }
378
+ if let doubleValue = Double ( floatLiteral. literal. text) {
379
+ let value = DefaultValue . double ( isNegative ? - doubleValue : doubleValue)
380
+ if type == nil || type? . isCompatibleWith ( . double) == true {
381
+ return value
382
+ }
383
+ }
384
+ }
385
+
386
+ return nil
387
+ }
388
+
155
389
override func visit( _ node: FunctionDeclSyntax ) -> SyntaxVisitorContinueKind {
156
390
guard node. attributes. hasJSAttribute ( ) else {
157
391
return . skipChildren
@@ -252,7 +486,10 @@ public class ExportSwift {
252
486
253
487
let name = param. secondName? . text ?? param. firstName. text
254
488
let label = param. firstName. text
255
- parameters. append ( Parameter ( label: label, name: name, type: type) )
489
+
490
+ let defaultValue = extractDefaultValue ( from: param. defaultValue, type: type)
491
+
492
+ parameters. append ( Parameter ( label: label, name: name, type: type, defaultValue: defaultValue) )
256
493
}
257
494
let returnType : BridgeType
258
495
if let returnClause = node. signature. returnClause {
@@ -409,7 +646,10 @@ public class ExportSwift {
409
646
}
410
647
let name = param. secondName? . text ?? param. firstName. text
411
648
let label = param. firstName. text
412
- parameters. append ( Parameter ( label: label, name: name, type: type) )
649
+
650
+ let defaultValue = extractDefaultValue ( from: param. defaultValue, type: type)
651
+
652
+ parameters. append ( Parameter ( label: label, name: name, type: type, defaultValue: defaultValue) )
413
653
}
414
654
415
655
guard let effects = collectEffects ( signature: node. signature) else {
@@ -630,7 +870,7 @@ public class ExportSwift {
630
870
swiftCallName: swiftCallName,
631
871
explicitAccessControl: explicitAccessControl,
632
872
cases: [ ] , // Will be populated in visit(EnumCaseDeclSyntax)
633
- rawType: rawType,
873
+ rawType: SwiftEnumRawType ( rawType) ,
634
874
namespace: effectiveNamespace,
635
875
emitStyle: emitStyle,
636
876
staticMethods: [ ] ,
@@ -668,9 +908,7 @@ public class ExportSwift {
668
908
669
909
if case . tsEnum = emitStyle {
670
910
// Check for Bool raw type limitation
671
- if let raw = exportedEnum. rawType,
672
- let rawEnum = SwiftEnumRawType . from ( raw) , rawEnum == . bool
673
- {
911
+ if exportedEnum. rawType == . bool {
674
912
diagnose (
675
913
node: jsAttribute,
676
914
message: " TypeScript enum style is not supported for Bool raw-value enums " ,
@@ -925,7 +1163,7 @@ public class ExportSwift {
925
1163
return Constants . supportedRawTypes. contains ( typeName)
926
1164
} ? . type. trimmedDescription
927
1165
928
- if let rawTypeString , let rawType = SwiftEnumRawType . from ( rawTypeString) {
1166
+ if let rawType = SwiftEnumRawType ( rawTypeString) {
929
1167
return . rawValueEnum( swiftCallName, rawType)
930
1168
} else {
931
1169
let hasAnyCases = enumDecl. memberBlock. members. contains { member in
@@ -1903,3 +2141,17 @@ extension WithModifiersSyntax {
1903
2141
}
1904
2142
}
1905
2143
}
2144
+
2145
+ fileprivate extension BridgeType {
2146
+ /// Returns true if a value of `expectedType` can be assigned to this type.
2147
+ func isCompatibleWith( _ expectedType: BridgeType ) -> Bool {
2148
+ switch ( self , expectedType) {
2149
+ case let ( lhs, rhs) where lhs == rhs:
2150
+ return true
2151
+ case ( . optional( let wrapped) , expectedType) :
2152
+ return wrapped == expectedType
2153
+ default :
2154
+ return false
2155
+ }
2156
+ }
2157
+ }
0 commit comments