Skip to content

Commit

Permalink
Report old PHP-Parser v4 class names in PHPStan-related code
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Oct 3, 2024
1 parent f19573c commit 1249a20
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 0 deletions.
1 change: 1 addition & 0 deletions conf/config.level0.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ rules:
- PHPStan\Rules\Api\ApiTraitUseRule
- PHPStan\Rules\Api\GetTemplateTypeRule
- PHPStan\Rules\Api\NodeConnectingVisitorAttributesRule
- PHPStan\Rules\Api\OldPhpParser4ClassRule
- PHPStan\Rules\Api\PhpStanNamespaceIn3rdPartyPackageRule
- PHPStan\Rules\Api\RuntimeReflectionInstantiationRule
- PHPStan\Rules\Api\RuntimeReflectionFunctionRule
Expand Down
79 changes: 79 additions & 0 deletions src/Rules/Api/OldPhpParser4ClassRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Api;

use PhpParser\Node;
use PhpParser\Node\Name;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use function array_change_key_case;
use function array_key_exists;
use function array_keys;
use function sprintf;
use function str_starts_with;

/**
* @implements Rule<Name>
*/
final class OldPhpParser4ClassRule implements Rule
{

private const NAME_MAPPING = [
// from https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md#renamed-nodes
'PhpParser\Node\Scalar\LNumber' => Node\Scalar\Int_::class,
'PhpParser\Node\Scalar\DNumber' => Node\Scalar\Float_::class,
'PhpParser\Node\Scalar\Encapsed' => Node\Scalar\InterpolatedString::class,
'PhpParser\Node\Scalar\EncapsedStringPart' => Node\InterpolatedStringPart::class,
'PhpParser\Node\Expr\ArrayItem' => Node\ArrayItem::class,
'PhpParser\Node\Expr\ClosureUse' => Node\ClosureUse::class,
'PhpParser\Node\Stmt\DeclareDeclare' => Node\DeclareItem::class,
'PhpParser\Node\Stmt\PropertyProperty' => Node\PropertyItem::class,
'PhpParser\Node\Stmt\StaticVar' => Node\StaticVar::class,
'PhpParser\Node\Stmt\UseUse' => Node\UseItem::class,
];

public function getNodeType(): string
{
return Name::class;
}

public function processNode(Node $node, Scope $scope): array
{
$nameMapping = array_change_key_case(self::NAME_MAPPING);
$lowerName = $node->toLowerString();
if (!array_key_exists($lowerName, $nameMapping)) {
return [];
}

$newName = $nameMapping[$lowerName];

if (!$scope->isInClass()) {
return [];
}

$classReflection = $scope->getClassReflection();
$hasPhpStanInterface = false;
foreach (array_keys($classReflection->getInterfaces()) as $interfaceName) {
if (!str_starts_with($interfaceName, 'PHPStan\\')) {
continue;
}

$hasPhpStanInterface = true;
}

if (!$hasPhpStanInterface) {
return [];
}

return [
RuleErrorBuilder::message(sprintf(
'Class %s not found. It has been renamed to %s in PHP-Parser v5.',
$node->toString(),
$newName,
))->identifier('phpParser.classRenamed')
->build(),
];
}

}
29 changes: 29 additions & 0 deletions tests/PHPStan/Rules/Api/OldPhpParser4ClassRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Api;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

/**
* @extends RuleTestCase<OldPhpParser4ClassRule>
*/
class OldPhpParser4ClassRuleTest extends RuleTestCase
{

protected function getRule(): Rule
{
return new OldPhpParser4ClassRule();
}

public function testRule(): void
{
$this->analyse([__DIR__ . '/data/old-php-parser-4-class.php'], [
[
'Class PhpParser\Node\Expr\ArrayItem not found. It has been renamed to PhpParser\Node\ArrayItem in PHP-Parser v5.',
24,
],
]);
}

}
32 changes: 32 additions & 0 deletions tests/PHPStan/Rules/Api/data/old-php-parser-4-class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace OldPhpParser4Class;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;

class Foo
{

public function doFoo(): void
{
echo \PhpParser\Node\Expr\ArrayItem::class;
}

}

class FooRule implements Rule
{

public function getNodeType(): string
{
return \PhpParser\Node\Expr\ArrayItem::class;
}

public function processNode(Node $node, Scope $scope): array
{
return [];
}

}

0 comments on commit 1249a20

Please sign in to comment.