From 04f549602270cbe02efff3a5a97872ce059a05f3 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 28 Sep 2025 22:32:16 +0200 Subject: [PATCH] avoid using array in nested doc type, used array --- .../Fixture/nested_array_is_mixed.php.inc | 48 +++++++++++++++++++ .../NodeAnalyzer/CallTypesResolver.php | 6 +-- .../NodeDocblockTypeDecorator.php | 4 +- .../AddReturnDocblockDataProviderRector.php | 1 - .../TypeMapper/ArrayTypeMapper.php | 2 + .../TypeMapper/ObjectTypeMapper.php | 10 +++- 6 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/nested_array_is_mixed.php.inc diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/nested_array_is_mixed.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/nested_array_is_mixed.php.inc new file mode 100644 index 00000000000..d2a686dd973 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector/Fixture/nested_array_is_mixed.php.inc @@ -0,0 +1,48 @@ + +----- +>> + */ + public static function provideData(): \Generator + { + yield [[]]; + yield [[1, 2, 3, 4]]; + } +} + +?> diff --git a/rules/TypeDeclaration/NodeAnalyzer/CallTypesResolver.php b/rules/TypeDeclaration/NodeAnalyzer/CallTypesResolver.php index d8a120838c0..b809c3eff7e 100644 --- a/rules/TypeDeclaration/NodeAnalyzer/CallTypesResolver.php +++ b/rules/TypeDeclaration/NodeAnalyzer/CallTypesResolver.php @@ -76,11 +76,7 @@ public function resolveTypesFromCalls(array $calls): array continue; } - if ($arg->name instanceof Identifier) { - $positionOrName = $arg->name->toString(); - } else { - $positionOrName = $position; - } + $positionOrName = $arg->name instanceof Identifier ? $arg->name->toString() : $position; $staticTypesByArgumentPosition[$positionOrName][] = $this->resolveArgValueType($arg); } diff --git a/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php b/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php index 38b913e9378..2178a14a9f6 100644 --- a/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php +++ b/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php @@ -62,13 +62,13 @@ public function decorateGenericIterableReturnType( PhpDocInfo $classMethodPhpDocInfo, ClassMethod $classMethod ): bool { - $typeNode = $this->createTypeNode($type); - if ($this->isBareMixedType($type)) { // no value return false; } + $typeNode = $this->createTypeNode($type); + // no value iterable type if ($typeNode instanceof IdentifierTypeNode) { return false; diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php index 51bdf3071d7..72672677702 100644 --- a/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/AddReturnDocblockDataProviderRector.php @@ -108,7 +108,6 @@ public function refactor(Node $node): ?Node $hasChanged = false; $dataProviderClassMethods = $this->dataProviderMethodsFinder->findDataProviderNodesInClass($node); - if ($dataProviderClassMethods === []) { return null; } diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php index 40ae405e13b..38349e2e2bf 100644 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php @@ -75,6 +75,7 @@ public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode } if ($itemType instanceof ArrayType && $this->isGenericArrayCandidate($itemType)) { + return $this->createGenericArrayType($type, true); } @@ -182,6 +183,7 @@ private function createGenericArrayType(ArrayType $arrayType, bool $withKey = fa } $identifierTypeNode->setAttribute(self::HAS_GENERIC_TYPE_PARENT, $withKey); + return new GenericTypeNode($identifierTypeNode, $genericTypes); } diff --git a/src/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php b/src/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php index 6ed4ddb195a..78cecd0feb4 100644 --- a/src/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php +++ b/src/PHPStanStaticTypeMapper/TypeMapper/ObjectTypeMapper.php @@ -9,7 +9,9 @@ use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PHPStan\PhpDocParser\Ast\Type\TypeNode; +use PHPStan\Type\ArrayType; use PHPStan\Type\Generic\GenericObjectType; +use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use PHPStan\Type\TypeTraverser; @@ -36,6 +38,10 @@ public function getNodeClass(): string public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode { $type = TypeTraverser::map($type, static function (Type $type, callable $traverse): Type { + if ($type instanceof ArrayType && ($type->getItemType() instanceof MixedType && $type->getKeyType() instanceof MixedType)) { + return new ArrayType(new MixedType(), new MixedType(true)); + } + if (! $type instanceof ObjectType) { return $traverse($type); } @@ -52,10 +58,10 @@ public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode } if ($type instanceof GenericObjectType) { - return $traverse(new GenericObjectType('\\' . $type->getClassName(), $type->getTypes())); + return $traverse(new GenericObjectType('\\' . $type->getClassName(), $type->getTypes()), $traverse); } - return $traverse($type); + return $traverse($type, $traverse); }); return $type->toPhpDocNode();