Browse Source

Merge branch 'master' into 3.0

# Conflicts:
#	tests/AutoReview/ProjectCodeTest.php
Dariusz Ruminski 6 years ago
parent
commit
26b6670dc5

+ 2 - 0
.php_cs.dist

@@ -39,6 +39,7 @@ $config = PhpCsFixer\Config::create()
         'header_comment' => ['header' => $header],
         'heredoc_to_nowdoc' => true,
         'list_syntax' => ['syntax' => 'long'],
+        'logical_operators' => true,
         'method_argument_space' => ['ensure_fully_multiline' => true],
         'method_chaining_indentation' => true,
         'multiline_comment_opening_closing' => true,
@@ -62,6 +63,7 @@ $config = PhpCsFixer\Config::create()
         'phpdoc_add_missing_param_annotation' => true,
         'phpdoc_order' => true,
         'phpdoc_types_order' => true,
+        'return_assignment' => true,
         'semicolon_after_instruction' => true,
         'single_line_comment_style' => true,
         'strict_comparison' => true,

+ 22 - 5
README.rst

@@ -357,7 +357,7 @@ Choose from the list of available rules:
 
   Comments with annotation should be docblock.
 
-  *Risky rule: risky as new docblocks might began mean more, e.g. Doctrine's entity might have a new column in database.*
+  *Risky rule: risky as new docblocks might mean more, e.g. a Doctrine entity might have a new column in database.*
 
 * **compact_nullable_typehint**
 
@@ -376,7 +376,7 @@ Choose from the list of available rules:
 
   Class ``DateTimeImmutable`` should be used instead of ``DateTime``.
 
-  *Risky rule: risky when the code relies on modifying ``DateTime`` object or if any of the ``date_create*`` functions are overridden.*
+  *Risky rule: risky when the code relies on modifying ``DateTime`` objects or if any of the ``date_create*`` functions are overridden.*
 
 * **declare_equal_normalize** [@Symfony]
 
@@ -689,6 +689,12 @@ Choose from the list of available rules:
   - ``syntax`` (``'long'``, ``'short'``): whether to use the ``long`` or ``short`` ``list``
     syntax; defaults to ``'long'``
 
+* **logical_operators**
+
+  Use ``&&`` and ``||`` logical operators instead of ``and`` and ``or``.
+
+  *Risky rule: risky, because you must double-check if using and/or with lower precedence was intentional.*
+
 * **lowercase_cast** [@Symfony]
 
   Cast should be written in lower case.
@@ -1106,10 +1112,10 @@ Choose from the list of available rules:
 
 * **php_unit_set_up_tear_down_visibility**
 
-  Changes the visibility of the ``setUp`` and ``tearDown`` functions of
-  PHPUnit to protected, to match the PHPUnit TestCase.
+  Changes the visibility of the ``setUp()`` and ``tearDown()`` functions of
+  PHPUnit to ``protected``, to match the PHPUnit TestCase.
 
-  *Risky rule: this fixer may change functions named setUp or tearDown outside of PHPUnit tests, when a class is wrongly seen as a PHPUnit test.*
+  *Risky rule: this fixer may change functions named ``setUp()`` or ``tearDown()`` outside of PHPUnit tests, when a class is wrongly seen as a PHPUnit test.*
 
 * **php_unit_strict**
 
@@ -1311,6 +1317,11 @@ Choose from the list of available rules:
     ones; defaults to ``['getrandmax' => 'mt_getrandmax', 'rand' =>
     'mt_rand', 'srand' => 'mt_srand']``
 
+* **return_assignment**
+
+  Non global, static or indirectly referenced variables should not be
+  assigned and directly returned by a function or method.
+
 * **return_type_declaration** [@Symfony]
 
   There should be one or no space before colon, and one space after it in
@@ -1332,6 +1343,12 @@ Choose from the list of available rules:
 
   Instructions must be terminated with a semicolon.
 
