Handle nullable providers

Refs psalm/psalm-plugin-phpunit#105
This commit is contained in:
Bruce Weirdan 2021-01-23 01:12:23 +02:00
parent a7c00148dd
commit d86d2790ba
No known key found for this signature in database
GPG Key ID: CFC3AAB181751B0D
2 changed files with 56 additions and 2 deletions

View File

@ -20,6 +20,7 @@ use Psalm\Plugin\Hook\AfterCodebasePopulatedInterface;
use Psalm\StatementsSource;
use Psalm\Storage\ClassLikeStorage;
use Psalm\Type;
use Psalm\Type\Atomic\TNull;
use RuntimeException;
class TestCaseHandler implements
@ -230,8 +231,26 @@ class TestCaseHandler implements
Type::getArray(),
]);
$non_null_provider_return_types = [];
foreach (self::getAtomics($provider_return_type) as $type) {
// PHPUnit allows returning null from providers and treats it as an empty set
// resulting in the test being skipped
if ($type instanceof TNull) {
continue;
}
if (!$type->isIterable($codebase)) {
IssueBuffer::accepts(new Issue\InvalidReturnType(
'Providers must return ' . $expected_provider_return_type->getId()
. ', ' . $provider_return_type_string . ' provided',
$provider_return_type_location
));
continue 2;
}
$non_null_provider_return_types[] = $type;
}
if ([] === $non_null_provider_return_types) {
IssueBuffer::accepts(new Issue\InvalidReturnType(
'Providers must return ' . $expected_provider_return_type->getId()
. ', ' . $provider_return_type_string . ' provided',
@ -239,13 +258,15 @@ class TestCaseHandler implements
));
continue;
}
}
// unionize iterable so that instead of array<int,string>|Traversable<object|int>
// we get iterable<int|object,string|int>
//
// TODO: this may get implemented in a future Psalm version, remove it then
$provider_return_type = self::unionizeIterables($codebase, $provider_return_type);
$provider_return_type = self::unionizeIterables(
$codebase,
new Type\Union($non_null_provider_return_types)
);
$provider_key_type_is_compatible = $codebase->isTypeContainedByType(
$provider_return_type->type_params[0],

View File

@ -1261,3 +1261,36 @@ Feature: TestCase
Then I see these errors
| Type | Message |
| InvalidArgument | Argument 1 of NS\MyTestCase::testSomething expects int, string provided by NS\MyTestCase::provide():(iterable<string, list<string>>) |
Scenario: Providers returning nullable generator are ok
Given I have the following code
"""
class MyTestCase extends TestCase {
/** @return ?\Generator<array-key, array<array-key,int>> */
public function provide(): ?\Generator {
return null;
}
/** @dataProvider provide */
public function testSomething(int $_p): void {}
}
"""
When I run Psalm
Then I see no errors
Scenario: Providers returning null are flagged
Given I have the following code
"""
class MyTestCase extends TestCase {
/** @return null */
public function provide() {
return null;
}
/** @dataProvider provide */
public function testSomething(): void {}
}
"""
When I run Psalm
Then I see these errors
| Type | Message |
| InvalidReturnType | Providers must return iterable<array-key, array<array-key, mixed>>, null provided |
And I see no other errors