Skip to content

Fix #12687: @param-out conditional check does not assign default value#5108

Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-u6bthro
Open

Fix #12687: @param-out conditional check does not assign default value#5108
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-u6bthro

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When a @param-out type uses a conditional expression that references its own parameter (e.g., @param-out ($arg is null ? A&I : A) $arg), and an undefined variable is passed to the by-reference parameter, PHPStan was not correctly evaluating the conditional. The undefined variable's type (ErrorType) was used directly to resolve the conditional instead of falling back to the parameter's default value type.

Changes

  • Modified src/Reflection/GenericParametersAcceptorResolver.php: When building passedArgs for conditional type resolution, if the argument type is ErrorType (indicating an undefined variable) and the parameter has a default value, use the default value type instead
  • Added regression test tests/PHPStan/Analyser/nsrt/bug-12687.php

Root cause

In GenericParametersAcceptorResolver::resolve(), the passedArgs map is built from actual argument types. When an undefined variable is passed to a by-reference parameter (like foo($c) where $c was never assigned), $scope->getType($c) returns ErrorType. This ErrorType was then used as the subject of the conditional type evaluation ($arg is null ? A&I : A), causing the condition to evaluate incorrectly.

The fix checks if the argument type is ErrorType (undefined variable) and the parameter has a default value, and uses the default value type instead. This mirrors the existing logic for parameters with no argument provided at all (the elseif ($param->getDefaultValue() !== null) branch).

Test

Added tests/PHPStan/Analyser/nsrt/bug-12687.php which verifies that:

  • Passing a non-null A object resolves the conditional to A
  • Passing an explicitly null variable resolves to A&I
  • Passing an undefined variable (never assigned) resolves to A&I (the fix)

Fixes phpstan/phpstan#12687

…d variables

- When an undefined variable is passed to a by-reference parameter with a default value, use the default value type for resolving conditional @param-out types
- In GenericParametersAcceptorResolver::resolve, ErrorType arguments now fall back to the parameter's default value
- New regression test in tests/PHPStan/Analyser/nsrt/bug-12687.php

Closes phpstan/phpstan#12687
foreach ($parameters as $param) {
if (isset($namedArgTypes[$param->getName()])) {
$argType = $namedArgTypes[$param->getName()];
if ($argType instanceof ErrorType && $param->getDefaultValue() !== null) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Checking ErrorType seems too permissive.

I wonder if we should introduce custom class extending ErrorType (or some custom data to this class like a reason) and update

return new ErrorType();

Cause something like

		$d = $a->getFoo();
		assertType('*ERROR*', $d);
		Testing::testMethod($d);
		assertType('Bug12687\A&Bug12687\I', $d);

seems wrong to me

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll add coverage with #5109

Copy link
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.

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.

2 participants