azjezz / psl

📚 PHP Standard Library - a modern, consistent, centralized, well-typed, non-blocking set of APIs for PHP programmers
MIT License
1.2k stars 69 forks source link

`Type\nonnull()` is not narrowed by phpstan #486

Open bendavies opened 1 week ago

bendavies commented 1 week ago

Describe the bug Type\nonnull() is not narrowed by phpstan

To Reproduce

<?php

require __DIR__.'/../vendor/autoload.php';

use Psl\Type;

/**
 * @template T
 *
 * @param T|null $value
 *
 * @ara-assert nonnull $value
 *
 * @psalm-assert T $value
 *
 * @ara-return nonnull
 *
 * @return ($value is null ? never : T)
 */
function assertNotNull(mixed $value): mixed
{

}

function a(?int $i): void
{
    \PHPStan\dumpType($i);
    \PHPStan\dumpType(assertNotNull($i));
}

function b(?int $i): void
{
    \PHPStan\dumpType($i);
    \PHPStan\dumpType(Type\nonnull()->assert($i));
}

Expected behavior First method shows a way (provided by @ondrejmirtes) that this can work in phpstan. The second method use Type\nonnull().

The dumped types here should be int|null followed by int for both dumpTypes. but we get:

  27     Dumped type: int|null
  28     Dumped type: int
  33     Dumped type: int|null
  34     Dumped type: mixed

The Psl version of the function "narrows" int|null to mixed, not int

ondrejmirtes commented 1 week ago

You need to dump the type on the line after the assert.

ondrejmirtes commented 1 week ago

This works as expected:

function b(?int $i): void
{
    \PHPStan\dumpType($i);
    Type\nonnull()->assert($i);
    \PHPStan\dumpType($i);
}

The fact that the returned value is not correctly inferred might be a bug in PHPStan.

bendavies commented 1 week ago

Thanks for looking like that. However, all the other PSL types work like i posted in the OP.

i.e. this does not need the dump to be on the line after the insert This works:

function c(?int $i): void
{
    \PHPStan\dumpType($i);
    \PHPStan\dumpType(Type\int()->assert($i));
}

Is this maybe what https://github.com/php-standard-library/phpstan-extension/blob/1.0.x/src/Type/AssertTypeSpecifyingExtension.php is handling, and is not working for Type\nonnull() for some reason?