-
Notifications
You must be signed in to change notification settings - Fork 803
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
Add pattern type check recovery #7711
Conversation
❤️ |
Cool stuff |
Needs some baseline updates. |
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 looks really good, though I'm concerned about the use of catch-all error handlers, are they certainly necessary, or are they for recovery after reported errors?
@dsyme These catch-all and forget handlers were added to places where it could fail and we couldn't recover afterwards. I could try listing particular exceptions but in some cases it'd probably be a long list, and since we don't need exception info at recovery point I just don't even look at what exception is. |
I feel we do need to dig into this. IIRC the right pattern is this one:
For example, |
@dsyme I've added |
It's ready. |
It's unrelated. The failures there are CI unreliability problems that we have on our .NET linux pools. |
@@ -63,12 +63,16 @@ but here has type | |||
|
|||
neg16.fs(85,8,85,18): typecheck error FS0039: The pattern discriminator 'FooA++' is not defined. | |||
|
|||
neg16.fs(85,7,85,22): typecheck error FS0025: Incomplete pattern matches on this expression. |
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 know this is minor, but the code for this test:
let (|``FooA++``|) (x:int) = x
let (``FooA++``(x)) = 10
Normally it would error saying that FooA++
is not defined because using "++" in the name is not allowed. But now it will include an additional error saying that the pattern matches are incomplete, but it isn't a valid match construct to begin with.
Again, this is minor and not a blocker.
@@ -4,7 +4,10 @@ | |||
// Regression test for FSharp1.0:3744 - Unable to apply attributes on individual patterns in a tupled pattern match let binding - Implementation doesn't match spec | |||
// Test against error emitted when attributes applied within pattern | |||
|
|||
//<Expects id="FS0683" span="(11,6-11,33)" status="error">Attributes are not allowed within patterns</Expects> | |||
//<Expects id="FS0683" span="(14,6-14,27)" status="error">Attributes are not allowed within patterns</Expects> | |||
//<Expects id="FS0842" span="(14,8-14,23)" status="error">This attribute is not valid for use on this language element</Expects> |
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 better considering it is now showing multiple errors on both attributes along with better ranges.
The only minor knit I have is that it is also giving an error saying the "attribute is not valid for use on this language element" - but the real error is that attributes are just not allowed in patterns.
@@ -1,6 +1,6 @@ | |||
// #Regression #Conformance #PatternMatching #Unions | |||
// Verify error if two union rules capture values with different types | |||
//<Expects id="FS0001" status="error" span="(8,7-9,11)">This expression was expected to have type. 'int' .but here has type. 'float'</Expects> | |||
//<Expects id="FS0001" status="error" span="(9,10-9,11)">This expression was expected to have type. 'int' .but here has type. 'float'</Expects> |
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 a much more accurate range :)
| _ -> ()) | ||
|
||
let names = NameMap.layer names1 names2 | ||
let takenNames = Set.union takenNames1 takenNames2 |
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.
What is the reason for doing a layer and union here? Could you put in a comment explaining why?
// so type T = Case of name: int * value: int | ||
// | Case(value = v) | ||
// will become | ||
// | Case(_, v) | ||
let result = Array.zeroCreate numArgTys | ||
let extraPatterns = List () |
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.
What is extraPatterns
used for? Could put in a comment explaining that?
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.
Looks good and a great improvement to error recovery! Only a few comments.
The only thing I would really like is adding more comments in the code as I noted.
@auduchinok, if you can submit a quick PR with the additional clarifications @TIHan asked for please. Thanks KEvin |
@auduchinok I know the PR is merged, but I would really like the comments added to the code that I noted. Is it ok that you create a separate PR for that? |
@TIHan sure, will do. |
* TcPat recovery: simple pats * TcPat recovery: report union case and active pattern items before checking args * TcPat recovery: type check attributes * TcPat recovery: literal patterns, union case named args * TcPat recovery: literal patterns, union case args * TcPat recovery: active patterns named args * TcPat recovery: union case 2 * TcPat recovery: update tests * TcPat recovery: different names/types in Or patterns * Move tests file to common place * Fix tupled union case args * Lost change * Fix test project file include * Review fix: use errorRecovery when catching exceptions * Minimize diff * Minimize diff * Update FCS project analysis test baselines * Merge fixes * Update test * Fix fsharpqa baselines * Another fsharpqa baselines update * Update desktop suite baselines * Update vsbsl
if numArgs < numArgTys then | ||
if numArgs <> 0 && numArgTys <> 0 then | ||
errorR (UnionCaseWrongArguments (env.DisplayEnv, numArgTys, numArgs, m)) | ||
args @ (List.init (numArgTys - numArgs) (fun _ -> SynPat.Wild (m.MakeSynthetic()))), extraPatterns |
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 code is too complex - all the cases could do with some comments
The problem this PR solves is almost every pattern type check error currently raises an exception making the rest of the match expression not type checked at all (so nothing of Highlighting, Find Usages, Rename, etc work). An example with named arguments for an active pattern (which is not allowed):
This PR adds recovery to the most of such errors making IDE features work as expected:
The difference is especially evident on match expressions with many clauses, due to all subsequent clauses were skipped on an error.
A naive approach to recover in most cases would be to produce typed Wild patterns (as it's already done in few places) but, due to how match expressions and bindings are compiled to decision trees, in many cases it'd make subsequent match clauses considered never matched, which doesn't look good:
This PR uses a different approach and introduces a new typed pattern for error and a fake Error decision tree test. These added parts of decision trees never match other parts of the tree so errors never make other parts considered never matched.
A tricky part here is extra patterns in the case of union case fields and literals. To prevent any effect on resulting decision tree, the arguments are type checked with fresh inference types and resulting names added to type check environment without creating new typed patterns. Due to variables for these patterns are not present in the tree, dummy unit expressions are created as initializers so Pattern Match Compilation doesn't fail to find them.
An example for unresolved function arguments:
Other cases producing errors, like patterns with attributes or optional values, are all supported too: