From 1c2b8eb5771a8dd669b28ce091d878b238c3c3eb Mon Sep 17 00:00:00 2001 From: Nicolai Ehrhardt <245527909+predictor2718@users.noreply.github.com> Date: Fri, 1 May 2026 11:57:26 +0200 Subject: [PATCH] Use pre-args scope for value types in array_push/array_unshift to avoid false mutations from nested by-ref calls --- src/Analyser/ExprHandler/FuncCallHandler.php | 5 ++-- tests/PHPStan/Analyser/nsrt/bug-13510.php | 25 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-13510.php diff --git a/src/Analyser/ExprHandler/FuncCallHandler.php b/src/Analyser/ExprHandler/FuncCallHandler.php index 292aea88cd5..a5a0dc3cee5 100644 --- a/src/Analyser/ExprHandler/FuncCallHandler.php +++ b/src/Analyser/ExprHandler/FuncCallHandler.php @@ -267,6 +267,7 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex } } + $scopeBeforeArgs = $scope; $argsResult = $nodeScopeResolver->processArgs($stmt, $functionReflection, null, $parametersAcceptor, $normalizedExpr, $scope, $storage, $nodeCallbackForArgs, $context); $scope = $argsResult->getScope(); $hasYield = $argsResult->hasYield(); @@ -395,8 +396,8 @@ public function processExpr(NodeScopeResolver $nodeScopeResolver, Stmt $stmt, Ex $stmt, $arrayArg, new NativeTypeExpr( - $this->getArrayFunctionAppendingType($functionReflection, $scope, $normalizedExpr), - $this->getArrayFunctionAppendingType($functionReflection, $scope->doNotTreatPhpDocTypesAsCertain(), $normalizedExpr), + $this->getArrayFunctionAppendingType($functionReflection, $scopeBeforeArgs, $normalizedExpr), + $this->getArrayFunctionAppendingType($functionReflection, $scopeBeforeArgs->doNotTreatPhpDocTypesAsCertain(), $normalizedExpr), ), $nodeCallback, )->getScope(); diff --git a/tests/PHPStan/Analyser/nsrt/bug-13510.php b/tests/PHPStan/Analyser/nsrt/bug-13510.php new file mode 100644 index 00000000000..86405ed71ae --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13510.php @@ -0,0 +1,25 @@ + $arr */ + public function test(array $arr): void + { + array_unshift($arr, array_pop($arr)); + assertType('non-empty-list', $arr); + } + + /** @param non-empty-list $arr */ + public function testTwoLines(array $arr): void + { + $popped = array_pop($arr); + array_unshift($arr, $popped); + assertType('non-empty-list', $arr); + } + +}