Skip to content

Commit

Permalink
CleaningParser - remove conditional code by PHP_VERSION_ID
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jan 18, 2022
1 parent 97a181e commit 433511a
Show file tree
Hide file tree
Showing 9 changed files with 196 additions and 6 deletions.
4 changes: 3 additions & 1 deletion src/Parser/CleaningParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@

use PhpParser\Node\Stmt;
use PhpParser\NodeTraverser;
use PHPStan\Php\PhpVersion;

class CleaningParser implements Parser
{

private NodeTraverser $traverser;

public function __construct(private Parser $wrappedParser)
public function __construct(private Parser $wrappedParser, PhpVersion $phpVersion)
{
$this->traverser = new NodeTraverser();
$this->traverser->addVisitor(new CleaningVisitor());
$this->traverser->addVisitor(new RemoveUnusedCodeByPhpVersionIdVisitor($phpVersion->getVersionString()));
}

public function parseFile(string $file): array
Expand Down
106 changes: 106 additions & 0 deletions src/Parser/RemoveUnusedCodeByPhpVersionIdVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php declare(strict_types = 1);

namespace PHPStan\Parser;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use PHPStan\Php\PhpVersion;
use function count;
use function version_compare;

class RemoveUnusedCodeByPhpVersionIdVisitor extends NodeVisitorAbstract
{

public function __construct(private string $phpVersionString)
{
}

public function enterNode(Node $node): Node|int|null
{
if ($node instanceof Node\Stmt\ClassLike) {
return null;
}
if ($node instanceof Node\FunctionLike) {
return null;
}

if (!$node instanceof Node\Stmt\If_) {
return null;
}

if (count($node->elseifs) > 0) {
return null;
}

if ($node->else === null) {
return null;
}

$cond = $node->cond;
if (
!$cond instanceof Node\Expr\BinaryOp\Smaller
&& !$cond instanceof Node\Expr\BinaryOp\SmallerOrEqual
&& !$cond instanceof Node\Expr\BinaryOp\Greater
&& !$cond instanceof Node\Expr\BinaryOp\GreaterOrEqual
&& !$cond instanceof Node\Expr\BinaryOp\Equal
&& !$cond instanceof Node\Expr\BinaryOp\NotEqual
&& !$cond instanceof Node\Expr\BinaryOp\Identical
&& !$cond instanceof Node\Expr\BinaryOp\NotIdentical
) {
return null;
}

$operator = $cond->getOperatorSigil();
if ($operator === '===') {
$operator = '==';
}
if ($operator === '!==') {
$operator = '!=';
}

$operands = $this->getOperands($cond->left, $cond->right);
if ($operands === null) {
return null;
}

$result = version_compare($operands[0], $operands[1], $operator);
if ($result) {
// remove else
$node->cond = new Node\Expr\ConstFetch(new Node\Name('true'));
$node->else = null;

return $node;
}

// remove if
$node->cond = new Node\Expr\ConstFetch(new Node\Name('false'));
$node->stmts = [];

return $node;
}

/**
* @return array{string, string}|null
*/
private function getOperands(Node\Expr $left, Node\Expr $right): ?array
{
if (
$left instanceof Node\Scalar\LNumber
&& $right instanceof Node\Expr\ConstFetch
&& $right->name->toString() === 'PHP_VERSION_ID'
) {
return [(new PhpVersion($left->value))->getVersionString(), $this->phpVersionString];
}

if (
$right instanceof Node\Scalar\LNumber
&& $left instanceof Node\Expr\ConstFetch
&& $left->name->toString() === 'PHP_VERSION_ID'
) {
return [$this->phpVersionString, (new PhpVersion($right->value))->getVersionString()];
}

return null;
}

}
10 changes: 6 additions & 4 deletions src/Type/FileTypeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PHPStan\Broker\AnonymousClassNameHelper;
use PHPStan\Cache\Cache;
use PHPStan\Parser\Parser;
use PHPStan\Php\PhpVersion;
use PHPStan\PhpDoc\PhpDocNodeResolver;
use PHPStan\PhpDoc\PhpDocStringResolver;
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
Expand Down Expand Up @@ -72,6 +73,7 @@ public function __construct(
private PhpDocNodeResolver $phpDocNodeResolver,
private Cache $cache,
private AnonymousClassNameHelper $anonymousClassNameHelper,
private PhpVersion $phpVersion,
)
{
}
Expand Down Expand Up @@ -187,8 +189,8 @@ private function resolvePhpDocStringToDocNode(string $phpDocString): PhpDocNode
private function getNameScopeMap(string $fileName): array
{
if (!isset($this->memoryCache[$fileName])) {
$cacheKey = sprintf('%s-phpdocstring-v17-require-once', $fileName);
$variableCacheKey = implode(',', array_map(static fn (array $file): string => sprintf('%s-%d', $file['filename'], $file['modifiedTime']), $this->getCachedDependentFilesWithTimestamps($fileName)));
$cacheKey = sprintf('%s-phpdocstring', $fileName);
$variableCacheKey = sprintf('%s-%s-v17-require-once', implode(',', array_map(static fn (array $file): string => sprintf('%s-%d', $file['filename'], $file['modifiedTime']), $this->getCachedDependentFilesWithTimestamps($fileName))), $this->phpVersion->getVersionString());
$map = $this->cache->load($cacheKey, $variableCacheKey);

if ($map === null) {
Expand Down Expand Up @@ -631,12 +633,12 @@ private function getNameScopeKey(
*/
private function getCachedDependentFilesWithTimestamps(string $fileName): array
{
$cacheKey = sprintf('dependentFilesTimestamps-%s-v2-enum', $fileName);
$cacheKey = sprintf('dependentFilesTimestamps-%s', $fileName);
$fileModifiedTime = filemtime($fileName);
if ($fileModifiedTime === false) {
$fileModifiedTime = time();
}
$variableCacheKey = sprintf('%d', $fileModifiedTime);
$variableCacheKey = sprintf('%d-%s-v2-enum', $fileModifiedTime, $this->phpVersion->getVersionString());
/** @var array<array{filename: string, modifiedTime: int}>|null $cachedFilesTimestamps */
$cachedFilesTimestamps = $this->cache->load($cacheKey, $variableCacheKey);
if ($cachedFilesTimestamps !== null) {
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Broker/BrokerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ protected function setUp(): void
$setterReflectionProviderProvider,
$classReflectionExtensionRegistryProvider,
$this->createMock(FunctionReflectionFactory::class),
new FileTypeMapper($setterReflectionProviderProvider, $this->getParser(), $phpDocStringResolver, $phpDocNodeResolver, $this->createMock(Cache::class), $anonymousClassNameHelper),
new FileTypeMapper($setterReflectionProviderProvider, $this->getParser(), $phpDocStringResolver, $phpDocNodeResolver, $this->createMock(Cache::class), $anonymousClassNameHelper, self::getContainer()->getByType(PhpVersion::class)),
self::getContainer()->getByType(PhpDocInheritanceResolver::class),
self::getContainer()->getByType(PhpVersion::class),
self::getContainer()->getByType(NativeFunctionReflectionProvider::class),
Expand Down
35 changes: 35 additions & 0 deletions tests/PHPStan/Parser/CleaningParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
use PhpParser\Parser\Php7;
use PhpParser\PrettyPrinter\Standard;
use PHPStan\File\FileReader;
use PHPStan\Php\PhpVersion;
use PHPStan\Testing\PHPStanTestCase;
use const PHP_VERSION_ID;

class CleaningParserTest extends PHPStanTestCase
{
Expand All @@ -18,6 +20,37 @@ public function dataParse(): iterable
[
__DIR__ . '/data/cleaning-1-before.php',
__DIR__ . '/data/cleaning-1-after.php',
PHP_VERSION_ID,
],
[
__DIR__ . '/data/cleaning-php-version-before.php',
__DIR__ . '/data/cleaning-php-version-after-81.php',
80100,
],
[
__DIR__ . '/data/cleaning-php-version-before.php',
__DIR__ . '/data/cleaning-php-version-after-81.php',
80200,
],
[
__DIR__ . '/data/cleaning-php-version-before.php',
__DIR__ . '/data/cleaning-php-version-after-74.php',
70400,
],
[
__DIR__ . '/data/cleaning-php-version-before2.php',
__DIR__ . '/data/cleaning-php-version-after-81.php',
80100,
],
[
__DIR__ . '/data/cleaning-php-version-before2.php',
__DIR__ . '/data/cleaning-php-version-after-81.php',
80200,
],
[
__DIR__ . '/data/cleaning-php-version-before2.php',
__DIR__ . '/data/cleaning-php-version-after-74.php',
70400,
],
];
}
Expand All @@ -28,13 +61,15 @@ public function dataParse(): iterable
public function testParse(
string $beforeFile,
string $afterFile,
int $phpVersionId,
): void
{
$parser = new CleaningParser(
new SimpleParser(
new Php7(new Emulative()),
new NameResolver(),
),
new PhpVersion($phpVersionId),
);
$printer = new Standard();
$ast = $parser->parseFile($beforeFile);
Expand Down
10 changes: 10 additions & 0 deletions tests/PHPStan/Parser/data/cleaning-php-version-after-74.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
declare (strict_types=1);
namespace TestCleanPhpVersion;

use const PHP_VERSION_ID;
if (false) {
} else {
doBar1();
doBar2();
}
9 changes: 9 additions & 0 deletions tests/PHPStan/Parser/data/cleaning-php-version-after-81.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php
declare (strict_types=1);
namespace TestCleanPhpVersion;

use const PHP_VERSION_ID;
if (true) {
doFoo1();
doFoo2();
}
13 changes: 13 additions & 0 deletions tests/PHPStan/Parser/data/cleaning-php-version-before.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types = 1);

namespace TestCleanPhpVersion;

use const PHP_VERSION_ID;

if (PHP_VERSION_ID >= 80100) {
doFoo1();
doFoo2();
} else {
doBar1();
doBar2();
}
13 changes: 13 additions & 0 deletions tests/PHPStan/Parser/data/cleaning-php-version-before2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types = 1);

namespace TestCleanPhpVersion;

use const PHP_VERSION_ID;

if (80100 <= PHP_VERSION_ID) {
doFoo1();
doFoo2();
} else {
doBar1();
doBar2();
}

0 comments on commit 433511a

Please sign in to comment.