-
Notifications
You must be signed in to change notification settings - Fork 824
Add with keyword to Syntax tree #12400
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I wonder if we could move ranges with keywords and punctuation like this into separate aggregating fields? |
How would that look like? If it is something entirely outside of the tree then that already has little value as I would need to piece these things together again somehow. Unless you mean something else.
You don't need any changes if you use named pattern matching. match expr with
| SynExpr.TryWith (tryExpr=innerComp; withCases=clauses; range=mTryToLast; tryDebugPoint=spTry) -> ... I'm updating all the code in the compiler like this as I go and for one commit I changed the tree and parser and no other changes were necessary. |
Great work @nojaf
I think @auduchinok means a systematic typed "trivia" field, as a version resilient class, in each node. We could in principle though not as part of this PR of course :) |
Yes, just to move it out of the main node types, so there're less fields to pattern match and update on FCS changes. |
I think this shouldn't be a requirement for FCS clients.
It'd likely be better if it was possible without that many changes. 🙂 |
Ok, thanks for elaborating. For better or worse, I'm getting a lot of ideas actually by this 🙈. |
@dsyme this is ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really great! Just a few nits, nothing much
Thanks
Renamed Range to range. Add comment above PropertyKeyword.
Thanks, never ever doubt about raising nitpicks. All good, always, any time ☺! |
@@ -3499,7 +3520,7 @@ declExpr: | |||
{ let mMatch = rhs parseState 1 | |||
let mWith, (clauses, mLast) = $3 | |||
let spBind = DebugPointAtBinding.Yes(unionRanges mMatch mWith) | |||
SynExpr.Match (spBind, $2, clauses, unionRanges mMatch mLast) } | |||
SynExpr.Match (mMatch, spBind, $2, mWith, clauses, unionRanges mMatch mLast) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nojaf What problem does a separate match
range field solve here? The keyword starts at the same range as the whole match
expression and is a single token of a known width. Given each added range is rather a big struct, it seems wasteful to add ranges that can be calculated from the nodes.
Maybe we could add some helpers that would calculate keyword ranges from their positions instead of storing them in trees? We could use computed properties and only store positions in Trivia
/Appendix
types for keywords where storing position is required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feels like we are talking in circles here:
I already expressed why ranges are preferred over calculations with positions.
@dsyme what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This time I'm suggesting this calculation to be the details of implementation in FCS, while keeping range properties that Fantomas could work with. 🙂
Calculating a range from position doesn't look too bad perf-wise and ranges are structs, so creating them shouldn't add GC pressure. On the other hand, the two keywords in match
expr use 8 ints to store data that could use 2 ints. And if we're going to add this for all keywords, it becomes a bit too much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I'm slowly getting on board with this.
Although, is there a way to get a position inside the parser?
Places where I now use rhs parseState 1
, is there something similar that just gets the start position of 1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@auduchinok would that then look something like:
#r "nuget: FSharp.Compiler.Service"
open FSharp.Compiler.Text
open FSharp.Compiler.Text.Range
open FSharp.Compiler.Text.Position
type NodeTrivia =
{ StartPos: pos
FileName: string }
member this.Range =
mkRange this.FileName this.StartPos (mkPos this.StartPos.Line (this.StartPos.Column + 4))
let myTrivia =
{ StartPos = mkPos 1 0
FileName = "MyFile" }
myTrivia.Range
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nojaf Yes, the idea for getting a range is roughly like this. I'm also trying to find a better way to keep the keyword position only and to get the file name from the overall node range. Something like this could work:
let getKeywordRange (containingRange: range) startPos length =
mkRange containingRange.FileName startPos (mkPos startPos.Line (startPos.Column + length))
[<Struct>]
type SynExprMatchTrivia =
{ WithPos: pos }
member this.GetMatchKeywordRange(exprRange: range) =
getKeywordRange exprRange exprRange.Start 5
member this.GetWithKeywordRange(exprRange: range) =
getKeywordRange exprRange this.WithPos 4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer a range than a position. The position on its own is of no use, we would always reconstruct the range. These particular nodes are of no intrinsic significance when it comes to memory performance
|
||
// 'match! expr with pats ...' --> build.Bind(e1, (function pats ...)) | ||
| SynExpr.MatchBang (spMatch, expr, clauses, m) -> | ||
| SynExpr.MatchBang (mMatch, spMatch, expr, _withKeyword, clauses, _m) -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_mWith to keep it consistent
let propertyNameBindingBuilder, _ = $2 | ||
let optPropertyType = $3 | ||
let isMutable = false | ||
(fun visNoLongerUsed memFlagsBuilder attrs rangeStart -> | ||
let mutable hasGet = false | ||
let mutable hasSet = false | ||
let xmlDoc = grabXmlDocAtRangeStart(parseState, attrs, rangeStart) | ||
|
||
let tryMkSynMemberDefnMember |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should lift all this code out into SyntaxTreeOps.fs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is quite tricky to just port.
reportParseErrorAt
and raiseParseErrorAt
are being used inside it.
Could we do this another time perhaps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes sure, I meant to say that
Nice work, just a couple of very minor nits |
This PR adds the
with
keyword range to various nodes in the Syntax tree.//cc @dsyme