Browse Source

Merge branch 'master' into 3.0

* master:
  NamedArgumentTransformer - Introduction
  NoEmptyStatementFixer - fix more cases
  clean ups

# Conflicts:
#	doc/rules/index.rst
#	src/Console/Command/DescribeCommand.php
SpacePossum 4 years ago
parent
commit
f236e1d34f

+ 1 - 1
doc/rules/index.rst

@@ -502,7 +502,7 @@ Semicolon
 - `multiline_whitespace_before_semicolons <./semicolon/multiline_whitespace_before_semicolons.rst>`_
     Forbid multi-line whitespace before the closing semicolon or move the semicolon to the new line for chained calls.
 - `no_empty_statement <./semicolon/no_empty_statement.rst>`_
-    Remove useless semicolon statements.
+    Remove useless (semicolon) statements.
 - `no_singleline_whitespace_before_semicolons <./semicolon/no_singleline_whitespace_before_semicolons.rst>`_
     Single-line whitespace before closing semicolon are prohibited.
 - `semicolon_after_instruction <./semicolon/semicolon_after_instruction.rst>`_

+ 25 - 1
doc/rules/semicolon/no_empty_statement.rst

@@ -2,7 +2,7 @@
 Rule ``no_empty_statement``
 ===========================
 
-Remove useless semicolon statements.
+Remove useless (semicolon) statements.
 
 Examples
 --------
@@ -18,6 +18,30 @@ Example #1
    -<?php $a = 1;;
    +<?php $a = 1;
 
+Example #2
+~~~~~~~~~~
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+   @@ -1 +1 @@
+   -<?php echo 1;2;
+   +<?php echo 1;
+
+Example #3
+~~~~~~~~~~
+
+.. code-block:: diff
+
+   --- Original
+   +++ New
+   @@ -1,3 +1,3 @@
+    <?php while(foo()){
+   -    continue 1;
+   +    continue ;
+    }
+
 Rule sets
 ---------
 

+ 1 - 5
src/Fixer/Phpdoc/PhpdocTypesFixer.php

