Updated Rector to commit d80ddd193deeebd47e765dfb58d300faaedc25b4

d80ddd193d [CodeQuality] Handle possible crash on ParametersAcceptorSelector::selectSingle() on OptionalParametersAfterRequiredRector (#3192)
This commit is contained in:
Tomas Votruba 2022-12-13 12:09:37 +00:00
parent d2059134fe
commit 04232cb88d
26 changed files with 147 additions and 453 deletions

View File

@ -7,7 +7,7 @@
],
"require": {
"php": "^7.2|^8.0",
"phpstan/phpstan": "^1.9.2"
"phpstan/phpstan": "^1.9.3"
},
"autoload": {
"files": [

View File

@ -4,6 +4,7 @@ declare (strict_types=1);
namespace Rector\NodeTypeResolver\PHPStan;
use PhpParser\Node\Expr\CallLike;
use PhpParser\Node\FunctionLike;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
@ -13,13 +14,17 @@ final class ParametersAcceptorSelectorVariantsWrapper
{
/**
* @param \PHPStan\Reflection\FunctionReflection|\PHPStan\Reflection\MethodReflection $reflection
* @param \PhpParser\Node\Expr\CallLike|\PhpParser\Node\FunctionLike $node
*/
public static function select($reflection, CallLike $callLike, Scope $scope) : ParametersAcceptor
public static function select($reflection, $node, Scope $scope) : ParametersAcceptor
{
$variants = $reflection->getVariants();
if ($callLike->isFirstClassCallable()) {
if ($node instanceof FunctionLike) {
return ParametersAcceptorSelector::selectSingle($variants);
}
return \count($variants) > 1 ? ParametersAcceptorSelector::selectFromArgs($scope, $callLike->getArgs(), $variants) : ParametersAcceptorSelector::selectSingle($variants);
if ($node->isFirstClassCallable()) {
return ParametersAcceptorSelector::selectSingle($variants);
}
return \count($variants) > 1 ? ParametersAcceptorSelector::selectFromArgs($scope, $node->getArgs(), $variants) : ParametersAcceptorSelector::selectSingle($variants);
}
}

View File

@ -92,7 +92,7 @@ final class TypeHasher
if ($currentType instanceof AliasedObjectType) {
return new FullyQualifiedObjectType($currentType->getFullyQualifiedName());
}
if ($currentType instanceof ObjectType && !$currentType instanceof GenericObjectType && !$currentType instanceof AliasedObjectType && $currentType->getClassName() !== 'Iterator' && $currentType->getClassName() !== 'iterable') {
if ($currentType instanceof ObjectType && !$currentType instanceof GenericObjectType && $currentType->getClassName() !== 'Iterator' && $currentType->getClassName() !== 'iterable') {
return new FullyQualifiedObjectType($currentType->getClassName());
}
return $traverseCallback($currentType);

View File

@ -5,7 +5,9 @@ namespace Rector\CodeQuality\Rector\ClassConstFetch;
use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use Rector\Core\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -43,7 +45,7 @@ CODE_SAMPLE
return [ClassConstFetch::class];
}
/**
* @param \PhpParser\Node\Expr\ClassConstFetch $node
* @param ClassConstFetch $node
*/
public function refactor(Node $node) : ?ClassConstFetch
{
@ -56,24 +58,24 @@ CODE_SAMPLE
$node->class = new Name('self');
return $node;
}
private function isUsingStatic(ClassConstFetch $node) : bool
private function isUsingStatic(ClassConstFetch $classConstFetch) : bool
{
if (!$node->class instanceof Name) {
if (!$classConstFetch->class instanceof Name) {
return \false;
}
return $node->class->toString() === 'static';
return $classConstFetch->class->toString() === 'static';
}
private function isPrivateConstant(ClassConstFetch $node) : bool
private function isPrivateConstant(ClassConstFetch $classConstFetch) : bool
{
$class = $this->betterNodeFinder->findParentType($node, Node\Stmt\Class_::class);
if (!$class instanceof Node\Stmt\Class_) {
$class = $this->betterNodeFinder->findParentType($classConstFetch, Class_::class);
if (!$class instanceof Class_) {
return \false;
}
if (!$class->isFinal()) {
return \false;
}
$constantName = $node->name;
if (!$constantName instanceof Node\Identifier) {
$constantName = $classConstFetch->name;
if (!$constantName instanceof Identifier) {
return \false;
}
foreach ($class->getConstants() as $classConst) {

View File

@ -10,11 +10,9 @@ use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use Rector\CodingStyle\Reflection\VendorLocationDetector;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper;
use Rector\Php80\NodeResolver\ArgumentSorter;
use Rector\Php80\NodeResolver\RequireOptionalParamResolver;
@ -25,7 +23,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*
* @see \Rector\Tests\CodeQuality\Rector\ClassMethod\OptionalParametersAfterRequiredRector\OptionalParametersAfterRequiredRectorTest
*/
final class OptionalParametersAfterRequiredRector extends AbstractRector
final class OptionalParametersAfterRequiredRector extends AbstractScopeAwareRector
{
/**
* @readonly
@ -84,17 +82,17 @@ CODE_SAMPLE
/**
* @param ClassMethod|New_|MethodCall|StaticCall $node
*/
public function refactor(Node $node) : ?Node
public function refactorWithScope(Node $node, Scope $scope)
{
if ($node instanceof ClassMethod) {
return $this->refactorClassMethod($node);
return $this->refactorClassMethod($node, $scope);
}
if ($node instanceof New_) {
return $this->refactorNew($node);
return $this->refactorNew($node, $scope);
}
return $this->refactorMethodCall($node);
return $this->refactorMethodCall($node, $scope);
}
private function refactorClassMethod(ClassMethod $classMethod) : ?ClassMethod
private function refactorClassMethod(ClassMethod $classMethod, Scope $scope) : ?ClassMethod
{
if ($classMethod->params === []) {
return null;
@ -103,14 +101,14 @@ CODE_SAMPLE
if (!$classMethodReflection instanceof MethodReflection) {
return null;
}
$expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($classMethodReflection, $classMethod);
$expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($classMethodReflection, $classMethod, $scope);
if ($expectedArgOrParamOrder === null) {
return null;
}
$classMethod->params = $this->argumentSorter->sortArgsByExpectedParamOrder($classMethod->params, $expectedArgOrParamOrder);
return $classMethod;
}
private function refactorNew(New_ $new) : ?New_
private function refactorNew(New_ $new, Scope $scope) : ?New_
{
if ($new->args === []) {
return null;
@ -119,7 +117,7 @@ CODE_SAMPLE
if (!$methodReflection instanceof MethodReflection) {
return null;
}
$expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection, $new);
$expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection, $new, $scope);
if ($expectedArgOrParamOrder === null) {
return null;
}
@ -130,13 +128,13 @@ CODE_SAMPLE
* @param \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall $methodCall
* @return \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall|null
*/
private function refactorMethodCall($methodCall)
private function refactorMethodCall($methodCall, Scope $scope)
{
$methodReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($methodCall);
if (!$methodReflection instanceof MethodReflection) {
return null;
}
$expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection, $methodCall);
$expectedArgOrParamOrder = $this->resolveExpectedArgParamOrderIfDifferent($methodReflection, $methodCall, $scope);
if ($expectedArgOrParamOrder === null) {
return null;
}
@ -151,21 +149,13 @@ CODE_SAMPLE
* @return int[]|null
* @param \PhpParser\Node\Expr\New_|\PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\StaticCall $node
*/
private function resolveExpectedArgParamOrderIfDifferent(MethodReflection $methodReflection, $node) : ?array
private function resolveExpectedArgParamOrderIfDifferent(MethodReflection $methodReflection, $node, Scope $scope) : ?array
{
if ($this->vendorLocationDetector->detectMethodReflection($methodReflection)) {
return null;
}
if ($node instanceof ClassMethod) {
$parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants());
} else {
$scope = $node->getAttribute(AttributeKey::SCOPE);
if (!$scope instanceof Scope) {
return null;
}
$parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select($methodReflection, $node, $scope);
}
$expectedParameterReflections = $this->requireOptionalParamResolver->resolveFromReflection($methodReflection);
$parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select($methodReflection, $node, $scope);
$expectedParameterReflections = $this->requireOptionalParamResolver->resolveFromParametersAcceptor($parametersAcceptor);
if ($expectedParameterReflections === $parametersAcceptor->getParameters()) {
return null;
}

View File

@ -3,17 +3,15 @@
declare (strict_types=1);
namespace Rector\Php80\NodeResolver;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\ParametersAcceptor;
final class RequireOptionalParamResolver
{
/**
* @return ParameterReflection[]
*/
public function resolveFromReflection(MethodReflection $methodReflection) : array
public function resolveFromParametersAcceptor(ParametersAcceptor $parametersAcceptor) : array
{
$parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants());
$optionalParams = [];
$requireParams = [];
foreach ($parametersAcceptor->getParameters() as $position => $parameterReflection) {

View File

@ -17,12 +17,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = 'de008767d91ad6cd5ed77bdc259454665e4bbde9';
public const PACKAGE_VERSION = 'd80ddd193deeebd47e765dfb58d300faaedc25b4';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2022-12-12 19:25:03';
public const RELEASE_DATE = '2022-12-13 13:05:23';
/**
* @var int
*/

View File

@ -231,14 +231,15 @@ CODE_SAMPLE;
/** @var MutatingScope|null $currentScope */
$currentScope = $originalNode->getAttribute(AttributeKey::SCOPE);
$filePath = $this->file->getFilePath();
// search "infinite recursion" in https://github.com/nikic/PHP-Parser/blob/master/doc/component/Walking_the_AST.markdown
$originalNodeHash = \spl_object_hash($originalNode);
if (\is_array($refactoredNode)) {
$originalNodeHash = \spl_object_hash($originalNode);
$this->nodesToReturn[$originalNodeHash] = $refactoredNode;
$firstNode = \current($refactoredNode);
$this->mirrorComments($firstNode, $originalNode);
$this->updateAndconnectParentNodes($refactoredNode, $parentNode);
$this->connectNodes($refactoredNode, $node);
$this->refreshScopeNodes($refactoredNode, $filePath, $currentScope);
$this->nodesToReturn[$originalNodeHash] = $refactoredNode;
// will be replaced in leaveNode() the original node must be passed
return $originalNode;
}
@ -246,12 +247,6 @@ CODE_SAMPLE;
$this->updateAndconnectParentNodes($refactoredNode, $parentNode);
$this->connectNodes([$refactoredNode], $node);
$this->refreshScopeNodes($refactoredNode, $filePath, $currentScope);
// is equals node type? return node early
if (\get_class($originalNode) === \get_class($refactoredNode)) {
return $refactoredNode;
}
// search "infinite recursion" in https://github.com/nikic/PHP-Parser/blob/master/doc/component/Walking_the_AST.markdown
$originalNodeHash = \spl_object_hash($originalNode);
$this->nodesToReturn[$originalNodeHash] = $refactoredNode;
return $refactoredNode;
}

2
vendor/autoload.php vendored
View File

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

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit3f30daaa94190933b9e2885c83a2ecbe
class ComposerAutoloaderInit8773007a1f91d515754b188a5237304c
{
private static $loader;
@ -22,19 +22,19 @@ class ComposerAutoloaderInit3f30daaa94190933b9e2885c83a2ecbe
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit3f30daaa94190933b9e2885c83a2ecbe', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit8773007a1f91d515754b188a5237304c', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit3f30daaa94190933b9e2885c83a2ecbe', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit8773007a1f91d515754b188a5237304c', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit3f30daaa94190933b9e2885c83a2ecbe::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit8773007a1f91d515754b188a5237304c::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$includeFiles = \Composer\Autoload\ComposerStaticInit3f30daaa94190933b9e2885c83a2ecbe::$files;
$includeFiles = \Composer\Autoload\ComposerStaticInit8773007a1f91d515754b188a5237304c::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire3f30daaa94190933b9e2885c83a2ecbe($fileIdentifier, $file);
composerRequire8773007a1f91d515754b188a5237304c($fileIdentifier, $file);
}
return $loader;
@ -46,7 +46,7 @@ class ComposerAutoloaderInit3f30daaa94190933b9e2885c83a2ecbe
* @param string $file
* @return void
*/
function composerRequire3f30daaa94190933b9e2885c83a2ecbe($fileIdentifier, $file)
function composerRequire8773007a1f91d515754b188a5237304c($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 ComposerStaticInit3f30daaa94190933b9e2885c83a2ecbe
class ComposerStaticInit8773007a1f91d515754b188a5237304c
{
public static $files = array (
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
@ -3027,9 +3027,9 @@ class ComposerStaticInit3f30daaa94190933b9e2885c83a2ecbe
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit3f30daaa94190933b9e2885c83a2ecbe::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit3f30daaa94190933b9e2885c83a2ecbe::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit3f30daaa94190933b9e2885c83a2ecbe::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit8773007a1f91d515754b188a5237304c::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit8773007a1f91d515754b188a5237304c::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit8773007a1f91d515754b188a5237304c::$classMap;
}, null, ClassLoader::class);
}

View File

@ -733,17 +733,17 @@
},
{
"name": "phpstan\/phpstan",
"version": "1.9.2",
"version_normalized": "1.9.2.0",
"version": "1.9.3",
"version_normalized": "1.9.3.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/phpstan\/phpstan.git",
"reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa"
"reference": "709999b91448d4f2bb07daffffedc889b33e461c"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpstan\/zipball\/d6fdf01c53978b6429f1393ba4afeca39cc68afa",
"reference": "d6fdf01c53978b6429f1393ba4afeca39cc68afa",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpstan\/zipball\/709999b91448d4f2bb07daffffedc889b33e461c",
"reference": "709999b91448d4f2bb07daffffedc889b33e461c",
"shasum": ""
},
"require": {
@ -752,7 +752,7 @@
"conflict": {
"phpstan\/phpstan-shim": "*"
},
"time": "2022-11-10T09:56:11+00:00",
"time": "2022-12-13T10:28:10+00:00",
"bin": [
"phpstan",
"phpstan.phar"
@ -775,7 +775,7 @@
],
"support": {
"issues": "https:\/\/github.com\/phpstan\/phpstan\/issues",
"source": "https:\/\/github.com\/phpstan\/phpstan\/tree\/1.9.2"
"source": "https:\/\/github.com\/phpstan\/phpstan\/tree\/1.9.3"
},
"funding": [
{
@ -795,22 +795,22 @@
},
{
"name": "phpstan\/phpstan-phpunit",
"version": "1.3.0",
"version_normalized": "1.3.0.0",
"version": "1.3.1",
"version_normalized": "1.3.1.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/phpstan\/phpstan-phpunit.git",
"reference": "4c06b7e3f2c40081334d86975350dda814bd064a"
"reference": "b9827cf8df2bd97c7c07b1bb27c694ee41052754"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpstan-phpunit\/zipball\/4c06b7e3f2c40081334d86975350dda814bd064a",
"reference": "4c06b7e3f2c40081334d86975350dda814bd064a",
"url": "https:\/\/api.github.com\/repos\/phpstan\/phpstan-phpunit\/zipball\/b9827cf8df2bd97c7c07b1bb27c694ee41052754",
"reference": "b9827cf8df2bd97c7c07b1bb27c694ee41052754",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpstan\/phpstan": "^1.9.0"
"phpstan\/phpstan": "^1.9.3"
},
"conflict": {
"phpunit\/phpunit": "<7.0"
@ -821,7 +821,7 @@
"phpstan\/phpstan-strict-rules": "^1.0",
"phpunit\/phpunit": "^9.5"
},
"time": "2022-12-07T15:46:24+00:00",
"time": "2022-12-12T21:02:25+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.3.0"
"source": "https:\/\/github.com\/phpstan\/phpstan-phpunit\/tree\/1.3.1"
},
"install-path": "..\/phpstan\/phpstan-phpunit"
},

File diff suppressed because one or more lines are too long

View File

@ -1,12 +0,0 @@
{
"require-dev": {
"consistence-community\/coding-standard": "^3.10",
"dealerdirect\/phpcodesniffer-composer-installer": "^0.7.0",
"slevomat\/coding-standard": "^7.0"
},
"config": {
"allow-plugins": {
"dealerdirect\/phpcodesniffer-composer-installer": true
}
}
}

View File

@ -1,322 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "4485bbedba7bcc71ace5f69dbb9b6c47",
"packages": [],
"packages-dev": [
{
"name": "consistence-community/coding-standard",
"version": "3.11.1",
"source": {
"type": "git",
"url": "https://github.com/consistence-community/coding-standard.git",
"reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/4632fead8c9ee8f50044fcbce9f66c797b34c0df",
"reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df",
"shasum": ""
},
"require": {
"php": ">=7.4",
"slevomat/coding-standard": "~7.0",
"squizlabs/php_codesniffer": "~3.6.0"
},
"replace": {
"consistence/coding-standard": "3.10.*"
},
"require-dev": {
"phing/phing": "2.16.4",
"php-parallel-lint/php-parallel-lint": "1.3.0",
"phpunit/phpunit": "9.5.4"
},
"type": "library",
"autoload": {
"psr-4": {
"Consistence\\": [
"Consistence"
]
},
"classmap": [
"Consistence"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Vašek Purchart",
"email": "me@vasekpurchart.cz",
"homepage": "http://vasekpurchart.cz"
}
],
"description": "Consistence - Coding Standard - PHP Code Sniffer rules",
"keywords": [
"Coding Standard",
"PHPCodeSniffer",
"codesniffer",
"coding",
"cs",
"phpcs",
"ruleset",
"sniffer",
"standard"
],
"support": {
"issues": "https://github.com/consistence-community/coding-standard/issues",
"source": "https://github.com/consistence-community/coding-standard/tree/3.11.1"
},
"time": "2021-05-03T18:13:22+00:00"
},
{
"name": "dealerdirect/phpcodesniffer-composer-installer",
"version": "v0.7.2",
"source": {
"type": "git",
"url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git",
"reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
"reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0 || ^2.0",
"php": ">=5.3",
"squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
},
"require-dev": {
"composer/composer": "*",
"php-parallel-lint/php-parallel-lint": "^1.3.1",
"phpcompatibility/php-compatibility": "^9.0"
},
"type": "composer-plugin",
"extra": {
"class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
"autoload": {
"psr-4": {
"Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Franck Nijhof",
"email": "franck.nijhof@dealerdirect.com",
"homepage": "http://www.frenck.nl",
"role": "Developer / IT Manager"
},
{
"name": "Contributors",
"homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors"
}
],
"description": "PHP_CodeSniffer Standards Composer Installer Plugin",
"homepage": "http://www.dealerdirect.com",
"keywords": [
"PHPCodeSniffer",
"PHP_CodeSniffer",
"code quality",
"codesniffer",
"composer",
"installer",
"phpcbf",
"phpcs",
"plugin",
"qa",
"quality",
"standard",
"standards",
"style guide",
"stylecheck",
"tests"
],
"support": {
"issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
"source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
},
"time": "2022-02-04T12:51:07+00:00"
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "981cc368a216c988e862a75e526b6076987d1b50"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/981cc368a216c988e862a75e526b6076987d1b50",
"reference": "981cc368a216c988e862a75e526b6076987d1b50",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.5",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5",
"symfony/process": "^5.2"
},
"type": "library",
"autoload": {
"psr-4": {
"PHPStan\\PhpDocParser\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.5.1"
},
"time": "2022-05-05T11:32:40+00:00"
},
{
"name": "slevomat/coding-standard",
"version": "7.2.1",
"source": {
"type": "git",
"url": "https://github.com/slevomat/coding-standard.git",
"reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slevomat/coding-standard/zipball/aff06ae7a84e4534bf6f821dc982a93a5d477c90",
"reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7",
"php": "^7.2 || ^8.0",
"phpstan/phpdoc-parser": "^1.5.1",
"squizlabs/php_codesniffer": "^3.6.2"
},
"require-dev": {
"phing/phing": "2.17.3",
"php-parallel-lint/php-parallel-lint": "1.3.2",
"phpstan/phpstan": "1.4.10|1.7.1",
"phpstan/phpstan-deprecation-rules": "1.0.0",
"phpstan/phpstan-phpunit": "1.0.0|1.1.1",
"phpstan/phpstan-strict-rules": "1.2.3",
"phpunit/phpunit": "7.5.20|8.5.21|9.5.20"
},
"type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
"dev-master": "7.x-dev"
}
},
"autoload": {
"psr-4": {
"SlevomatCodingStandard\\": "SlevomatCodingStandard"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
"support": {
"issues": "https://github.com/slevomat/coding-standard/issues",
"source": "https://github.com/slevomat/coding-standard/tree/7.2.1"
},
"funding": [
{
"url": "https://github.com/kukulich",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
"type": "tidelift"
}
],
"time": "2022-05-25T10:58:12+00:00"
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.6.2",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "5e4e71592f69da17871dba6e80dd51bce74a351a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a",
"reference": "5e4e71592f69da17871dba6e80dd51bce74a351a",
"shasum": ""
},
"require": {
"ext-simplexml": "*",
"ext-tokenizer": "*",
"ext-xmlwriter": "*",
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0"
},
"bin": [
"bin/phpcs",
"bin/phpcbf"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Greg Sherwood",
"role": "lead"
}
],
"description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
"homepage": "https://github.com/squizlabs/PHP_CodeSniffer",
"keywords": [
"phpcs",
"standards"
],
"support": {
"issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues",
"source": "https://github.com/squizlabs/PHP_CodeSniffer",
"wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
},
"time": "2021-12-12T21:44:58+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.3.0"
}

View File

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

View File

@ -12,6 +12,7 @@ services:
class: PHPStan\Rules\PHPUnit\DataProviderDeclarationRule
arguments:
checkFunctionNameCase: %checkFunctionNameCase%
deprecationRulesInstalled: %deprecationRulesInstalled%
- class: PHPStan\Rules\PHPUnit\NoMissingSpaceInClassAnnotationRule
- class: PHPStan\Rules\PHPUnit\NoMissingSpaceInMethodAnnotationRule

View File

@ -11,7 +11,6 @@ use PhpParser\NodeAbstract;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use function count;
use function strtolower;
/**
* @implements Rule<NodeAbstract>
*/
@ -31,7 +30,7 @@ class AssertSameBooleanExpectedRule implements Rule
if (count($node->getArgs()) < 2) {
return [];
}
if (!$node->name instanceof Node\Identifier || strtolower($node->name->name) !== 'assertsame') {
if (!$node->name instanceof Node\Identifier || $node->name->toLowerString() !== 'assertsame') {
return [];
}
$expectedArgumentValue = $node->getArgs()[0]->value;

View File

@ -11,7 +11,6 @@ use PhpParser\NodeAbstract;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use function count;
use function strtolower;
/**
* @implements Rule<NodeAbstract>
*/
@ -31,7 +30,7 @@ class AssertSameNullExpectedRule implements Rule
if (count($node->getArgs()) < 2) {
return [];
}
if (!$node->name instanceof Node\Identifier || strtolower($node->name->name) !== 'assertsame') {
if (!$node->name instanceof Node\Identifier || $node->name->toLowerString() !== 'assertsame') {
return [];
}
$expectedArgumentValue = $node->getArgs()[0]->value;

View File

@ -12,7 +12,6 @@ use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Type\ObjectType;
use function count;
use function strtolower;
/**
* @implements Rule<NodeAbstract>
*/
@ -32,14 +31,14 @@ class AssertSameWithCountRule implements Rule
if (count($node->getArgs()) < 2) {
return [];
}
if (!$node->name instanceof Node\Identifier || strtolower($node->name->name) !== 'assertsame') {
if (!$node->name instanceof Node\Identifier || $node->name->toLowerString() !== 'assertsame') {
return [];
}
$right = $node->getArgs()[1]->value;
if ($right instanceof Node\Expr\FuncCall && $right->name instanceof Node\Name && strtolower($right->name->toString()) === 'count') {
if ($right instanceof Node\Expr\FuncCall && $right->name instanceof Node\Name && $right->name->toLowerString() === 'count') {
return ['You should use assertCount($expectedCount, $variable) instead of assertSame($expectedCount, count($variable)).'];
}
if ($right instanceof Node\Expr\MethodCall && $right->name instanceof Node\Identifier && strtolower($right->name->toString()) === 'count' && count($right->getArgs()) === 0) {
if ($right instanceof Node\Expr\MethodCall && $right->name instanceof Node\Identifier && $right->name->toLowerString() === 'count' && count($right->getArgs()) === 0) {
$type = $scope->getType($right->var);
if ((new ObjectType(Countable::class))->isSuperTypeOf($type)->yes()) {
return ['You should use assertCount($expectedCount, $variable) instead of assertSame($expectedCount, $variable->count()).'];

View File

@ -32,11 +32,18 @@ class DataProviderDeclarationRule implements Rule
* @var bool
*/
private $checkFunctionNameCase;
public function __construct(\PHPStan\Rules\PHPUnit\DataProviderHelper $dataProviderHelper, FileTypeMapper $fileTypeMapper, bool $checkFunctionNameCase)
/**
* When phpstan-deprecation-rules is installed, it reports deprecated usages.
*
* @var bool
*/
private $deprecationRulesInstalled;
public function __construct(\PHPStan\Rules\PHPUnit\DataProviderHelper $dataProviderHelper, FileTypeMapper $fileTypeMapper, bool $checkFunctionNameCase, bool $deprecationRulesInstalled)
{
$this->dataProviderHelper = $dataProviderHelper;
$this->fileTypeMapper = $fileTypeMapper;
$this->checkFunctionNameCase = $checkFunctionNameCase;
$this->deprecationRulesInstalled = $deprecationRulesInstalled;
}
public function getNodeType() : string
{
@ -56,7 +63,7 @@ class DataProviderDeclarationRule implements Rule
$annotations = $this->dataProviderHelper->getDataProviderAnnotations($methodPhpDoc);
$errors = [];
foreach ($annotations as $annotation) {
$errors = array_merge($errors, $this->dataProviderHelper->processDataProvider($scope, $annotation, $this->checkFunctionNameCase));
$errors = array_merge($errors, $this->dataProviderHelper->processDataProvider($scope, $annotation, $this->checkFunctionNameCase, $this->deprecationRulesInstalled));
}
return $errors;
}

View File

@ -6,14 +6,28 @@ namespace PHPStan\Rules\PHPUnit;
use PHPStan\Analyser\Scope;
use PHPStan\PhpDoc\ResolvedPhpDocBlock;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\MissingMethodFromReflectionException;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Rules\RuleError;
use PHPStan\Rules\RuleErrorBuilder;
use function array_merge;
use function count;
use function explode;
use function preg_match;
use function sprintf;
class DataProviderHelper
{
/**
* Reflection provider.
*
* @var ReflectionProvider
*/
private $reflectionProvider;
public function __construct(ReflectionProvider $reflectionProvider)
{
$this->reflectionProvider = $reflectionProvider;
}
/**
* @return array<PhpDocTagNode>
*/
@ -32,38 +46,55 @@ class DataProviderHelper
/**
* @return RuleError[] errors
*/
public function processDataProvider(Scope $scope, PhpDocTagNode $phpDocTag, bool $checkFunctionNameCase) : array
public function processDataProvider(Scope $scope, PhpDocTagNode $phpDocTag, bool $checkFunctionNameCase, bool $deprecationRulesInstalled) : array
{
$dataProviderName = $this->getDataProviderName($phpDocTag);
if ($dataProviderName === null) {
// Missing name is already handled in NoMissingSpaceInMethodAnnotationRule
$dataProviderValue = $this->getDataProviderValue($phpDocTag);
if ($dataProviderValue === null) {
// Missing value is already handled in NoMissingSpaceInMethodAnnotationRule
return [];
}
$classReflection = $scope->getClassReflection();
[$classReflection, $method] = $this->parseDataProviderValue($scope, $dataProviderValue);
if ($classReflection === null) {
// Should not happen
return [];
$error = RuleErrorBuilder::message(sprintf('@dataProvider %s related class not found.', $dataProviderValue))->build();
return [$error];
}
try {
$dataProviderMethodReflection = $classReflection->getNativeMethod($dataProviderName);
$dataProviderMethodReflection = $classReflection->getNativeMethod($method);
} catch (MissingMethodFromReflectionException $missingMethodFromReflectionException) {
$error = RuleErrorBuilder::message(sprintf('@dataProvider %s related method not found.', $dataProviderName))->build();
$error = RuleErrorBuilder::message(sprintf('@dataProvider %s related method not found.', $dataProviderValue))->build();
return [$error];
}
$errors = [];
if ($checkFunctionNameCase && $dataProviderName !== $dataProviderMethodReflection->getName()) {
$errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method is used with incorrect case: %s.', $dataProviderName, $dataProviderMethodReflection->getName()))->build();
if ($checkFunctionNameCase && $method !== $dataProviderMethodReflection->getName()) {
$errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method is used with incorrect case: %s.', $dataProviderValue, $dataProviderMethodReflection->getName()))->build();
}
if (!$dataProviderMethodReflection->isPublic()) {
$errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method must be public.', $dataProviderName))->build();
$errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method must be public.', $dataProviderValue))->build();
}
if ($deprecationRulesInstalled && !$dataProviderMethodReflection->isStatic()) {
$errors[] = RuleErrorBuilder::message(sprintf('@dataProvider %s related method must be static.', $dataProviderValue))->build();
}
return $errors;
}
private function getDataProviderName(PhpDocTagNode $phpDocTag) : ?string
private function getDataProviderValue(PhpDocTagNode $phpDocTag) : ?string
{
if (preg_match('/^[^ \\t]+/', (string) $phpDocTag->value, $matches) !== 1) {
return null;
}
return $matches[0];
}
/**
* @return array{ClassReflection|null, string}
*/
private function parseDataProviderValue(Scope $scope, string $dataProviderValue) : array
{
$parts = explode('::', $dataProviderValue, 2);
if (count($parts) <= 1) {
return [$scope->getClassReflection(), $dataProviderValue];
}
if ($this->reflectionProvider->hasClass($parts[0])) {
return [$this->reflectionProvider->getClass($parts[0]), $parts[1]];
}
return [null, $dataProviderValue];
}
}

View File

@ -76,7 +76,7 @@ class ShouldCallParentMethodsRule implements Rule
if (!$stmt->expr->name instanceof Node\Identifier) {
continue;
}
if (strtolower($stmt->expr->name->name) === $methodName) {
if ($stmt->expr->name->toLowerString() === $methodName) {
return \true;
}
}

View File

@ -57,6 +57,8 @@ can be checked before you run the actual line.
<a href="https://www.crisp.nl/"><img src="https://i.imgur.com/jRJyPve.png" alt="Crisp.nl" width="283" height="64"></a>
<br>
<a href="https://inviqa.com/"><img src="https://i.imgur.com/G99rj45.png" alt="Inviqa" width="254" height="65"></a>
&nbsp;&nbsp;&nbsp;
<a href="https://www.cdn77.com/"><img src="https://i.imgur.com/Oo3wA3m.png" alt="CDN77" width="283" height="64"></a>
[**You can now sponsor my open-source work on PHPStan through GitHub Sponsors.**](https://github.com/sponsors/ondrejmirtes)

Binary file not shown.

View File

@ -1,16 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEE0yaA1ZV9xxFr4pwUzxoQjQ565yAFAmNsyqwACgkQzxoQjQ56
5yD3tA/+MouciBUujGgM+PP/j3xTWK8nm3JSRk1Yyuf8TJ1fDWAR1WBXNyyJV1Kz
oJ+Py1awXcJpS5wuWiePEd7mm3qPEP9dqB8zRQIycrScRXhyH5tkhki4wyPRbAFJ
COu3EHr0jPRkYIcYOBxz0zHG4OLUSqiswFBrU38U5gddhJY0CEOSrSFkjfzqb2D+
rjnaiBHXm5XdahtJEpkbrBvknNSxko6eRqben44VgWKG4QXQf9GSRb7AopTE/nI8
bnYatA+jINf5gEp830WaUgBDd9IUA5KjUMrBcU3BcBTW4Y3Rhebl2mNRh+OKvFOK
tr4zGesRmA1nU5ztsM0HdyJmgkY8q82D0/5EtMjDOdCO57Gbyork0fTY6LNtWoT+
vIgxfh13Ss8F46ES0lPTxDxoCHJESRr5O2W8x/Q/CSzZEb+Ogr4662yfyLHE33hR
G1ww7XwLl8a7cCR7/nJZT712e1Qe+32MAKuY9venrxHC4JTfLPiqOlMlsISFmFvh
89V0TP7wltyulBsrSVA5CF2aDO7sBpWtmjbUTJPElRKhP0NTqhpIl0bUQZ4CQy6n
cUUXWtldK9TAJHVHi2S9cwHpTMcnOpKS0lWG/6gNzjlLMyQ2t38/8DLNjqIUqVUn
XYKhGpdgumygN30qa4muZPmv1qybTluWP5Z4SXzJ38GiU8zZPuM=
=FymU
iQIzBAABCgAdFiEE0yaA1ZV9xxFr4pwUzxoQjQ565yAFAmOYU6cACgkQzxoQjQ56
5yAUXg//b8toUyLpFiRCLrS5Omd4ahKMaSjFb+0o6Mtmb9WWRNKtkeZqTFVmqSoY
aVh+TBcbdvVUtlwIIJAhm84SRIGrh+pJ9JQX3YEje9o1xX/l8FH3O5o6tuYxQcHs
wIHB9gcEhIFrrzdeD+Q/5rMm08Xeu5+qS1hXnongsWfIIHHsvfGUt58ZHiq+CmEk
5QB+/IPqgimPHWObf2LLKiNW1BaEmJl+TsRnAXg37AaMeHaaXOt7j0cmRXz7OVzd
axsTIpl/zrJCoWiYr3+P/sJoHD9Ii92o/HDvZQ8dEeL6b/rPsiCUHmjxwfjqXTbj
gm5D86YRlw6r4vMCnltzF69nOOi+sfZsF/gIyclIhymKNddSllsGdP+hXHT3ZHYt
x81jF/R0AcJqx9whB3tZj2cRWueoWalZMHJixjT7W8cYLdIXKo29kDH9MHF/+6yl
iwoXiTJQJCCYjbKLadctGTFm7BzGLGNIiAhitKOqnf9GzkTVk7m8iNVQKw5qzoU5
tlAa0W7NV2r0azP0x3ACUTvAd1/IZNPcr7y3BVCn5yOBZwp3icCvUiLPw4+nvRWH
3oTGY047C+OJL+tFXnw6sCeZ3fu6+e6+zjtjSlpDVBm/8mlnpVdx9uWRtbGdgORW
eGJc6AW4zoqbHkJ56/XeClsEFiPW4nWmobITLdABH0ceaA4Iy34=
=413e
-----END PGP SIGNATURE-----