Просмотр исходного кода

Merge branch '1.12'

Conflicts:
	README.rst
	Symfony/CS/Tests/FixerTest.php
	Symfony/CS/Tests/Tokenizer/TokensTest.php
	src/Fixer/PhpUnit/PhpUnitConstructFixer.php
	src/Tokenizer/Tokens.php
Dariusz Ruminski 9 лет назад
Родитель
Сommit
da902a4062

+ 1 - 0
.php_cs.dist

@@ -20,6 +20,7 @@ return PhpCsFixer\Config::create()
         'no_useless_return' => true,
         'ordered_imports' => true,
         'php_unit_construct' => true,
+        'php_unit_dedicate_assert' => true,
         'php_unit_strict' => true,
         'strict_comparison' => true,
         'strict_param' => true,

+ 1 - 0
LICENSE

@@ -1,4 +1,5 @@
 Copyright (c) 2012-2016 Fabien Potencier
+                        Dariusz Rumiński
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 7 - 0
README.rst

@@ -513,6 +513,13 @@ Choose from the list of available fixers:
                          "->assertTrue($foo)". (Risky
                          fixer!)
 
+* **php_unit_dedicate_assert**
+                         PHPUnit assertions like
+                         "assertInternalType",
+                         "assertFileExists", should be
+                         used over "assertTrue".
+                         (Risky fixer!)
+
 * **php_unit_strict**
                          PHPUnit methods like
                          "assertSame" should be used

+ 9 - 0
src/Fixer/PhpTag/BlankLineAfterOpeningTagFixer.php

@@ -76,4 +76,13 @@ final class BlankLineAfterOpeningTagFixer extends AbstractFixer
     {
         return 'Ensure there is no code on the same line as the PHP open tag and it is followed by a blankline.';
     }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPriority()
+    {
+        // should be run before the NoBlankLinesBeforeNamespaceFixer
+        return 1;
+    }
 }

+ 23 - 1
src/Fixer/PhpUnit/PhpUnitConstructFixer.php

@@ -105,10 +105,17 @@ final class PhpUnitConstructFixer extends AbstractFixer
      */
     public function getPriority()
     {
-        // should be run after the PhpUnitStrictFixer
+        // should be run after the PhpUnitStrictFixer and before PhpUnitDedicateAssertFixer.
         return -10;
     }
 
