diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/default_array_with_known_type.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/default_array_with_known_type.php.inc new file mode 100644 index 00000000000..83ad952e275 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/default_array_with_known_type.php.inc @@ -0,0 +1,38 @@ +run(['item1', 'item2']); + } + + private function run(array $items = []) + { + } +} + +?> +----- +run(['item1', 'item2']); + } + + /** + * @param string[] $items + */ + private function run(array $items = []) + { + } +} + +?> diff --git a/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_non_empty_array_contradicting_default.php.inc b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_non_empty_array_contradicting_default.php.inc new file mode 100644 index 00000000000..14f70991cf0 --- /dev/null +++ b/rules-tests/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector/Fixture/skip_non_empty_array_contradicting_default.php.inc @@ -0,0 +1,21 @@ +callee($row); + } + + public function caller2() + { + return $this->callee(['state' => 5, 'name' => 'x']); + } +} diff --git a/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php b/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php index cf30d0e0384..b11d5613e5c 100644 --- a/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php +++ b/rules/TypeDeclarationDocblocks/NodeDocblockTypeDecorator.php @@ -149,6 +149,7 @@ private function isArrayMixed(Type $type): bool return false; } - return $type->getKeyType() instanceof IntegerType; + // both plain "mixed[]" (integer key) and a fully mixed-keyed array carry no useful value + return $type->getKeyType() instanceof IntegerType || $type->getKeyType() instanceof MixedType; } } diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php index 3c3dd427ae7..d58f0c973cb 100644 --- a/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php @@ -5,6 +5,7 @@ namespace Rector\TypeDeclarationDocblocks\Rector\Class_; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; @@ -130,6 +131,14 @@ public function refactor(Node $node): ?Node // in case of array type declaration, null cannot be passed or is already casted $resolvedParameterType = TypeCombinator::removeNull($resolvedParameterType); + // the param default value must always be accepted; a locally inferred, flow-narrowed type such as + // "non-empty-array" would otherwise contradict an "= []" default - unite with the default type so + // the resulting @param never conflicts with the method signature + if ($param->default instanceof Expr) { + $defaultType = $this->nodeTypeResolver->getType($param->default); + $resolvedParameterType = TypeCombinator::union($resolvedParameterType, $defaultType); + } + $hasClassMethodChanged = $this->nodeDocblockTypeDecorator->decorateGenericIterableParamType( $resolvedParameterType, $classMethodPhpDocInfo,