From e0792fdd519f34a4ac9bd9087f07246df8d8b559 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 11 Oct 2026 18:05:46 +0200 Subject: [PATCH 1/2] Make ArrayHash stub generic The ArrayHash stub now follows the PHPDoc of nette/utils: ArrayHash is generic over its values and extends stdClass. Generic types like ArrayHash from nette/forms 3.2.9+ PHPDoc are no longer collapsed into non-generic ArrayHash. ArrayHash is added to skipCheckGenericClasses so that ArrayHash without a type argument is not reported as missing its generic types. Co-Authored-By: Claude Opus 5.5 (1M context) --- extension.neon | 3 ++ phpstan.neon | 3 ++ stubs/Forms/Container.stub | 2 +- stubs/Utils/ArrayHash.stub | 11 ++-- .../Rule/Nette/ArrayHashGenericsRuleTest.php | 37 +++++++++++++ tests/Rule/Nette/data/array-hash-generics.php | 22 ++++++++ tests/Type/Nette/ArrayHashStubTest.php | 54 +++++++++++++++++++ tests/Type/Nette/data/array-hash-iterator.php | 17 ++++++ tests/Type/Nette/data/array-hash.php | 16 ++++++ 9 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 tests/Rule/Nette/ArrayHashGenericsRuleTest.php create mode 100644 tests/Rule/Nette/data/array-hash-generics.php create mode 100644 tests/Type/Nette/ArrayHashStubTest.php create mode 100644 tests/Type/Nette/data/array-hash-iterator.php create mode 100644 tests/Type/Nette/data/array-hash.php diff --git a/extension.neon b/extension.neon index 3f5c7a2..0554b2c 100644 --- a/extension.neon +++ b/extension.neon @@ -37,6 +37,9 @@ parameters: - Nette\Http\SessionSection - Nette\Security\Identity - Nette\Security\SimpleIdentity + featureToggles: + skipCheckGenericClasses: + - Nette\Utils\ArrayHash earlyTerminatingMethodCalls: Nette\Application\UI\Component: - error diff --git a/phpstan.neon b/phpstan.neon index c69233c..7e2fcee 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -14,3 +14,6 @@ parameters: ignoreErrors: - '~^Variable property access on PhpParser\\Node\.$~' + - + identifier: phpstanApi.classConstant + path: tests/Rule/Nette/ArrayHashGenericsRuleTest.php diff --git a/stubs/Forms/Container.stub b/stubs/Forms/Container.stub index d7381af..935baa1 100644 --- a/stubs/Forms/Container.stub +++ b/stubs/Forms/Container.stub @@ -5,7 +5,7 @@ namespace Nette\Forms; use Nette\Utils\ArrayHash; /** - * @property ArrayHash $values + * @property ArrayHash $values * @property-read \Iterator $controls * @property-read Form|null $form * @phpstan-implements \ArrayAccess diff --git a/stubs/Utils/ArrayHash.stub b/stubs/Utils/ArrayHash.stub index 59b8b98..cff2bc7 100644 --- a/stubs/Utils/ArrayHash.stub +++ b/stubs/Utils/ArrayHash.stub @@ -3,16 +3,17 @@ namespace Nette\Utils; /** - * @implements \IteratorAggregate - * @implements \ArrayAccess + * @template T + * @implements \IteratorAggregate + * @implements \ArrayAccess */ -class ArrayHash implements \ArrayAccess, \IteratorAggregate +class ArrayHash extends \stdClass implements \ArrayAccess, \IteratorAggregate { /** - * @return \RecursiveArrayIterator + * @return \Iterator */ - public function getIterator(): \RecursiveArrayIterator + public function &getIterator(): \Iterator { // nothing } diff --git a/tests/Rule/Nette/ArrayHashGenericsRuleTest.php b/tests/Rule/Nette/ArrayHashGenericsRuleTest.php new file mode 100644 index 0000000..be81409 --- /dev/null +++ b/tests/Rule/Nette/ArrayHashGenericsRuleTest.php @@ -0,0 +1,37 @@ + + */ +class ArrayHashGenericsRuleTest extends RuleTestCase +{ + + protected function getRule(): Rule + { + return self::getContainer()->getByType(MissingMethodParameterTypehintRule::class); + } + + public function testRule(): void + { + $this->analyse([__DIR__ . '/data/array-hash-generics.php'], [ + [ + 'Method PHPStan\Rule\Nette\Data\ArrayHashGenerics\Foo::doBar() has parameter $values with generic class ArrayObject but does not specify its types: TKey, TValue', + 18, + ], + ]); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/../../../extension.neon', + ]; + } + +} diff --git a/tests/Rule/Nette/data/array-hash-generics.php b/tests/Rule/Nette/data/array-hash-generics.php new file mode 100644 index 0000000..702047c --- /dev/null +++ b/tests/Rule/Nette/data/array-hash-generics.php @@ -0,0 +1,22 @@ +assertFileAsserts($assertType, $file, ...$args); + } + + public static function getAdditionalConfigFiles(): array + { + return [ + __DIR__ . '/phpstan.neon', + ]; + } + +} diff --git a/tests/Type/Nette/data/array-hash-iterator.php b/tests/Type/Nette/data/array-hash-iterator.php new file mode 100644 index 0000000..3fe8664 --- /dev/null +++ b/tests/Type/Nette/data/array-hash-iterator.php @@ -0,0 +1,17 @@ + $hash + */ +function test(ArrayHash $hash): void +{ + foreach ($hash as $key => $value) { + assertType('(int|string)', $key); + assertType('int', $value); + } +} diff --git a/tests/Type/Nette/data/array-hash.php b/tests/Type/Nette/data/array-hash.php new file mode 100644 index 0000000..c15d257 --- /dev/null +++ b/tests/Type/Nette/data/array-hash.php @@ -0,0 +1,16 @@ + $hash + */ +function test(ArrayHash $hash): void +{ + assertType('Nette\\Utils\\ArrayHash', $hash); + assertType('int|null', $hash['foo']); + assertType('mixed', $hash->foo); +} From 0e096770d816a2863fb5b8cb94e1695a40455ecd Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 11 Oct 2026 18:07:44 +0200 Subject: [PATCH 2/2] Remove ArrayHashGenericsRuleTest Co-Authored-By: Claude Opus 5.5 (1M context) --- phpstan.neon | 3 -- .../Rule/Nette/ArrayHashGenericsRuleTest.php | 37 ------------------- tests/Rule/Nette/data/array-hash-generics.php | 22 ----------- 3 files changed, 62 deletions(-) delete mode 100644 tests/Rule/Nette/ArrayHashGenericsRuleTest.php delete mode 100644 tests/Rule/Nette/data/array-hash-generics.php diff --git a/phpstan.neon b/phpstan.neon index 7e2fcee..c69233c 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -14,6 +14,3 @@ parameters: ignoreErrors: - '~^Variable property access on PhpParser\\Node\.$~' - - - identifier: phpstanApi.classConstant - path: tests/Rule/Nette/ArrayHashGenericsRuleTest.php diff --git a/tests/Rule/Nette/ArrayHashGenericsRuleTest.php b/tests/Rule/Nette/ArrayHashGenericsRuleTest.php deleted file mode 100644 index be81409..0000000 --- a/tests/Rule/Nette/ArrayHashGenericsRuleTest.php +++ /dev/null @@ -1,37 +0,0 @@ - - */ -class ArrayHashGenericsRuleTest extends RuleTestCase -{ - - protected function getRule(): Rule - { - return self::getContainer()->getByType(MissingMethodParameterTypehintRule::class); - } - - public function testRule(): void - { - $this->analyse([__DIR__ . '/data/array-hash-generics.php'], [ - [ - 'Method PHPStan\Rule\Nette\Data\ArrayHashGenerics\Foo::doBar() has parameter $values with generic class ArrayObject but does not specify its types: TKey, TValue', - 18, - ], - ]); - } - - public static function getAdditionalConfigFiles(): array - { - return [ - __DIR__ . '/../../../extension.neon', - ]; - } - -} diff --git a/tests/Rule/Nette/data/array-hash-generics.php b/tests/Rule/Nette/data/array-hash-generics.php deleted file mode 100644 index 702047c..0000000 --- a/tests/Rule/Nette/data/array-hash-generics.php +++ /dev/null @@ -1,22 +0,0 @@ -