@@ -12,10 +12,10 @@ struct SwiftContext {
1212 /// `variableDecl` rule before translating the accessor block; read by the
1313 /// inner `accessorDecl` rules to name each `accessor_declaration`.
1414 property_name_node : Option < yeast:: OutputId > ,
15- /// Translated type node for the property type. Set (for computed
16- /// properties) by the accessor-bearing `variableDecl` rule; read by the
17- /// inner `accessorDecl` rules. Left `None` for stored properties with
18- /// observers, so their `willSet`/`didSet` accessors carry no type .
15+ /// Translated type node for the property type. Set by the
16+ /// accessor-bearing `variableDecl` rule; read by the inner `accessorDecl`
17+ /// rules for both the getter return type and its `newValue`/`oldValue`
18+ /// parameter type, when available .
1919 property_type : Option < yeast:: OutputId > ,
2020 /// Translated outer modifiers to attach to each child of a flattening
2121 /// outer rule — e.g. the `let`/`var` binding modifier on each
@@ -334,7 +334,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
334334 ( accessor_declaration
335335 modifier: ( modifier #{ spec} )
336336 name_node: ( identifier #{ name} )
337- type : { ty}
337+ return_type : { ty}
338338 accessor_kind: { ctx. literal_at_start_of( "accessor_kind" , "get" , body) }
339339 body: { body} )
340340 ) ,
@@ -345,12 +345,12 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
345345 // * With an initializer (`var x: T = e { willSet {…} didSet {…} }`) it is
346346 // a *stored* property with observers: emit the backing
347347 // `variable_declaration` first, then one `accessor_declaration` per
348- // observer (observers carry no type) .
348+ // observer.
349349 // * Without an initializer (`var v: T { get set }`, incl. protocol
350- // requirements) it is a *computed* property: no backing variable; the
351- // type is published so the get/set accessors carry it.
350+ // requirements) it is a *computed* property: no backing variable.
352351 //
353- // In both cases the first emitted declaration is unchained and every
352+ // In both cases the type is published so every accessor carries it, and
353+ // the first emitted declaration is unchained and every
354354 // subsequent one is tagged `chained_declaration` (the `!result.is_empty()`
355355 // test). Must precede the plain `variableDecl` rules.
356356 rule!(
@@ -365,6 +365,7 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
365365 member* {
366366 ctx. outer_modifiers = vec![ tree!( ( modifier #{ spec} ) ) ] ;
367367 ctx. property_name_node = Some ( tree!( ( identifier #{ name} ) ) ) ;
368+ ctx. property_type = Some ( ty) ;
368369 let mut result = Vec :: new( ) ;
369370 if let Some ( val) = val {
370371 // Stored property with observers: the initializer is not part
@@ -380,9 +381,6 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
380381 type : { ty}
381382 value: { val} )
382383 ) ) ;
383- } else {
384- // Computed property: the accessors carry the type.
385- ctx. property_type = Some ( ty) ;
386384 }
387385 for acc in accessors. into_iter( ) {
388386 ctx. is_chained = !result. is_empty( ) ;
@@ -400,27 +398,70 @@ fn translation_rules() -> Vec<Rule<SwiftContext>> {
400398 // one (a protocol requirement) carries neither. The property context is
401399 // read out *before* translating the body, which resets `ctx` so the
402400 // accessor's context does not leak into the body subtree.
403- rule!(
404- ( accessorDecl accessorSpecifier: @@spec body: _? @@body)
405- =>
406- ( accessor_declaration
407- modifier: {
408- if body. is_some( ) {
409- ctx. outer_modifiers. clone( )
410- } else {
411- Vec :: new( )
401+ //
402+ // `set` and `willSet` accessors get a parameter holding the incoming
403+ // value, and `didSet` gets one holding the previous value. It is named
404+ // explicitly (`set(v)`) or else implicitly `newValue`/`oldValue`.
405+ rule!(
406+ ( accessorDecl
407+ accessorSpecifier: @@spec
408+ parameters: ( accessorParameters name: @@param_name) ?
409+ body: _? @@body)
410+ =>
411+ accessor_declaration {
412+ let spec_text = ctx. source_text( spec) ;
413+ let implicit_param_name = match spec_text. as_str( ) {
414+ "set" | "willSet" => Some ( "newValue" ) ,
415+ "didSet" => Some ( "oldValue" ) ,
416+ _ => None ,
417+ } ;
418+ let param_ty = ctx. property_type;
419+ let param = match ( param_name, implicit_param_name) {
420+ ( Some ( n) , _) => {
421+ Some ( tree!( ( parameter pattern: ( identifier #{ n} ) type : { param_ty} ) ) )
412422 }
413- }
414- modifier: { chained_modifier( & mut ctx) }
415- name_node: {
416- ctx. property_name_node
417- . ok_or( "accessor outside property context" ) ?
418- }
419- type : { ctx. property_type}
420- accessor_kind: ( accessor_kind #{ spec} )
421- body: { ctx. reset( ) ; body}
422- location: { spec}
423- location: { body} )
423+ ( None , Some ( n) ) => {
424+ let n = n. to_string( ) ;
425+ Some ( tree!( ( parameter
426+ modifier: ( modifier "generated" )
427+ pattern: ( identifier #{ n} location: { spec} )
428+ type : { param_ty}
429+ location: { spec} ) ) )
430+ }
431+ ( None , None ) => None ,
432+ } ;
433+ let binding = if body. is_some( ) {
434+ ctx. outer_modifiers. clone( )
435+ } else {
436+ Vec :: new( )
437+ } ;
438+ let chained = chained_modifier( & mut ctx) ;
439+ let name = ctx
440+ . property_name_node
441+ . ok_or( "accessor outside property context" ) ?;
442+ let return_ty = if spec_text == "get" {
443+ ctx. property_type
444+ } else {
445+ None
446+ } ;
447+ let body = match body {
448+ Some ( block) => {
449+ ctx. reset( ) ;
450+ ctx. translate( block) ?. into_iter( ) . next( )
451+ }
452+ None => None ,
453+ } ;
454+ tree!( ( accessor_declaration
455+ modifier: { binding}
456+ modifier: { chained}
457+ name_node: { name}
458+ return_type: { return_ty}
459+ accessor_kind: ( accessor_kind #{ spec} )
460+ parameter: { param}
461+ body: { body}
462+ location: { spec}
463+ location: { body} ) )
464+ }
424465 ) ,
425466 // ---- Variables ----
426467 // The individual bindings of a `variableDecl`. The binding modifier and
0 commit comments