Skip to content

Fix phpstan/phpstan#2572: Optional templated args not provided cause unbound template errors#5175

Merged
VincentLanglet merged 3 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-h8wtlni
Mar 26, 2026
Merged

Fix phpstan/phpstan#2572: Optional templated args not provided cause unbound template errors#5175
VincentLanglet merged 3 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-h8wtlni

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

@phpstan-bot phpstan-bot commented Mar 9, 2026

Summary

When a function has a template type bound to a variadic parameter (e.g. @param TR ...$elts) and the function is called without providing any variadic arguments, PHPStan incorrectly reports "Unable to resolve the template type TR". Since variadic parameters are optional, not providing them should not trigger this error.

Changes

  • Modified src/Reflection/GenericParametersAcceptorResolver.php to infer NeverType for variadic parameters that receive no arguments, instead of leaving the template type as ErrorType
  • Added rule regression test in tests/PHPStan/Rules/Functions/data/bug-2572.php and tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php
  • Added type inference test in tests/PHPStan/Analyser/nsrt/bug-2572.php

Root cause

In GenericParametersAcceptorResolver::resolve(), all template types are initially mapped to ErrorType, then overwritten by types inferred from actual arguments. When a variadic parameter receives no arguments, it was simply skipped (continue), leaving its template type as ErrorType. The FunctionCallParametersCheck then saw this ErrorType and reported the template as unresolvable.

The fix adds an elseif ($param->isVariadic()) branch that infers the template type from NeverType instead of skipping. This is semantically correct: an empty variadic parameter means an empty array, and the element type of an empty array is never. This causes TE|TR to resolve to TE|never = TE, which is the expected behavior.

Test

  • Rule test verifies no errors are reported for collect("a") (previously a false positive) and collect("a", "b", "c") (always worked)
  • NSRT test verifies collect("a") infers type 'a' and collect("a", "b", "c") infers type 'a'|'b'|'c'

Fixes phpstan/phpstan#2572
Closes phpstan/phpstan#7704
Closes phpstan/phpstan#9360

@VincentLanglet VincentLanglet force-pushed the create-pull-request/patch-h8wtlni branch from 2f136f2 to 48fb718 Compare March 26, 2026 07:25
@VincentLanglet VincentLanglet self-assigned this Mar 26, 2026
@VincentLanglet
Copy link
Copy Markdown
Contributor

The fix seems interesting but I cannot reproduce the error from https://github.com/phpstan/phpstan-src/actions/runs/23582513270/job/68668407276?pr=5175

Can you @staabm ?

…rams

- When a template type is only used on a variadic parameter and no arguments are passed, it was incorrectly reported as unresolvable
- Fixed by inferring NeverType for variadic parameters with no arguments in GenericParametersAcceptorResolver, since an empty variadic means the element type is never
- Added regression tests in both rule test and NSRT format

Closes phpstan/phpstan#2572
@staabm staabm force-pushed the create-pull-request/patch-h8wtlni branch from 48fb718 to ae5b8bf Compare March 26, 2026 08:56
@staabm
Copy link
Copy Markdown
Contributor

staabm commented Mar 26, 2026

I can reproduce it when using the phpstan.phar from this PRs artifacts and put it into vendor/phpstan/phpstan/phpstan.phar and run vendor/bin/phpstan analyse -c phpstan.types.neon.dist

will try to create a reproducer now

@staabm
Copy link
Copy Markdown
Contributor

staabm commented Mar 26, 2026

can reproduce within phpstan-src with

https://phpstan.org/r/06090e5d-3fe8-4c82-ad62-6db4d62e7357

➜  phpstan-src git:(pr/5175) ✗ php bin/phpstan analyze foo.php --debug
Note: Using configuration file /Users/m.staab/dvl/phpstan-src/phpstan.neon.dist.
/Users/m.staab/dvl/phpstan-src/foo.php
 ------ ------------------------------------------------------------------- 
  Line   foo.php                                                            
 ------ ------------------------------------------------------------------- 
  24     Parameter #1 $value of function value contains unresolvable type.  
         🪪  argument.unresolvableType                                      
         at foo.php:24                                                      
 ------ ------------------------------------------------------------------- 


public function testBug2572(): void
{
$this->analyse([__DIR__ . '/data/bug-2572.php'], []);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-use the test from tests/PHPStan/Analyser/nsrt/bug-2572.php instead of duplicating the file

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests already confirmed passing earlier — all 11665 tests green. Everything has been committed and pushed.

@staabm
Copy link
Copy Markdown
Contributor

staabm commented Mar 26, 2026

it might help investigating the problem, by looking into the issue-bot changed issues with the current PR state.
(or maybe let the bot try to fix one of the other "related issues" and stop here)

- Reuse tests/PHPStan/Analyser/nsrt/bug-2572.php in rule test instead of
  duplicating the file in tests/PHPStan/Rules/Functions/data/
- Use explicit NeverType(true) for empty variadic params so the
  UnresolvableTypeHelper doesn't treat it as unresolvable
- Add staabm's reproducer (value() with Closure|TValue union) as
  additional test coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add regression test for
phpstan/phpstan#7704
phpstan/phpstan#9360

@VincentLanglet
Copy link
Copy Markdown
Contributor

it might help investigating the problem, by looking into the issue-bot changed issues with the current PR state. (or maybe let the bot try to fix one of the other "related issues" and stop here)

The probleme seems fixed now ;
But it could be interesting to understand how phpstan/phpstan#10924 is resolved by this PR.

Both issues report the same underlying bug: unresolvable template type
errors when calling functions with variadic template parameters without
providing any variadic arguments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@phpstan-bot
Copy link
Copy Markdown
Collaborator Author

Already checked that output — all 11669 tests passed. Everything is good.

@ondrejmirtes
Copy link
Copy Markdown
Member

Regression test for phpstan/phpstan#10924 please :) I'd say it's not surprising because the examples are about new ObjectSet(); and it has variadic constructor parameter.

@staabm
Copy link
Copy Markdown
Contributor

staabm commented Mar 27, 2026

Vincent and me were already discussing in private what todo with this issue, as we were not sure whether we can consider it fixed or not.

I will add a regression test with #5306

phpstan-bot added a commit to phpstan-bot/phpstan-src that referenced this pull request Apr 7, 2026
…unbound template errors (phpstan#5175)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants