Browse Source

minor: extract NoMultipleStatementsPerLineFixer from BracesFixer (#6458)

extract NoMultipleStatementsPerLineFixer from BracesFixer

Co-authored-by: SpacePossum <possumfromspace@gmail.com>
Julien Falque 2 years ago
parent
commit
505adc1f05

+ 5 - 0
doc/list.rst

@@ -1492,6 +1492,11 @@ List of Available Rules
    Part of rule sets `@PhpCsFixer <./ruleSets/PhpCsFixer.rst>`_ `@Symfony <./ruleSets/Symfony.rst>`_
 
    `Source PhpCsFixer\\Fixer\\ArrayNotation\\NoMultilineWhitespaceAroundDoubleArrowFixer <./../src/Fixer/ArrayNotation/NoMultilineWhitespaceAroundDoubleArrowFixer.php>`_
+-  `no_multiple_statements_per_line <./rules/basic/no_multiple_statements_per_line.rst>`_
+
+   There must not be more than one statement per line.
+
+   `Source PhpCsFixer\\Fixer\\Basic\\NoMultipleStatementsPerLineFixer <./../src/Fixer/Basic/NoMultipleStatementsPerLineFixer.php>`_
 -  `no_null_property_initialization <./rules/class_notation/no_null_property_initialization.rst>`_
 
    Properties MUST not be explicitly initialized with ``null`` except when they have a type declaration (PHP 7.4).

+ 20 - 0
doc/rules/basic/no_multiple_statements_per_line.rst

@@ -0,0 +1,20 @@
+========================================
+Rule ``no_multiple_statements_per_line``
+========================================
+
+There must not be more than one statement per line.
+
+Examples
+--------
+
+Example #1
+~~~~~~~~~~
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+    <?php
+   -foo(); bar();
+   +foo();
+   +bar();

+ 3 - 0
doc/rules/index.rst

@@ -76,6 +76,9 @@ Basic
 - `encoding <./basic/encoding.rst>`_
 
   PHP code MUST use only UTF-8 without BOM (remove BOM).
+- `no_multiple_statements_per_line <./basic/no_multiple_statements_per_line.rst>`_
+
+  There must not be more than one statement per line.
 - `non_printable_character <./basic/non_printable_character.rst>`_ *(risky)*
 
   Remove Zero-width space (ZWSP), Non-breaking space (NBSP) and other invisible unicode symbols.

+ 2 - 0
src/Fixer/Basic/BracesFixer.php

@@ -187,6 +187,7 @@ class Foo
     {
         $this->fixCommentBeforeBrace($tokens);
         $this->proxyFixers['control_structure_braces']->fix($file, $tokens);
+        $this->proxyFixers['no_multiple_statements_per_line']->fix($file, $tokens);
         $this->fixIndents($tokens);
         $this->fixSpaceAroundToken($tokens);
         $this->proxyFixers['control_structure_continuation_position']->fix($file, $tokens);
@@ -231,6 +232,7 @@ class Foo
             $this->getCurlyBracesPositionFixer(),
             $this->getControlStructureContinuationPositionFixer(),
             new DeclareParenthesesFixer(),
+            new NoMultipleStatementsPerLineFixer(),
             new StatementIndentationFixer(true),
         ];
     }

+ 109 - 0
src/Fixer/Basic/NoMultipleStatementsPerLineFixer.php

