@@ -1057,6 +1057,29 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
10571057 return Effects ( isAsync : isAsync , isThrows : isThrows , isStatic : isStatic )
10581058 }
10591059
1060+ private func collectEffectsFromAccessor ( _ accessor : AccessorDeclSyntax ) -> Effects ? {
1061+ let isAsync = accessor . effectSpecifiers ? . asyncSpecifier != nil
1062+ var isThrows = false
1063+ if let throwsClause = accessor . effectSpecifiers ? . throwsClause {
1064+ guard let thrownType = throwsClause . type else {
1065+ diagnose (
1066+ node : throwsClause ,
1067+ message : " Thrown type is not specified, only JSException is supported for now "
1068+ )
1069+ return nil
1070+ }
1071+ guard thrownType . trimmedDescription == " JSException " else {
1072+ diagnose (
1073+ node : throwsClause ,
1074+ message : " Only JSException is supported for thrown type, got \( thrownType . trimmedDescription ) "
1075+ )
1076+ return nil
1077+ }
1078+ isThrows = true
1079+ }
1080+ return Effects ( isAsync : isAsync , isThrows : isThrows , isStatic : false )
1081+ }
1082+
10601083 private func extractNamespace (
10611084 from jsAttribute : AttributeSyntax
10621085 ) -> [ String ] ? {
@@ -1654,18 +1677,62 @@ private final class ExportSwiftAPICollector: SyntaxAnyVisitor {
16541677 guard let accessorBlock = binding . accessorBlock else {
16551678 diagnose (
16561679 node : binding ,
1657- message : " Protocol property must specify { get } or { get set } " ,
1658- hint : " Add { get } for readonly or { get set } for readwrite property "
1680+ message : " Protocol property must specify { get throws(JSException) } " ,
1681+ hint : " Add { get throws(JSException) } for the property accessor "
16591682 )
16601683 continue
16611684 }
16621685
1663- let isReadonly = hasOnlyGetter ( accessorBlock )
1686+ // Find the getter accessor
1687+ let getterAccessor : AccessorDeclSyntax ?
1688+ switch accessorBlock . accessors {
1689+ case . accessors ( let accessors ) :
1690+ getterAccessor = accessors . first { $0 . accessorSpecifier . tokenKind == . keyword ( . get ) }
1691+ case . getter :
1692+ diagnose (
1693+ node : accessorBlock ,
1694+ message : " @JS protocol property getter must declare throws(JSException) " ,
1695+ hint : " Use { get throws(JSException) } syntax "
1696+ )
1697+ continue
1698+ }
1699+
1700+ guard let getter = getterAccessor else {
1701+ diagnose ( node : accessorBlock , message : " Protocol property must have a getter " )
1702+ continue
1703+ }
1704+
1705+ // Check for setter - not allowed with throwing getter
1706+ if case . accessors ( let accessors ) = accessorBlock . accessors {
1707+ if accessors . contains ( where : { $0 . accessorSpecifier . tokenKind == . keyword ( . set ) } ) {
1708+ diagnose (
1709+ node : accessorBlock ,
1710+ message : " @JS protocol cannot have { get set } properties " ,
1711+ hint :
1712+ " Use readonly property with setter method: var \( propertyName ) : \( typeAnnotation . type . trimmedDescription ) { get throws(JSException) } and func set \( propertyName . capitalized ) (_ value: \( typeAnnotation . type . trimmedDescription ) ) throws(JSException) "
1713+ )
1714+ continue
1715+ }
1716+ }
1717+
1718+ guard let effects = collectEffectsFromAccessor ( getter ) else {
1719+ continue
1720+ }
1721+
1722+ guard effects . isThrows else {
1723+ diagnose (
1724+ node : getter ,
1725+ message : " @JS protocol property getter must be throws " ,
1726+ hint : " Declare the getter as 'get throws(JSException)' "
1727+ )
1728+ continue
1729+ }
16641730
16651731 let exportedProperty = ExportedProtocolProperty (
16661732 name : propertyName ,
16671733 type : propertyType ,
1668- isReadonly : isReadonly
1734+ isReadonly : true , // Always readonly since { get set } with throws is not allowed
1735+ effects : effects
16691736 )
16701737
16711738 if var currentProtocol = exportedProtocolByName [ protocolKey ] {
0 commit comments