Skip to content

Conversation

@nojaf
Copy link
Contributor

@nojaf nojaf commented Jun 14, 2022

This is a first attempt to address #13273.
So far I've only touched the untyped tree. However, these changes are already quite large.
I'd like to get some feedback from @dsyme, @auduchinok and others before trying to finish this.

Where the member has both get and set, I've introduced SynMemberDefn.ReadWriteMember.
If there is only get or set, I'm constructing the same AST as before (using a helper function in SynTreeOps).
Both grammar rules still return a list, I'd like to get rid of that as well.

Happy to hear your thoughts on this.

let attrs = $2 |> List.map (fun attrList -> { attrList with Attributes = attrList.Attributes |> List.map (fun a -> { a with AppliesToGetterAndSetter = true } ) })
let mSet = rhs parseState 3
let mEquals = rhs parseState 6
let m = rhs2 parseState 1 7
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This range may include subsequent nodes when error happens inside typedSequentialExprBlock. A safer approach would be to get the range of the parsed expression and to join it with the start range. Here's an example of a similar range issue related to error recovery: #12224


| Member of memberDefn: SynBinding * range: range

| ReadWriteMember of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to GetSetProperty.

xmlDoc: PreXmlDoc *
identifier: SynPat *
returnTypeOpt: SynReturnInfo option *
read: SynMemberDefnPropertyInfo *
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to getter and setter

IsInline: bool
Attributes: SynAttributes
/// 'get' or 'set
IsWrite: bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to IsSetter


/* The "with get, set" part of a member definition */
classDefnMemberGetSet:
| WITH classDefnMemberGetSetElements
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be best to avoid making any grammar changes as part of this. That should be possible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, but I feel like this is a bit of a missed opportunity to separate some logic based on what the parser actually detects.

Having the rule:

/* The "get, set" part of a member definition */
classDefnMemberGetSetElements: 
  | classDefnMemberGetSetElement 
     { [$1], None  }
  | classDefnMemberGetSetElement AND classDefnMemberGetSetElement
     { let mAnd = rhs parseState 2
       [$1;$3], Some mAnd }

forces the consumer to deal with this in code. Both get and set or single get or single set need to be processed correctly where classDefnMemberGetSetElements is used.

/* Properties with explicit get/set, also indexer properties */
/* Properties with explicit get and set, also indexer properties */
| opt_inline bindingPattern opt_topReturnTypeWithTypeConstraints classDefnMemberGetSet
{ let mWith, (classDefnMemberGetSetElements, mAnd) = $4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend moving this entire helper to ParseHelpers.fs or SyntaxTreeOps.fs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could do this as pre-PR cleanup perhaps

We should move all such large sections of code out of pars.fsy

@dsyme
Copy link
Contributor

dsyme commented Jun 14, 2022

I'm in two minds about how to go about this. The current approach just doesn't feel right

member x.Method args = expr
member x.Property = expr
member x.Property with get() = expr
member x.Property with set value = expr
member x.Property with get() = expr and set value = expr

It's just odd that the GetSet case is captured so differently to the Get case and Set case.

That said I'm sure you've thought of this too and I personally can't think of a much better way yet.

@dsyme
Copy link
Contributor

dsyme commented Jun 14, 2022

Note you'll need to adjust all code matching on SynBinding, especially all tree-walking code.

@nojaf
Copy link
Contributor Author

nojaf commented Jun 14, 2022

It's just odd that the GetSet case is captured so differently to the Get case and Set case.

To some degree, I think that SynMemberDefn.Member is already a bit off by re-using SynBinding.
Because of the get/set cases, it stores more information that is not used in regular let bindings for example.

  • SynMemberFlags in SynValData is empty for SynModuleDecl.Let.
  • propertyKeyword and extraId are also empty for SynModuleDecl.Let.

To me, this feels like a construct that is being re-used for historical reasons but is different enough at the syntax level for us to start noticing it.

The perhaps safer alternative was to just introduce a node that stores both SynBindings.
Something like: SynMemberDefn.GetSetProperty of getter: SynBinding * setter: SynBinding * range: range.

@nojaf
Copy link
Contributor Author

nojaf commented Jun 30, 2022

Closing in favour of #13420

@nojaf nojaf closed this Jun 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

3 participants