From 4885e58cbd4e949862012f874e7aca0c9bf7a8a2 Mon Sep 17 00:00:00 2001 From: Sander Muller Date: Tue, 18 Aug 2026 22:55:49 +0200 Subject: [PATCH] Require array_search()'s haystack argument before reading it The list-preserving `$list[array_search($needle, $list)]` heuristic accepted a call with a single argument and then read the second one, so `$list[array_search($list)] = 4;` aborted the analysis with AssignHandler::isSameVariable(): Argument #2 ($b) must be of type PhpParser\Node\Expr, null given The call is malformed either way; with the argument count checked it is reported as such instead of ending the run. A single unpacked argument counts as one argument too and hit the same read. Co-Authored-By: Claude Opus 5 (1M context) --- src/Analyser/ExprHandler/AssignHandler.php | 2 +- .../array-search-offset-argument-count.php | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Analyser/nsrt/array-search-offset-argument-count.php diff --git a/src/Analyser/ExprHandler/AssignHandler.php b/src/Analyser/ExprHandler/AssignHandler.php index c0dade166a0..746d7777638 100644 --- a/src/Analyser/ExprHandler/AssignHandler.php +++ b/src/Analyser/ExprHandler/AssignHandler.php @@ -1912,7 +1912,7 @@ private function shouldKeepList(ArrayDimFetch $arrayDimFetch, Scope $scope, Type $arrayDimFetch->dim instanceof Expr\FuncCall && $arrayDimFetch->dim->name instanceof Name && $arrayDimFetch->dim->name->toLowerString() === 'array_search' - && count($arrayDimFetch->dim->getArgs()) >= 1 + && count($arrayDimFetch->dim->getArgs()) >= 2 // the haystack is the second argument && $this->isSameVariable($arrayDimFetch->var, $arrayDimFetch->dim->getArgs()[1]->value) ) { return true; diff --git a/tests/PHPStan/Analyser/nsrt/array-search-offset-argument-count.php b/tests/PHPStan/Analyser/nsrt/array-search-offset-argument-count.php new file mode 100644 index 00000000000..639d684d7de --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/array-search-offset-argument-count.php @@ -0,0 +1,24 @@ +|int<3, max>|string, 4>}', $list); +} + +/** + * @param array{int, list} $args + */ +function unpackedArguments(array $args): void +{ + $list = [1, 2, 3]; + $list[array_search(...$args)] = 4; + assertType('array{1|4, 2|4, 3|4, ...|int<3, max>|string, 4>}', $list); +}