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

Inconsistent handling of "empty"-status of arrays as return values #9188

Open
evang522 opened this issue Jan 26, 2023 · 4 comments · May be fixed by #10777
Open

Inconsistent handling of "empty"-status of arrays as return values #9188

evang522 opened this issue Jan 26, 2023 · 4 comments · May be fixed by #10777
Labels

Comments

@evang522
Copy link

Description
The assumption about whether arrays are non-empty seems to be inconsistent between the types list and array as output typing from a function.

To Reproduce
https://psalm.dev/r/7320dbad2e
In this first snippet, the return type of the function map is typed with "list". When attempting to use the spread operator on this return value as the parameter of a function which requires at least 1 member to be present, it triggers a warning "possibly undefined".

https://psalm.dev/r/5f017e6180
The second snippet is the same setup -- but the return value is array not list -- but the above error does not occur.

Expected behavior
I don't have intimate knowledge of Psalm internals, so I can't say whether the one behaviour or the other is correct -- but it does seem that it is inconsistent between the two.

I would expect that Psalm would consider both array and list to be potentially undefined in this spread operator -- or that they would both not cause a problem.

Environment (please complete the following information):

  • OS: Linux Fedora
  • PHP version 8.1
  • Psalm version 5.5.0

Thanks a lot for a great library !

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/7320dbad2e
<?php

/**
 * @template Tk
 * @template Tv
 * @template T
 *
 * @param iterable<Tk, Tv> $iterable Iterable to be mapped over
 * @param (Closure(Tv): T) $function
 *
 * @return list<T>
 */
function map(iterable $iterable, Closure $function): array
{
    if (is_array($iterable)) {
        return array_values(array_map(
        /**
         * @param Tv $v
         */
            static fn($v) => $function($v),
            $iterable
        ));
    }

    $result = [];
    foreach ($iterable as $value) {
        $result[] = $function($value);
    }

    return $result;
}

function echoThings(string $first, string ...$rest): void
{
    foreach ([$first, ...$rest] as $thingToSay) {
        echo $thingToSay;
    }
}


/**
 * @var array<string> $things
 * @phpstan-var array<non-empty-string> $things
 * @psalm-var non-empty-array<non-empty-string> $things
 */
$things =  ['example'];

echoThings(...map($things, static fn (string $thing) => $thing));
Psalm output (using commit aec0edc):

ERROR: InvalidScalarArgument - 48:15 - Argument 1 of echoThings expects string, but possibly undefined non-empty-string provided
https://psalm.dev/r/5f017e6180
<?php

/**
 * @template Tk
 * @template Tv
 * @template T
 *
 * @param iterable<Tk, Tv> $iterable Iterable to be mapped over
 * @param (Closure(Tv): T) $function
 *
 * @return array<T>
 */
function map(iterable $iterable, Closure $function): array
{
    if (is_array($iterable)) {
        return array_values(array_map(
        /**
         * @param Tv $v
         */
            static fn($v) => $function($v),
            $iterable
        ));
    }

    $result = [];
    foreach ($iterable as $value) {
        $result[] = $function($value);
    }

    return $result;
}

function echoThings(string $first, string ...$rest): void
{
    foreach ([$first, ...$rest] as $thingToSay) {
        echo $thingToSay;
    }
}


/**
 * @var array<string> $things
 * @phpstan-var array<non-empty-string> $things
 * @psalm-var non-empty-array<non-empty-string> $things
 */
$things =  ['example'];

echoThings(...map($things, static fn (string $thing) => $thing));
Psalm output (using commit aec0edc):

No issues!

@weirdan
Copy link
Collaborator

weirdan commented Jan 26, 2023

Simplified: https://psalm.dev/r/8d4e5c66da

@psalm-github-bot
Copy link

I found these snippets:

https://psalm.dev/r/8d4e5c66da
<?php

function echoThings(string $_first, string ...$_rest): void {}

/** @return list<non-empty-string> */
function mapList(): array { throw new Exception; }

$mapped = mapList();
/** @psalm-trace $mapped */;
echoThings(...$mapped);

// -------------------------------

/** @return non-empty-array<non-empty-string> */
function mapArray(): array { throw new Exception; }

$mapped = mapArray();
/** @psalm-trace $mapped */;
echoThings(...$mapped);
Psalm output (using commit aec0edc):

INFO: Trace - 9:28 - $mapped: list<non-empty-string>

ERROR: InvalidScalarArgument - 10:15 - Argument 1 of echoThings expects string, but possibly undefined non-empty-string provided

INFO: Trace - 18:28 - $mapped: non-empty-array<array-key, non-empty-string>

@kkmuffme
Copy link
Contributor

This is fixed in #10777

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
4 participants