Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dependant types - parsing logic (can't think of a better subject - please change) #4695

Closed
pauljherring opened this issue Mar 12, 2021 · 3 comments
Labels

Comments

@pauljherring
Copy link

Bug report

As requested from #4694

Complicated logic not being recognised, leading to erroneous errors.

Code snippet that reproduces the problem

https://phpstan.org/r/3d974ed6-7d4b-4f91-8dcf-7187222d3004

<?php declare(strict_types = 1);
/**
* @return no-return
*/
function no_return(){
    echo "Dying\n";
    die(1);
}

/**
* @param int|null $u
* @param int|null $a
* @return array<int, int>
*/
function foo($u=null, $a=null){
    if (is_null($u) && is_null($a)){
        no_return();
	return [0,0]; // line 18
    }
    if ($u){
        $a = $u;
    }else if ($a){
        $u = $a;
    }
    return [$u, $a]; // line 25
}

print_r(foo(1)); // first only
print_r(foo(null,2)); // second only
print_r(foo(1,2)); // both
print_r(foo()); // none - will die() and not return

Actual output

18 | Unreachable statement - code above always terminates.
-- | --
25 | Function foo() should return array<int, int> but returns array<int, int|null>.

Expected output

18 | Unreachable statement - code above always terminates.

Line 18 added to check that no-return being honoured.

However $a (the int|null) will never be null if it gets that far through the function.

@ondrejmirtes ondrejmirtes added this to the Dependent types milestone Mar 12, 2021
@pauljherring
Copy link
Author

pauljherring commented Mar 12, 2021

More complicated example (https://phpstan.org/r/b0b6a1f8-be8b-46a2-a936-c8b83dd960f7):

<?php declare(strict_types = 1);
/**
* @return no-return
*/
function no_return(){
    echo "Dying\n";
    die(1);
}

/**
* @param string $s
* @param int $x
* @return array<int>
*/
function x($s, $x){
	return [$s=>$x];
}
/**
* @param int|null $u
* @param int|null $a
* @return array{u:int, a:int}
*/
function foo($u=null, $a=null){
    $_u = null;
    $_a = null;
    if (is_null($u) && is_null($a)){
        no_return();
	return ['u'=>0,'a'=>0]; // line 28
    }
    if ($u){
	$_u = ['u'=>$u];
        $_a = x('a', $u);
    }else if ($a){
	$_a = ['a'=>$a];
        $_u = x('u', $a);
    }
    return ['u'=>$_u['u'], 'a'=>$_a['a']]; // line 37
}

$x = foo(1);
echo $x['a'];
echo $x['u'];

Output

28 | Unreachable statement - code above always terminates.
-- | --
37 | Offset 'a' does not exist on array<int>\|null.
37 | Offset 'u' does not exist on array<int>\|null.

Expected

28 | Unreachable statement - code above always terminates.

@ondrejmirtes
Copy link
Member

I offer two alternatives that are already understood by the static analyser as of dev-master:

@github-actions
Copy link

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 29, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants