Skip to content
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

After calling a method with explicit exceptions, calls to methods with implicit exceptions are ignored #9878

Closed
lpd-au opened this issue Sep 13, 2023 · 9 comments · May be fixed by #9887
Closed

Comments

@lpd-au
Copy link

lpd-au commented Sep 13, 2023

Bug report

Take a look at this example:

	public function testCatchException(): void
	{
		try {
			$foo = $this->mayThrow(); // declares @throws Exception
			$foo += $this->mayAlsoThrow(); // doesn't have a docblock but returns $this->mayThrow()
			var_dump($foo);
		} catch (Exception) {
			var_dump($foo ?? 0); // $foo can be undefined or the first return value
		}
	}

This example reports "Variable $foo on left side of ?? is never defined", in the default configuration at https://phpstan.org/try and even with:

parameters:
  level: 1
  exceptions:
    implicitThrows: true

From my understanding, implicitThrows should treat every method that doesn't specify @throws void as @throws Exception.

Here's an example of testCatchException() hitting the exception handler with $foo sometimes defined: https://3v4l.org/dufRX.

Code snippet that reproduces the problem

https://phpstan.org/r/d4c83865-5ed9-4080-961d-882ceb95cf15

Expected output

No issues should be reported, since $foo can be either undefined or 1, not only undefined.

Did PHPStan help you today? Did it make you happy in any way?

Not so much today but many times before today :)

@ondrejmirtes
Copy link
Member

Yes, this is expected behaviour. Once a try block contains explicit throw points, implicit throw points are ignored. It kind of assumes that once you start documenting your code with @throws, you should document all your code with @throws :)

It's covered by this sentence in the article: https://phpstan.org/blog/precise-try-catch-finally-analysis

But once you start adding @throws to your functions and methods, they will take priority over the ones with only implicit @throws. As you add more @throws annotations, you get rewarded with more precise analysis!

@lpd-au
Copy link
Author

lpd-au commented Sep 13, 2023

:/ That's very unfortunate. What's the purpose of @throws void then?

Is it possible you could document this somewhere more discoverable than an old blog post, such as the config reference for exceptions?

@ondrejmirtes
Copy link
Member

Why is it unfortunate?

You can fix your code with: https://phpstan.org/r/ecf0ba5a-86e3-40cd-86cd-02cd47784945

@throws void marks method as not throwing anything. For example: https://phpstan.org/r/15de3506-8860-4220-bbb7-ddc898097dfb

Marking functions with @throws void is still valuable. See: https://phpstan.org/r/94b22d94-1381-4ffd-bc2e-b2b334b99d7a

@lpd-au
Copy link
Author

lpd-au commented Sep 13, 2023

Why is it unfortunate?

Because it creates an all or nothing scenario where to avoid false positives, you have to have @throws everywhere or nowhere; there's no option to gradually improve large code over time. It would be highly beneficial if I could opt into not ignoring implicit throws where @throws void isn't specified.

Thanks for the other examples!

@ondrejmirtes
Copy link
Member

In my testing the issue you're experiencing was very rare. I tried other variants of this algorithm, they were much worse in practice.

@ondrejmirtes
Copy link
Member

By "very rare" I mean I had to add correct @throws ... above a couple of functions to get rid of errors with the same root cause in 1M LoC codebase. I didn't have to add @throws everywhere.

@lpd-au
Copy link
Author

lpd-au commented Sep 14, 2023

This is a similar size legacy php code base. In the first case I'm looking at, because the @throws is for an unchecked exception (a database error) on a very widely used method (querying the database), unfortunately I don't see a practical solution besides removing the tag. That's not ideal but at least you're probably correct that it'd knock off many of the false positives in one fell swoop and I may not need to add/remove many more :)

If you're sure the behaviour is the best it possibly can be (thanks for your work!), I still believe there is opportunity to improve the documentation. What does absent @throws above a function mean?, prominently linked from as the config reference for exceptions.implicitThrows, describes the exact opposite behaviour with no caveats. Even reading the blog post from #9878 (comment), without your additional knowledge kindly shared here, to me it's not especially clear what this means:

But once you start adding @throws to your functions and methods, they will take priority over the ones with only implicit @throws. As you add more @throws annotations, you get rewarded with more precise analysis!

From the examples given above it, I think it'd be pretty fair to interpret this to mean that adding @throws to any called method will only start ignoring implicit throw points of that specific exception class and its descendents, not ancestors all the way up to Exception as well. For a warning, it's very jovial phrasing near the end of the post and in that context, one could overlook the strength with which "take priority over" is intended (it wholly overrides rather than taking precedence and sometimes falling back).

Wdyt about a couple of light edits to each post, PRs welcome?

@ondrejmirtes
Copy link
Member

Yes, PRs always welcome :)

@github-actions
Copy link

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants