Store external dataprovider references in afterClassLikeVisit

This fixes false-positive UndefinedClass issues when Psalm is run on a
single test file with cold cache.

Fixes psalm/psalm-plugin-phpunit#57
This commit is contained in:
Bruce Weirdan 2020-03-30 04:50:31 +03:00
parent ab21faac5c
commit 8f98135364
No known key found for this signature in database
GPG Key ID: CFC3AAB181751B0D
2 changed files with 52 additions and 2 deletions

View File

@ -7,11 +7,11 @@ namespace Psalm\PhpUnitPlugin\Hooks;
use PHPUnit\Framework\TestCase;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use Psalm\CodeLocation;
use Psalm\Codebase;
use Psalm\DocComment;
use Psalm\Exception\DocblockParseException;
use Psalm\FileSource;
use Psalm\Internal\PhpVisitor\ReflectorVisitor;
use Psalm\IssueBuffer;
use Psalm\Issue;
use Psalm\Plugin\Hook\AfterClassLikeAnalysisInterface;
@ -76,6 +76,24 @@ class TestCaseHandler implements
if (self::hasInitializers($class_storage, $class_node)) {
$class_storage->custom_metadata[__NAMESPACE__] = ['hasInitializers' => true];
}
$file_path = $statements_source->getFilePath();
$file_storage = $codebase->file_storage_provider->get($file_path);
foreach ($class_node->getMethods() as $method) {
$specials = self::getSpecials($method);
if (!isset($specials['dataProvider'])) {
continue;
}
foreach ($specials['dataProvider'] as $provider) {
if (false !== strpos($provider, '::')) {
[$class_name] = explode('::', $provider);
$fq_class_name = Type::getFQCLNFromString($class_name, $statements_source->getAliases());
$codebase->scanner->queueClassLikeForScanning($fq_class_name, $file_path);
$file_storage->referenced_classlikes[strtolower($fq_class_name)] = $fq_class_name;
}
}
}
}
/**

View File

@ -7,7 +7,7 @@ Feature: TestCase
Given I have the following config
"""
<?xml version="1.0"?>
<psalm totallyTyped="true">
<psalm totallyTyped="true" %s>
<projectFiles>
<directory name="."/>
<ignoreFiles> <directory name="../../vendor"/> </ignoreFiles>
@ -1176,6 +1176,38 @@ Feature: TestCase
| Type | Message |
| InvalidArgument | Argument 1 of NS\MyTestCase::testSomething expects int, string provided by NS\External::provide():(iterable<string, array<int, string>>) |
@ExternalProviders
Scenario: External providers are available even when Psalm is asked to analyze single test case
Given I have the following code in "test.php"
"""
<?php
namespace NS;
use PHPUnit\Framework\TestCase;
class MyTestCase extends TestCase {
/** @dataProvider External::provide */
public function testSomething(int $_p): void {}
}
"""
And I have the following code in "ext.php"
"""
<?php
namespace NS;
class External {
/** @return iterable<string, array<int,int>> */
public function provide(): iterable {
yield "dataset name" => [1];
}
}
"""
And I have the following classmap
| Class | File |
| NS\MyTestCase | test.php |
| NS\External | ext.php |
When I run Psalm on "test.php"
Then I see no errors
@List
Scenario: Providers returning list are ok
Given I have the following code