Improved try-catch scope resolution#472
Conversation
|
Hi, you can achieve faster feedback loop by running |
|
Anyway, I'm not sure it's possible what you want to achieve without advanced exception tracking. There's a milestone grouping similar issues: https://github.com/phpstan/phpstan/milestone/11 My thinking about implementing this is that ExpressionResult and also StatementResult (which NodeScopeResolver works with heavily) would also carry information about possible thrown exceptions. The result of Also once a called function/method would have a |
|
Thanks, I'll do that. I was hoping to get some early feedback on this edge case:
|
|
Something that might throw wouldn't be an exit point, just something that always throws/exits/returns. I believe we need a new field for possibly thrown exceptions in ExpressionResult and StatementResult. |
|
Just |
|
I would argue that in |
|
That's not what the exit points do. They're useful as terminating statements and detecting unreachable code.
Nope, my thinking is that:
If the user annotates their methods really honestly, they're going to be rewarded with precise analysis like this: try {
$foo = throwsInvalidArgumentException(); // scope 1
$bar = throwsLogicException(); // scope 2
} catch (\InvalidArgumentException $e) {
// scope 1
} catch (\LogicException $e) {
// scope 2
}Additionally, we can write a new rule that will show that some |
Well not really, |
|
If they have to be separate from exit points, then I think we should start by having |
I also thought about this, but also at the same time unchecked exceptions are rarely in a catch statement :)
I disagree. It can include them in the first iteration. The most time consuming thing on this task is to go through all the node types in processStmtNode and processExprNode and handle whether each node can throw an exception (and merge those informations in high level nodes that work with different nested nodes). You need to read This task isn't that hard, it's just time-consuming. It's about passing around those possible thrown types in StatementResult and ExpressionResult, and then reimplement try-catch analysis to merge the right scopes based on the captured data. |
Yeah that's true. In practice it will probably be the best fit. I don't think it would be a big change to change the behavior or make it configurable later anyway.
I wasn't aware of that. In that case it definitely makes sense to include the types from the start.
As is already done for |
Yes :) I'm also for doing features in phases, but I'm not sure how to divide this one. Just passing stuff around without reading it anywhere will make it hard to verify the code works correctly... |
|
I'll figure out something to assert them, I can base it off other tests in |
|
Ah wait I see what you mean... only scope information is assertable, the local state of the resolver is not. 🤔 |
|
Something like |
|
I prefer everything to be tested in NodeScopeResolverTest::testFileAsserts(). Any implementation detail should surface as a Scope state. The simplest test that should pass is: function (): void {
try {
$foo = 1;
} catch (\Exception $e) {
assertVariableCertainty(TrinaryLogic::createYes(), $foo);
}
try {
$foo = doFoo();
} catch (\Exception $e) {
assertVariableCertainty(TrinaryLogic::createMaybe(), $foo);
}
};Once this is green, you know you're on the right path :) Another point - you should get yourself familiar with |
|
I believe that in the first iteration assertType and assertVariableCertainty is everything you need. |
|
Superseded by #481 |
Better late than never :)
As discussed on phpstan/phpstan#778, here's an alternate implementation of try/catch/finally scope resolution. It's mostly based around keeping track of the scopes for different flows separately and merging them.
I already added test cases replicating the scenario from the issue but I'll be adding a lot more.
Q: is there a way we could implement
assertUnreachableorassertAlwaysTerminating?