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

Incorrect type inference when using key/current/reset on non-empty array #8521

Closed
boesing opened this issue Sep 29, 2022 · 5 comments · Fixed by #8584
Closed

Incorrect type inference when using key/current/reset on non-empty array #8521

boesing opened this issue Sep 29, 2022 · 5 comments · Fixed by #8584

Comments

@boesing
Copy link
Contributor

boesing commented Sep 29, 2022

Hey there,

when both key and value types of an array are known, I'd expect psalm to be able to infer the return value of key/current in case of a non-empty-array or non-empty-list.

https://psalm.dev/r/bc365e8080

Not sure if this can already be done with some conditional magic (even tho, I did never understood how these internal functions are typed 😅).

But something like this might work, right?

/**
  * @template T of array-key
  * @param array<T,mixed> $array
  * @return ($array is non-empty-list ? 0|positive-int : ($array is non-empty-array ? T : mixed))
  */
function key(array $array): mixed
{
}

/**
  * @template T
  * @param array<mixed,T> $array
  * @return ($array is non-empty-list ? T : ($array is non-empty-array ? T : T|false))
  */
function current(array $array): mixed
{}

/**
  * @template T
  * @param array<mixed,T> $array
  * @return ($array is non-empty-list ? T : ($array is non-empty-array ? T : T|false))
  */
function reset(array $array): mixed
{}
@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/bc365e8080
<?php

/** @var array<string,int> */
$foo = [];

if ($foo === []) {
    return;
}

$string = key($foo);
$int = current($foo);

/** 
 * @psalm-trace $string 
 * @psalm-trace $int
 */
Psalm output (using commit 028ac7f):

INFO: Trace - 16:4 - $string: null|string

INFO: Trace - 16:4 - $int: false|int

INFO: UnusedVariable - 10:1 - $string is never referenced or the value is not used

INFO: UnusedVariable - 11:1 - $int is never referenced or the value is not used

@boesing boesing changed the title Incorrect type inference when using key/current on non-empty array Incorrect type inference when using key/current/reset on non-empty array Sep 29, 2022
@orklah
Copy link
Collaborator

orklah commented Sep 29, 2022

Yeah, that looks like it would work :) In fact, you can check that it does by just renaming the functions: https://psalm.dev/r/b9f82704bf

Note that is theory, a non-empty-list is a non-empty-array so you should be able to simplify your condition by getting rid of the non-empty-list case (for current and reset at least)

PS: internal functions are typed through the callmap when they're simple enough. If some conditional magic needs to be applied, stubs take priority, then if you need to have specialized code, TypeProviders can override all that. Here you should be able to add a stub (or rather adapt it) in CoreGenericFunctions.phpstub

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/b9f82704bf
<?php

/** @var array<string,int> */
$foo = [];

if ($foo === []) {
    return;
}

$string = key2($foo);
$int = current2($foo);

/** @psalm-trace $string  */;
/** @psalm-trace $int */;

/**
  * @template T of array-key
  * @param array<T,mixed> $array
  * @return ($array is non-empty-list ? 0|positive-int : ($array is non-empty-array ? T : mixed))
  */
function key2(array $array): mixed
{
}

/**
  * @template T
  * @param array<mixed,T> $array
  * @return ($array is non-empty-list ? T : ($array is non-empty-array ? T : T|false))
  */
function current2(array $array): mixed
{}

/**
  * @template T
  * @param array<mixed,T> $array
  * @return ($array is non-empty-list ? T : ($array is non-empty-array ? T : T|false))
  */
function reset2(array $array): mixed
{}
Psalm output (using commit 028ac7f):

INFO: Trace - 13:29 - $string: string

INFO: Trace - 14:25 - $int: int

INFO: UnusedParam - 21:21 - Param $array is never referenced in this method

ERROR: InvalidReturnType - 19:13 - No return statements were found for method key2 but return type 'T|int<0, max>|mixed' was expected

INFO: UnusedParam - 30:25 - Param $array is never referenced in this method

ERROR: InvalidReturnType - 28:13 - No return statements were found for method current2 but return type 'T|false' was expected

INFO: UnusedParam - 38:23 - Param $array is never referenced in this method

ERROR: InvalidReturnType - 36:13 - No return statements were found for method reset2 but return type 'T|false' was expected

INFO: UnusedVariable - 10:1 - $string is never referenced or the value is not used

INFO: UnusedVariable - 11:1 - $int is never referenced or the value is not used

@boesing
Copy link
Contributor Author

boesing commented Sep 29, 2022

Nice, I'll give that a try when I find some time.
Sorry for raising so many issues without trying to fix them on my on in an appropriate timeframe. 😅

@orklah
Copy link
Collaborator

orklah commented Sep 29, 2022

It's okay :) We're short staffed too! Issues are still useful for other users when searching an issue or to centralize discussions

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 a pull request may close this issue.

2 participants