Updated Rector to commit 81d79a85ee8b75e8e61800aefffb3286e870109b

81d79a85ee [PHPStan] Fix Multiple services of type PHPStan\Rules\PHPUnit\CoversHelper on require phpstan-phpunit ^1.2.1 (#3020)
This commit is contained in:
Tomas Votruba 2022-10-28 09:01:53 +00:00
parent 52256749df
commit 34ee94e559
16 changed files with 402 additions and 24 deletions

View File

@ -17,12 +17,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '40ab9ab2b4d529c8d465d5730c29b09efd5ed3ba';
public const PACKAGE_VERSION = '81d79a85ee8b75e8e61800aefffb3286e870109b';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2022-10-27 19:39:22';
public const RELEASE_DATE = '2022-10-28 10:55:56';
/**
* @var int
*/

2
vendor/autoload.php vendored
View File

@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitc793b2903aefa0a9aada52e902460e8f::getLoader();
return ComposerAutoloaderInit98ae6f54e9a394b892c328c6cc59d05a::getLoader();

View File

@ -71,11 +71,17 @@ return array(
'PHPStan\\PhpDocParser\\Parser\\TokenIterator' => $vendorDir . '/phpstan/phpdoc-parser/src/Parser/TokenIterator.php',
'PHPStan\\PhpDocParser\\Parser\\TypeParser' => $vendorDir . '/phpstan/phpdoc-parser/src/Parser/TypeParser.php',
'PHPStan\\PhpDoc\\PHPUnit\\MockObjectTypeNodeResolverExtension' => $vendorDir . '/phpstan/phpstan-phpunit/src/PhpDoc/PHPUnit/MockObjectTypeNodeResolverExtension.php',
'PHPStan\\Rules\\PHPUnit\\AnnotationHelper' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AnnotationHelper.php',
'PHPStan\\Rules\\PHPUnit\\AssertRuleHelper' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertRuleHelper.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameBooleanExpectedRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameBooleanExpectedRule.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameNullExpectedRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameNullExpectedRule.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameWithCountRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameWithCountRule.php',
'PHPStan\\Rules\\PHPUnit\\ClassCoversExistsRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ClassCoversExistsRule.php',
'PHPStan\\Rules\\PHPUnit\\ClassMethodCoversExistsRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ClassMethodCoversExistsRule.php',
'PHPStan\\Rules\\PHPUnit\\CoversHelper' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/CoversHelper.php',
'PHPStan\\Rules\\PHPUnit\\MockMethodCallRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/MockMethodCallRule.php',
'PHPStan\\Rules\\PHPUnit\\NoMissingSpaceInClassAnnotationRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/NoMissingSpaceInClassAnnotationRule.php',
'PHPStan\\Rules\\PHPUnit\\NoMissingSpaceInMethodAnnotationRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/NoMissingSpaceInMethodAnnotationRule.php',
'PHPStan\\Rules\\PHPUnit\\ShouldCallParentMethodsRule' => $vendorDir . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ShouldCallParentMethodsRule.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertFunctionTypeSpecifyingExtension' => $vendorDir . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertFunctionTypeSpecifyingExtension.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertMethodTypeSpecifyingExtension' => $vendorDir . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertMethodTypeSpecifyingExtension.php',

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitc793b2903aefa0a9aada52e902460e8f
class ComposerAutoloaderInit98ae6f54e9a394b892c328c6cc59d05a
{
private static $loader;
@ -22,19 +22,19 @@ class ComposerAutoloaderInitc793b2903aefa0a9aada52e902460e8f
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitc793b2903aefa0a9aada52e902460e8f', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit98ae6f54e9a394b892c328c6cc59d05a', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInitc793b2903aefa0a9aada52e902460e8f', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit98ae6f54e9a394b892c328c6cc59d05a', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitc793b2903aefa0a9aada52e902460e8f::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit98ae6f54e9a394b892c328c6cc59d05a::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$includeFiles = \Composer\Autoload\ComposerStaticInitc793b2903aefa0a9aada52e902460e8f::$files;
$includeFiles = \Composer\Autoload\ComposerStaticInit98ae6f54e9a394b892c328c6cc59d05a::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequirec793b2903aefa0a9aada52e902460e8f($fileIdentifier, $file);
composerRequire98ae6f54e9a394b892c328c6cc59d05a($fileIdentifier, $file);
}
return $loader;
@ -46,7 +46,7 @@ class ComposerAutoloaderInitc793b2903aefa0a9aada52e902460e8f
* @param string $file
* @return void
*/
function composerRequirec793b2903aefa0a9aada52e902460e8f($fileIdentifier, $file)
function composerRequire98ae6f54e9a394b892c328c6cc59d05a($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

View File

@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInitc793b2903aefa0a9aada52e902460e8f
class ComposerStaticInit98ae6f54e9a394b892c328c6cc59d05a
{
public static $files = array (
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
@ -321,11 +321,17 @@ class ComposerStaticInitc793b2903aefa0a9aada52e902460e8f
'PHPStan\\PhpDocParser\\Parser\\TokenIterator' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Parser/TokenIterator.php',
'PHPStan\\PhpDocParser\\Parser\\TypeParser' => __DIR__ . '/..' . '/phpstan/phpdoc-parser/src/Parser/TypeParser.php',
'PHPStan\\PhpDoc\\PHPUnit\\MockObjectTypeNodeResolverExtension' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/PhpDoc/PHPUnit/MockObjectTypeNodeResolverExtension.php',
'PHPStan\\Rules\\PHPUnit\\AnnotationHelper' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AnnotationHelper.php',
'PHPStan\\Rules\\PHPUnit\\AssertRuleHelper' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertRuleHelper.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameBooleanExpectedRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameBooleanExpectedRule.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameNullExpectedRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameNullExpectedRule.php',
'PHPStan\\Rules\\PHPUnit\\AssertSameWithCountRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/AssertSameWithCountRule.php',
'PHPStan\\Rules\\PHPUnit\\ClassCoversExistsRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ClassCoversExistsRule.php',
'PHPStan\\Rules\\PHPUnit\\ClassMethodCoversExistsRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ClassMethodCoversExistsRule.php',
'PHPStan\\Rules\\PHPUnit\\CoversHelper' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/CoversHelper.php',
'PHPStan\\Rules\\PHPUnit\\MockMethodCallRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/MockMethodCallRule.php',
'PHPStan\\Rules\\PHPUnit\\NoMissingSpaceInClassAnnotationRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/NoMissingSpaceInClassAnnotationRule.php',
'PHPStan\\Rules\\PHPUnit\\NoMissingSpaceInMethodAnnotationRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/NoMissingSpaceInMethodAnnotationRule.php',
'PHPStan\\Rules\\PHPUnit\\ShouldCallParentMethodsRule' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Rules/PHPUnit/ShouldCallParentMethodsRule.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertFunctionTypeSpecifyingExtension' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertFunctionTypeSpecifyingExtension.php',
'PHPStan\\Type\\PHPUnit\\Assert\\AssertMethodTypeSpecifyingExtension' => __DIR__ . '/..' . '/phpstan/phpstan-phpunit/src/Type/PHPUnit/Assert/AssertMethodTypeSpecifyingExtension.php',
@ -3041,9 +3047,9 @@ class ComposerStaticInitc793b2903aefa0a9aada52e902460e8f
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitc793b2903aefa0a9aada52e902460e8f::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitc793b2903aefa0a9aada52e902460e8f::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitc793b2903aefa0a9aada52e902460e8f::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit98ae6f54e9a394b892c328c6cc59d05a::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit98ae6f54e9a394b892c328c6cc59d05a::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit98ae6f54e9a394b892c328c6cc59d05a::$classMap;
}, null, ClassLoader::class);
}

View File

@ -795,22 +795,22 @@
},
{
"name": "phpstan\/phpstan-phpunit",
"version": "1.1.3",
"version_normalized": "1.1.3.0",
"version": "1.2.1",
"version_normalized": "1.2.1.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/phpstan\/phpstan-phpunit.git",
"reference": "6b93db7fae6d6f3e81a5b4297f93af6fe4146785"
"reference": "09b5c9ab38d4d601bcff04b4c9240832b86f5dda"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpstan-phpunit\/zipball\/6b93db7fae6d6f3e81a5b4297f93af6fe4146785",
"reference": "6b93db7fae6d6f3e81a5b4297f93af6fe4146785",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpstan-phpunit\/zipball\/09b5c9ab38d4d601bcff04b4c9240832b86f5dda",
"reference": "09b5c9ab38d4d601bcff04b4c9240832b86f5dda",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan\/phpstan": "^1.8.0"
"phpstan\/phpstan": "^1.8.11"
},
"conflict": {
"phpunit\/phpunit": "<7.0"
@ -821,7 +821,7 @@
"phpstan\/phpstan-strict-rules": "^1.0",
"phpunit\/phpunit": "^9.5"
},
"time": "2022-10-24T11:38:17+00:00",
"time": "2022-10-26T19:38:49+00:00",
"type": "phpstan-extension",
"extra": {
"phpstan": {
@ -844,7 +844,7 @@
"description": "PHPUnit extensions and rules for PHPStan",
"support": {
"issues": "https:\/\/github.com\/phpstan\/phpstan-phpunit\/issues",
"source": "https:\/\/github.com\/phpstan\/phpstan-phpunit\/tree\/1.1.3"
"source": "https:\/\/github.com\/phpstan\/phpstan-phpunit\/tree\/1.2.1"
},
"install-path": "..\/phpstan\/phpstan-phpunit"
},

File diff suppressed because one or more lines are too long

View File

@ -7,7 +7,7 @@
],
"require": {
"php": "^7.2 || ^8.0",
"phpstan\/phpstan": "^1.8.0"
"phpstan\/phpstan": "^1.8.11"
},
"conflict": {
"phpunit\/phpunit": "<7.0"

View File

@ -51,6 +51,10 @@ services:
class: PHPStan\Type\PHPUnit\MockObjectDynamicReturnTypeExtension
tags:
- phpstan.broker.dynamicMethodReturnTypeExtension
-
class: PHPStan\Rules\PHPUnit\CoversHelper
-
class: PHPStan\Rules\PHPUnit\AnnotationHelper
conditionalTags:
PHPStan\PhpDoc\PHPUnit\MockObjectTypeNodeResolverExtension:

View File

@ -4,3 +4,19 @@ rules:
- PHPStan\Rules\PHPUnit\AssertSameWithCountRule
- PHPStan\Rules\PHPUnit\MockMethodCallRule
- PHPStan\Rules\PHPUnit\ShouldCallParentMethodsRule
services:
- class: PHPStan\Rules\PHPUnit\ClassCoversExistsRule
- class: PHPStan\Rules\PHPUnit\ClassMethodCoversExistsRule
- class: PHPStan\Rules\PHPUnit\NoMissingSpaceInClassAnnotationRule
- class: PHPStan\Rules\PHPUnit\NoMissingSpaceInMethodAnnotationRule
conditionalTags:
PHPStan\Rules\PHPUnit\ClassCoversExistsRule:
phpstan.rules.rule: %featureToggles.bleedingEdge%
PHPStan\Rules\PHPUnit\ClassMethodCoversExistsRule:
phpstan.rules.rule: %featureToggles.bleedingEdge%
PHPStan\Rules\PHPUnit\NoMissingSpaceInClassAnnotationRule:
phpstan.rules.rule: %featureToggles.bleedingEdge%
PHPStan\Rules\PHPUnit\NoMissingSpaceInMethodAnnotationRule:
phpstan.rules.rule: %featureToggles.bleedingEdge%

View File

@ -0,0 +1,43 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Comment\Doc;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use function array_key_exists;
use function in_array;
use function preg_match;
use function preg_split;
class AnnotationHelper
{
private const ANNOTATIONS_WITH_PARAMS = ['backupGlobals', 'backupStaticAttributes', 'covers', 'coversDefaultClass', 'dataProvider', 'depends', 'group', 'preserveGlobalState', 'requires', 'testDox', 'testWith', 'ticket', 'uses'];
/**
* @return RuleError[] errors
*/
public function processDocComment(Doc $docComment) : array
{
$errors = [];
$docCommentLines = preg_split("/((\r?\n)|(\r\n?))/", $docComment->getText());
if ($docCommentLines === \false) {
return [];
}
foreach ($docCommentLines as $docCommentLine) {
// These annotations can't be retrieved using the getResolvedPhpDoc method on the FileTypeMapper as they are not present when they are invalid
$annotation = preg_match('/(?<annotation>@(?<property>[a-zA-Z]+)(?<whitespace>\\s*)(?<value>.*))/', $docCommentLine, $matches);
if ($annotation === \false) {
continue;
// Line without annotation
}
if (array_key_exists('property', $matches) === \false || array_key_exists('whitespace', $matches) === \false || array_key_exists('annotation', $matches) === \false) {
continue;
}
if (!in_array($matches['property'], self::ANNOTATIONS_WITH_PARAMS, \true) || $matches['whitespace'] !== '') {
continue;
}
$errors[] = RuleErrorBuilder::message('Annotation "' . $matches['annotation'] . '" is invalid, "@' . $matches['property'] . '" should be followed by a space and a value.')->build();
}
return $errors;
}
}

View File

@ -0,0 +1,68 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassNode;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPUnit\Framework\TestCase;
use function array_merge;
use function array_shift;
use function count;
use function sprintf;
/**
* @implements Rule<InClassNode>
*/
class ClassCoversExistsRule implements Rule
{
/**
* Covers helper.
*
* @var CoversHelper
*/
private $coversHelper;
/**
* Reflection provider.
*
* @var ReflectionProvider
*/
private $reflectionProvider;
public function __construct(\PHPStan\Rules\PHPUnit\CoversHelper $coversHelper, ReflectionProvider $reflectionProvider)
{
$this->reflectionProvider = $reflectionProvider;
$this->coversHelper = $coversHelper;
}
public function getNodeType() : string
{
return InClassNode::class;
}
public function processNode(Node $node, Scope $scope) : array
{
$classReflection = $node->getClassReflection();
if (!$classReflection->isSubclassOf(TestCase::class)) {
return [];
}
$errors = [];
$classPhpDoc = $classReflection->getResolvedPhpDoc();
[$classCovers, $classCoversDefaultClasses] = $this->coversHelper->getCoverAnnotations($classPhpDoc);
if (count($classCoversDefaultClasses) >= 2) {
$errors[] = RuleErrorBuilder::message(sprintf('@coversDefaultClass is defined multiple times.'))->build();
return $errors;
}
$coversDefaultClass = array_shift($classCoversDefaultClasses);
if ($coversDefaultClass !== null) {
$className = (string) $coversDefaultClass->value;
if (!$this->reflectionProvider->hasClass($className)) {
$errors[] = RuleErrorBuilder::message(sprintf('@coversDefaultClass references an invalid class %s.', $className))->build();
}
}
foreach ($classCovers as $covers) {
$errors = array_merge($errors, $this->coversHelper->processCovers($node, $covers, null));
}
return $errors;
}
}

View File

@ -0,0 +1,79 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\FileTypeMapper;
use PHPUnit\Framework\TestCase;
use function array_map;
use function array_merge;
use function array_shift;
use function count;
use function in_array;
use function sprintf;
/**
* @implements Rule<Node\Stmt\ClassMethod>
*/
class ClassMethodCoversExistsRule implements Rule
{
/**
* Covers helper.
*
* @var CoversHelper
*/
private $coversHelper;
/**
* The file type mapper.
*
* @var FileTypeMapper
*/
private $fileTypeMapper;
public function __construct(\PHPStan\Rules\PHPUnit\CoversHelper $coversHelper, FileTypeMapper $fileTypeMapper)
{
$this->coversHelper = $coversHelper;
$this->fileTypeMapper = $fileTypeMapper;
}
public function getNodeType() : string
{
return Node\Stmt\ClassMethod::class;
}
public function processNode(Node $node, Scope $scope) : array
{
$classReflection = $scope->getClassReflection();
if ($classReflection === null) {
return [];
}
if (!$classReflection->isSubclassOf(TestCase::class)) {
return [];
}
$errors = [];
$classPhpDoc = $classReflection->getResolvedPhpDoc();
[$classCovers, $classCoversDefaultClasses] = $this->coversHelper->getCoverAnnotations($classPhpDoc);
$classCoversStrings = array_map(static function (PhpDocTagNode $covers) : string {
return (string) $covers->value;
}, $classCovers);
$docComment = $node->getDocComment();
if ($docComment === null) {
return [];
}
$coversDefaultClass = count($classCoversDefaultClasses) === 1 ? array_shift($classCoversDefaultClasses) : null;
$methodPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc($scope->getFile(), $classReflection->getName(), $scope->isInTrait() ? $scope->getTraitReflection()->getName() : null, $node->name->toString(), $docComment->getText());
[$methodCovers, $methodCoversDefaultClasses] = $this->coversHelper->getCoverAnnotations($methodPhpDoc);
$errors = [];
if (count($methodCoversDefaultClasses) > 0) {
$errors[] = RuleErrorBuilder::message(sprintf('@coversDefaultClass defined on class method %s.', $node->name))->build();
}
foreach ($methodCovers as $covers) {
if (in_array((string) $covers->value, $classCoversStrings, \true)) {
$errors[] = RuleErrorBuilder::message(sprintf('Class already @covers %s so the method @covers is redundant.', $covers->value))->build();
}
$errors = array_merge($errors, $this->coversHelper->processCovers($node, $covers, $coversDefaultClass));
}
return $errors;
}
}

View File

@ -0,0 +1,72 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use function array_merge;
use function explode;
use function sprintf;
use function strpos;
class CoversHelper
{
/**
* Reflection provider.
*
* @var ReflectionProvider
*/
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
{
$this->reflectionProvider = $reflectionProvider;
}
/**
* Gathers @covers and @coversDefaultClass annotations from phpdocs.
*
* @return array{PhpDocTagNode[], PhpDocTagNode[]}
*/
public function getCoverAnnotations(?ResolvedPhpDocBlock $phpDoc) : array
{
if ($phpDoc === null) {
return [[], []];
}
$phpDocNodes = $phpDoc->getPhpDocNodes();
$covers = [];
$coversDefaultClasses = [];
foreach ($phpDocNodes as $docNode) {
$covers = array_merge($covers, $docNode->getTagsByName('@covers'));
$coversDefaultClasses = array_merge($coversDefaultClasses, $docNode->getTagsByName('@coversDefaultClass'));
}
return [$covers, $coversDefaultClasses];
}
/**
* @return RuleError[] errors
*/
public function processCovers(Node $node, PhpDocTagNode $phpDocTag, ?PhpDocTagNode $coversDefaultClass) : array
{
$errors = [];
$covers = (string) $phpDocTag->value;
if (strpos($covers, '::') !== \false) {
[$className, $method] = explode('::', $covers);
} else {
$className = $covers;
}
if ($className === '' && $node instanceof Node\Stmt\ClassMethod && $coversDefaultClass !== null) {
$className = (string) $coversDefaultClass->value;
}
if ($this->reflectionProvider->hasClass($className)) {
$class = $this->reflectionProvider->getClass($className);
if (isset($method) && $method !== '' && !$class->hasMethod($method)) {
$errors[] = RuleErrorBuilder::message(sprintf('@covers value %s references an invalid method.', $covers))->build();
}
} else {
$errors[] = RuleErrorBuilder::message(sprintf('@covers value %s references an invalid class.', $covers))->build();
}
return $errors;
}
}

View File

@ -0,0 +1,42 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassNode;
use PHPStan\Rules\Rule;
use PHPUnit\Framework\TestCase;
/**
* @implements Rule<InClassNode>
*/
class NoMissingSpaceInClassAnnotationRule implements Rule
{
/**
* Covers helper.
*
* @var AnnotationHelper
*/
private $annotationHelper;
public function __construct(\PHPStan\Rules\PHPUnit\AnnotationHelper $annotationHelper)
{
$this->annotationHelper = $annotationHelper;
}
public function getNodeType() : string
{
return InClassNode::class;
}
public function processNode(Node $node, Scope $scope) : array
{
$classReflection = $scope->getClassReflection();
if ($classReflection === null || $classReflection->isSubclassOf(TestCase::class) === \false) {
return [];
}
$docComment = $node->getDocComment();
if ($docComment === null) {
return [];
}
return $this->annotationHelper->processDocComment($docComment);
}
}

View File

@ -0,0 +1,42 @@
<?php
declare (strict_types=1);
namespace PHPStan\Rules\PHPUnit;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassMethodNode;
use PHPStan\Rules\Rule;
use PHPUnit\Framework\TestCase;
/**
* @implements Rule<InClassMethodNode>
*/
class NoMissingSpaceInMethodAnnotationRule implements Rule
{
/**
* Covers helper.
*
* @var AnnotationHelper
*/
private $annotationHelper;
public function __construct(\PHPStan\Rules\PHPUnit\AnnotationHelper $annotationHelper)
{
$this->annotationHelper = $annotationHelper;
}
public function getNodeType() : string
{
return InClassMethodNode::class;
}
public function processNode(Node $node, Scope $scope) : array
{
$classReflection = $scope->getClassReflection();
if ($classReflection === null || $classReflection->isSubclassOf(TestCase::class) === \false) {
return [];
}
$docComment = $node->getDocComment();
if ($docComment === null) {
return [];
}
return $this->annotationHelper->processDocComment($docComment);
}
}