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

PSR2.ControlStructures.SwitchDeclaration.TerminatingComment does not handle try/finally blocks #3297

Closed
umherirrender opened this issue Apr 12, 2021 · 6 comments · Fixed by #3354

Comments

@umherirrender
Copy link

Describe the bug
Using the sniff PSR2.ControlStructures.SwitchDeclaration brings in an edge case for the error code TerminatingComment when having try/finally in the switch case body

Code sample

<?php

$statusValue = mt_rand();
$resource = SomeClass::getWorker();

switch ( $statusValue ) {
	case 0:
		// fall-through
	case 2:
		try {
			return $resource->doWork();
		} finally {
			$resource->release();
		}
	case 3:
		$other = $code;
		break;
}

Custom ruleset

<?xml version="1.0"?>
<ruleset name="My Custom Standard">
  <rule ref="PSR2.ControlStructures.SwitchDeclaration" />
</ruleset>

To reproduce
Steps to reproduce the behavior:

  1. Create a file called test.php with the code sample above...
  2. Run phpcs test.php ...
  3. See error message displayed
 9 | ERROR | There must be a comment when fall-through is intentional
   |       | in a non-empty case body
   |       | (PSR2.ControlStructures.SwitchDeclaration.TerminatingComment)

Expected behavior
The try/finally always returns, that means there is no fall-through which does not need a comment.
That also the same when using finally + catch or catch alone

Versions (please complete the following information):

  • OS: Windows 10
  • PHP: 8.0
  • PHPCS: 3.6.0
  • Standard: -

Additional context

@jrfnl
Copy link
Contributor

jrfnl commented May 12, 2021

The try/finally always returns, that means there is no fall-through which does not need a comment.

Sorry, but I think you may be misunderstanding how finally works. finally only returns when it contains a return statement.
Yes, it will always be executed, but no, it does not end the switch execution.

See this simple demonstration: https://3v4l.org/t2pt6

In other words, the fact that the sniff demands a "fall through" comment is 100% correct and in my opinion, this issue can be closed.

@umherirrender
Copy link
Author

In my example the try block always returns and in that case there is no fall-through, because the switch execution is ended by the return inside the try block.

If the try block does not always returns than the comment is needed, that is correct, but not the issue here.

https://3v4l.org/a4nlI

@jrfnl
Copy link
Contributor

jrfnl commented May 12, 2021

@umherirrender Sorry, help me out - the try is there to catch a possible Exception, so the return in the try may not be reached and the code does not catch whatever Exception you are expecting and the finally - which will always be executed - does not contain a return....

So the only reason the code does not fall through to case 3 is that the try was successful and happened not to encounter an Exception and if it would have encountered an Exception, the only reason the code doesn't fall through to case 3, is that the program stops with an uncaught Exception.

@umherirrender
Copy link
Author

If the exception (explicit thrown in the try block or implicit within its called function) is uncatched, the switch is also ended. No catch block would throw the exception to the caller of the function with the switch (after the finally block is executed).
https://3v4l.org/hYTqu

No catch block does not mean the exception is ignored, that needs an catch block without terminate statement (return or throw)
https://3v4l.org/D4KMS

@jrfnl
Copy link
Contributor

jrfnl commented May 13, 2021

@umherirrender Okay, so if we want to address this, what would be rules need to be ?

  • If there is a finally statement and the body of that contains a "terminating statement" (return, break etc), we don't need to check the body of the try or any of the catch statements as, no matter what, all execution branches are then covered by that one terminating statement.
  • If there is no finally or if there is, but it does not contain a terminating statement, then all try and catch structures should each contain a terminating statement in the body.

Does that sound about right ? Am I still overlooking something ?

jrfnl added a commit to jrfnl/PHP_CodeSniffer that referenced this issue May 13, 2021
Add code to handle `try-catch-finally` statements correctly when determining whether a `case` needs a terminating comment.

As per squizlabs#3297 (comment):

> * If there is a `finally` statement and the body of that contains a "terminating statement" (`return`, `break` etc), we don't need to check the body of the `try` or any of the `catch` statements as, no matter what, all execution branches are then covered by that one terminating statement.
> * If there is **_no_** `finally` or if there is, but it does not contain a terminating statement, then all `try` and `catch` structures should each contain a terminating statement in the body.

Includes plenty of unit tests.

Fixes 3297
@jrfnl
Copy link
Contributor

jrfnl commented May 13, 2021

@umherirrender I've added a fix for this to PR #3354 which was open with another fix for the same sniff. Testing appreciated.

@gsherwood gsherwood added this to Idea Bank in PHPCS v3 Development via automation May 23, 2021
@gsherwood gsherwood added this to the 3.6.1 milestone May 23, 2021
gsherwood pushed a commit that referenced this issue May 27, 2021
Add code to handle `try-catch-finally` statements correctly when determining whether a `case` needs a terminating comment.

As per #3297 (comment):

> * If there is a `finally` statement and the body of that contains a "terminating statement" (`return`, `break` etc), we don't need to check the body of the `try` or any of the `catch` statements as, no matter what, all execution branches are then covered by that one terminating statement.
> * If there is **_no_** `finally` or if there is, but it does not contain a terminating statement, then all `try` and `catch` structures should each contain a terminating statement in the body.

Includes plenty of unit tests.

Fixes 3297
gsherwood added a commit that referenced this issue May 27, 2021
PHPCS v3 Development automation moved this from Idea Bank to Ready for Release May 27, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
PHPCS v3 Development
Ready for Release
Development

Successfully merging a pull request may close this issue.

3 participants