@@ -138,11 +138,7 @@ final class PhpdocTypesFixer extends AbstractPhpdocTypesFixer implements Configu
     {
         $lower = strtolower($type);
 
-        if (\in_array($lower, $this->typesToFix, true)) {
-            return $lower;
-        }
-
-        return $type;
+        return \in_array($lower, $this->typesToFix, true) ? $lower : $type;
     }
 
     /**

+ 45 - 18
src/Fixer/Semicolon/NoEmptyStatementFixer.php

@@ -30,8 +30,12 @@ final class NoEmptyStatementFixer extends AbstractFixer
     public function getDefinition()
     {
         return new FixerDefinition(
-            'Remove useless semicolon statements.',
-            [new CodeSample("<?php \$a = 1;;\n")]
+            'Remove useless (semicolon) statements.',
+            [
+                new CodeSample("<?php \$a = 1;;\n"),
+                new CodeSample("<?php echo 1;2;\n"),
+                new CodeSample("<?php while(foo()){\n    continue 1;\n}\n"),
+            ]
         );
     }
 
@@ -59,7 +63,17 @@ final class NoEmptyStatementFixer extends AbstractFixer
     protected function applyFix(\SplFileInfo $file, Tokens $tokens)
     {
         for ($index = 0, $count = $tokens->count(); $index < $count; ++$index) {
-            // skip T_FOR parenthesis to ignore duplicated `;` like `for ($i = 1; ; ++$i) {...}`
+            if ($tokens[$index]->isGivenKind([T_BREAK, T_CONTINUE])) {
+                $index = $tokens->getNextMeaningfulToken($index);
+
+                if ($tokens[$index]->equals([T_LNUMBER, '1'])) {
+                    $tokens->clearTokenAndMergeSurroundingWhitespace($index);
+                }
+
+                continue;
+            }
+
+            // skip T_FOR parenthesis to ignore double `;` like `for ($i = 1; ; ++$i) {...}`
             if ($tokens[$index]->isGivenKind(T_FOR)) {
                 $index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $tokens->getNextMeaningfulToken($index)) + 1;
 
@@ -82,6 +96,19 @@ final class NoEmptyStatementFixer extends AbstractFixer
             // A semicolon might be removed if it follows a '}' but only if the brace is part of certain structures.
             if ($tokens[$previousMeaningfulIndex]->equals('}')) {
                 $this->fixSemicolonAfterCurlyBraceClose($tokens, $index, $previousMeaningfulIndex);
+
+                continue;
+            }
+
+            // A semicolon might be removed together with its noop statement, for example "<?php 1;"
+            $prePreviousMeaningfulIndex = $tokens->getPrevMeaningfulToken($previousMeaningfulIndex);
+
+            if (
+                $tokens[$prePreviousMeaningfulIndex]->equalsAny([';', '{', '}', [T_OPEN_TAG]])
+                && $tokens[$previousMeaningfulIndex]->isGivenKind([T_CONSTANT_ENCAPSED_STRING, T_DNUMBER, T_LNUMBER, T_STRING, T_VARIABLE])
+            ) {
+                $tokens->clearTokenAndMergeSurroundingWhitespace($index);
+                $tokens->clearTokenAndMergeSurroundingWhitespace($previousMeaningfulIndex);
             }
         }
     }
@@ -112,27 +139,27 @@ final class NoEmptyStatementFixer extends AbstractFixer
         }
 
         $curlyOpeningIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $curlyCloseIndex);
-        $beforeCurlyOpening = $tokens->getPrevMeaningfulToken($curlyOpeningIndex);
+        $beforeCurlyOpeningIndex = $tokens->getPrevMeaningfulToken($curlyOpeningIndex);
 
-        if ($tokens[$beforeCurlyOpening]->isGivenKind($beforeCurlyOpeningKinds) || $tokens[$beforeCurlyOpening]->equalsAny([';', '{', '}'])) {
+        if ($tokens[$beforeCurlyOpeningIndex]->isGivenKind($beforeCurlyOpeningKinds) || $tokens[$beforeCurlyOpeningIndex]->equalsAny([';', '{', '}'])) {
             $tokens->clearTokenAndMergeSurroundingWhitespace($index);
 
             return;
         }
 
         // check for namespaces and class, interface and trait definitions
-        if ($tokens[$beforeCurlyOpening]->isGivenKind(T_STRING)) {
-            $classyTest = $tokens->getPrevMeaningfulToken($beforeCurlyOpening);
+        if ($tokens[$beforeCurlyOpeningIndex]->isGivenKind(T_STRING)) {
+            $classyTestIndex = $tokens->getPrevMeaningfulToken($beforeCurlyOpeningIndex);
 
-            while ($tokens[$classyTest]->equals(',') || $tokens[$classyTest]->isGivenKind([T_STRING, T_NS_SEPARATOR, T_EXTENDS, T_IMPLEMENTS])) {
-                $classyTest = $tokens->getPrevMeaningfulToken($classyTest);
+            while ($tokens[$classyTestIndex]->equals(',') || $tokens[$classyTestIndex]->isGivenKind([T_STRING, T_NS_SEPARATOR, T_EXTENDS, T_IMPLEMENTS])) {
+                $classyTestIndex = $tokens->getPrevMeaningfulToken($classyTestIndex);
             }
 
             $tokensAnalyzer = new TokensAnalyzer($tokens);
 
             if (
-                $tokens[$classyTest]->isGivenKind(T_NAMESPACE)
-                || ($tokens[$classyTest]->isClassy() && !$tokensAnalyzer->isAnonymousClass($classyTest))
+                $tokens[$classyTestIndex]->isGivenKind(T_NAMESPACE)
+                || ($tokens[$classyTestIndex]->isClassy() && !$tokensAnalyzer->isAnonymousClass($classyTestIndex))
             ) {
                 $tokens->clearTokenAndMergeSurroundingWhitespace($index);
             }
@@ -141,24 +168,24 @@ final class NoEmptyStatementFixer extends AbstractFixer
         }
 
         // early return check, below only control structures with conditions are fixed
-        if (!$tokens[$beforeCurlyOpening]->equals(')')) {
+        if (!$tokens[$beforeCurlyOpeningIndex]->equals(')')) {
             return;
         }
 
-        $openingBrace = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $beforeCurlyOpening);
-        $beforeOpeningBrace = $tokens->getPrevMeaningfulToken($openingBrace);
+        $openingBraceIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $beforeCurlyOpeningIndex);
+        $beforeOpeningBraceIndex = $tokens->getPrevMeaningfulToken($openingBraceIndex);
 
-        if ($tokens[$beforeOpeningBrace]->isGivenKind([T_IF, T_ELSEIF, T_FOR, T_FOREACH, T_WHILE, T_SWITCH, T_CATCH, T_DECLARE])) {
+        if ($tokens[$beforeOpeningBraceIndex]->isGivenKind([T_IF, T_ELSEIF, T_FOR, T_FOREACH, T_WHILE, T_SWITCH, T_CATCH, T_DECLARE])) {
             $tokens->clearTokenAndMergeSurroundingWhitespace($index);
 
             return;
         }
 
         // check for function definition
-        if ($tokens[$beforeOpeningBrace]->isGivenKind(T_STRING)) {
-            $beforeString = $tokens->getPrevMeaningfulToken($beforeOpeningBrace);
+        if ($tokens[$beforeOpeningBraceIndex]->isGivenKind(T_STRING)) {
+            $beforeStringIndex = $tokens->getPrevMeaningfulToken($beforeOpeningBraceIndex);
 
-            if ($tokens[$beforeString]->isGivenKind(T_FUNCTION)) {
+            if ($tokens[$beforeStringIndex]->isGivenKind(T_FUNCTION)) {
                 $tokens->clearTokenAndMergeSurroundingWhitespace($index); // implicit return
             }
         }

+ 2 - 0
src/Tokenizer/CT.php

@@ -48,6 +48,8 @@ final class CT
     const T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED = 10029;
     const T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE = 10030;
     const T_ATTRIBUTE_CLOSE = 10031;
+    const T_NAMED_ARGUMENT_NAME = 10032;
+    const T_NAMED_ARGUMENT_COLON = 10033;
 
     private function __construct()
     {

+ 85 - 0
src/Tokenizer/Transformer/NamedArgumentTransformer.php

@@ -0,0 +1,85 @@
+<?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\Tokenizer\Transformer;
+
+use PhpCsFixer\Tokenizer\AbstractTransformer;
+use PhpCsFixer\Tokenizer\CT;
+use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\Tokenizer\Tokens;
+
+/**
+ * Transform named argument tokens.
+ *
+ * @author SpacePossum
+ *
+ * @internal
+ */
+final class NamedArgumentTransformer extends AbstractTransformer
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getPriority()
+    {
+        // needs to run after TypeColonTransformer
+        return -15;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRequiredPhpVersionId()
+    {
+        return 80000;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function process(Tokens $tokens, Token $token, $index)
+    {
+        if (!$tokens[$index]->equals(':')) {
+            return;
+        }
+
+        $stringIndex = $tokens->getPrevMeaningfulToken($index);
+
+        if (!$tokens[$stringIndex]->isGivenKind(T_STRING)) {
+            return;
+        }
+
+        $preStringIndex = $tokens->getPrevMeaningfulToken($stringIndex);
+
+        // if equals any [';', '{', '}', [T_OPEN_TAG]] than it is a goto label
+        // if equals ')' than likely it is a type colon, but sure not a name argument
+        // if equals '?' than it is part of ternary statement
+
+        if (!$tokens[$preStringIndex]->equalsAny([',', '('])) {
+            return;
+        }
+
+        $tokens[$stringIndex] = new Token([CT::T_NAMED_ARGUMENT_NAME, $tokens[$stringIndex]->getContent()]);
+        $tokens[$index] = new Token([CT::T_NAMED_ARGUMENT_COLON, ':']);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getDeprecatedCustomTokens()
+    {
+        return [
+            CT::T_NAMED_ARGUMENT_COLON,
+            CT::T_NAMED_ARGUMENT_NAME,
+        ];
+    }
+}

+ 1 - 0
src/Tokenizer/Transformer/TypeColonTransformer.php

@@ -69,6 +69,7 @@ final class TypeColonTransformer extends AbstractTransformer
         }
 
         $prevKinds = [T_FUNCTION, CT::T_RETURN_REF, CT::T_USE_LAMBDA];
+
         if (\PHP_VERSION_ID >= 70400) {
             $prevKinds[] = T_FN;
         }

+ 1 - 1
src/Utils.php

@@ -167,7 +167,7 @@ final class Utils
     public static function naturalLanguageJoinWithBackticks(array $names)
     {
         if (empty($names)) {
-            throw new \InvalidArgumentException('Array of names cannot be empty');
+            throw new \InvalidArgumentException('Array of names cannot be empty.');
         }
 
         $names = array_map(static function ($name) {

+ 1 - 0
tests/AutoReview/TransformerTest.php

@@ -84,6 +84,7 @@ final class TransformerTest extends TestCase
             [$transformers['name_qualified'], $transformers['namespace_operator']],
             [$transformers['return_ref'], $transformers['type_colon']],
             [$transformers['square_brace'], $transformers['brace_class_instantiation']],
+            [$transformers['type_colon'], $transformers['named_argument']],
             [$transformers['type_colon'], $transformers['nullable_type']],
             [$transformers['use'], $transformers['type_colon']],
         ];

+ 0 - 17
tests/Fixer/Basic/NonPrintableCharacterFixerTest.php

@@ -309,21 +309,4 @@ INPUT
             'use_escape_sequences_in_strings' => true,
         ]);
     }
-
-    /**
-     * @requires PHP 8.0
-     */
-    public function testFixPhp80()
-    {
-        $this->doTest(
-            '<?php class Foo {
-    #[Required]
-    public $bar;
-}',
-            '<?php class Foo {
-    #[Requi'.pack('H*', 'e2808b').'red]
-    public $bar;
-}'
-        );
-    }
 }

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