Skip to content

Commit 9f4e6b9

Browse files
staabmphpstan-bot
authored andcommitted
Resolve property, trait constant, arrow function never and attribute PHP version checks from Scope::getPhpVersion()
* Added `supportsConstantsInTraits()`, `supportsNeverReturnTypeInArrowFunction()`, `supportsArrayUnpackingWithStringKeys()`, `supportsPropertyHooks()`, `supportsFinalProperties()`, `supportsAsymmetricVisibilityForStaticProperties()`, `supportsOverrideAttributeOnProperty()`, `supportsAttributesOnGlobalConstants()` and `supportsUnsetCast()` to `PhpVersions`. * `PropertyInClassRule` now resolves the final-property (8.4), static asymmetric-visibility (8.5) and property-hooks (8.4) checks from the scope. * `PropertiesInInterfaceRule`, `PropertyAttributesRule`, `ConstantsInTraitsRule`, `ExistingClassesInArrowFunctionTypehintsRule`, `ConstantAttributesRule`, `UnsetCastRule` and `ArrayUnpackingRule` do the same for their own version gates. * `UnsetCastRule` reports only when the scope version is definitely >= 8.0 (`supportsUnsetCast()->no()`), which is the inverse-direction member of the family. * Dropped the now-unused `PhpVersion` constructor dependencies from all of these rules. * Probed and deliberately left alone: `supportsPureIntersectionTypes()` in `FunctionDefinitionCheck`/`ExistingClassesInPropertiesRule` and the covariance/visibility/deprecation gates in `OverridingMethodRule`, `MethodParameterComparisonHelper`, `OverridingPropertyRule`, `DeprecatedCastRule` and `BacktickRule` - those pick between two valid behaviours rather than emitting a non-ignorable "supported only on PHP X" error. Closes phpstan/phpstan#13133
1 parent 76aa1af commit 9f4e6b9

26 files changed

Lines changed: 548 additions & 161 deletions

‎src/Php/PhpVersions.php‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,51 @@ public function supportsNativeTypesInClassConstants(): TrinaryLogic
5656
return IntegerRangeType::fromInterval(80300, null)->isSuperTypeOf($this->phpVersions)->result;
5757
}
5858