@@ -0,0 +1,109 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of PHP CS Fixer.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace PhpCsFixer\Fixer\Basic;
+
+use PhpCsFixer\AbstractFixer;
+use PhpCsFixer\Fixer\Indentation;
+use PhpCsFixer\Fixer\WhitespacesAwareFixerInterface;
+use PhpCsFixer\FixerDefinition\CodeSample;
+use PhpCsFixer\FixerDefinition\FixerDefinition;
+use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
+use PhpCsFixer\Preg;
+use PhpCsFixer\Tokenizer\Tokens;
+
+/**
+ * Fixer for rules defined in PSR2 ¶2.3 Lines: There must not be more than one statement per line.
+ */
+final class NoMultipleStatementsPerLineFixer extends AbstractFixer implements WhitespacesAwareFixerInterface
+{
+    use Indentation;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition(): FixerDefinitionInterface
+    {
+        return new FixerDefinition(
+            'There must not be more than one statement per line.',
+            [new CodeSample("<?php\nfoo(); bar();\n")]
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     *
+     * Must run after NoEmptyStatementFixer.
+     */
+    public function getPriority(): int
+    {
+        return 39;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isCandidate(Tokens $tokens): bool
+    {
+        return $tokens->isTokenKindFound(';');
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function applyFix(\SplFileInfo $file, Tokens $tokens): void
+    {
+        for ($index = 1, $max = \count($tokens) - 1; $index < $max; ++$index) {
+            if ($tokens[$index]->isGivenKind(T_FOR)) {
+                $index = $tokens->findBlockEnd(
+                    Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
+                    $tokens->getNextTokenOfKind($index, ['('])
+                );
+
+                continue;
+            }
+
+            if (!$tokens[$index]->equals(';')) {
+                continue;
+            }
+
+            for ($nextIndex = $index + 1; $nextIndex < $max; ++$nextIndex) {
+                $token = $tokens[$nextIndex];
+
+                if ($token->isWhitespace() || $token->isComment()) {
+                    if (1 === Preg::match('/\R/', $token->getContent())) {
+                        break;
+                    }
+
+                    continue;
+                }
+
+                if (!$token->equalsAny(['}', [T_CLOSE_TAG], [T_ENDIF], [T_ENDFOR], [T_ENDSWITCH], [T_ENDWHILE], [T_ENDFOREACH]])) {
+                    $whitespaceIndex = $index;
+                    do {
+                        $token = $tokens[++$whitespaceIndex];
+                    } while ($token->isComment());
+
+                    $newline = $this->whitespacesConfig->getLineEnding().$this->getLineIndentation($tokens, $index);
+
+                    if ($tokens->ensureWhitespaceAtIndex($whitespaceIndex, 0, $newline)) {
+                        ++$max;
+                    }
+                }
+
+                break;
+            }
+        }
+    }
+}

+ 1 - 1
src/Fixer/Semicolon/NoEmptyStatementFixer.php

@@ -44,7 +44,7 @@ final class NoEmptyStatementFixer extends AbstractFixer
     /**
      * {@inheritdoc}
      *
-     * Must run before BracesFixer, CombineConsecutiveUnsetsFixer, EmptyLoopBodyFixer, MultilineWhitespaceBeforeSemicolonsFixer, NoExtraBlankLinesFixer, NoSinglelineWhitespaceBeforeSemicolonsFixer, NoTrailingWhitespaceFixer, NoUselessElseFixer, NoUselessReturnFixer, NoWhitespaceInBlankLineFixer, ReturnAssignmentFixer, SpaceAfterSemicolonFixer, SwitchCaseSemicolonToColonFixer.
+     * Must run before BracesFixer, CombineConsecutiveUnsetsFixer, EmptyLoopBodyFixer, MultilineWhitespaceBeforeSemicolonsFixer, NoExtraBlankLinesFixer, NoMultipleStatementsPerLineFixer, NoSinglelineWhitespaceBeforeSemicolonsFixer, NoTrailingWhitespaceFixer, NoUselessElseFixer, NoUselessReturnFixer, NoWhitespaceInBlankLineFixer, ReturnAssignmentFixer, SpaceAfterSemicolonFixer, SwitchCaseSemicolonToColonFixer.
      * Must run after NoUselessSprintfFixer.
      */
     public function getPriority(): int

+ 1 - 0
tests/AutoReview/FixerFactoryTest.php

@@ -545,6 +545,7 @@ final class FixerFactoryTest extends TestCase
                 'empty_loop_body',
                 'multiline_whitespace_before_semicolons',
                 'no_extra_blank_lines',
+                'no_multiple_statements_per_line',
                 'no_singleline_whitespace_before_semicolons',
                 'no_trailing_whitespace',
                 'no_useless_else',

+ 85 - 0
tests/Fixer/Basic/NoMultipleStatementsPerLineFixerTest.php

@@ -0,0 +1,85 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of PHP CS Fixer.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *     Dariusz Rumiński <dariusz.ruminski@gmail.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace PhpCsFixer\Tests\Fixer\Basic;
+
+use PhpCsFixer\Tests\Test\AbstractFixerTestCase;
+
+/**
+ * @internal
+ *
+ * @covers \PhpCsFixer\Fixer\Basic\NoMultipleStatementsPerLineFixer
+ */
+final class NoMultipleStatementsPerLineFixerTest extends AbstractFixerTestCase
+{
+    /**
+     * @dataProvider provideFixCases
+     */
+    public function testFix(string $expected, ?string $input = null): void
+    {
+        $this->doTest($expected, $input);
+    }
+
+    public function provideFixCases(): iterable
+    {
+        yield 'simple' => [
+            '<?php
+                foo();
+                bar();',
+            '<?php
+                foo(); bar();',
+        ];
+
+        yield 'for loop' => [
+            '<?php
+                for ($i = 0; $i < 10; ++$i) {
+                    foo();
+                }',
+        ];
+
+        yield 'mixed `;` and close tag' => [
+            '<?php ++$a;
+++$b ?>',
+            '<?php ++$a; ++$b ?>',
+        ];
+
+        yield 'followed by closing brace' => [
+            '<?php if ($foo) { foo(); }',
+        ];
+
+        yield 'followed by closing tag' => [
+            '<?php foo(); ?>',
+        ];
+
+        yield 'if alternative syntax' => [
+            '<?php if ($foo): foo(); endif;',
+        ];
+
+        yield 'for alternative syntax' => [
+            '<?php for (;;): foo(); endfor;',
+        ];
+
+        yield 'foreach alternative syntax' => [
+            '<?php foreach ($foo as $bar): foo(); endforeach;',
+        ];
+
+        yield 'while alternative syntax' => [
+            '<?php while ($foo): foo(); endwhile;',
+        ];
+
+        yield 'switch alternative syntax' => [
+            '<?php switch ($foo): case true: foo(); endswitch;',
+        ];
+    }
+}

+ 18 - 0
tests/Fixtures/Integration/misc/no_multiple_statements_per_line,statement_indentation.test

@@ -0,0 +1,18 @@
+--TEST--
+Integration of fixers: no_multiple_statements_per_line,statement_indentation.
+--RULESET--
+{"no_multiple_statements_per_line": true, "statement_indentation": true}
+--EXPECT--
+<?php
+
+if (true) {
+    foo();
+    bar();
+}
+
+--INPUT--
+<?php
+
+if (true) {
+  foo(); bar();
+}

+ 12 - 0
tests/Fixtures/Integration/priority/no_empty_statement,no_multiple_statements_per_line.test

@@ -0,0 +1,12 @@
+--TEST--
+Integration of fixers: no_empty_statement,no_multiple_statements_per_line.
+--RULESET--
+{"no_empty_statement": true, "no_multiple_statements_per_line": true}
+--EXPECT--
+<?php
+$foo = 2;
+$bar = 3;
+
+--INPUT--
+<?php
+$foo = 2;;$bar = 3;;