+    /**
+     * @param Tokens $tokens
+     * @param int    $index
+     * @param string $method
+     *
+     * @return int|null
+     */
     private function fixAssertNegative(Tokens $tokens, $index, $method)
     {
         static $map = array(
@@ -120,6 +127,13 @@ final class PhpUnitConstructFixer extends AbstractFixer
         return $this->fixAssert($map, $tokens, $index, $method);
     }
 
+    /**
+     * @param Tokens $tokens
+     * @param int    $index
+     * @param string $method
+     *
+     * @return int|null
+     */
     private function fixAssertPositive(Tokens $tokens, $index, $method)
     {
         static $map = array(
@@ -131,6 +145,14 @@ final class PhpUnitConstructFixer extends AbstractFixer
         return $this->fixAssert($map, $tokens, $index, $method);
     }
 
+    /**
+     * @param array<string, string> $map
+     * @param Tokens                $tokens
+     * @param int                   $index
+     * @param string                $method
+     *
+     * @return int|null
+     */
     private function fixAssert(array $map, Tokens $tokens, $index, $method)
     {
         $sequence = $tokens->findSequence(

+ 276 - 0
src/Fixer/PhpUnit/PhpUnitDedicateAssertFixer.php

@@ -0,0 +1,276 @@
+<?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\PhpUnit;
+
+use PhpCsFixer\AbstractFixer;
+use PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException;
+use PhpCsFixer\Tokenizer\Token;
+use PhpCsFixer\Tokenizer\Tokens;
+
+/**
+ * @author SpacePossum
+ */
+final class PhpUnitDedicateAssertFixer extends AbstractFixer
+{
+    private $fixMap = array(
+        'array_key_exists' => array('assertArrayNotHasKey', 'assertArrayHasKey'),
+        'empty' => array('assertNotEmpty', 'assertEmpty'),
+        'file_exists' => array('assertFileNotExists', 'assertFileExists'),
+        'is_infinite' => array('assertFinite', 'assertInfinite'),
+        'is_nan' => array(false, 'assertNan'),
+        'is_null' => array('assertNotNull', 'assertNull'),
+        'is_array' => true,
+        'is_bool' => true,
+        'is_boolean' => true,
+        'is_callable' => true,
+        'is_double' => true,
+        'is_float' => true,
+        'is_int' => true,
+        'is_integer' => true,
+        'is_long' => true,
+        'is_​numeric' => true,
+        'is_object' => true,
+        'is_real' => true,
+        'is_​resource' => true,
+        'is_scalar' => true,
+        'is_string' => true,
+    );
+
+    private $configuration = array(
+        'array_key_exists',
+        'empty',
+        'file_exists',
+        'is_infinite',
+        'is_nan',
+        'is_null',
+        'is_array',
+        'is_bool',
+        'is_boolean',
+        'is_callable',
+        'is_double',
+        'is_float',
+        'is_int',
+        'is_integer',
+        'is_long',
+        'is_​numeric',
+        'is_object',
+        'is_real',
+        'is_​resource',
+        'is_scalar',
+        'is_string',
+    );
+
+    /**
+     * @param array|null $configuration
+     */
+    public function configure(array $configuration = null)
+    {
+        if (null === $configuration) {
+            return;
+        }
+
+        $this->configuration = array();
+        foreach ($configuration as $method) {
+            if (!array_key_exists($method, $this->fixMap)) {
+                throw new InvalidFixerConfigurationException($this->getName(), sprintf('Unknown configuration method "%s".', $method));
+            }
+
+            $this->configuration[] = $method;
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isCandidate(Tokens $tokens)
+    {
+        return $tokens->isTokenKindFound(T_STRING);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isRisky()
+    {
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function fix(\SplFileInfo $file, Tokens $tokens)
+    {
+        static $searchSequence = array(
+            array(T_VARIABLE, '$this'),
+            array(T_OBJECT_OPERATOR, '->'),
+            array(T_STRING),
+        );
+
+        $index = 1;
+        $candidate = $tokens->findSequence($searchSequence, $index);
+        while (null !== $candidate) {
+            end($candidate);
+            $index = $this->getAssertCandidate($tokens, key($candidate));
+            if (is_array($index)) {
+                $index = $this->fixAssert($tokens, $index);
+            }
+
+            ++$index;
+            $candidate = $tokens->findSequence($searchSequence, $index);
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDescription()
+    {
+        return 'PHPUnit assertions like "assertInternalType", "assertFileExists", should be used over "assertTrue".';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getPriority()
+    {
+        // should be run after the PhpUnitConstructFixer.
+        return -15;
+    }
+
+    /**
+     * @param Tokens $tokens
+     * @param int    $assertCallIndex Token index of assert method call.
+     *
+     * @return int|int[] indexes of assert call, test call and positive flag, or last index checked.
+     */
+    private function getAssertCandidate(Tokens $tokens, $assertCallIndex)
+    {
+        $content = strtolower($tokens[$assertCallIndex]->getContent());
+        if ('asserttrue' === $content) {
+            $isPositive = 1;
+        } elseif ('assertfalse' === $content) {
+            $isPositive = 0;
+        } else {
+            return $assertCallIndex;
+        }
+
+        // test candidate for simple calls like: ([\]+'some fixable call'(...))
+        $assertCallOpenIndex = $tokens->getNextMeaningfulToken($assertCallIndex);
+        if (!$tokens[$assertCallOpenIndex]->equals('(')) {
+            return $assertCallIndex;
+        }
+
+        $testDefaultNamespaceTokenIndex = false;
+        $testIndex = $tokens->getNextMeaningfulToken($assertCallOpenIndex);
+
+        if (!$tokens[$testIndex]->isGivenKind(array(T_EMPTY, T_STRING))) {
+            if (!$tokens[$testIndex]->isGivenKind(T_NS_SEPARATOR)) {
+                return $testIndex;
+            }
+
+            $testDefaultNamespaceTokenIndex = $testIndex;
+            $testIndex = $tokens->getNextMeaningfulToken($testIndex);
+        }
+
+        $testOpenIndex = $tokens->getNextMeaningfulToken($testIndex);
+        if (!$tokens[$testOpenIndex]->equals('(')) {
+            return $testOpenIndex;
+        }
+
+        $testCloseIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $testOpenIndex);
+
+        $assertCallCloseIndex = $tokens->getNextMeaningfulToken($testCloseIndex);
+        if (!$tokens[$assertCallCloseIndex]->equalsAny(array(')', ','))) {
+            return $assertCallCloseIndex;
+        }
+
+        return array(
+            $isPositive,
+            $assertCallIndex,
+            $assertCallOpenIndex,
+            $testDefaultNamespaceTokenIndex,
+            $testIndex,
+            $testOpenIndex,
+            $testCloseIndex,
+            $assertCallCloseIndex,
+        );
+    }
+
+    /**
+     * @param Tokens $tokens
+     * @param array  $assertIndexes
+     *
+     * @return int index up till processed, number of tokens added
+     */
+    private function fixAssert(Tokens $tokens, array $assertIndexes)
+    {
+        list(
+            $isPositive,
+            $assertCallIndex,
+            $assertCallOpenIndex,
+            $testDefaultNamespaceTokenIndex,
+            $testIndex,
+            $testOpenIndex,
+            $testCloseIndex,
+            $assertCallCloseIndex
+        ) = $assertIndexes;
+
+        $content = strtolower($tokens[$testIndex]->getContent());
+        if (!in_array($content, $this->configuration, true)) {
+            return $assertCallCloseIndex;
+        }
+
+        if (is_array($this->fixMap[$content])) {
+            if (false !== $this->fixMap[$content][$isPositive]) {
+                $tokens[$assertCallIndex]->setContent($this->fixMap[$content][$isPositive]);
+                $this->removeFunctionCall($tokens, $testDefaultNamespaceTokenIndex, $testIndex, $testOpenIndex, $testCloseIndex);
+            }
+
+            return $assertCallCloseIndex;
+        }
+
+        $type = substr($content, 3);
+        $tokens[$assertCallIndex]->setContent($isPositive ? 'assertInternalType' : 'assertNotInternalType');
+        $tokens->overrideAt($testIndex, array(T_CONSTANT_ENCAPSED_STRING, "'".$type."'"));
+        $tokens->overrideAt($testOpenIndex, ',');
+        $tokens->clearTokenAndMergeSurroundingWhitespace($testCloseIndex);
+
+        if (!$tokens[$testOpenIndex + 1]->isWhitespace()) {
+            $tokens->insertAt($testOpenIndex + 1, new Token(array(T_WHITESPACE, ' ')));
+        }
+
+        if (false !== $testDefaultNamespaceTokenIndex) {
+            $tokens->clearTokenAndMergeSurroundingWhitespace($testDefaultNamespaceTokenIndex);
+        }
+
+        return $assertCallCloseIndex;
+    }
+
+    /**
+     * @param Tokens    $tokens
+     * @param int|false $callNSIndex
+     * @param int       $callIndex
+     * @param int       $openIndex
+     * @param int       $closeIndex
+     */
+    private function removeFunctionCall(Tokens $tokens, $callNSIndex, $callIndex, $openIndex, $closeIndex)
+    {
+        $tokens->clearTokenAndMergeSurroundingWhitespace($callIndex);
+        if (false !== $callNSIndex) {
+            $tokens->clearTokenAndMergeSurroundingWhitespace($callNSIndex);
+        }
+
+        $tokens->clearTokenAndMergeSurroundingWhitespace($openIndex);
+        $tokens->clearTokenAndMergeSurroundingWhitespace($closeIndex);
+    }
+}

+ 1 - 14
src/Tokenizer/TokensAnalyzer.php

@@ -314,20 +314,7 @@ final class TokensAnalyzer
             $startParenthesisToken = $tokens[$startParenthesisIndex];
         }
 
-        if (!$startParenthesisToken->equals('(')) {
-            return false;
-        }
-
-        $endParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $startParenthesisIndex);
-
-        $nextIndex = $tokens->getNextMeaningfulToken($endParenthesisIndex);
-        $nextToken = $tokens[$nextIndex];
-
-        if (!$nextToken->equalsAny(array('{', array(CT_USE_LAMBDA)))) {
-            return false;
-        }
-
-        return true;
+        return $startParenthesisToken->equals('(');
     }
 
     /**

+ 206 - 0
tests/Fixer/PhpUnit/PhpUnitDedicateAssertFixerTest.php

@@ -0,0 +1,206 @@
+<?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\Tests\Fixer\PhpUnit;
+
+use PhpCsFixer\Test\AbstractFixerTestCase;
+
+/**
+ * @author SpacePossum
+ *
+ * @internal
+ */
+final class PhpUnitDedicateAssertFixerTest extends AbstractFixerTestCase
+{
+    /**
+     * @dataProvider provideInternalTypeMethods
+     */
+    public function testInternalTypeMethods($expected, $input = null)
+    {
+        $this->doTest($expected, $input);
+    }
+
+    public function provideInternalTypeMethods()
+    {
+        $cases = array();
+        foreach (array('array', 'bool', 'boolean', 'callable', 'double', 'float', 'int', 'integer', 'long', '​numeric', 'object', '​resource', 'real', 'scalar', 'string') as $type) {
+            $cases[] = array(
+                sprintf('<?php $this->assertInternalType(\'%s\', $a);', $type),
+                sprintf('<?php $this->assertTrue(is_%s($a));', $type),
+            );
+
+            $cases[] = array(
+                sprintf('<?php $this->assertNotInternalType(\'%s\', $a);', $type),
+                sprintf('<?php $this->assertFalse(is_%s($a));', $type),
+            );
+        }
+
+        $cases[] = array(
+            '<?php $this->assertInternalType(\'float\', $a, "my message");',
+            '<?php $this->assertTrue(is_float( $a), "my message");',
+        );
+
+        $cases[] = array(
+            '<?php $this->assertInternalType(\'float\', $a);',
+            '<?php $this->assertTrue(\IS_FLOAT($a));',
+        );
+
+        return $cases;
+    }
+
+    /**
+     * @dataProvider provideDedicatedAssertsCases
+     */
+    public function testDedicatedAsserts($expected, $input = null)
+    {
+        $this->doTest($expected, $input);
+    }
+
+    public function provideDedicatedAssertsCases()
+    {
+        return array(
+            array(
+                '<?php
+                    $this->assertNan($a);
+                    $this->assertNan($a);
+                    $this->assertTrue(test\is_nan($a));
+                    $this->assertTrue(test\a\is_nan($a));
+                ',
+                '<?php
+                    $this->assertTrue(is_nan($a));
+                    $this->assertTrue(\is_nan($a));
+                    $this->assertTrue(test\is_nan($a));
+                    $this->assertTrue(test\a\is_nan($a));
+                ',
+            ),
+            array(
+                '<?php
+                    $this->assertFileExists($a);
+                    $this->assertFileNotExists($a);
+                    $this->assertFileExists($a);
+                    $this->assertFileNotExists($a);
+                ',
+                '<?php
+                    $this->assertTrue(file_exists($a));
+                    $this->assertFalse(file_exists($a));
+                    $this->assertTrue(\file_exists($a));
+                    $this->assertFalse(\file_exists($a));
+                ',
+            ),
+            array(
+                '<?php
+                    $this->assertNull($a);
+                    $this->assertNotNull($a);
+                    $this->assertNull($a);
+                    $this->assertNotNull($a, "my message");
+                ',
+                '<?php
+                    $this->assertTrue(is_null($a));
+                    $this->assertFalse(is_null($a));
+                    $this->assertTrue(\is_null($a));
+                    $this->assertFalse(\is_null($a), "my message");
+                ',
+            ),
+            array(
+                '<?php
+                    $this->assertEmpty($a);
+                    $this->assertNotEmpty($a);
+                ',
+                '<?php
+                    $this->assertTrue(empty($a));
+                    $this->ASSERTFALSE(empty($a));
+                ',
+            ),
+            array(
+                '<?php
+                    $this->assertInfinite($a);
+                    $this->assertFinite($a, "my message");
+                    $this->assertInfinite($a);
+                    $this->assertFinite($a, "my message");
+                ',
+                '<?php
+                    $this->assertTrue(is_infinite($a));
+                    $this->assertFalse(is_infinite($a), "my message");
+                    $this->assertTrue(\is_infinite($a));
+                    $this->assertFalse(\is_infinite($a), "my message");
+                ',
+            ),
+            array(
+                '<?php
+                    $this->assertArrayHasKey("test", $a);
+                    $this->assertArrayNotHasKey($b, $a, $c);
+                ',
+                '<?php
+                    $this->assertTrue(\array_key_exists("test", $a));
+                    $this->ASSERTFALSE(array_key_exists($b, $a), $c);
+                ',
+            ),
+        );
+    }
+
+    /**
+     * @dataProvider provideNotFixCases
+     */
+    public function testNotFix($expected)
+    {
+        $this->doTest($expected);
+    }
+
+    public function provideNotFixCases()
+    {
+        return array(
+            array(
+                '<?php echo $this->assertTrue;',
+            ),
+            array(
+                '<?php echo $this->assertTrue?>',
+            ),
+            array(
+                '<?php
+                    const is_null = 1;
+                    $this->assertTrue(is_null);
+                    $this->assertTrue(is_int($a) && $b);
+                    $this->assertFalse(is_nan($a));
+                    $this->assertTrue(is_int($a) || \is_bool($b));
+                    $this->assertTrue($a&&is_int($a));
+                ',
+            ),
+        );
+    }
+
+    public function testConfig()
+    {
+        $fixer = $this->getFixer();
+        $fixer->configure(array('file_exists'));
+        $this->doTest(
+            '<?php
+                    $this->assertFileExists($a);
+                    $this->assertTrue(is_infinite($a));
+            ',
+            '<?php
+                    $this->assertTrue(file_exists($a));
+                    $this->assertTrue(is_infinite($a));
+            ',
+            null,
+            $fixer
+        );
+    }
+
+    /**
+     * @expectedException PhpCsFixer\ConfigurationException\InvalidFixerConfigurationException
+     * @expectedExceptionMessageRegExp /^\[php_unit_dedicate_assert\] Unknown configuration method "_unknown_".$/
+     */
+    public function testInvalidConfig()
+    {
+        $this->getFixer()->configure(array('_unknown_'));
+    }
+}

+ 1 - 0
tests/FixerFactoryTest.php

@@ -249,6 +249,7 @@ final class FixerFactoryTest extends \PHPUnit_Framework_TestCase
             array($fixers['combine_consecutive_unsets'], $fixers['no_extra_consecutive_blank_lines']), // tested also in: combine_consecutive_unsets,no_extra_consecutive_blank_lines.test
             array($fixers['no_duplicate_semicolons'], $fixers['combine_consecutive_unsets']), // tested also in: no_duplicate_semicolons,combine_consecutive_unsets.test
             array($fixers['phpdoc_type_to_var'], $fixers['phpdoc_single_line_var_spacing']), // tested also in: phpdoc_type_to_var,phpdoc_single_line_var_spacing.test
+            array($fixers['blank_line_after_opening_tag'], $fixers['no_blank_lines_before_namespace']), // tested also in: blank_line_after_opening_tag,no_blank_lines_before_namespace.test
         );
 
         // prepare bulk tests for phpdoc fixers to test that:

+ 20 - 0
tests/Fixtures/Integration/priority/blank_line_after_opening_tag,no_blank_lines_before_namespace.test

@@ -0,0 +1,20 @@
+--TEST--
+Integration of fixers: blank_line_after_opening_tag,no_blank_lines_before_namespace.
+--CONFIG--
+{"blank_line_after_opening_tag": true, "no_blank_lines_before_namespace": true}
+--EXPECT--
+<?php
+namespace A;
+
+class A
+{
+}
+
+--INPUT--
+<?php
+
+namespace A;
+
+class A
+{
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов