diff --git a/src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php b/src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php index a1eaf7e131..89e2d65bb7 100644 --- a/src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php +++ b/src/Rules/Arrays/DuplicateKeysInLiteralArraysRule.php @@ -13,6 +13,8 @@ use function array_keys; use function count; use function implode; +use function is_float; +use function key; use function max; use function sprintf; use function var_export; @@ -85,23 +87,24 @@ public function processNode(Node $node, Scope $scope): array } $value = $keyType->getValue(); + $index = is_float($value) ? (int) $value : key([$value => null]); $printedValue = $key !== null ? $this->exprPrinter->printExpr($key) : $value; - $printedValues[$value][] = $printedValue; + $printedValues[$index][] = $printedValue; - if (!isset($valueLines[$value])) { - $valueLines[$value] = $item->getStartLine(); + if (!isset($valueLines[$index])) { + $valueLines[$index] = $item->getStartLine(); } $previousCount = count($values); - $values[$value] = $printedValue; + $values[$index] = $printedValue; if ($previousCount !== count($values)) { continue; } - $duplicateKeys[$value] = true; + $duplicateKeys[$index] = true; } $messages = []; diff --git a/src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php b/src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php index 1242f85cfd..38bf4f47bb 100644 --- a/src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php +++ b/src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php @@ -4,6 +4,7 @@ use PhpParser\Node; use PHPStan\Analyser\Scope; +use PHPStan\Php\PhpVersion; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Rules\RuleLevelHelper; @@ -22,6 +23,7 @@ class InvalidKeyInArrayDimFetchRule implements Rule public function __construct( private RuleLevelHelper $ruleLevelHelper, private bool $reportMaybes, + private PhpVersion $phpVersion, ) { } @@ -54,15 +56,34 @@ public function processNode(Node $node, Scope $scope): array } $isSuperType = AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType); - if ($isSuperType->yes() || ($isSuperType->maybe() && !$this->reportMaybes)) { - return []; + $isOk = $isSuperType->yes() || ($isSuperType->maybe() && !$this->reportMaybes); + if (!$isOk) { + return [ + RuleErrorBuilder::message( + sprintf('%s array key type %s.', $isSuperType->no() ? 'Invalid' : 'Possibly invalid', $dimensionType->describe(VerbosityLevel::typeOnly())), + )->identifier('offsetAccess.invalidOffset')->build(), + ]; + } + + if ($this->phpVersion->getVersionId() >= 80100) { + $isFloat = $dimensionType->isFloat(); + + if ($isFloat->yes()) { + return [ + RuleErrorBuilder::message( + 'Float used as array key, this emits deprecation notice.', + )->identifier('array.invalidOffset')->build(), + ]; + } elseif ($this->reportMaybes && $isFloat->maybe()) { + return [ + RuleErrorBuilder::message( + 'Float possibly used as array key, this emits deprecation notice.', + )->identifier('array.invalidOffset')->build(), + ]; + } } - return [ - RuleErrorBuilder::message( - sprintf('%s array key type %s.', $isSuperType->no() ? 'Invalid' : 'Possibly invalid', $dimensionType->describe(VerbosityLevel::typeOnly())), - )->identifier('offsetAccess.invalidOffset')->build(), - ]; + return []; } } diff --git a/src/Rules/Arrays/InvalidKeyInArrayItemRule.php b/src/Rules/Arrays/InvalidKeyInArrayItemRule.php index 61c4fd342c..43f52cee22 100644 --- a/src/Rules/Arrays/InvalidKeyInArrayItemRule.php +++ b/src/Rules/Arrays/InvalidKeyInArrayItemRule.php @@ -4,6 +4,7 @@ use PhpParser\Node; use PHPStan\Analyser\Scope; +use PHPStan\Php\PhpVersion; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\MixedType; @@ -16,7 +17,10 @@ class InvalidKeyInArrayItemRule implements Rule { - public function __construct(private bool $reportMaybes) + public function __construct( + private bool $reportMaybes, + private PhpVersion $phpVersion, + ) { } @@ -47,6 +51,24 @@ public function processNode(Node $node, Scope $scope): array ]; } + if ($this->phpVersion->getVersionId() >= 80100) { + $isFloat = $dimensionType->isFloat(); + + if ($isFloat->yes()) { + return [ + RuleErrorBuilder::message( + 'Float used as array key, this emits deprecation notice.', + )->identifier('array.invalidKey')->build(), + ]; + } elseif ($this->reportMaybes && $isFloat->maybe() && !$dimensionType instanceof MixedType) { + return [ + RuleErrorBuilder::message( + 'Float possibly used as array key, this emits deprecation notice.', + )->identifier('array.invalidKey')->build(), + ]; + } + } + return []; } diff --git a/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php index 70c671135a..3cfba0f143 100644 --- a/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php @@ -2,9 +2,12 @@ namespace PHPStan\Rules\Arrays; +use PHPStan\Php\PhpVersion; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleLevelHelper; use PHPStan\Testing\RuleTestCase; +use function array_filter; +use function array_values; use const PHP_VERSION_ID; /** @@ -16,12 +19,12 @@ class InvalidKeyInArrayDimFetchRuleTest extends RuleTestCase protected function getRule(): Rule { $ruleLevelHelper = new RuleLevelHelper($this->createReflectionProvider(), true, false, true, false, false, true, false); - return new InvalidKeyInArrayDimFetchRule($ruleLevelHelper, true); + return new InvalidKeyInArrayDimFetchRule($ruleLevelHelper, true, new PhpVersion(PHP_VERSION_ID)); } public function testInvalidKey(): void { - $this->analyse([__DIR__ . '/data/invalid-key-array-dim-fetch.php'], [ + $this->analyse([__DIR__ . '/data/invalid-key-array-dim-fetch.php'], array_values(array_filter([ [ 'Invalid array key type DateTimeImmutable.', 7, @@ -30,6 +33,12 @@ public function testInvalidKey(): void 'Invalid array key type array.', 8, ], + PHP_VERSION_ID >= 80100 + ? [ + 'Float used as array key, this emits deprecation notice.', + 10, + ] + : null, [ 'Possibly invalid array key type stdClass|string.', 24, @@ -58,7 +67,13 @@ public function testInvalidKey(): void 'Invalid array key type DateTimeImmutable.', 48, ], - ]); + PHP_VERSION_ID >= 80100 + ? [ + 'Float possibly used as array key, this emits deprecation notice.', + 52, + ] + : null, + ]))); } public function testBug6315(): void diff --git a/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php b/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php index 7a40122d1c..a38c2d587d 100644 --- a/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php @@ -2,8 +2,11 @@ namespace PHPStan\Rules\Arrays; +use PHPStan\Php\PhpVersion; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; +use function array_filter; +use function array_values; use const PHP_VERSION_ID; /** @@ -14,12 +17,12 @@ class InvalidKeyInArrayItemRuleTest extends RuleTestCase protected function getRule(): Rule { - return new InvalidKeyInArrayItemRule(true); + return new InvalidKeyInArrayItemRule(true, new PhpVersion(PHP_VERSION_ID)); } public function testInvalidKey(): void { - $this->analyse([__DIR__ . '/data/invalid-key-array-item.php'], [ + $this->analyse([__DIR__ . '/data/invalid-key-array-item.php'], array_values(array_filter([ [ 'Invalid array key type DateTimeImmutable.', 13, @@ -32,7 +35,19 @@ public function testInvalidKey(): void 'Possibly invalid array key type stdClass|string.', 15, ], - ]); + PHP_VERSION_ID >= 80100 + ? [ + 'Float used as array key, this emits deprecation notice.', + 16, + ] + : null, + PHP_VERSION_ID >= 80100 + ? [ + 'Float possibly used as array key, this emits deprecation notice.', + 24, + ] + : null, + ]))); } public function testInvalidKeyInList(): void diff --git a/tests/PHPStan/Rules/Arrays/data/invalid-key-array-dim-fetch.php b/tests/PHPStan/Rules/Arrays/data/invalid-key-array-dim-fetch.php index fb7514c6cb..d4b480d1e3 100644 --- a/tests/PHPStan/Rules/Arrays/data/invalid-key-array-dim-fetch.php +++ b/tests/PHPStan/Rules/Arrays/data/invalid-key-array-dim-fetch.php @@ -46,3 +46,8 @@ $array[5][new \DateTimeImmutable()]; $array[new \stdClass()][new \DateTimeImmutable()]; $array[new \DateTimeImmutable()][] = 5; + +/** @var float|null $floatOrNull */ +$floatOrNull = null; +$a[$floatOrNull]; +$a[$mixed]; diff --git a/tests/PHPStan/Rules/Arrays/data/invalid-key-array-item.php b/tests/PHPStan/Rules/Arrays/data/invalid-key-array-item.php index cde86133df..0f97f7c052 100644 --- a/tests/PHPStan/Rules/Arrays/data/invalid-key-array-item.php +++ b/tests/PHPStan/Rules/Arrays/data/invalid-key-array-item.php @@ -13,4 +13,13 @@ new \DateTimeImmutable() => 'aaa', [] => 'bbb', $stringOrObject => 'aaa', + 1.0 => 'aaa', + $mixed => 'aaa', +]; + +/** @var float|null $floatOrNull */ +$floatOrNull = null; + +$b = [ + $floatOrNull => 'aaa', ];