2019-02-17 07:28:22 +01:00
|
|
|
<?php
|
|
|
|
namespace Psalm\PhpUnitPlugin\Hooks;
|
|
|
|
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
use PhpParser\Comment\Doc;
|
|
|
|
use PhpParser\Node\Stmt\ClassLike;
|
|
|
|
use PhpParser\Node\Stmt\ClassMethod;
|
2019-02-18 16:51:21 +01:00
|
|
|
use Psalm\CodeLocation;
|
2019-02-17 07:28:22 +01:00
|
|
|
use Psalm\Codebase;
|
|
|
|
use Psalm\DocComment;
|
|
|
|
use Psalm\FileSource;
|
2019-02-18 16:51:21 +01:00
|
|
|
use Psalm\IssueBuffer;
|
|
|
|
use Psalm\Issue\UndefinedMethod;
|
|
|
|
use Psalm\Plugin\Hook\AfterClassLikeAnalysisInterface;
|
2019-02-17 07:28:22 +01:00
|
|
|
use Psalm\Plugin\Hook\AfterClassLikeVisitInterface;
|
2019-02-18 16:51:21 +01:00
|
|
|
use Psalm\StatementsSource;
|
2019-02-17 07:28:22 +01:00
|
|
|
use Psalm\Storage\ClassLikeStorage;
|
|
|
|
|
2019-02-18 16:51:21 +01:00
|
|
|
class TestCaseHandler implements AfterClassLikeVisitInterface, AfterClassLikeAnalysisInterface
|
2019-02-17 07:28:22 +01:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public static function afterClassLikeVisit(
|
|
|
|
ClassLike $classNode,
|
|
|
|
ClassLikeStorage $classStorage,
|
|
|
|
FileSource $statements_source,
|
|
|
|
Codebase $codebase,
|
|
|
|
array &$file_replacements = []
|
|
|
|
) {
|
2019-02-18 16:51:21 +01:00
|
|
|
if (!$codebase->classExtends($classStorage->name, TestCase::class)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self::hasInitializers($classStorage, $classNode)) {
|
|
|
|
$classStorage->suppressed_issues[] = 'MissingConstructor';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* {@inheritDoc}
|
|
|
|
*/
|
|
|
|
public static function afterStatementAnalysis(
|
|
|
|
ClassLike $classNode,
|
|
|
|
ClassLikeStorage $classStorage,
|
|
|
|
StatementsSource $statements_source,
|
|
|
|
Codebase $codebase,
|
|
|
|
array &$file_replacements = []
|
|
|
|
) {
|
|
|
|
foreach ($classStorage->methods as $method_name => $method_storage) {
|
|
|
|
if (!$method_storage->location) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$stmt_method = $classNode->getMethod($method_name);
|
|
|
|
|
|
|
|
if (!$stmt_method) {
|
|
|
|
throw new \RuntimeException('Failed to find ' . $method_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
$specials = self::getSpecials($stmt_method);
|
|
|
|
|
|
|
|
if (!isset($specials['dataProvider'])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($specials['dataProvider'] as $line => $provider) {
|
|
|
|
$provider_method_id = $classStorage->name . '::' . (string) $provider;
|
|
|
|
|
|
|
|
if (!$codebase->methodExists($provider_method_id)) {
|
|
|
|
$location = clone $method_storage->location;
|
|
|
|
$location->setCommentLine($line);
|
|
|
|
|
|
|
|
IssueBuffer::accepts(new UndefinedMethod(
|
|
|
|
'Provider method ' . $provider_method_id . ' is not defined',
|
|
|
|
$location,
|
|
|
|
$provider_method_id
|
|
|
|
));
|
|
|
|
}
|
2019-02-17 07:28:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-18 16:51:21 +01:00
|
|
|
|
2019-02-17 07:28:22 +01:00
|
|
|
private static function hasInitializers(ClassLikeStorage $storage, ClassLike $stmt): bool
|
|
|
|
{
|
|
|
|
if (isset($storage->methods['setup'])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($storage->methods as $method => $_) {
|
|
|
|
$stmt_method = $stmt->getMethod($method);
|
|
|
|
if (!$stmt_method) {
|
|
|
|
throw new \RuntimeException('Failed to find ' . $method);
|
|
|
|
}
|
|
|
|
if (self::isBeforeInitializer($stmt_method)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static function isBeforeInitializer(ClassMethod $method): bool
|
|
|
|
{
|
2019-02-18 16:51:21 +01:00
|
|
|
$specials = self::getSpecials($method);
|
|
|
|
return isset($specials['before']);
|
|
|
|
}
|
2019-02-17 07:28:22 +01:00
|
|
|
|
2019-02-18 16:51:21 +01:00
|
|
|
/** @return array<string, array<int,string>> */
|
|
|
|
private static function getSpecials(ClassMethod $method): array
|
|
|
|
{
|
|
|
|
$docblock = $method->getDocComment();
|
2019-02-17 07:28:22 +01:00
|
|
|
|
2019-02-18 16:51:21 +01:00
|
|
|
if ($docblock) {
|
|
|
|
$parsed_comment = DocComment::parse((string)$docblock->getReformattedText(), $docblock->getLine());
|
|
|
|
if (isset($parsed_comment['specials'])) {
|
|
|
|
return $parsed_comment['specials'];
|
2019-02-17 07:28:22 +01:00
|
|
|
}
|
|
|
|
}
|
2019-02-18 16:51:21 +01:00
|
|
|
return [];
|
2019-02-17 07:28:22 +01:00
|
|
|
}
|
|
|
|
}
|