Browse Source

Unneeded control parentheses fixers

Sullivan SENECHAL 9 years ago
parent
commit
eb9a0f4260

+ 4 - 0
README.rst

@@ -525,6 +525,10 @@ Choose from the list of available fixers:
                         placed adjacent to their
                         operands.
 
+* **unneeded_control_parentheses** [symfony]
+                        Removes unneeded parentheses
+                        around control statements.
+
 * **unused_use** [symfony]
                         Unused use statements must be
                         removed.

+ 122 - 0
Symfony/CS/Fixer/Symfony/UnneededControlParenthesesFixer.php

@@ -0,0 +1,122 @@
+<?php
+
+/*
+ * This file is part of the PHP CS utility.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Symfony\CS\Fixer\Symfony;
+
+use Symfony\CS\AbstractFixer;
+use Symfony\CS\Tokenizer\Tokens;
+
+/**
+ * @author Sullivan Senechal <soullivaneuh@gmail.com>
+ */
+final class UnneededControlParenthesesFixer extends AbstractFixer
+{
+    /**
+     * To be removed when PHP support will be 5.5+.
+     *
+     * @var string[] List of statements to fix.
+     */
+    private static $controlStatements = array(
+        'switch_case',
+        'echo_print',
+        'return',
+        'clone',
+        'yield',
+    );
+
+    private static $loops = array(
+        'switch_case' => array('lookupTokens' => T_CASE, 'neededSuccessors' => array(';', ':')),
+        'echo_print' => array('lookupTokens' => array(T_ECHO, T_PRINT), 'neededSuccessors' => array(';', array(T_CLOSE_TAG))),
+        'return' => array('lookupTokens' => T_RETURN, 'neededSuccessors' => array(';')),
+        'clone' => array('lookupTokens' => T_CLONE, 'neededSuccessors' => array(';', ':', ',', ')')),
+    );
+
+    /**
+     * Dynamic yield option set on constructor.
+     */
+    public function __construct()
+    {
+        // To be moved back on static when PHP support will be 5.5+
+        if (defined('T_YIELD')) {
+            self::$loops['yield'] = array('lookupTokens' => T_YIELD, 'neededSuccessors' => array(';', ')'));
+        }
+    }
+
+    /**
+     * @param array $controlStatements
+     */
+    public static function configure(array $controlStatements)
+    {
+        self::$controlStatements = $controlStatements;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fix(\SplFileInfo $file, $content)
+    {
+        $tokens = Tokens::fromCode($content);
+
+        // Checks if specific statements are set and uses them in this case.
+        $loops = array_intersect_key(self::$loops, array_flip(self::$controlStatements));
+
+        foreach ($tokens as $index => $token) {
+            if (!$token->equals('(')) {
+                continue;
+            }
+
+            $blockStartIndex = $index;
+            $index = $tokens->getPrevMeaningfulToken($index);
+            $token = $tokens[$index];
+
+            foreach ($loops as $loop) {
+                if (!$token->isGivenKind($loop['lookupTokens'])) {
+                    continue;
+                }
+
+                $blockEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $blockStartIndex);
+                $blockEndNextIndex = $tokens->getNextMeaningfulToken($blockEndIndex);
+
+                if (!$tokens[$blockEndNextIndex]->equalsAny($loop['neededSuccessors'])) {
+                    continue;
+                }
+
+                if ($tokens[$blockStartIndex - 1]->isWhitespace() || $tokens[$blockStartIndex - 1]->isComment()) {
+                    $tokens[$blockStartIndex]->clear();
+                } else {
+                    // Adds a space to prevent broken code like `return2`.
+                    $tokens->overrideAt($blockStartIndex, array(T_WHITESPACE, ' '));
+                }
+                $tokens[$blockEndIndex]->clear();
+            }
+        }
+
+        return $tokens->generateCode();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDescription()
+    {
+        return 'Removes unneeded parentheses around control statements.';
+    }
+
+    /**
+     * Should be run before trailing_spaces.
+     *
+     * {@inheritdoc}
+     */
+    public function getPriority()
+    {
+        return 30;
+    }
+}

+ 391 - 0
Symfony/CS/Tests/Fixer/Symfony/UnneededControlParenthesesFixerTest.php

@@ -0,0 +1,391 @@
+<?php
+
+/*
+ * This file is part of the PHP CS utility.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * This source file is subject to the MIT license that is bundled
+ * with this source code in the file LICENSE.
+ */
+
+namespace Symfony\CS\Tests\Fixer\Symfony;
+
+use Symfony\CS\Fixer\Symfony\UnneededControlParenthesesFixer;
+use Symfony\CS\Tests\Fixer\AbstractFixerTestBase;
+
+/**
+ * @author Sullivan Senechal <soullivaneuh@gmail.com>
+ *
+ * @internal
+ */
+final class UnneededControlParenthesesFixerTest extends AbstractFixerTestBase
+{
+    private static $defaultStatements = null;
+
+    public static function setUpBeforeClass()
+    {
+        $controlStatementsProperty = new \ReflectionProperty('Symfony\CS\Fixer\Symfony\UnneededControlParenthesesFixer', 'controlStatements');
+        $controlStatementsProperty->setAccessible(true);
+        self::$defaultStatements = $controlStatementsProperty->getValue(null);
+    }
+
+    /**
+     * @dataProvider provideFixCases
+     */
+    public function testFix($expected, $input = null)
+    {
+        // PHP <5.5 BC
+        if (version_compare(PHP_VERSION, '5.5', '<') && false !== strpos($input, 'yield')) {
+            $input = null;
+        }
+
+        // Default config. Fixes all statements.
+        UnneededControlParenthesesFixer::configure(self::$defaultStatements);
+        $this->makeTest($expected, $input);
+
+        // Empty array config. Should not fix anything.
+        UnneededControlParenthesesFixer::configure(array());
+        $this->makeTest($expected, null);
+
+        // Test with only one statement
+        foreach (self::$defaultStatements as $statement) {
+            UnneededControlParenthesesFixer::configure(array($statement));
+            $this->makeTest(
+                $expected,
+                $input && false !== strpos($input, $statement) ? $input : null
+            );
+        }
+    }
+
+    public function provideFixCases()
+    {
+        return array(
+            array(
+                '<?php
+                yield "prod";
+                ',
+            ),
+            array(
+                '<?php
+                yield (1 + 2) * 10;
+                ',
+            ),
+            array(
+                '<?php
+                yield (1 + 2) * 10;
+                ',
+                '<?php
+                yield ((1 + 2) * 10);
+                ',
+            ),
+            array(
+                '<?php
+                yield "prod";
+                ',
+                '<?php
+                yield ("prod");
+                ',
+            ),
+            array(
+                '<?php
+                yield 2;
+                ',
+                '<?php
+                yield(2);
+                ',
+            ),
+            array(
+                '<?php
+                $a = (yield $x);
+                ',
+                '<?php
+                $a = (yield($x));
+                ',
+            ),
+            array(
+                '<?php
+                clone $object;
+                ',
+            ),
+            array(
+                '<?php
+                clone new Class();
+                ',
+            ),
+            array(
+                '<?php
+                clone $object;
+                ',
+                '<?php
+                clone ($object);
+                ',
+            ),
+            array(
+                '<?php
+                clone new Class();
+                ',
+                '<?php
+                clone (new Class());
+                ',
+            ),
+            array(
+                '<?php
+                foo(clone $a);
+                foo(clone $a, 1);
+                $a = $b ? clone $b : $c;
+                ',
+                '<?php
+                foo(clone($a));
+                foo(clone($a), 1);
+                $a = $b ? clone($b) : $c;
+                ',
+            ),
+            array(
+                '<?php
+                echo "foo";
+                print "foo";
+                ',
+            ),
+            array(
+                '<?php
+                echo (1 + 2) . $foo;
+                print (1 + 2) . $foo;
+                ',
+            ),
+            array(
+                '<?php
+                echo (1 + 2) * 10, "\n";
+                ',
+            ),
+            array(
+                '<?php echo (1 + 2) * 10, "\n" ?>',
+            ),
+            array(
+                '<?php
+                echo (1 + 2) * 10, "\n";
+                ',
+                '<?php
+                echo ((1 + 2) * 10, "\n");
+                ',
+            ),
+            array(
+                '<?php
+                echo (1 + 2) * 10, "\n";
+                ',
+                '<?php
+                echo((1 + 2) * 10, "\n");
+                ',
+            ),
+            array(
+                '<?php echo "foo" ?>',
+                '<?php echo ("foo") ?>',
+            ),
+            array(
+                '<?php print "foo" ?>',
+                '<?php print ("foo") ?>',
+            ),
+            array(
+                '<?php
+                echo "foo";
+                print "foo";
+                ',
+                '<?php
+                echo ("foo");
+                print ("foo");
+                ',
+            ),
+            array(
+                '<?php
+                echo "foo";
+                print "foo";
+                ',
+                '<?php
+                echo("foo");
+                print("foo");
+                ',
+            ),
+            array(
+                '<?php
+                echo 2;
+                print 2;
+                ',
+                '<?php
+                echo(2);
+                print(2);
+                ',
+            ),
+            array(
+                '<?php
+                echo $a ? $b : $c;
+                echo ($a ? $b : $c) ? $d : $e;
+                echo 10 * (2 + 3);
+                echo ("foo"), ("bar");
+                echo my_awesome_function("foo");
+                echo $this->getOutput(1);
+                ',
+                '<?php
+                echo ($a ? $b : $c);
+                echo ($a ? $b : $c) ? $d : $e;
+                echo 10 * (2 + 3);
+                echo ("foo"), ("bar");
+                echo my_awesome_function("foo");
+                echo $this->getOutput(1);
+                ',
+            ),
+            array(
+                '<?php
+                return "prod";
+                ',
+            ),
+            array(
+                '<?php
+                return (1 + 2) * 10;
+                ',
+            ),
+            array(
+                '<?php
+                return (1 + 2) * 10;
+                ',
+                '<?php
+                return ((1 + 2) * 10);
+                ',
+            ),
+            array(
+                '<?php
+                return "prod";
+                ',
+                '<?php
+                return ("prod");
+                ',
+            ),
+            array(
+                '<?php
+                return $x;
+                ',
+                '<?php
+                return($x);
+                ',
+            ),
+            array(
+                '<?php
+                return 2;
+                ',
+                '<?php
+                return(2);
+                ',
+            ),
+            array(
+                '<?php
+                switch ($a) {
+                    case "prod":
+                        break;
+                }
+                ',
+            ),
+            array(
+                '<?php
+                switch ($a) {
+                    case "prod":
+                        break;
+                }
+                ',
+                '<?php
+                switch ($a) {
+                    case ("prod"):
+                        break;
+                }
+                ',
+            ),
+            array(
+                '<?php
+                case $x;
+                ',
+                '<?php
+                case($x);
+                ',
+            ),
+            array(
+                '<?php
+                case 2;
+                ',
+                '<?php
+                case(2);
+                ',
+            ),
+            array(
+                '<?php
+                $a = 5.1;
+                $b = 1.0;
+                switch($a) {
+                    case (int) $a < 1 : {
+                        echo "leave alone";
+                        break;
+                    }
+                    case $a < 2/* test */: {
+                        echo "fix 1";
+                        break;
+                    }
+                    case 3 : {
+                        echo "fix 2";
+                        break;
+                    }
+                    case /**//**/ // test
+                        4
+                        /**///
+                        /**/: {
+                        echo "fix 3";
+                        break;
+                    }
+                    case ((int)$b) + 4.1: {
+                        echo "fix 4";
+                        break;
+                    }
+                    case ($b + 1) * 2: {
+                        echo "leave alone";
+                        break;
+                    }
+                }
+                ',
+                '<?php
+                $a = 5.1;
+                $b = 1.0;
+                switch($a) {
+                    case (int) $a < 1 : {
+                        echo "leave alone";
+                        break;
+                    }
+                    case ($a < 2)/* test */: {
+                        echo "fix 1";
+                        break;
+                    }
+                    case (3) : {
+                        echo "fix 2";
+                        break;
+                    }
+                    case /**/(/**/ // test
+                        4
+                        /**/)//
+                        /**/: {
+                        echo "fix 3";
+                        break;
+                    }
+                    case (((int)$b) + 4.1): {
+                        echo "fix 4";
+                        break;
+                    }
+                    case ($b + 1) * 2: {
+                        echo "leave alone";
+                        break;
+                    }
+                }
+                ',
+            ),
+        );
+    }
+
+    public static function tearDownAfterClass()
+    {
+        UnneededControlParenthesesFixer::configure(self::$defaultStatements);
+    }
+}

+ 1 - 0
Symfony/CS/Tests/FixerTest.php

@@ -207,6 +207,7 @@ class FixerTest extends \PHPUnit_Framework_TestCase
             array($fixers['unary_operators_spaces'], $fixers['logical_not_operators_with_successor_space']),
             array($fixers['short_echo_tag'], $fixers['echo_to_print']), // tested also in: echo_to_print,short_echo_tag.test
             array($fixers['short_bool_cast'], $fixers['spaces_cast']),
+            array($fixers['unneeded_control_parentheses'], $fixers['trailing_spaces']), // tested also in: trailing_spaces,unneeded_control_parentheses.test
         );
 
         $docFixerNames = array_filter(

+ 20 - 0
Symfony/CS/Tests/Fixtures/Integration/trailing_spaces,unneeded_control_parentheses.test

@@ -0,0 +1,20 @@
+--TEST--
+Integration of fixers: trailing_spaces,unneeded_control_parentheses.
+--CONFIG--
+level=none
+fixers=trailing_spaces,unneeded_control_parentheses
+--INPUT--
+<?php
+return (
+    0 === strpos($method->name, 'get') &&
+    3 < strlen($method->name) &&
+    0 === $method->getNumberOfRequiredParameters()
+);
+
+--EXPECT--
+<?php
+return
+    0 === strpos($method->name, 'get') &&
+    3 < strlen($method->name) &&
+    0 === $method->getNumberOfRequiredParameters()
+;