Skip to content

Commit

Permalink
Correctly print out attributes of SynMemberDefn.GetSetMember (#3115)
Browse files Browse the repository at this point in the history
* Correctly print out attributes of SynMemberDefn.GetSetMember

* Add changelog entry
  • Loading branch information
nojaf authored Sep 5, 2024
1 parent a4788cb commit 87a6577
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 9 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 6.3.12 - 2024-09-05

### Fixed
* Fantomas deletes attributes from getters. [#3114](https://github.com/fsprojects/fantomas/issues/3114)

## 6.3.11 - 2024-08-16

### Fixed
Expand Down
48 changes: 48 additions & 0 deletions src/Fantomas.Core.Tests/AttributeTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1050,3 +1050,51 @@ let ``trivia in nested multiline tuple expression in attribute, 2525`` () =
Justification = "Bytecode delta only")>]
()
"""

[<Test>]
let ``attributes on member and get, set properties`` () =
formatSourceString
"""
type Object3D() =
[<X>]
member this.position
with [<Y>] set v = _position <- v
and [<Z>] get () = _position
"""
config
|> prepend newline
|> should
equal
"""
type Object3D() =
[<X>]
member this.position
with [<Y>] set v = _position <- v
and [<Z>] get () = _position
"""

[<Test>]
let ``attributes on get,set properties, 3114`` () =
formatSourceString
"""
[<Erase>]
type Object3D() =
let mutable _position: Vector3 = null
member this.position
with [<Emit("$0.position")>] set v = _position <- v
and [<Emit("$0.position = $1")>] get () = _position
"""
config
|> prepend newline
|> should
equal
"""
[<Erase>]
type Object3D() =
let mutable _position: Vector3 = null
member this.position
with [<Emit("$0.position")>] set v = _position <- v
and [<Emit("$0.position = $1")>] get () = _position
"""
48 changes: 39 additions & 9 deletions src/Fantomas.Core/ASTTransformer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2650,17 +2650,30 @@ let mkWithGetSet (withKeyword: range option) (getSet: GetSetKeywords option) =

let mkPropertyGetSetBinding
(creationAide: CreationAide)
(withOrAndKeyword: range)
(accessibility: SynAccess option)
(leadingKeyword: SingleTextNode)
(binding: SynBinding)
: PropertyGetSetBindingNode =
match binding with
| SynBinding(
attributes = attributes
headPat = SynPat.LongIdent(extraId = Some extraIdent; argPats = SynArgPats.Pats ps)
returnInfo = returnInfo
expr = expr
trivia = { EqualsRange = Some mEq
InlineKeyword = inlineKw }) ->
// Attribute are not accurate in this case.
// The binding could contain attributes for the entire member and the getter or setter member.
// We use the `with` or `and` keyword to filter them.
let attributes =
attributes
|> List.map (fun al ->
{ al with
Attributes =
al.Attributes
|> List.filter (fun a -> Position.posGt a.Range.Start withOrAndKeyword.End) })

let e = parseExpressionInSynBinding returnInfo expr
let returnTypeNode = mkBindingReturnInfo creationAide returnInfo

Expand Down Expand Up @@ -2694,6 +2707,7 @@ let mkPropertyGetSetBinding

PropertyGetSetBindingNode(
Option.map (stn "inline") inlineKw,
mkAttributes creationAide attributes,
mkSynAccess accessibility,
leadingKeyword,
pats,
Expand Down Expand Up @@ -2863,7 +2877,7 @@ let mkMemberDefn (creationAide: CreationAide) (md: SynMemberDefn) =
GetKeyword = Some getKeyword
SetKeyword = Some setKeyword
WithKeyword = withKeyword
AndKeyword = andKeyword }) ->
AndKeyword = Some andKeyword }) ->

let firstAccessibility, firstBinding, firstKeyword, lastBinding, lastKeyword =
if Position.posLt getKeyword.Start setKeyword.Start then
Expand All @@ -2885,27 +2899,43 @@ let mkMemberDefn (creationAide: CreationAide) (md: SynMemberDefn) =
| SynBinding(headPat = SynPat.LongIdent(accessibility = Some vis)) when
rangeBeforePos memberName.Range vis.Range.Start
->
mkPropertyGetSetBinding creationAide (Some vis) firstKeyword firstBinding
| _ -> mkPropertyGetSetBinding creationAide None firstKeyword firstBinding
mkPropertyGetSetBinding creationAide withKeyword (Some vis) firstKeyword firstBinding
| _ -> mkPropertyGetSetBinding creationAide withKeyword None firstKeyword firstBinding

let lastBinding =
match lastBinding with
| SynBinding(headPat = SynPat.LongIdent(accessibility = Some vis)) when
rangeBeforePos memberName.Range vis.Range.Start
->
mkPropertyGetSetBinding creationAide (Some vis) lastKeyword lastBinding
| _ -> mkPropertyGetSetBinding creationAide None lastKeyword lastBinding
mkPropertyGetSetBinding creationAide andKeyword (Some vis) lastKeyword lastBinding
| _ -> mkPropertyGetSetBinding creationAide andKeyword None lastKeyword lastBinding

// Attributes placed on the member will be included in both bindings for the getter and setter.
// We need to filter out the attributes above the leading keyword (typically `member`).
let memberAttributes =
ats
|> List.choose (fun al ->
let filteredAttributeList =
{ al with
Attributes =
al.Attributes
|> List.filter (fun a -> Position.posLt a.Range.End lk.Range.Start) }

if filteredAttributeList.Attributes.IsEmpty then
None
else
Some filteredAttributeList)

MemberDefnPropertyGetSetNode(
mkXmlDoc px,
mkAttributes creationAide ats,
mkAttributes creationAide memberAttributes,
mkSynLeadingKeyword lk,
Option.map (stn "inline") inlineKw,
mkSynAccess accessibility,
mkSynLongIdent memberName,
stn "with" withKeyword,
firstBinding,
Option.map (stn "and") andKeyword,
Some(stn "and" andKeyword),
Some lastBinding,
memberDefinitionRange
)
Expand Down Expand Up @@ -2945,7 +2975,7 @@ let mkMemberDefn (creationAide: CreationAide) (md: SynMemberDefn) =
match getKeyword, setKeyword with
| Some getKeyword, None ->
let bindingNode =
mkPropertyGetSetBinding creationAide visProperty (stn "get" getKeyword) binding
mkPropertyGetSetBinding creationAide withKeyword visProperty (stn "get" getKeyword) binding

MemberDefnPropertyGetSetNode(
mkXmlDoc px,
Expand All @@ -2963,7 +2993,7 @@ let mkMemberDefn (creationAide: CreationAide) (md: SynMemberDefn) =
|> MemberDefn.PropertyGetSet
| None, Some setKeyword ->
let bindingNode =
mkPropertyGetSetBinding creationAide visProperty (stn "set" setKeyword) binding
mkPropertyGetSetBinding creationAide withKeyword visProperty (stn "set" setKeyword) binding

MemberDefnPropertyGetSetNode(
mkXmlDoc px,
Expand Down
1 change: 1 addition & 0 deletions src/Fantomas.Core/CodePrinter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3877,6 +3877,7 @@ let genMemberDefn (md: MemberDefn) =
| MemberDefn.PropertyGetSet node ->
let genProperty (node: PropertyGetSetBindingNode) =
genInlineOpt node.Inline
+> genOnelinerAttributes node.Attributes
+> genAccessOpt node.Accessibility
+> genSingleTextNode node.LeadingKeyword
+> sepSpace
Expand Down
3 changes: 3 additions & 0 deletions src/Fantomas.Core/SyntaxOak.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2521,6 +2521,7 @@ type MemberDefnAbstractSlotNode
type PropertyGetSetBindingNode
(
inlineNode: SingleTextNode option,
attributes: MultipleAttributeListNode option,
accessibility: SingleTextNode option,
leadingKeyword: SingleTextNode,
parameters: Pattern list,
Expand All @@ -2533,6 +2534,7 @@ type PropertyGetSetBindingNode

override val Children: Node array =
[| yield! noa inlineNode
yield! noa attributes
yield! noa accessibility
yield leadingKeyword
yield! List.map Pattern.Node parameters
Expand All @@ -2541,6 +2543,7 @@ type PropertyGetSetBindingNode
yield Expr.Node expr |]

member val Inline = inlineNode
member val Attributes = attributes
member val Accessibility = accessibility
member val LeadingKeyword = leadingKeyword
member val Parameters = parameters
Expand Down

0 comments on commit 87a6577

Please sign in to comment.