+* **set_type_to_cast**
+
+  Cast shall be used, not ``settype``.
+
+  *Risky rule: risky when the ``settype`` function is overridden or when used as the 2nd or 3rd expression in a ``for`` loop .*
+
 * **short_scalar_cast** [@Symfony]
 
   Cast ``(boolean)`` and ``(integer)`` should be written as ``(bool)`` and

+ 1 - 3
src/Console/Command/ReadmeCommand.php

@@ -241,9 +241,7 @@ EOF;
                     $result = Preg::replace("#<\\?{$matches[1]}\\s*#", '', $result);
                 }
 
-                $result = Preg::replace("#\n\n +\\?>#", '', $result);
-
-                return $result;
+                return Preg::replace("#\n\n +\\?>#", '', $result);
             },
             $help
         );

+ 2 - 2
src/Fixer/Alias/BacktickToShellExecFixer.php

@@ -49,7 +49,7 @@ $withVar = `ls -lah $var1 ${var2} {$var3} {$var4[0]} {$var5->call()}`;
 EOT
                 ),
             ],
-            'Convertion is done only when it is non risky, so when special chars like single-quotes, double-quotes and backticks are not used inside the command.'
+            'Conversion is done only when it is non risky, so when special chars like single-quotes, double-quotes and backticks are not used inside the command.'
         );
     }
 
@@ -106,7 +106,7 @@ EOT
         array_shift($backtickTokens);
         array_pop($backtickTokens);
 
-        // Double-quoted strings are parsed differenly if they contain
+        // Double-quoted strings are parsed differently if they contain
         // variables or not, so we need to build the new token array accordingly
         $count = count($backtickTokens);
 

+ 251 - 0
src/Fixer/Alias/SetTypeToCastFixer.php

