mirror of https://github.com/rectorphp/rector.git
Fix PHPStan parser for different PHP version parsing (#1791)
This commit is contained in:
parent
dbadefcdf0
commit
bc0eac36f4
|
@ -0,0 +1,44 @@
|
|||
# This workflow runs system tests: Use the Rector application from the source
|
||||
# checkout to process "fixture" projects in e2e/ directory
|
||||
# to see if those can be processed successfully
|
||||
name: End to End tests on PHP 7.4
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
# see https://github.com/composer/composer/issues/9368#issuecomment-718112361
|
||||
COMPOSER_ROOT_VERSION: "dev-main"
|
||||
|
||||
jobs:
|
||||
end_to_end:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
directory:
|
||||
- 'e2e/php74-parse-static'
|
||||
|
||||
name: End to end test - ${{ matrix.directory }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 7.4
|
||||
coverage: none
|
||||
|
||||
# run in e2e subdir
|
||||
-
|
||||
run: composer install --ansi
|
||||
working-directory: ${{ matrix.directory }}
|
||||
|
||||
# run e2e test - there should be no reports
|
||||
- run: vendor/bin/rector --ansi --dry-run
|
||||
working-directory: ${{ matrix.directory }}
|
|
@ -0,0 +1,27 @@
|
|||
# ref https://github.com/rectorphp/rector/issues/6970
|
||||
name: Downgrade PHPStan Parser
|
||||
|
||||
on:
|
||||
pull_request: null
|
||||
|
||||
env:
|
||||
# see https://github.com/composer/composer/issues/9368#issuecomment-718112361
|
||||
COMPOSER_ROOT_VERSION: "dev-main"
|
||||
|
||||
jobs:
|
||||
downgrade_phpstan_parser:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
# see https://github.com/shivammathur/setup-php
|
||||
-
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.1
|
||||
coverage: none
|
||||
|
||||
- uses: "ramsey/composer-install@v1"
|
||||
|
||||
- run: bin/rector p vendor/clue/ndjson-react/src/Decoder.php --config tests/config/downgrade-phpstan-parser.php --dry-run
|
|
@ -10,27 +10,15 @@ services:
|
|||
arguments:
|
||||
originalParser: @rectorParser
|
||||
cachedNodesByStringCountMax: %cache.nodesByStringCountMax%
|
||||
autowired: no
|
||||
|
||||
cachedRectorSimpleParser:
|
||||
class: PHPStan\Parser\CachedParser
|
||||
arguments:
|
||||
originalParser: @rectorSimpleParser
|
||||
cachedNodesByStringCountMax: %cache.nodesByStringCountMax%
|
||||
|
||||
rectorSimpleParser:
|
||||
class: PHPStan\Parser\CleaningParser
|
||||
arguments:
|
||||
wrappedParser: @currentPhpVersionSimpleDirectParser
|
||||
autowired: no
|
||||
autowired: false
|
||||
|
||||
pathRoutingParser:
|
||||
class: Rector\Core\PhpParser\Parser\RectorPathRoutingParser
|
||||
class: PHPStan\Parser\PathRoutingParser
|
||||
arguments:
|
||||
currentPhpVersionRichParser: @cachedRectorParser
|
||||
currentPhpVersionSimpleParser: @cachedRectorSimpleParser
|
||||
currentPhpVersionSimpleParser: @cachedRectorParser
|
||||
php8Parser: @php8Parser
|
||||
autowired: no
|
||||
autowired: false
|
||||
|
||||
rectorParser:
|
||||
class: PHPStan\Parser\RichParser
|
||||
|
|
|
@ -72,6 +72,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
__DIR__ . '/../src/PhpParser/ValueObject',
|
||||
__DIR__ . '/../src/functions',
|
||||
__DIR__ . '/../src/constants.php',
|
||||
|
||||
]);
|
||||
|
||||
$services->alias(Application::class, ConsoleApplication::class);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"require": {
|
||||
"php": "7.4.*",
|
||||
"api-platform/core": "^2.6",
|
||||
"symfony/framework-bundle": "^5.4",
|
||||
"symfony/http-client": "^5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"rector/rector": "dev-main"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\DowngradePhp72\Rector\FuncCall\DowngradeJsonDecodeNullAssociativeArgRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use Rector\Core\Configuration\Option;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
|
||||
$parameters->set(Option::PARALLEL, true);
|
||||
$parameters->set(Option::PATHS, [
|
||||
__DIR__ . '/tests',
|
||||
]);
|
||||
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(DowngradeJsonDecodeNullAssociativeArgRector::class);
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase;
|
||||
use ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Client;
|
||||
|
||||
class ApiTest extends ApiTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
static::createClient();
|
||||
}
|
||||
}
|
|
@ -603,6 +603,3 @@ parameters:
|
|||
-
|
||||
message: '#Array with keys is not allowed\. Use value object to pass data instead#'
|
||||
path: packages/PhpAttribute/Printer/PhpAttributeGroupFactory.php
|
||||
|
||||
# on purpose to fix tests + old PHP version parsing code and keep compatbility
|
||||
- '#Extending PHPStan\\Parser\\PathRoutingParser is not covered by backward compatibility promise\. The class might change in a minor PHPStan version#'
|
||||
|
|
|
@ -10,7 +10,6 @@ use Rector\CodingStyle\ValueObject\ReturnArrayClassMethodToYield;
|
|||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Nette\Set\NetteSetList;
|
||||
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
|
||||
use Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector;
|
||||
use Rector\Php81\Rector\Class_\MyCLabsClassToEnumRector;
|
||||
use Rector\Php81\Rector\Class_\SpatieEnumClassToEnumRector;
|
||||
use Rector\PHPUnit\Set\PHPUnitSetList;
|
||||
|
@ -74,11 +73,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
MyCLabsClassToEnumRector::class,
|
||||
SpatieEnumClassToEnumRector::class,
|
||||
|
||||
// on purpose with private property parent - @todo fix later
|
||||
ClassPropertyAssignToConstructorPromotionRector::class => [
|
||||
__DIR__ . '/src/PhpParser/Parser/RectorPathRoutingParser.php',
|
||||
],
|
||||
|
||||
// test paths
|
||||
'*/tests/**/Fixture/*',
|
||||
'*/rules-tests/**/Fixture/*',
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\FuncCall\DowngradeJsonDecodeNullAssociativeArgRector\Fixture;
|
||||
|
||||
final class ArrayAssoc
|
||||
{
|
||||
public function run($data, array $items)
|
||||
{
|
||||
$data = \json_decode($data, $items[0]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\FuncCall\DowngradeJsonDecodeNullAssociativeArgRector\Fixture;
|
||||
|
||||
final class ArrayAssoc
|
||||
{
|
||||
public function run($data, array $items)
|
||||
{
|
||||
$data = \json_decode($data, $items[0] === null ?: $items[0]);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\FuncCall\DowngradeJsonDecodeNullAssociativeArgRector\Fixture;
|
||||
|
||||
/**
|
||||
* @see \Clue\React\NDJson\Decoder
|
||||
*/
|
||||
final class ClueJsonDecode
|
||||
{
|
||||
private $assoc;
|
||||
private $depth;
|
||||
private $options;
|
||||
|
||||
public function run($data)
|
||||
{
|
||||
if ($this->options === 0) {
|
||||
$data = \json_decode($data, $this->assoc, $this->depth);
|
||||
} else {
|
||||
$data = \json_decode($data, $this->assoc, $this->depth, $this->options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\FuncCall\DowngradeJsonDecodeNullAssociativeArgRector\Fixture;
|
||||
|
||||
/**
|
||||
* @see \Clue\React\NDJson\Decoder
|
||||
*/
|
||||
final class ClueJsonDecode
|
||||
{
|
||||
private $assoc;
|
||||
private $depth;
|
||||
private $options;
|
||||
|
||||
public function run($data)
|
||||
{
|
||||
if ($this->options === 0) {
|
||||
$data = \json_decode($data, $this->assoc === null ?: $this->assoc, $this->depth);
|
||||
} else {
|
||||
$data = \json_decode($data, $this->assoc === null ?: $this->assoc, $this->depth, $this->options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -24,10 +24,7 @@ class PossiblyNull
|
|||
{
|
||||
function run(string $json, ?bool $associative)
|
||||
{
|
||||
if ($associative === null) {
|
||||
$associative = true;
|
||||
}
|
||||
$value = json_decode($json, $associative);
|
||||
$value = json_decode($json, $associative === null ?: $associative);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\FuncCall\DowngradeJsonDecodeNullAssociativeArgRector\Fixture;
|
||||
|
||||
final class SkipAlreadyDowngraded
|
||||
{
|
||||
public function run(string $json, $associative)
|
||||
{
|
||||
$value = json_decode($json, $associative === null ?: $associative);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\DowngradePhp72\Rector\FuncCall\DowngradeJsonDecodeNullAssociativeArgRector\Fixture;
|
||||
|
||||
final class SkipClueJsonDecodeFilledInConstructor
|
||||
{
|
||||
private $assoc;
|
||||
private $depth;
|
||||
|
||||
/**
|
||||
* @param bool $assoc
|
||||
*/
|
||||
public function __construct($assoc = false)
|
||||
{
|
||||
$this->assoc = $assoc;
|
||||
}
|
||||
|
||||
public function run($data)
|
||||
{
|
||||
$data = \json_decode($data, $this->assoc, $this->depth);
|
||||
}
|
||||
}
|
|
@ -5,31 +5,25 @@ declare(strict_types=1);
|
|||
namespace Rector\DowngradePhp72\Rector\FuncCall;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\BinaryOp\Identical;
|
||||
use PhpParser\Node\Expr\Cast\Bool_;
|
||||
use PhpParser\Node\Expr\ConstFetch;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\StaticPropertyFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Expr\Ternary;
|
||||
use PHPStan\Type\BooleanType;
|
||||
use Rector\Core\NodeAnalyzer\ArgsAnalyzer;
|
||||
use Rector\Core\NodeManipulator\IfManipulator;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
/**
|
||||
* @see \Rector\Tests\DowngradePhp72\Rector\FuncCall\DowngradeJsonDecodeNullAssociativeArgRector\DowngradeJsonDecodeNullAssociativeArgRectorTest
|
||||
*
|
||||
* @changelog https://3v4l.org/b1mA6
|
||||
*/
|
||||
final class DowngradeJsonDecodeNullAssociativeArgRector extends AbstractRector
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ArgsAnalyzer $argsAnalyzer,
|
||||
private readonly IfManipulator $ifManipulator
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -38,8 +32,6 @@ final class DowngradeJsonDecodeNullAssociativeArgRector extends AbstractRector
|
|||
return new RuleDefinition('Downgrade json_decode() with null associative argument function', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
declare(strict_types=1);
|
||||
|
||||
function exactlyNull(string $json)
|
||||
{
|
||||
$value = json_decode($json, null);
|
||||
|
@ -53,8 +45,6 @@ CODE_SAMPLE
|
|||
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
declare(strict_types=1);
|
||||
|
||||
function exactlyNull(string $json)
|
||||
{
|
||||
$value = json_decode($json, true);
|
||||
|
@ -62,10 +52,7 @@ function exactlyNull(string $json)
|
|||
|
||||
function possiblyNull(string $json, ?bool $associative)
|
||||
{
|
||||
if ($associative === null) {
|
||||
$associative = true;
|
||||
}
|
||||
$value = json_decode($json, $associative);
|
||||
$value = json_decode($json, $associative === null ?: $associative);
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
|
@ -100,35 +87,25 @@ CODE_SAMPLE
|
|||
|
||||
$associativeValue = $args[1]->value;
|
||||
|
||||
if ($associativeValue instanceof Bool_) {
|
||||
// already converted
|
||||
if ($associativeValue instanceof Ternary && $associativeValue->if === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$type = $this->nodeTypeResolver->getType($associativeValue);
|
||||
|
||||
if ($type instanceof BooleanType) {
|
||||
$associativeValueType = $this->nodeTypeResolver->getType($associativeValue);
|
||||
if ($associativeValueType instanceof BooleanType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($associativeValue instanceof ConstFetch && $this->valueResolver->isNull($associativeValue)) {
|
||||
$node->args[1]->value = $this->nodeFactory->createTrue();
|
||||
$args[1]->value = $this->nodeFactory->createTrue();
|
||||
return $node;
|
||||
}
|
||||
|
||||
if (! in_array(
|
||||
$associativeValue::class,
|
||||
[Variable::class, PropertyFetch::class, StaticPropertyFetch::class],
|
||||
true
|
||||
)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$currentExpression = $node->getAttribute(AttributeKey::CURRENT_STATEMENT);
|
||||
$if = $this->ifManipulator->createIfExpr(
|
||||
new Identical($associativeValue, $this->nodeFactory->createNull()),
|
||||
new Expression(new Assign($associativeValue, $this->nodeFactory->createTrue()))
|
||||
);
|
||||
$this->nodesToAddCollector->addNodeBeforeNode($if, $currentExpression);
|
||||
// add conditional ternary
|
||||
$nullIdentical = new Identical($associativeValue, $this->nodeFactory->createNull());
|
||||
$ternary = new Ternary($nullIdentical, null, $associativeValue);
|
||||
$args[1]->value = $ternary;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\PhpParser\Parser;
|
||||
|
||||
use PhpParser\Node\Stmt;
|
||||
use PHPStan\File\FileHelper;
|
||||
use PHPStan\Parser\Parser;
|
||||
use PHPStan\Parser\PathRoutingParser;
|
||||
|
||||
/**
|
||||
* @api Used in PHPStan internals for parsing nodes:
|
||||
* 1) with types for tests:
|
||||
* 2) removing unsupported PHP-version code on real run
|
||||
*/
|
||||
final class RectorPathRoutingParser extends PathRoutingParser
|
||||
{
|
||||
private readonly Parser $currentPhpVersionRichParser;
|
||||
|
||||
public function __construct(
|
||||
FileHelper $fileHelper,
|
||||
Parser $currentPhpVersionRichParser,
|
||||
Parser $currentPhpVersionSimpleParser,
|
||||
Parser $php8Parser
|
||||
) {
|
||||
$this->currentPhpVersionRichParser = $currentPhpVersionRichParser;
|
||||
parent::__construct($fileHelper, $currentPhpVersionRichParser, $currentPhpVersionSimpleParser, $php8Parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Stmt[]
|
||||
*/
|
||||
public function parseFile(string $file): array
|
||||
{
|
||||
// for tests, always parse nodes with directly rich parser to be aware of types
|
||||
if (defined('PHPUNIT_COMPOSER_INSTALL')) {
|
||||
return $this->currentPhpVersionRichParser->parseFile($file);
|
||||
}
|
||||
|
||||
return parent::parseFile($file);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\DowngradePhp72\Rector\FuncCall\DowngradeJsonDecodeNullAssociativeArgRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(DowngradeJsonDecodeNullAssociativeArgRector::class);
|
||||
};
|
Loading…
Reference in New Issue