Skip to content

Commit

Permalink
Merge pull request #106 from weirdan/handle-nullable-providers
Browse files Browse the repository at this point in the history
Handle nullable providers
  • Loading branch information
weirdan committed Jan 23, 2021
2 parents a7c0014 + d86d279 commit 30ca25c
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 2 deletions.
25 changes: 23 additions & 2 deletions src/Hooks/TestCaseHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Psalm\StatementsSource;
use Psalm\Storage\ClassLikeStorage;
use Psalm\Type;
use Psalm\Type\Atomic\TNull;
use RuntimeException;

class TestCaseHandler implements
Expand Down Expand Up @@ -230,22 +231,42 @@ public static function afterStatementAnalysis(
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;
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',
$provider_return_type_location
));
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],
Expand Down
33 changes: 33 additions & 0 deletions tests/acceptance/TestCase.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 30ca25c

Please sign in to comment.