diff --git a/src/Analyser/ExprHandler/ArrayHandler.php b/src/Analyser/ExprHandler/ArrayHandler.php index e5fae7851b..6facf4fbec 100644 --- a/src/Analyser/ExprHandler/ArrayHandler.php +++ b/src/Analyser/ExprHandler/ArrayHandler.php @@ -15,6 +15,7 @@ use PHPStan\Node\LiteralArrayItem; use PHPStan\Node\LiteralArrayNode; use PHPStan\Reflection\InitializerExprTypeResolver; +use PHPStan\Type\MixedType; use PHPStan\Type\Type; use function array_merge; @@ -60,12 +61,22 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex $scope = $keyResult->getScope(); } + if ($arrayItem->byRef) { + $scope = $nodeScopeResolver->lookForSetAllowedUndefinedExpressions($scope, $arrayItem->value); + } + $valueResult = $nodeScopeResolver->processExprNode($stmt, $arrayItem->value, $scope, $storage, $nodeCallback, $context->enterDeep()); $hasYield = $hasYield || $valueResult->hasYield(); $throwPoints = array_merge($throwPoints, $valueResult->getThrowPoints()); $impurePoints = array_merge($impurePoints, $valueResult->getImpurePoints()); $isAlwaysTerminating = $isAlwaysTerminating || $valueResult->isAlwaysTerminating(); $scope = $valueResult->getScope(); + if (!$arrayItem->byRef) { + continue; + } + + $scope = $nodeScopeResolver->lookForUnsetAllowedUndefinedExpressions($scope, $arrayItem->value); + $scope = $scope->assignExpression($arrayItem->value, new MixedType(), new MixedType()); } $nodeScopeResolver->callNodeCallback($nodeCallback, new LiteralArrayNode($expr, $itemNodes), $scope, $storage); diff --git a/tests/PHPStan/Analyser/nsrt/bug-6799.php b/tests/PHPStan/Analyser/nsrt/bug-6799.php new file mode 100644 index 0000000000..99938ec9ac --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-6799.php @@ -0,0 +1,87 @@ + $value) { + call_user_func_array(array($this, 'listingAddWhereFilterAtableDefault'), array(&$whereFilter, 'xxxx', $filters[$type], $value)); + } + assertType('mixed', $whereFilter); + if (count($whereFilter) > 0) { + $where[] = "(" . implode(" AND ", $whereFilter) . ")"; + } + } + } +} + +/** + * @param mixed $foo + */ +function foo($foo): void {} + +function testByRefInArray(): void +{ + $a = []; + assertType('array{}', $a); + + $b = [&$a]; + assertType('mixed', $a); // Could stay array{} + + foo($b); + assertType('mixed', $a); +} + +function testByRefInArrayWithKey(): void +{ + $a = 'hello'; + assertType("'hello'", $a); + + $b = ['key' => &$a]; + assertType('mixed', $a); // Could stay 'hello' + + $b['key'] = 42; + assertType('mixed', $a); // Could be 42 +} + +function testMultipleByRefInArray(): void +{ + $a = 1; + $c = 'test'; + + $b = [&$a, 'normal', &$c]; + assertType('mixed', $a); // Could stay 1 + assertType('mixed', $c); // Could stay 'test' + + $b[0] = 2; + $b[1] = 'foo'; + $b[2] = 'bar'; + + assertType('mixed', $a); // Could be 2 + assertType('mixed', $c); // Could be 'bar' +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-6799b.php b/tests/PHPStan/Analyser/nsrt/bug-6799b.php new file mode 100644 index 0000000000..f0af35d829 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-6799b.php @@ -0,0 +1,47 @@ + $value) { + $this->listingAddWhereFilterAtableDefault($whereFilter, 'xxxxx', $filters[$type], $value); + } + assertType('array', $whereFilter); + } + } +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-6799c.php b/tests/PHPStan/Analyser/nsrt/bug-6799c.php new file mode 100644 index 0000000000..43e34ecb25 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-6799c.php @@ -0,0 +1,17 @@ +analyse([__DIR__ . '/../../Analyser/nsrt/bug-6799.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php index 2dde59f142..4dd5b0bced 100644 --- a/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php +++ b/tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php @@ -1430,4 +1430,14 @@ public function testBug14117(): void ]); } + public function testBug6799c(): void + { + $this->cliArgumentsVariablesRegistered = true; + $this->polluteScopeWithLoopInitialAssignments = false; + $this->checkMaybeUndefinedVariables = true; + $this->polluteScopeWithAlwaysIterableForeach = true; + + $this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-6799c.php'], []); + } + }