@@ -0,0 +1,251 @@
+<?php
+
+/*
+ * 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\Alias;
+
+use PhpCsFixer\AbstractFunctionReferenceFixer;
+use PhpCsFixer\FixerDefinition\CodeSample;
+use PhpCsFixer\FixerDefinition\FixerDefinition;
+use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
+use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\Tokenizer\Tokens;
+
+/**
+ * @author SpacePossum
+ */
+final class SetTypeToCastFixer extends AbstractFunctionReferenceFixer
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition()
+    {
+        return new FixerDefinition(
+            'Cast shall be used, not `settype`.',
+            [
+                new CodeSample(
+                    '<?php
+settype($foo, "integer");
+settype($bar, "string");
+settype($bar, "null");
+'
+                ),
+            ],
+            null,
+            'Risky when the `settype` function is overridden or when used as the 2nd or 3rd expression in a `for` loop .'
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isCandidate(Tokens $tokens)
+    {
+        return $tokens->isAllTokenKindsFound([T_CONSTANT_ENCAPSED_STRING, T_STRING, T_VARIABLE]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
+    {
+        $map = [
+            'array' => [T_ARRAY_CAST, '(array)'],
+            'bool' => [T_BOOL_CAST, '(bool)'],
+            'boolean' => [T_BOOL_CAST, '(bool)'],
+            'double' => [T_DOUBLE_CAST, '(float)'],
+            'float' => [T_DOUBLE_CAST, '(float)'],
+            'int' => [T_INT_CAST, '(int)'],
+            'integer' => [T_INT_CAST, '(int)'],
+            'object' => [T_OBJECT_CAST, '(object)'],
+            'string' => [T_STRING_CAST, '(string)'],
+            // note: `'null' is dealt with later on
+        ];
+
+        $argumentsAnalyzer = new ArgumentsAnalyzer();
+
+        foreach (array_reverse($this->findSettypeCalls($tokens)) as $candidate) {
+            $functionNameIndex = $candidate[0];
+
+            $arguments = $argumentsAnalyzer->getArguments($tokens, $candidate[1], $candidate[2]);
+            if (2 !== count($arguments)) {
+                continue; // function must be overridden or used incorrectly
+            }
+
+            $prev = $tokens->getPrevMeaningfulToken($functionNameIndex);
+            if (!$tokens[$prev]->isGivenKind(T_OPEN_TAG) && !$tokens[$prev]->equalsAny([';', '{'])) {
+                continue; // return value of the function is used
+            }
+
+            reset($arguments);
+
+            // --- Test first argument --------------------
+
+            $firstArgumentStart = key($arguments);
+            if ($tokens[$firstArgumentStart]->isComment() || $tokens[$firstArgumentStart]->isWhitespace()) {
+                $firstArgumentStart = $tokens->getNextMeaningfulToken($firstArgumentStart);
+            }
+
+            if (!$tokens[$firstArgumentStart]->isGivenKind(T_VARIABLE)) {
+                continue; // settype only works with variables pass by reference, function must be overridden
+            }
+
+            $commaIndex = $tokens->getNextMeaningfulToken($firstArgumentStart);
+
+            if (null === $commaIndex || !$tokens[$commaIndex]->equals(',')) {
+                continue; // first argument is complex statement; function must be overridden
+            }
+
+            // --- Test second argument -------------------
+
+            next($arguments);
+            $secondArgumentStart = key($arguments);
+            $secondArgumentEnd = $arguments[$secondArgumentStart];
+
+            if ($tokens[$secondArgumentStart]->isComment() || $tokens[$secondArgumentStart]->isWhitespace()) {
+                $secondArgumentStart = $tokens->getNextMeaningfulToken($secondArgumentStart);
+            }
+
+            if (
+                !$tokens[$secondArgumentStart]->isGivenKind(T_CONSTANT_ENCAPSED_STRING)
+                || $tokens->getNextMeaningfulToken($secondArgumentStart) < $secondArgumentEnd
+            ) {
+                continue; // second argument is of the wrong type or is a (complex) statement of some sort (function is overridden)
+            }
+
+            // --- Test type ------------------------------
+
+            $type = strtolower(trim($tokens[$secondArgumentStart]->getContent(), '"\'"'));
+
+            if ('null' !== $type && !isset($map[$type])) {
+                continue; // we don't know how to map
+            }
+
+            // --- Fixing ---------------------------------
+
+            $argumentToken = $tokens[$firstArgumentStart];
+
+            $this->removeSettypeCall(
+                $tokens,
+                $functionNameIndex,
+                $candidate[1],
+                $firstArgumentStart,
+                $commaIndex,
+                $secondArgumentStart,
+                $candidate[2]
+            );
+
+            if ('null' === $type) {
+                $this->findSettypeNullCall($tokens, $functionNameIndex, $argumentToken);
+            } else {
+                $this->fixSettypeCall($tokens, $functionNameIndex, $argumentToken, new Token($map[$type]));
+            }
+        }
+    }
+
+    private function findSettypeCalls(Tokens $tokens)
+    {
+        $candidates = [];
+
+        $end = count($tokens);
+        for ($i = 1; $i < $end; ++$i) {
+            $candidate = $this->find('settype', $tokens, $i, $end);
+            if (null === $candidate) {
+                break;
+            }
+
+            $i = $candidate[1]; // proceed to openParenthesisIndex
+            $candidates[] = $candidate;
+        }
+
+        return $candidates;
+    }
+
+    /**
+     * @param Tokens $tokens
+     * @param int    $functionNameIndex
+     * @param int    $openParenthesisIndex
+     * @param int    $firstArgumentStart
+     * @param int    $commaIndex
+     * @param int    $secondArgumentStart
+     * @param int    $closeParenthesisIndex
+     */
+    private function removeSettypeCall(
+        Tokens $tokens,
+        $functionNameIndex,
+        $openParenthesisIndex,
+        $firstArgumentStart,
+        $commaIndex,
+        $secondArgumentStart,
+        $closeParenthesisIndex
+    ) {
+        $tokens->clearTokenAndMergeSurroundingWhitespace($closeParenthesisIndex);
+        $tokens->clearTokenAndMergeSurroundingWhitespace($secondArgumentStart);
+        $tokens->clearTokenAndMergeSurroundingWhitespace($commaIndex);
+        $tokens->clearTokenAndMergeSurroundingWhitespace($firstArgumentStart);
+        $tokens->clearTokenAndMergeSurroundingWhitespace($openParenthesisIndex);
+        $tokens->clearAt($functionNameIndex); // we'll be inserting here so no need to merge the space tokens
+        $tokens->clearEmptyTokens();
+    }
+
+    /**
+     * @param Tokens $tokens
+     * @param Token  $castToken
+     * @param int    $functionNameIndex
+     * @param Token  $argumentToken
+     */
+    private function fixSettypeCall(
+        Tokens $tokens,
+        $functionNameIndex,
+        Token $argumentToken,
+        Token $castToken
+    ) {
+        $tokens->insertAt(
+            $functionNameIndex,
+            [
+                clone $argumentToken,
+                new Token([T_WHITESPACE, ' ']),
+                new Token('='),
+                new Token([T_WHITESPACE, ' ']),
+                $castToken,
+                new Token([T_WHITESPACE, ' ']),
+                clone $argumentToken,
+            ]
+        );
+
+        $tokens->removeTrailingWhitespace($functionNameIndex + 6); // 6 = number of inserted tokens -1 for offset correction
+    }
+
+    /**
+     * @param Tokens $tokens
+     * @param int    $functionNameIndex
+     * @param Token  $argumentToken
+     */
+    private function findSettypeNullCall(
+        Tokens $tokens,
+        $functionNameIndex,
+        Token $argumentToken
+    ) {
+        $tokens->insertAt(
+            $functionNameIndex,
+            [
+                clone $argumentToken,
+                new Token([T_WHITESPACE, ' ']),
+                new Token('='),
+                new Token([T_WHITESPACE, ' ']),
+                new Token([T_STRING, 'null']),
+            ]
+        );
+
+        $tokens->removeTrailingWhitespace($functionNameIndex + 4); // 4 = number of inserted tokens -1 for offset correction
+    }
+}

+ 2 - 3
src/Fixer/ClassUsage/DateTimeImmutableFixer.php

@@ -13,7 +13,6 @@
 namespace PhpCsFixer\Fixer\ClassUsage;
 
 use PhpCsFixer\AbstractFixer;
-use PhpCsFixer\Fixer\FixerInterface;
 use PhpCsFixer\FixerDefinition\CodeSample;
 use PhpCsFixer\FixerDefinition\FixerDefinition;
 use PhpCsFixer\Tokenizer\Token;
@@ -22,7 +21,7 @@ use PhpCsFixer\Tokenizer\Tokens;
 /**
  * @author Kuba Werłos <werlos@gmail.com>
  */
-final class DateTimeImmutableFixer extends AbstractFixer implements FixerInterface
+final class DateTimeImmutableFixer extends AbstractFixer
 {
     /**
      * {@inheritdoc}
@@ -33,7 +32,7 @@ final class DateTimeImmutableFixer extends AbstractFixer implements FixerInterfa
             'Class `DateTimeImmutable` should be used instead of `DateTime`.',
             [new CodeSample("<?php\nnew DateTime();\n")],
             null,
-            'Risky when the code relies on modifying `DateTime` object or if any of the `date_create*` functions are overridden.'
+            'Risky when the code relies on modifying `DateTime` objects or if any of the `date_create*` functions are overridden.'
         );
     }
 

+ 1 - 1
src/Fixer/Comment/CommentToPhpdocFixer.php

@@ -61,7 +61,7 @@ final class CommentToPhpdocFixer extends AbstractFixer implements WhitespacesAwa
             'Comments with annotation should be docblock.',
             [new CodeSample("<?php /* @var bool \$isFoo */\n")],
             null,
-            "Risky as new docblocks might began mean more, e.g. Doctrine's entity might have a new column in database"
+            'Risky as new docblocks might mean more, e.g. a Doctrine entity might have a new column in database'
         );
     }
 

+ 76 - 0
src/Fixer/Operator/LogicalOperatorsFixer.php

@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * 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\Operator;
+
+use PhpCsFixer\AbstractFixer;
+use PhpCsFixer\FixerDefinition\CodeSample;
+use PhpCsFixer\FixerDefinition\FixerDefinition;
+use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\Tokenizer\Tokens;
+
+/**
+ * @author Haralan Dobrev <hkdobrev@gmail.com>
+ */
+final class LogicalOperatorsFixer extends AbstractFixer
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getDefinition()
+    {
+        return new FixerDefinition(
+            'Use `&&` and `||` logical operators instead of `and` and `or`.',
+            [
+                new CodeSample(
+'<?php
+
+if ($a == "foo" and ($b == "bar" or $c == "baz")) {
+}
+'
+                ),
+            ],
+            null,
+            'Risky, because you must double-check if using and/or with lower precedence was intentional.'
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isCandidate(Tokens $tokens)
+    {
+        return $tokens->isAnyTokenKindsFound([T_LOGICAL_AND, T_LOGICAL_OR]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isRisky()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function applyFix(\SplFileInfo $file, Tokens $tokens)
+    {
+        foreach ($tokens as $index => $token) {
+            if ($token->isGivenKind(T_LOGICAL_AND)) {
+                $tokens[$index] = new Token([T_BOOLEAN_AND, '&&']);
+            } elseif ($token->isGivenKind(T_LOGICAL_OR)) {
+                $tokens[$index] = new Token([T_BOOLEAN_OR, '||']);
+            }
+        }
+    }
+}

+ 2 - 2
src/Fixer/PhpUnit/PhpUnitSetUpTearDownVisibilityFixer.php

@@ -31,7 +31,7 @@ final class PhpUnitSetUpTearDownVisibilityFixer extends AbstractFixer
     public function getDefinition()
     {
         return new FixerDefinition(
-            'Changes the visibility of the `setUp` and `tearDown` functions of PHPUnit to protected, to match the PHPUnit TestCase.',
+            'Changes the visibility of the `setUp()` and `tearDown()` functions of PHPUnit to `protected`, to match the PHPUnit TestCase.',
             [
                 new CodeSample(
                     '<?php
@@ -52,7 +52,7 @@ final class MyTest extends \PHPUnit_Framework_TestCase
                 ),
             ],
             null,
-            'This fixer may change functions named setUp or tearDown outside of PHPUnit tests, '.
+            'This fixer may change functions named `setUp()` or `tearDown()` outside of PHPUnit tests, '.
             'when a class is wrongly seen as a PHPUnit test.'
         );
     }

+ 3 - 5
src/Fixer/PhpUnit/PhpUnitTestAnnotationFixer.php

@@ -443,13 +443,12 @@ public function testItDoesSomething() {}}'.$this->whitespacesConfig->getLineEndi
         $lineContent = $this->getSingleLineDocBlockEntry($lines);
         $lineEnd = $this->whitespacesConfig->getLineEnding();
         $originalIndent = $this->detectIndent($tokens, $tokens->getNextNonWhitespace($docBlockIndex));
-        $lines = [
+
+        return [
             new Line('/**'.$lineEnd),
             new Line($originalIndent.' * '.$lineContent.$lineEnd),
             new Line($originalIndent.' */'),
         ];
-
-        return $lines;
     }
 
     /**
@@ -471,9 +470,8 @@ public function testItDoesSomething() {}}'.$this->whitespacesConfig->getLineEndi
             ++$i;
         }
         $line = array_slice($line, $i);
-        $line = implode($line);
 
-        return $line;
+        return implode($line);
     }
 
     /**

Some files were not shown because too many files changed in this diff