-
Notifications
You must be signed in to change notification settings - Fork 833
Parser: recover on missing items in tuple expression #13352
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
|
Looks sort of ok. However... I wonder if we could do this much more generically across all the parser rules. Currently I'm wondering why this doesn't at least say This message is produced by this code for processing any syntax error. It has access to the parser stack at the point of failure - it then looks down this stack for something it recognises, and in this case the first thing recognised is an "interaction" that is an input to FSI. That indicates that the parser stack has been unwound more than we would like. However there may be a way to avoid that. Doing the work to adjust the generic grammar process code would have the advantage that it might improve all syntax errors, and mean we don't have to write specific rules in the grammar for cases like this. It would also cover cases like ( , ) // unexpected COMMA, expected expression
let f (x: List< , int>) = // unexpected COMMA, expected typeI think the underlying problem with doing this generically is that we don't know which subset of unexpected syntax-error tokens (COMMA, SEMICOLON, ARROW etc) should get this treatment (keeping the stack as "declExpr" or similar), and which unexpected tokens should result in an unwind of the stack (e.g. TYPE, MODULE etc). In this case you're hard-wiring one of these tokens. |
|
In addition to having the parser recovered, we're also interested in getting some node for the missing code. Even if we change the generic syntax error recovery code to produce better errors, how do we get a Or am I misunderstanding how a more generic recovery could work here? Would it somehow produce an empty/error expression (or, say, a pattern/type) whenever an expression is expected but got a comma instead? |
True. I guess However looking at fantomas preview tooling it seems we're getting a good node in this case already? https://fsprojects.github.io/fantomas-tools/#/ast?data=N4KABGBEAmCmBmBLAdrAzpAXFSAacUiaAYmolmPAIYA2as%2BEkAxgPZwWQAUYuYAjAEoAOgCdhySCAC%2BQA Which makes me wonder why we are saying "in declaration" or "in interaction" instead of "in expression".... Note that the SyntaxError processing relies on floating the syntax error up to some construct that has Line 4708 in d91b6c5
The case seems to also give an OK AST I'm not saying these are perfect, and it would be wonderful if our grammar didn't have any rules like Line 4708 in d91b6c5
|
|
For comparison: The final pattern one looks a bit whacky, the tuple contains the right-hand-side expression |
The parens are lost and the first item range is wrong, though. An app expression With the change from this PR both |
|
OK, given the existence of other error recovery rules in e.g. Line 4379 in d91b6c5
Do you think you could write out a test matrix for all combinations up to size 3 pinning down the error recovery please? Also could you look at similar matrix for type arguments Thanks |
Yes, I was going to look into adding new parser tests once the existing CI is green. |
|
Looking at the pattern rules for tuples, it would be kind of nice if they were exactly symmetric in structure with the ones for expressions - they certainly aren't at the moment There are two cases really - "simple patterns" that always have parens (e.g. used as constructore arguments) and tuple patterns e.g. Line 3300 in d91b6c5
https://github.com/dotnet/fsharp/blob/main/src/Compiler/pars.fsy#L3256 |
0109f5d to
e62f004
Compare
|
@dsyme Thanks for suggesting to test it on tuples of size 3! It uncovered that the initial implementation didn't work properly on them and produced inner tuple expressions at recovery points. The only way I found to recognize that the parser recovered from such a situation was to check the result in the parent rule. What do you think about this approach? |
e62f004 to
4b78bdc
Compare
94b8454 to
16d7d58
Compare
|
I'd be happy to take this as is, but while you're in context I'm wondering if it's worth adding tests for "(1," and other incomplete tuples? If they don't pass it's OK, we can still accept, it would just be good to get these cases pinned down if they pass. |
|
Code needs formatting :) |
You mean incomplete tuples inside incomplete paren expressions specifically? The incomplete tuple cases are already present in this PR. |
* add an extra line break in a line that has less than 120 chars * add an extra space after an capitalized pattern
|
@dsyme Done. |
|
@auduchinok I meant the same systematic test matrix but each without the closing parens please :) May as well get it all pinned down. |
|
@dsyme I think unfinished parens aren't that interesting: IDEs insert the closing parens automatically. A much more interesting thing to look into is more cases with unfinished tuples (i.e. where the last element is missing) but I'd rather work on it separately, keeping this PR smaller and focused on missing elements before commas. After working on that it'd be much more interesting to pin more parens examples. |
|
OK, missing last items are also recovered now, let's see if anything fails. |
I understand, but regardless I feel we should pin down any positive recovery we have for these, so we don't regress in the future. It's common enough that people delete a I will merge this - if you do get a chance to pin down these cases I'd appreciate it, thanks |
|
Great work btw! |
Adds the parser recovery on missing first and intermediate items in a tuple expression, e.g. when adding a new item in the front of existing ones or between them, or when replacing an existing item.
The error is placed on the comma range:
The recovered tree is available for analysis and can be used in features like Parameter Info popup:
The change makes the parser produce the correct tree structure for cases like
(,1,1),(1,,1), and even cases with multiple missing items like(,1,,2,3,,4,).