From 870a551d8a56596630e64994047eb903d10d7151 Mon Sep 17 00:00:00 2001 From: phpstan-bot <79867460+phpstan-bot@users.noreply.github.com> Date: Mon, 11 May 2026 03:25:53 +0000 Subject: [PATCH 1/8] Return `null` from `ArgumentsNormalizer::reorderArgs()` when positional args after named args create holes beyond parameter count - In `reorderArgs()`, positional arguments keep their original index from `$callArgs`. When a named argument appears between positional arguments (which is invalid PHP), the named arg is removed from the positional sequence, creating a hole in the `$reorderedArgs` array at an index beyond the parameter signature length. - The hole-filling loop at line 401 threw `ShouldNotHappenException` when encountering a hole index not present in `$signatureParameters`. - Replace the throw with `return null`, which tells all callers (func calls, method calls, static calls, `new`, `ParametersAcceptorSelector`) that argument reordering failed, and they fall back gracefully. - The fix applies to all callable types (functions, methods, static methods, constructors) since they all share `reorderArgs()`. - Added two test cases to `ArgumentsNormalizerTest::dataReorderInvalid`: one with a variadic parameter and one without. --- src/Analyser/ArgumentsNormalizer.php | 2 +- .../Analyser/ArgumentsNormalizerTest.php | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Analyser/ArgumentsNormalizer.php b/src/Analyser/ArgumentsNormalizer.php index 6c254df7632..ae6fb6511ca 100644 --- a/src/Analyser/ArgumentsNormalizer.php +++ b/src/Analyser/ArgumentsNormalizer.php @@ -403,7 +403,7 @@ public static function reorderArgs(ParametersAcceptor $parametersAcceptor, array continue; } if (!array_key_exists($j, $signatureParameters)) { - throw new ShouldNotHappenException('Parameter signatures cannot have holes'); + return null; } $parameter = $signatureParameters[$j]; diff --git a/tests/PHPStan/Analyser/ArgumentsNormalizerTest.php b/tests/PHPStan/Analyser/ArgumentsNormalizerTest.php index b2cc10be5da..a9226fc3131 100644 --- a/tests/PHPStan/Analyser/ArgumentsNormalizerTest.php +++ b/tests/PHPStan/Analyser/ArgumentsNormalizerTest.php @@ -323,6 +323,33 @@ public static function dataReorderInvalid(): iterable [new StringType(), 'three'], ], ]; + + // positional arg after named arg with variadic parameter + yield [ + [ + ['value', false, false, null], + ['values', true, true, new StringType()], + ], + [ + [new IntegerType(), null], + [new IntegerType(), null], + [new IntegerType(), null], + [new StringType(), 'd'], + [new IntegerType(), null], + ], + ]; + + // positional arg after named arg without variadic parameter + yield [ + [ + ['one', false, false, null], + ], + [ + [new IntegerType(), null], + [new StringType(), 'd'], + [new IntegerType(), null], + ], + ]; } /** From f6df3381fc2ad07ec938250856be23a34ce156ad Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Mon, 11 May 2026 06:18:25 +0000 Subject: [PATCH 2/8] Add AnalyserIntegrationTest for bug-14596 crash Co-Authored-By: Claude Opus 4.6 --- tests/PHPStan/Analyser/AnalyserIntegrationTest.php | 7 +++++++ tests/PHPStan/Analyser/data/bug-14596.php | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 tests/PHPStan/Analyser/data/bug-14596.php diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index 803d37fe8c9..0763f789a5c 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -1563,6 +1563,13 @@ public function testBug14550(): void $this->assertNotEmpty($errors); } + public function testBug14596(): void + { + // crash + $errors = $this->runAnalyse(__DIR__ . '/data/bug-14596.php'); + $this->assertNotEmpty($errors); + } + /** * @param string[]|null $allAnalysedFiles * @return list diff --git a/tests/PHPStan/Analyser/data/bug-14596.php b/tests/PHPStan/Analyser/data/bug-14596.php new file mode 100644 index 00000000000..6756d494463 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-14596.php @@ -0,0 +1,5 @@ + Date: Mon, 11 May 2026 07:14:29 +0000 Subject: [PATCH 3/8] Test all sorts of callables in bug-14596 integration test Co-Authored-By: Claude Opus 4.6 --- tests/PHPStan/Analyser/data/bug-14596.php | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/PHPStan/Analyser/data/bug-14596.php b/tests/PHPStan/Analyser/data/bug-14596.php index 6756d494463..8904c0c8059 100644 --- a/tests/PHPStan/Analyser/data/bug-14596.php +++ b/tests/PHPStan/Analyser/data/bug-14596.php @@ -2,4 +2,33 @@ namespace Bug14596; +function foo(int $a, int $b, int $c, string ...$rest): void {} + +class Foo { + public function bar(int $a, int $b, int $c, string ...$rest): void {} + public static function baz(int $a, int $b, int $c, string ...$rest): void {} + public function __construct(int $a, int $b, int $c, string ...$rest) {} +} + +// built-in function \PHPStan\dumpType(1, 2, 3, d: 'foo', 5); + +// user-defined function +foo(1, 2, 3, d: 'foo', 5); + +// method call +$obj = new Foo(1, 2, 3); +$obj->bar(1, 2, 3, d: 'foo', 5); + +// static method call +Foo::baz(1, 2, 3, d: 'foo', 5); + +// constructor +new Foo(1, 2, 3, d: 'foo', 5); + +// closure +$closure = function (int $a, int $b, int $c, string ...$rest): void {}; +$closure(1, 2, 3, d: 'foo', 5); + +// call_user_func +call_user_func('Bug14596\foo', 1, 2, 3, d: 'foo', 5); From 1845afd0cfe5bb34af107843f21c8e1766ef956c Mon Sep 17 00:00:00 2001 From: phpstan-bot Date: Mon, 11 May 2026 07:42:03 +0000 Subject: [PATCH 4/8] Split bug-14596 test data to avoid autoloader crash on invalid PHP syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test data file defines a class in the Bug14596 namespace, which gets indexed by Composer's classmap autoloader. Since the file also contains `positional argument after named argument` (a PHP compile-time error), any autoload attempt crashes the process — including PHPStan's parallel workers. Move class and function definitions to a separate bug-14596-definition.php file with valid PHP syntax, keeping only the invalid calls in the main test file. Co-Authored-By: Claude Opus 4.6 --- tests/PHPStan/Analyser/data/bug-14596-definition.php | 11 +++++++++++ tests/PHPStan/Analyser/data/bug-14596.php | 8 -------- 2 files changed, 11 insertions(+), 8 deletions(-) create mode 100644 tests/PHPStan/Analyser/data/bug-14596-definition.php diff --git a/tests/PHPStan/Analyser/data/bug-14596-definition.php b/tests/PHPStan/Analyser/data/bug-14596-definition.php new file mode 100644 index 00000000000..14f52b35d20 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-14596-definition.php @@ -0,0 +1,11 @@ + Date: Mon, 11 May 2026 09:46:32 +0200 Subject: [PATCH 5/8] Revert "Split bug-14596 test data to avoid autoloader crash on invalid PHP syntax" This reverts commit 1845afd0cfe5bb34af107843f21c8e1766ef956c. --- tests/PHPStan/Analyser/data/bug-14596-definition.php | 11 ----------- tests/PHPStan/Analyser/data/bug-14596.php | 8 ++++++++ 2 files changed, 8 insertions(+), 11 deletions(-) delete mode 100644 tests/PHPStan/Analyser/data/bug-14596-definition.php diff --git a/tests/PHPStan/Analyser/data/bug-14596-definition.php b/tests/PHPStan/Analyser/data/bug-14596-definition.php deleted file mode 100644 index 14f52b35d20..00000000000 --- a/tests/PHPStan/Analyser/data/bug-14596-definition.php +++ /dev/null @@ -1,11 +0,0 @@ - Date: Mon, 11 May 2026 07:55:56 +0000 Subject: [PATCH 6/8] Add bug-14596 tests to CallMethodsRuleTest and CallStaticMethodsRuleTest Assert that positional arguments after named arguments on method calls and static method calls report the expected error instead of crashing. Co-Authored-By: Claude Opus 4.6 --- tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php | 13 +++++++++++++ .../Rules/Methods/CallStaticMethodsRuleTest.php | 11 +++++++++++ tests/PHPStan/Rules/Methods/data/bug-14596.php | 13 +++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 tests/PHPStan/Rules/Methods/data/bug-14596.php diff --git a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php index 681ee8a02c5..239d5663e3c 100644 --- a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php @@ -4078,4 +4078,17 @@ public function testBug8048(): void $this->analyse([__DIR__ . '/data/bug-8048.php'], []); } + public function testBug14596(): void + { + $this->checkThisOnly = false; + $this->checkNullables = true; + $this->checkUnionTypes = true; + $this->analyse([__DIR__ . '/data/bug-14596.php'], [ + [ + 'Named argument cannot be followed by a positional argument.', + 11, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php index 1b82fd6ba3a..6a99e1c406a 100644 --- a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php @@ -1021,4 +1021,15 @@ public function testBug11894(): void $this->analyse([__DIR__ . '/data/bug-11894.php'], []); } + public function testBug14596(): void + { + $this->checkThisOnly = false; + $this->analyse([__DIR__ . '/data/bug-14596.php'], [ + [ + 'Named argument cannot be followed by a positional argument.', + 12, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/bug-14596.php b/tests/PHPStan/Rules/Methods/data/bug-14596.php new file mode 100644 index 00000000000..f35305cdc31 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/bug-14596.php @@ -0,0 +1,13 @@ +bar(1, 2, 3, d: 'foo', 5); + Foo::baz(1, 2, 3, d: 'foo', 5); +}; From fab7761592026a8261fd5e07f45f246a6c85d8bd Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 11 May 2026 10:05:29 +0200 Subject: [PATCH 7/8] requires php8 --- tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php | 1 + tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php index 239d5663e3c..fdbcc669339 100644 --- a/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php @@ -4078,6 +4078,7 @@ public function testBug8048(): void $this->analyse([__DIR__ . '/data/bug-8048.php'], []); } + #[RequiresPhp('>= 8.0.0')] public function testBug14596(): void { $this->checkThisOnly = false; diff --git a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php index 6a99e1c406a..a3f3ec00299 100644 --- a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php @@ -1021,6 +1021,7 @@ public function testBug11894(): void $this->analyse([__DIR__ . '/data/bug-11894.php'], []); } + #[RequiresPhp(">= 8.0.0")] public function testBug14596(): void { $this->checkThisOnly = false; From 9731b86a67a43bc69e1389fca5645729fdabecd7 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 11 May 2026 10:09:48 +0200 Subject: [PATCH 8/8] cs --- tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php index a3f3ec00299..47a70c9d02a 100644 --- a/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php +++ b/tests/PHPStan/Rules/Methods/CallStaticMethodsRuleTest.php @@ -1021,7 +1021,7 @@ public function testBug11894(): void $this->analyse([__DIR__ . '/data/bug-11894.php'], []); } - #[RequiresPhp(">= 8.0.0")] + #[RequiresPhp('>= 8.0.0')] public function testBug14596(): void { $this->checkThisOnly = false;