coding-standard icon indicating copy to clipboard operation
coding-standard copied to clipboard

Spacing around T_BREAK

Open stlrnz opened this issue 4 years ago • 4 comments

I have some trouble configuring spacing around T_BREAK.

Consider the following code as valid:

<?php

declare(strict_types=1);

namespace Demo;

use Exception;

class BreakTest
{
    public function switchTest(): void
    {
        $var = null;

        switch (true) {
            case 1:
                $var = 1;
                break;

            case 2:
                $var = 2;
                break;

            default:
                throw new Exception();
        }
    }

    public function loopTest(): void
    {
        $vars = [0, 1, 2];
        $sum = 0;
        $msg = null;

        foreach ($vars as $var) {
            if ($var === 0) {
                break;
            }

            if ($var === 1) {
                $msg = '';

                break;
            }

            $sum += $var;
        }
    }
}

The expected configuration should force:

  • 0 empty lines before T_BREAK in switch
  • 1 empty line after T_BREAK in switch
  • 1 empty line before T_BREAK in loop (unless its the first statement in a control structure)
  • 0 empty lines after T_BREAK in loop

First, I've tried the following rule:

    <rule ref="SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing">
        <properties>
            <property name="linesCountBeforeWhenFirstInCaseOrDefault" value="0"/>
            <property name="linesCountAfterWhenLastInCaseOrDefault" value="1"/>
            <property name="linesCountAfterWhenLastInLastCaseOrDefault" value="0"/>
        </properties>
    </rule>

It always expects an empty line before T_BREAK resulting in:

FOUND 2 ERRORS AFFECTING 2 LINES
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
 18 | ERROR | [x] Expected 1 lines before "break", found 0.
    |       |     (SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing.IncorrectLinesCountBeforeControlStructure)

If I change the configuration to fix this problem for the switsch stamement, it doesnt fit for loops anymore.

    <rule ref="SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing">
        <properties>
            <property name="linesCountBeforeControlStructure" value="0"/>
            <property name="linesCountBeforeWhenFirstInCaseOrDefault" value="0"/>
            <property name="linesCountAfterWhenLastInCaseOrDefault" value="1"/>
            <property name="linesCountAfterWhenLastInLastCaseOrDefault" value="0"/>
        </properties>
    </rule>
FOUND 1 ERROR AFFECTING 1 LINE
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
 42 | ERROR | [x] Expected 0 lines before "break", found 1.
    |       |     (SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing.IncorrectLinesCountBeforeControlStructure)

In https://github.com/slevomat/coding-standard/issues/867 you discussed a related problem resulting in three new options for JumpStatementsSpacing (which are helpfull indeed). However, the issue was fixed for doctrine, but not for my scenario.

Doctrine is ignoring T_BREAK in their standard. Thats why they don't have any issues like this. However, ignoring T_BREAK opens the door for some other creative variants which I try to avoid.

Is there any way to configure the expected behaviour like described? If thats not the case, would you consider implementing SwitchSpacingSniff?

stlrnz avatar May 05 '20 12:05 stlrnz

            case 2:
                $var = 2;
                break;
            if ($var === 1) {
                $msg = '';

                break;
            }

These two examples are same and should work equally as reported in your first configuration. There's always some code before break.

kukulich avatar May 05 '20 13:05 kukulich

Yes, I know what you mean. Technically, its really the same. But for me, it does not feel the same.

A break inside switch is like a terminating statement. I see case and break more like forming a block. Thats why I prefer break direcly after the last statement (just like I forbid an empty line before }).

A break inside a loop is however more like any other jump statement. So I prefer the same behaviour as I would expect from return.

For me the context really matters. So I tried to figure out how others are handling this. As I said, doctrine is ignoring this case. In consequence you can find different implementations, including the way described above (break in loop, break in switch). Maybe doctrine would profit from this rule, too?

stlrnz avatar May 05 '20 14:05 stlrnz

I've looked at it again and it still does not make sense to me.

	case 2:
		if ($something) {
			return;
		}

		break;

In this example I expect you don't want this:

	case 2:
		if ($something) {
			return;
		}
		break;

kukulich avatar May 06 '20 12:05 kukulich

You are right, that would be an inconsistency as our standard forces an empty line after the if (via BlockControlStructureSpacing rule). So only your first example is valid. But that's in conflict with the behaviour I described above.

So, what about the following: You could add an option maxLinesCountBeforeWhenLastInCaseOrDefault which defaults to null (= ignored). But if set, it supersedes the linesCountBeforeControlStructure rule only inside case or default.

I could set the new option to 1. That way for loops the "exactly one line before break" (linesCountBeforeControlStructure = 1) rule will still be valid. But inside case or default there could be one or zero empty lines. So both of your examples will be possible. But the "one empty line after if" rule from BlockControlStructureSpacing would still force your first example.

The only downside is that without an if two possibilities are left (coming from the max in maxLinesCountBeforeWhenLastInCaseOrDefault):

            case 1:
                $var = 1;

                break;

            case 2:
                $var = 2;
                break;

But that would be okay for me.

stlrnz avatar May 06 '20 14:05 stlrnz