Fix try/catch to choose the more specific exception handler#2429
Fix try/catch to choose the more specific exception handler#2429
Conversation
|
Hi @daxian-dbw, I'm your friendly neighborhood Microsoft Pull Request Bot (You can call me MSBOT). Thanks for your contribution!
TTYL, MSBOT; |
|
The potential breaking changes comes from trying to catch If that's it, it seems safe. Most people just catch everything anyway. I'd like some investigation to know for sure that ActionPreferenceStopException and CmdletInvocationException are always wrapped in a ParentContainsErrorRecordException. |
|
I will investigate |
|
@lzybkr it turns out it causes quite a few regressions Before the change After the change And the awkward part of the current behavior is that sometimes You can see that |
|
The root problem is we don't have a specific exception to search for handler. Instead, we have multiple candidate exceptions from which we pick up to suit the given handlers. The fix needs to be re-worked, and Therefore, |
c29372e to
a7e0e7a
Compare
Search state can be saved and used throughout the searching process without the recursion.
| } | ||
|
|
||
| It "Catch by type #4" { | ||
| $a = . { try { 1; $a = 0; 1/$a; "test failed" } catch [DivideByZeroException],[ArgumentNullException] { 2 } } |
There was a problem hiding this comment.
$a = . { try { 1; $a = 0; 1/$a; "test failed" } catch [DivideByZeroException],[ArgumentNullException] { 2 } } [](start = 12, length = 109)
should this be explicit?
$a = . { try { 1; throw ([ArgumentNullException]::new("bad")) } catch [DivideByZeroException],[ArgumentNullException] { 2 } }
There was a problem hiding this comment.
This test case was moved from source depot. It originally was catch [DivideByZeroException],[StackOverflowException], and it looks to me it tries to verify the DivideByZeroException thrown by 1/0. StackOverflowException is not in CoreCLR so I replaced it with ArgumentNullException.
There was a problem hiding this comment.
yah - since the test immediately prior is catching DivideByZeroException I wasn't sure what this test was really doing. Should we have two tests? One to catch DivideByZeroException and on to catch ArgumentNullException (e.g., we can catch both exception types) - if we can improve on our existing tests and make them better/clearer that's awesome
There was a problem hiding this comment.
Sure, I will add a test for ArgumentNullException.
| } | ||
|
|
||
| # Disabled - Compiled script has differing (but better) behavior | ||
| It "return in try" -Skip:$true { |
There was a problem hiding this comment.
should this be marked as pending rather than skip?
There was a problem hiding this comment.
Sure, I will change it to -Pending.
|
@lzybkr I think this try/catch fix is ready. Can you please review this PR? |
| finally | ||
| { | ||
| } | ||
| } | Should Not Throw |
There was a problem hiding this comment.
Should Not Throw doesn't do anything useful here - parse only tests will fail to parse, so nothing would run.
There was a problem hiding this comment.
Right, if parsing failed, | Should Not Throw will never run. The original test was to make sure those use of try/catch would not cause parsing error.
How about I just have a dummy $true | Should Be $true following the try/catch blocks? So if no parsing error, the test will pass.
| Context "Line after exception should not be invoked" { | ||
| It "line after exception should not be invoked" { | ||
| $a = . {trap {"trapped"; continue;}; . {"hello"; throw "exception"; "world"}} | ||
| $a.Length | Should Be 2 |
There was a problem hiding this comment.
I think the test should be more explicit in the expected results so it's easier to see the expected flow and to avoid a cascade of failures from helping the test succeed when it should fail.
There was a problem hiding this comment.
My bad. I converted the Lite tests to Pester tests, but didn't look closely to their assert messages. Fixed.
| foo 2 | ||
| } | ||
|
|
||
| AssertArraysEqual $a (1, "finally: 1", "finally: 2", "return: 2") |
There was a problem hiding this comment.
I've started using -join ' ' on the array so that I can use Should Be ... which somehow feels more idiomatic to Pester even if it's a little weird. This is just a comment to discuss, I'm not asking for a change.
There was a problem hiding this comment.
Yes, I will use -join ',' on array comparison in future. I used it in Trap.Tests.ps1.
Fix #2293
Idea behind the Fix
After selecting a handler, for both try/catch and trap{}, we set$_withnew ErrorRecord(rte.ErrorRecord, exceptionToPass). TheexceptionToPasswill be adopted only if(rte.ErrorRecord.Exception is ParentContainsErrorRecordException) == true. So basically$_.Exceptionwill always berte.ErrorRecord.Exceptionunless it's aParentContainsErrorRecordExceptioninstance. So we should just select the handler based onrte.ErrorRecord.Exceptionif it's notParentContainsErrorRecordException. When it isParentContainsErrorRecordException, then we go through the original code path to determine what exception to pass to user.The old fix would cause unacceptable breaking changes (see the discussion below). The following is the new fix:
First rank the given exception types -- a smaller rank indicates a more specific exception type, for example,
rank == 0means the corresponding exception type is not derived by another other given exception types, whilerank == 1means the corresponding exception type is derived by another exception type in the given types.CatchAllis considered to be derived by any other exception type.Then during the search, we try all existing searching methods in the same existing order, as long as we haven't found a handler whose exception type's rank is 0.
When a new handler is found, we use the following algorithm to decide whether to replace the current handler (the handler that was found before the new handler):
With the Fix
Task