Skip to content

Commit 1249a20

Browse files
committed
Report old PHP-Parser v4 class names in PHPStan-related code
1 parent f19573c commit 1249a20

File tree

4 files changed

+141
-0
lines changed

4 files changed

+141
-0
lines changed

conf/config.level0.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ rules:
1818
- PHPStan\Rules\Api\ApiTraitUseRule
1919
- PHPStan\Rules\Api\GetTemplateTypeRule
2020
- PHPStan\Rules\Api\NodeConnectingVisitorAttributesRule
21+
- PHPStan\Rules\Api\OldPhpParser4ClassRule
2122
- PHPStan\Rules\Api\PhpStanNamespaceIn3rdPartyPackageRule
2223
- PHPStan\Rules\Api\RuntimeReflectionInstantiationRule
2324
- PHPStan\Rules\Api\RuntimeReflectionFunctionRule
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Api;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Name;
7+
use PHPStan\Analyser\Scope;
8+
use PHPStan\Rules\Rule;
9+
use PHPStan\Rules\RuleErrorBuilder;
10+
use function array_change_key_case;
11+
use function array_key_exists;
12+
use function array_keys;
13+
use function sprintf;
14+
use function str_starts_with;
15+
16+
/**
17+
* @implements Rule<Name>
18+
*/
19+
final class OldPhpParser4ClassRule implements Rule
20+
{
21+
22+
private const NAME_MAPPING = [
23+
// from https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md#renamed-nodes
24+
'PhpParser\Node\Scalar\LNumber' => Node\Scalar\Int_::class,
25+
'PhpParser\Node\Scalar\DNumber' => Node\Scalar\Float_::class,
26+
'PhpParser\Node\Scalar\Encapsed' => Node\Scalar\InterpolatedString::class,
27+
'PhpParser\Node\Scalar\EncapsedStringPart' => Node\InterpolatedStringPart::class,
28+
'PhpParser\Node\Expr\ArrayItem' => Node\ArrayItem::class,
29+
'PhpParser\Node\Expr\ClosureUse' => Node\ClosureUse::class,
30+
'PhpParser\Node\Stmt\DeclareDeclare' => Node\DeclareItem::class,
31+
'PhpParser\Node\Stmt\PropertyProperty' => Node\PropertyItem::class,
32+
'PhpParser\Node\Stmt\StaticVar' => Node\StaticVar::class,
33+
'PhpParser\Node\Stmt\UseUse' => Node\UseItem::class,
34+
];
35+
36+
public function getNodeType(): string
37+
{
38+
return Name::class;
39+
}
40+
41+
public function processNode(Node $node, Scope $scope): array
42+
{
43+
$nameMapping = array_change_key_case(self::NAME_MAPPING);
44+
$lowerName = $node->toLowerString();
45+
if (!array_key_exists($lowerName, $nameMapping)) {
46+
return [];
47+
}
48+
49+
$newName = $nameMapping[$lowerName];
50+
51+
if (!$scope->isInClass()) {
52+
return [];
53+
}
54+
55+
$classReflection = $scope->getClassReflection();
56+
$hasPhpStanInterface = false;
57+
foreach (array_keys($classReflection->getInterfaces()) as $interfaceName) {
58+
if (!str_starts_with($interfaceName, 'PHPStan\\')) {
59+
continue;
60+
}
61+
62+
$hasPhpStanInterface = true;
63+
}
64+
65+
if (!$hasPhpStanInterface) {
66+
return [];
67+
}
68+
69+
return [
70+
RuleErrorBuilder::message(sprintf(
71+
'Class %s not found. It has been renamed to %s in PHP-Parser v5.',
72+
$node->toString(),
73+
$newName,
74+
))->identifier('phpParser.classRenamed')
75+
->build(),
76+
];
77+
}
78+
79+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\Api;
4+
5+
use PHPStan\Rules\Rule;
6+
use PHPStan\Testing\RuleTestCase;
7+
8+
/**
9+
* @extends RuleTestCase<OldPhpParser4ClassRule>
10+
*/
11+
class OldPhpParser4ClassRuleTest extends RuleTestCase
12+
{
13+
14+
protected function getRule(): Rule
15+
{
16+
return new OldPhpParser4ClassRule();
17+
}
18+
19+
public function testRule(): void
20+
{
21+
$this->analyse([__DIR__ . '/data/old-php-parser-4-class.php'], [
22+
[
23+
'Class PhpParser\Node\Expr\ArrayItem not found. It has been renamed to PhpParser\Node\ArrayItem in PHP-Parser v5.',
24+
24,
25+
],
26+
]);
27+
}
28+
29+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace OldPhpParser4Class;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Rules\Rule;
8+
9+
class Foo
10+
{
11+
12+
public function doFoo(): void
13+
{
14+
echo \PhpParser\Node\Expr\ArrayItem::class;
15+
}
16+
17+
}
18+
19+
class FooRule implements Rule
20+
{
21+
22+
public function getNodeType(): string
23+
{
24+
return \PhpParser\Node\Expr\ArrayItem::class;
25+
}
26+
27+
public function processNode(Node $node, Scope $scope): array
28+
{
29+
return [];
30+
}
31+
32+
}

0 commit comments

Comments
 (0)