59+
public function supportsConstantsInTraits(): TrinaryLogic
60+
{
61+
return IntegerRangeType::fromInterval(80200, null)->isSuperTypeOf($this->phpVersions)->result;
62+
}
63+
64+
public function supportsNeverReturnTypeInArrowFunction(): TrinaryLogic
65+
{
66+
return IntegerRangeType::fromInterval(80200, null)->isSuperTypeOf($this->phpVersions)->result;
67+
}
68+
69+
public function supportsArrayUnpackingWithStringKeys(): TrinaryLogic
70+
{
71+
return IntegerRangeType::fromInterval(80100, null)->isSuperTypeOf($this->phpVersions)->result;
72+
}
73+
74+
public function supportsPropertyHooks(): TrinaryLogic
75+
{
76+
return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result;
77+
}
78+
79+
public function supportsFinalProperties(): TrinaryLogic
80+
{
81+
return IntegerRangeType::fromInterval(80400, null)->isSuperTypeOf($this->phpVersions)->result;
82+
}
83+
84+
public function supportsAsymmetricVisibilityForStaticProperties(): TrinaryLogic
85+
{
86+
return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result;
87+
}
88+
89+
public function supportsOverrideAttributeOnProperty(): TrinaryLogic
90+
{
91+
return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result;
92+
}
93+
94+
public function supportsAttributesOnGlobalConstants(): TrinaryLogic
95+
{
96+
return IntegerRangeType::fromInterval(80500, null)->isSuperTypeOf($this->phpVersions)->result;
97+
}
98+
99+
public function supportsUnsetCast(): TrinaryLogic
100+
{
101+
return IntegerRangeType::fromInterval(null, 79999)->isSuperTypeOf($this->phpVersions)->result;
102+
}
103+
59104
public function supportsTrueAndFalseStandaloneType(): TrinaryLogic
60105
{
61106
return IntegerRangeType::fromInterval(80200, null)->isSuperTypeOf($this->phpVersions)->result;

‎src/Rules/Arrays/ArrayUnpackingRule.php‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use PHPStan\Analyser\Scope;
88
use PHPStan\DependencyInjection\RegisteredRule;
99
use PHPStan\Node\Expr\NativeTypeExpr;
10-
use PHPStan\Php\PhpVersion;
1110
use PHPStan\Rules\Rule;
1211
use PHPStan\Rules\RuleErrorBuilder;
1312
use PHPStan\Rules\RuleLevelHelper;
@@ -23,7 +22,7 @@
2322
final class ArrayUnpackingRule implements Rule
2423
{
2524

26-
public function __construct(private PhpVersion $phpVersion, private RuleLevelHelper $ruleLevelHelper)
25+
public function __construct(private RuleLevelHelper $ruleLevelHelper)
2726
{
2827
}
2928

@@ -34,7 +33,7 @@ public function getNodeType(): string
3433

3534
public function processNode(Node $node, Scope $scope): array
3635
{
37-
if ($node->unpack === false || $this->phpVersion->supportsArrayUnpackingWithStringKeys()) {
36+
if ($node->unpack === false || $scope->getPhpVersion()->supportsArrayUnpackingWithStringKeys()->yes()) {
3837
return [];
3938
}
4039

‎src/Rules/Cast/UnsetCastRule.php‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\RegisteredRule;
8-
use PHPStan\Php\PhpVersion;
98
use PHPStan\Rules\Rule;
109
use PHPStan\Rules\RuleErrorBuilder;
1110

@@ -16,18 +15,14 @@
1615
final class UnsetCastRule implements Rule
1716
{
1817

19-
public function __construct(private PhpVersion $phpVersion)
20-
{
21-
}
22-
2318
public function getNodeType(): string
2419
{
2520
return Node\Expr\Cast\Unset_::class;
2621
}
2722

2823
public function processNode(Node $node, Scope $scope): array
2924
{
30-
if ($this->phpVersion->supportsUnsetCast()) {
25+
if (!$scope->getPhpVersion()->supportsUnsetCast()->no()) {
3126
return [];
3227
}
3328

‎src/Rules/Constants/ConstantAttributesRule.php‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use PHPStan\Analyser\NodeCallbackInvoker;
99
use PHPStan\Analyser\Scope;
1010
use PHPStan\DependencyInjection\RegisteredRule;
11-
use PHPStan\Php\PhpVersion;
1211
use PHPStan\Rules\AttributesCheck;
1312
use PHPStan\Rules\Rule;
1413
use PHPStan\Rules\RuleErrorBuilder;
@@ -23,7 +22,6 @@ final class ConstantAttributesRule implements Rule
2322

2423
public function __construct(
2524
private AttributesCheck $attributesCheck,
26-
private PhpVersion $phpVersion,
2725
)
2826
{
2927
}
@@ -39,7 +37,7 @@ public function processNode(Node $node, Scope&NodeCallbackInvoker&CollectedDataE
3937
return [];
4038
}
4139

42-
if (!$this->phpVersion->supportsAttributesOnGlobalConstants()) {
40+
if (!$scope->getPhpVersion()->supportsAttributesOnGlobalConstants()->yes()) {
4341
return [
4442
RuleErrorBuilder::message('Attributes on global constants are supported only on PHP 8.5 and later.')
4543
->identifier('constant.attributesNotSupported')

‎src/Rules/Functions/ExistingClassesInArrowFunctionTypehintsRule.php‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\RegisteredRule;
8-
use PHPStan\Php\PhpVersion;
98
use PHPStan\Rules\FunctionDefinitionCheck;
109
use PHPStan\Rules\Rule;
1110
use PHPStan\Rules\RuleErrorBuilder;
@@ -20,7 +19,7 @@
2019
final class ExistingClassesInArrowFunctionTypehintsRule implements Rule
2120
{
2221

23-
public function __construct(private FunctionDefinitionCheck $check, private PhpVersion $phpVersion)
22+
public function __construct(private FunctionDefinitionCheck $check)
2423
{
2524
}
2625

@@ -32,7 +31,7 @@ public function getNodeType(): string
3231
public function processNode(Node $node, Scope $scope): array
3332
{
3433
$messages = [];
35-
if ($node->returnType !== null && !$this->phpVersion->supportsNeverReturnTypeInArrowFunction()) {
34+
if ($node->returnType !== null && !$scope->getPhpVersion()->supportsNeverReturnTypeInArrowFunction()->yes()) {
3635
$returnType = ParserNodeTypeToPHPStanType::resolve($node->returnType, $scope->isInClass() ? $scope->getClassReflection() : null);
3736
if ($returnType instanceof NonAcceptingNeverType) {
3837
$messages[] = RuleErrorBuilder::message('Never return type in arrow function is supported only on PHP 8.2 and later.')

‎src/Rules/Properties/PropertiesInInterfaceRule.php‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\RegisteredRule;
88
use PHPStan\Node\ClassPropertyNode;
9-
use PHPStan\Php\PhpVersion;
109
use PHPStan\Rules\Rule;
1110
use PHPStan\Rules\RuleErrorBuilder;
1211

@@ -17,10 +16,6 @@
1716
final class PropertiesInInterfaceRule implements Rule
1817
{
1918

20-
public function __construct(private PhpVersion $phpVersion)
21-
{
22-
}
23-
2419
public function getNodeType(): string
2520
{
2621
return ClassPropertyNode::class;
@@ -32,7 +27,7 @@ public function processNode(Node $node, Scope $scope): array
3227
return [];
3328
}
3429

35-
if (!$this->phpVersion->supportsPropertyHooks()) {
30+
if (!$scope->getPhpVersion()->supportsPropertyHooks()->yes()) {
3631
return [
3732
RuleErrorBuilder::message('Interfaces can include properties only on PHP 8.4 and later.')
3833
->nonIgnorable()

‎src/Rules/Properties/PropertyAttributesRule.php‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use PHPStan\Analyser\Scope;
1010
use PHPStan\DependencyInjection\RegisteredRule;
1111
use PHPStan\Node\ClassPropertyNode;
12-
use PHPStan\Php\PhpVersion;
1312
use PHPStan\Rules\AttributesCheck;
1413
use PHPStan\Rules\Rule;
1514
use PHPStan\Rules\RuleErrorBuilder;
@@ -24,7 +23,6 @@ final class PropertyAttributesRule implements Rule
2423

2524
public function __construct(
2625
private AttributesCheck $attributesCheck,
27-
private PhpVersion $phpVersion,
2826
)
2927
{
3028
}
@@ -36,7 +34,7 @@ public function getNodeType(): string
3634

3735
public function processNode(Node $node, Scope&NodeCallbackInvoker&CollectedDataEmitter $scope): array
3836
{
39-
if (!$this->phpVersion->supportsOverrideAttributeOnProperty()) {
37+
if (!$scope->getPhpVersion()->supportsOverrideAttributeOnProperty()->yes()) {
4038
$propertyReflection = $node->getClassReflection()->getNativeProperty($node->getName());
4139
if (count($propertyReflection->getNativeReflection()->getAttributes('Override')) > 0) {
4240
return [

‎src/Rules/Properties/PropertyInClassRule.php‎

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\RegisteredRule;
88
use PHPStan\Node\ClassPropertyNode;
9-
use PHPStan\Php\PhpVersion;
109
use PHPStan\Rules\Rule;
1110
use PHPStan\Rules\RuleErrorBuilder;
1211

@@ -17,10 +16,6 @@
1716
final class PropertyInClassRule implements Rule
1817
{
1918

20-
public function __construct(private PhpVersion $phpVersion)
21-
{
22-
}
23-
2419
public function getNodeType(): string
2520
{
2621
return ClassPropertyNode::class;
@@ -36,7 +31,7 @@ public function processNode(Node $node, Scope $scope): array
3631

3732
if (
3833
$node->isFinal()
39-
&& !$this->phpVersion->supportsFinalProperties()
34+
&& !$scope->getPhpVersion()->supportsFinalProperties()->yes()
4035
) {
4136
return [
4237
RuleErrorBuilder::message('Final properties are supported only on PHP 8.4 and later.')
@@ -164,7 +159,7 @@ public function processNode(Node $node, Scope $scope): array
164159
];
165160
}
166161
if (
167-
!$this->phpVersion->supportsAsymmetricVisibilityForStaticProperties()
162+
!$scope->getPhpVersion()->supportsAsymmetricVisibilityForStaticProperties()->yes()
168163
&& (
169164
$node->isPrivateSet()
170165
|| $node->isProtectedSet()
@@ -191,7 +186,7 @@ public function processNode(Node $node, Scope $scope): array
191186
}
192187
}
193188

194-
if (!$this->phpVersion->supportsPropertyHooks()) {
189+
if (!$scope->getPhpVersion()->supportsPropertyHooks()->yes()) {
195190
if ($node->hasHooks()) {
196191
return [
197192
RuleErrorBuilder::message('Property hooks are supported only on PHP 8.4 and later.')

‎src/Rules/Traits/ConstantsInTraitsRule.php‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use PhpParser\Node;
66
use PHPStan\Analyser\Scope;
77
use PHPStan\DependencyInjection\RegisteredRule;
8-
use PHPStan\Php\PhpVersion;
98
use PHPStan\Rules\Rule;
109
use PHPStan\Rules\RuleErrorBuilder;
1110

@@ -16,10 +15,6 @@
1615
final class ConstantsInTraitsRule implements Rule
1716
{
1817

19-
public function __construct(private PhpVersion $phpVersion)
20-
{
21-
}
22-
2318
public function getNodeType(): string
2419
{
2520
return Node\Stmt\ClassConst::class;
@@ -30,7 +25,7 @@ public function getNodeType(): string
3025
*/
3126
public function processNode(Node $node, Scope $scope): array
3227
{
33-
if ($this->phpVersion->supportsConstantsInTraits()) {
28+
if ($scope->getPhpVersion()->supportsConstantsInTraits()->yes()) {
3429
return [];
3530
}
3631

‎tests/PHPStan/Rules/Arrays/ArrayUnpackingRuleTest.php‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
namespace PHPStan\Rules\Arrays;
44

5-
use PHPStan\Php\PhpVersion;
65
use PHPStan\Rules\Rule;
76
use PHPStan\Rules\RuleLevelHelper;
87
use PHPStan\Testing\RuleTestCase;
98
use PHPUnit\Framework\Attributes\DataProvider;
109
use PHPUnit\Framework\Attributes\RequiresPhp;
10+
use const PHP_VERSION_ID;
1111

1212
/**
1313
* @extends RuleTestCase<ArrayUnpackingRule>
@@ -22,7 +22,6 @@ class ArrayUnpackingRuleTest extends RuleTestCase
2222
protected function getRule(): Rule
2323
{
2424
return new ArrayUnpackingRule(
25-
self::getContainer()->getByType(PhpVersion::class),
2625
new RuleLevelHelper(
2726
self::createReflectionProvider(),
2827
checkNullables: true,
@@ -117,6 +116,26 @@ public function testRuleDoNotCheckUnions(): void
117116
]);
118117
}
119118

119+
public function testPhpVersionNarrowedScope(): void
120+
{
121+
$this->checkUnions = true;
122+
123+
$errors = [
124+
[
125+
'Array unpacking cannot be used on an array with string keys: array<string, string>',
126+
11,
127+
],
128+
];
129+
if (PHP_VERSION_ID < 80100) {
130+
$errors[] = [
131+
'Array unpacking cannot be used on an array with string keys: array<string, string>',
132+
18,
133+
];
134+
}
135+
136+
$this->analyse([__DIR__ . '/data/array-unpacking-php-versions.php'], $errors);
137+
}
138+
120139
public static function dataRuleOnPHP81(): array
121140
{
122141
return [

0 commit comments

Comments
 (0)