[CI] run rules dir by dir to identify static reflection weak sposts (#5720)

* [CI] run rules dir by dir to identify static reflection weak sposts

* [ci-review] Rector Rectify

* consistency

* remove unused property

* add data provider test fixture

* add InvertedIfFactory to lower complexity

* [DeadCode] Skip unused public method if data provider

* misc

* [DeadCode] Do not remove class method if required by parent ocntract

* make use of ContextAnalyzer to find the loop

* narrow dirs one level

* narrow packages

* narrow

* use directly

* narrow

* correct constant name to keep BC

* bump deps

* add version_compare to removed extra params, just to be sure

* do not return function node on unchanged name

* narrow

* add support for multiple variants

* widen

* remove double check [skip ci]

Co-authored-by: kaizen-ci <info@kaizen-ci.org>
This commit is contained in:
Tomas Votruba 2021-03-02 14:43:36 +01:00 committed by GitHub
parent 66f6b4a573
commit d5bf66f9cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 458 additions and 190 deletions

View File

@ -10,7 +10,7 @@
#
# https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token#permissions-for-the-github_token
####
name: Rector CI
name: Rector
on:
pull_request: null
@ -22,10 +22,14 @@ jobs:
matrix:
directories:
#- rules
- rules/naming
- rules/privatization
- rules/code-quality
- rules/php74
- rules/arguments rules/autodiscovery rules/cakephp rules/carbon rules/code-quality rules/code-quality-strict rules/coding-style rules/composer rules/dead-code rules/dead-doc-block rules/defluent rules/dependency-injection rules/doctrine rules/doctrine-code-quality rules/doctrine-gedmo-to-knplabs rules/downgrade-php70 rules/downgrade-php71 rules/downgrade-php72 rules/downgrade-php73
- rules/downgrade-php74 rules/downgrade-php80 rules/early-return rules/generics rules/laravel rules/legacy rules/mockery-to-prophecy rules/mockista-to-mockery rules/mysql-to-mysqli rules/naming rules/nette rules/nette-code-quality rules/nette-kdyby
- rules/nette-tester-to-phpunit rules/nette-to-symfony rules/nette-utils-code-quality rules/order rules/php-office rules/php-spec-to-phpunit rules/php52 rules/php53 rules/php54 rules/php55 rules/php56 rules/php70 rules/php71 rules/php72 rules/php73 rules/php74 rules/php80 rules/phpunit rules/phpunit-symfony rules/privatization rules/psr4 rules/removing rules/removing-static rules/renaming rules/restoration rules/sensio rules/symfony rules/symfony-code-quality rules/symfony-php-config rules/symfony2
- rules/symfony3 rules/symfony4 rules/symfony5 rules/transform rules/type-declaration rules/visibility
- packages
- src
- tests

View File

@ -37,7 +37,7 @@
"nette/utils": "^3.2",
"nikic/php-parser": "^4.10.4",
"phpstan/phpdoc-parser": "^0.4.9",
"phpstan/phpstan": "^0.12.79",
"phpstan/phpstan": "^0.12.80",
"phpstan/phpstan-phpunit": "^0.12.17",
"psr/simple-cache": "^1.0",
"sebastian/diff": "^4.0.4",

View File

@ -13626,7 +13626,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(InferParamFromClassMethodReturnRector::class)
->call('configure', [[
InferParamFromClassMethodReturnRector::PARAM_FROM_CLASS_METHOD_RETURNS => ValueObjectInliner::inline([
InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => ValueObjectInliner::inline([
new InferParamFromClassMethodReturn('SomeClass', 'process', 'getNodeTypes'),
]),
]]);

View File

@ -604,3 +604,5 @@ parameters:
message: '#Do not use "array_filter" function with complex content, make it more readable with extracted method or single\-line statement#'
paths:
- src/Application/ActiveRectorsProvider.php
- '#Content of method "getIfNextReturn\(\)" is duplicated with method "getIfNextReturn\(\)" in "Rector\\EarlyReturn\\NodeFactory\\InvertedIfFactory" class\. Use unique content or service instead#'

View File

@ -25,7 +25,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
]);
$services->set(InferParamFromClassMethodReturnRector::class)
->call('configure', [[
InferParamFromClassMethodReturnRector::PARAM_FROM_CLASS_METHOD_RETURNS => $configuration,
InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => $configuration,
]]);
$services->set(PreferThisOrSelfMethodCallRector::class)

View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace Rector\DeadCode\NodeAnalyzer;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Type\ObjectType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\PHPUnit\PHPUnitDataProviderTagValueNode;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\RemovingStatic\ValueObject\PHPUnitClass;
final class DataProviderMethodNamesResolver
{
/**
* @var array<string, string[]>
*/
private $cachedDataProviderMethodNamesByClassHash = [];
/**
* @var PhpDocInfoFactory
*/
private $phpDocInfoFactory;
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
public function __construct(PhpDocInfoFactory $phpDocInfoFactory, NodeTypeResolver $nodeTypeResolver)
{
$this->phpDocInfoFactory = $phpDocInfoFactory;
$this->nodeTypeResolver = $nodeTypeResolver;
}
/**
* @return string[]
*/
public function resolveFromClass(Class_ $class): array
{
$classKey = spl_object_hash($class);
if (isset($this->cachedDataProviderMethodNamesByClassHash[$classKey])) {
return $this->cachedDataProviderMethodNamesByClassHash[$classKey];
}
$dataProviderMethodNames = [];
$phpunitDataProviderTagValueNodes = $this->resolvePHPUnitDataProviderTagValueNodes($class);
foreach ($phpunitDataProviderTagValueNodes as $phpunitDataProviderTagValueNode) {
$dataProviderMethodNames[] = $phpunitDataProviderTagValueNode->getMethodName();
}
$this->cachedDataProviderMethodNamesByClassHash[$classKey] = $dataProviderMethodNames;
return $dataProviderMethodNames;
}
/**
* @return PHPUnitDataProviderTagValueNode[]
*/
private function resolvePHPUnitDataProviderTagValueNodes(Class_ $class): array
{
if (! $this->nodeTypeResolver->isObjectType($class, new ObjectType(PHPUnitClass::TEST_CASE))) {
return [];
}
$phpunitDataProviderTagValueNodes = [];
foreach ($class->getMethods() as $classMethod) {
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
$foundPHPUnitDataProviderTagValueNodes = $phpDocInfo->findAllByType(PHPUnitDataProviderTagValueNode::class);
$phpunitDataProviderTagValueNodes = array_merge(
$phpunitDataProviderTagValueNodes,
$foundPHPUnitDataProviderTagValueNodes
);
}
return $phpunitDataProviderTagValueNodes;
}
}

View File

@ -114,12 +114,12 @@ final class CallDefaultParamValuesResolver
}
// non existing function
$functionNameNode = new Name($functionName);
if (! $this->reflectionProvider->hasFunction($functionNameNode, null)) {
$name = new Name($functionName);
if (! $this->reflectionProvider->hasFunction($name, null)) {
return [];
}
$functionReflection = $this->reflectionProvider->getFunction($functionNameNode, null);
$functionReflection = $this->reflectionProvider->getFunction($name, null);
if ($functionReflection->isBuiltin()) {
return [];
}
@ -128,10 +128,10 @@ final class CallDefaultParamValuesResolver
$parametersAcceptor = $functionReflection->getVariants()[0];
foreach ($parametersAcceptor->getParameters() as $key => $reflectionParameter) {
foreach ($parametersAcceptor->getParameters() as $key => $parameterReflection) {
/** @var ReflectionParameter $nativeReflectionParameter */
$nativeReflectionParameter = $this->privatesAccessor->getPrivateProperty(
$reflectionParameter,
$parameterReflection,
'reflection'
);
if (! $nativeReflectionParameter->isDefaultValueAvailable()) {

View File

@ -7,10 +7,14 @@ namespace Rector\DeadCode\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Caching\Contract\Rector\ZeroCacheRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\DeadCode\NodeAnalyzer\DataProviderMethodNamesResolver;
use Rector\NodeCollector\ValueObject\ArrayCallable;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnVendorLockResolver;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -19,11 +23,29 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*/
final class RemoveUnusedPublicMethodRector extends AbstractRector implements ZeroCacheRectorInterface
{
/**
* @var DataProviderMethodNamesResolver
*/
private $dataProviderMethodNamesResolver;
/**
* @var MethodCall[]|StaticCall[]|ArrayCallable[]
*/
private $calls = [];
/**
* @var ClassMethodReturnVendorLockResolver
*/
private $classMethodReturnVendorLockResolver;
public function __construct(
DataProviderMethodNamesResolver $dataProviderMethodNamesResolver,
ClassMethodReturnVendorLockResolver $classMethodReturnVendorLockResolver
) {
$this->dataProviderMethodNamesResolver = $dataProviderMethodNamesResolver;
$this->classMethodReturnVendorLockResolver = $classMethodReturnVendorLockResolver;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Remove unused public method', [
@ -92,14 +114,8 @@ CODE_SAMPLE
return null;
}
/** @var MethodCall[] $calls */
$calls = $this->calls;
foreach ($calls as $call) {
$classMethod = $this->betterNodeFinder->findParentType($call, ClassMethod::class);
if ($this->nodeComparator->areNodesEqual($classMethod, $node)) {
return null;
}
if ($this->isRecursionCallClassMethod($node)) {
return null;
}
$this->removeNode($node);
@ -108,6 +124,11 @@ CODE_SAMPLE
private function shouldSkip(ClassMethod $classMethod): bool
{
$class = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
if (! $class instanceof Class_) {
return true;
}
if ($this->isOpenSourceProjectType()) {
return true;
}
@ -116,14 +137,40 @@ CODE_SAMPLE
return true;
}
if ($this->classMethodReturnVendorLockResolver->isVendorLocked($classMethod)) {
return true;
}
if ($classMethod->isMagic()) {
return true;
}
if ($this->isNames($classMethod, ['test'])) {
if ($this->isNames($classMethod, ['test', 'test*'])) {
return true;
}
$class = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
$phpunitDataProviderMethodNames = $this->dataProviderMethodNamesResolver->resolveFromClass($class);
return $this->isNames($classMethod, $phpunitDataProviderMethodNames);
}
private function isRecursionCallClassMethod(ClassMethod $currentClassMethod): bool
{
/** @var MethodCall[] $calls */
$calls = $this->calls;
foreach ($calls as $call) {
$parentClassMethod = $call->getAttribute(AttributeKey::METHOD_NODE);
if (! $parentClassMethod) {
continue;
}
if ($this->nodeComparator->areNodesEqual($parentClassMethod, $currentClassMethod)) {
return true;
}
}
return false;
}
}

View File

@ -141,12 +141,12 @@ CODE_SAMPLE
return false;
}
$functionNameNode = new Name($functionName);
if (! $this->reflectionProvider->hasFunction($functionNameNode, null)) {
$name = new Name($functionName);
if (! $this->reflectionProvider->hasFunction($name, null)) {
return false;
}
$reflectionFunction = $this->reflectionProvider->getFunction($functionNameNode, null);
$reflectionFunction = $this->reflectionProvider->getFunction($name, null);
// skip native functions, hard to analyze without stubs (stubs would make working with IDE non-practical)
return $reflectionFunction->isBuiltin();

View File

@ -163,8 +163,8 @@ final class ClassUnusedPrivateClassMethodResolver
return false;
}
$nativeClassReflection = $classReflection->getNativeReflection();
$reflectionMethod = $nativeClassReflection->getMethod($method);
$reflectionClass = $classReflection->getNativeReflection();
$reflectionMethod = $reflectionClass->getMethod($method);
return $reflectionMethod->isAbstract();
}

View File

@ -0,0 +1,17 @@
<?php
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveUnusedPublicMethodRector\Fixture;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use Rector\Core\Contract\Rector\PhpRectorInterface;
final class SkipRequiredByContract extends NodeVisitorAbstract implements PhpRectorInterface
{
public function getNodeTypes(): array
{
}
public function refactor(Node $node): ?Node
{
}
}

View File

@ -0,0 +1,20 @@
<?php
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveUnusedPublicMethodRector\Fixture;
use PHPUnit\Framework\TestCase;
final class SkipTestDataProvider extends TestCase
{
/**
* @dataProvider provideData()
*/
public function test(): void
{
$this->assertTrue('yes');
}
public function provideData()
{
return [1, 2, 3];
}
}

View File

@ -17,17 +17,17 @@ final class InjectTagValueNodeToServiceTypeResolver
/**
* @var JMSDITypeResolver
*/
private $jmsDITypeResolver;
private $jmsdiTypeResolver;
public function __construct(JMSDITypeResolver $jmsDITypeResolver)
public function __construct(JMSDITypeResolver $jmsdiTypeResolver)
{
$this->jmsDITypeResolver = $jmsDITypeResolver;
$this->jmsdiTypeResolver = $jmsdiTypeResolver;
}
public function resolve(Property $property, PhpDocInfo $phpDocInfo, PhpDocTagValueNode $phpDocTagValueNode): Type
{
if ($phpDocTagValueNode instanceof JMSInjectTagValueNode) {
return $this->jmsDITypeResolver->resolve($property, $phpDocTagValueNode);
return $this->jmsdiTypeResolver->resolve($property, $phpDocTagValueNode);
}
if ($phpDocTagValueNode instanceof PHPDIInjectTagValueNode) {

View File

@ -137,10 +137,8 @@ final class PhpDocFromTypeDeclarationDecorator
*/
private function isTypeMatch(Node $typeNode, string $requireTypeNodeClass): bool
{
if (is_a($requireTypeNodeClass, Node::class, true)) {
if (! is_a($typeNode, $requireTypeNodeClass, true)) {
return false;
}
if (is_a($requireTypeNodeClass, Node::class, true) && ! is_a($typeNode, $requireTypeNodeClass, true)) {
return false;
}
if (is_a($requireTypeNodeClass, Type::class, true)) {

View File

@ -285,8 +285,8 @@ CODE_SAMPLE
return false;
}
$nativeClassReflection = $classReflection->getNativeReflection();
$reflectionMethodReflection = $nativeClassReflection->getMethod($methodName);
$reflectionClass = $classReflection->getNativeReflection();
$reflectionMethodReflection = $reflectionClass->getMethod($methodName);
foreach ($reflectionMethodReflection->getParameters() as $reflectionParameter) {
if ($reflectionParameter->getName() !== $paramName) {

View File

@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
namespace Rector\EarlyReturn\NodeFactory;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Stmt\Continue_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Return_;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\EarlyReturn\NodeTransformer\ConditionInverter;
use Rector\NodeNestingScope\ContextAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class InvertedIfFactory
{
/**
* @var BetterNodeFinder
*/
private $betterNodeFinder;
/**
* @var ConditionInverter
*/
private $conditionInverter;
/**
* @var ContextAnalyzer
*/
private $contextAnalyzer;
public function __construct(
BetterNodeFinder $betterNodeFinder,
ConditionInverter $conditionInverter,
ContextAnalyzer $contextAnalyzer
) {
$this->betterNodeFinder = $betterNodeFinder;
$this->conditionInverter = $conditionInverter;
$this->contextAnalyzer = $contextAnalyzer;
}
/**
* @param Expr[] $conditions
* @return If_[]
*/
public function createFromConditions(If_ $if, array $conditions, Return_ $return): array
{
$ifs = [];
$stmt = $this->contextAnalyzer->isInLoop($if) && ! $this->getIfNextReturn($if)
? [new Continue_()]
: [$return];
$getNextReturnExpr = $this->getNextReturnExpr($if);
if ($getNextReturnExpr instanceof Return_) {
$return->expr = $getNextReturnExpr->expr;
}
foreach ($conditions as $condition) {
$invertedCondition = $this->conditionInverter->createInvertedCondition($condition);
$if = new If_($invertedCondition);
$if->stmts = $stmt;
$ifs[] = $if;
}
return $ifs;
}
private function getNextReturnExpr(If_ $if): ?Node
{
/** @var Closure|null $closure */
$closure = $if->getAttribute(AttributeKey::CLOSURE_NODE);
if ($closure instanceof Closure) {
return null;
}
return $this->betterNodeFinder->findFirstNext($if, function (Node $node): bool {
return $node instanceof Return_ && $node->expr instanceof Expr;
});
}
private function getIfNextReturn(If_ $if): ?Return_
{
$nextNode = $if->getAttribute(AttributeKey::NEXT_NODE);
if (! $nextNode instanceof Return_) {
return null;
}
return $nextNode;
}
}

View File

@ -7,20 +7,15 @@ namespace Rector\EarlyReturn\Rector\If_;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Continue_;
use PhpParser\Node\Stmt\Else_;
use PhpParser\Node\Stmt\ElseIf_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\For_;
use PhpParser\Node\Stmt\Foreach_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Return_;
use PhpParser\Node\Stmt\While_;
use Rector\Core\NodeManipulator\IfManipulator;
use Rector\Core\Rector\AbstractRector;
use Rector\EarlyReturn\NodeTransformer\ConditionInverter;
use Rector\EarlyReturn\NodeFactory\InvertedIfFactory;
use Rector\NodeNestingScope\ContextAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -30,25 +25,27 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*/
final class ChangeAndIfToEarlyReturnRector extends AbstractRector
{
/**
* @var array<class-string<Stmt>>
*/
public const LOOP_TYPES = [Foreach_::class, For_::class, While_::class];
/**
* @var IfManipulator
*/
private $ifManipulator;
/**
* @var ConditionInverter
* @var InvertedIfFactory
*/
private $conditionInverter;
private $invertedIfFactory;
public function __construct(ConditionInverter $conditionInverter, IfManipulator $ifManipulator)
/**
* @var ContextAnalyzer
*/
private $contextAnalyzer;
public function __construct(IfManipulator $ifManipulator, InvertedIfFactory $invertedIfFactory,
ContextAnalyzer $contextAnalyzer)
{
$this->ifManipulator = $ifManipulator;
$this->conditionInverter = $conditionInverter;
$this->invertedIfFactory = $invertedIfFactory;
$this->contextAnalyzer = $contextAnalyzer;
}
public function getRuleDefinition(): RuleDefinition
@ -120,7 +117,8 @@ CODE_SAMPLE
? clone $ifNextReturn
: new Return_();
$isInLoop = $this->isIfInLoop($node);
$isInLoop = $this->contextAnalyzer->isInLoop($node);
if (! $ifNextReturn instanceof Return_) {
$this->addNodeAfterNode($node->stmts[0], $node);
return $this->processReplaceIfs($node, $conditions, $ifNextReturnClone);
@ -150,7 +148,7 @@ CODE_SAMPLE
*/
private function processReplaceIfs(If_ $node, array $conditions, Return_ $ifNextReturnClone): If_
{
$ifs = $this->createInvertedIfNodesFromConditions($node, $conditions, $ifNextReturnClone);
$ifs = $this->invertedIfFactory->createFromConditions($node, $conditions, $ifNextReturnClone);
$this->mirrorComments($ifs[0], $node);
foreach ($ifs as $if) {
@ -171,8 +169,10 @@ CODE_SAMPLE
if (! $this->ifManipulator->isIfWithOnlyOneStmt($if)) {
return true;
}
if (! $if->cond instanceof BooleanAnd || ! $this->ifManipulator->isIfWithoutElseAndElseIfs($if)) {
if (! $if->cond instanceof BooleanAnd) {
return true;
}
if (! $this->ifManipulator->isIfWithoutElseAndElseIfs($if)) {
return true;
}
@ -208,32 +208,6 @@ CODE_SAMPLE
return false;
}
/**
* @param Expr[] $conditions
* @return If_[]
*/
private function createInvertedIfNodesFromConditions(If_ $if, array $conditions, Return_ $return): array
{
$ifs = [];
$stmt = $this->isIfInLoop($if) && ! $this->getIfNextReturn($if)
? [new Continue_()]
: [$return];
$getNextReturnExpr = $this->getNextReturnExpr($if);
if ($getNextReturnExpr instanceof Return_) {
$return->expr = $getNextReturnExpr->expr;
}
foreach ($conditions as $key => $condition) {
$invertedCondition = $this->conditionInverter->createInvertedCondition($condition);
$if = new If_($invertedCondition);
$if->stmts = $stmt;
$ifs[] = $if;
}
return $ifs;
}
private function getIfNextReturn(If_ $if): ?Return_
{
$nextNode = $if->getAttribute(AttributeKey::NEXT_NODE);
@ -244,25 +218,6 @@ CODE_SAMPLE
return $nextNode;
}
private function getNextReturnExpr(If_ $if): ?Return_
{
$hasClosureParent = (bool) $this->betterNodeFinder->findParentType($if, Closure::class);
if ($hasClosureParent) {
return null;
}
return $this->betterNodeFinder->findFirstNext($if, function (Node $node): bool {
return $node instanceof Return_ && $node->expr instanceof Expr;
});
}
private function isIfInLoop(If_ $if): bool
{
$parentLoop = $this->betterNodeFinder->findParentTypes($if, self::LOOP_TYPES);
return $parentLoop !== null;
}
private function isParentIfReturnsVoidOrParentIfHasNextNode(If_ $if): bool
{
$parentNode = $if->getAttribute(AttributeKey::PARENT_NODE);
@ -276,7 +231,7 @@ CODE_SAMPLE
private function isNestedIfInLoop(If_ $if): bool
{
if (! $this->isIfInLoop($if)) {
if (! $this->contextAnalyzer->isInLoop($if)) {
return false;
}
return (bool) $this->betterNodeFinder->findParentTypes($if, [If_::class, Else_::class, ElseIf_::class]);

View File

@ -93,7 +93,7 @@ final class MagicNetteFactoryInterfaceFormControlTypeResolver implements FormCon
}
$classReflection = $this->resolveClassReflectionByMethodCall($node);
if ($classReflection === null) {
if (! $classReflection instanceof ClassReflection) {
return [];
}
@ -132,7 +132,7 @@ final class MagicNetteFactoryInterfaceFormControlTypeResolver implements FormCon
private function resolveReflectionClassMethod(MethodCall $methodCall, string $methodName): ?ClassMethod
{
$classReflection = $this->resolveClassReflectionByMethodCall($methodCall);
if ($classReflection === null) {
if (! $classReflection instanceof ClassReflection) {
return null;
}

View File

@ -240,8 +240,8 @@ CODE_SAMPLE
}
// has method
$method = $calledOnType->getMethod($methodName, $scope);
$methodReflection = $calledOnType->getMethod($methodName, $scope);
return ParametersAcceptorSelector::selectSingle($method->getVariants())->getReturnType();
return ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
}
}

View File

@ -319,8 +319,8 @@ CODE_SAMPLE
return false;
}
$reflectionMethod = $classReflection->getNativeMethod($methodName);
$parametersAcceptor = $reflectionMethod->getVariants()[0];
$methodReflection = $classReflection->getNativeMethod($methodName);
$parametersAcceptor = $methodReflection->getVariants()[0];
$returnType = $parametersAcceptor->getReturnType();

View File

@ -11,6 +11,7 @@ use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\ObjectType;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
@ -211,7 +212,7 @@ CODE_SAMPLE
}
$classReflection = $scope->getClassReflection();
if ($classReflection === null) {
if (! $classReflection instanceof ClassReflection) {
throw new ShouldNotHappenException();
}

View File

@ -37,9 +37,9 @@ final class PHPUnitTypeDeclarationDecorator
}
$classReflection = $this->reflectionProvider->getClass('PHPUnit\Framework\TestCase');
$nativeClassReflection = $classReflection->getNativeReflection();
$reflectionClass = $classReflection->getNativeReflection();
$reflectionMethod = $nativeClassReflection->getMethod(MethodName::SET_UP);
$reflectionMethod = $reflectionClass->getMethod(MethodName::SET_UP);
if (! $reflectionMethod->hasReturnType()) {
return;
}

View File

@ -186,18 +186,18 @@ CODE_SAMPLE
}
$classReflection = $this->reflectionProvider->getClass($className);
$nativeClassReflection = $classReflection->getNativeReflection();
$reflectionClass = $classReflection->getNativeReflection();
$constructorMethodReflection = $nativeClassReflection->getConstructor();
if (! $constructorMethodReflection instanceof ReflectionMethod) {
$reflectionMethod = $reflectionClass->getConstructor();
if (! $reflectionMethod instanceof ReflectionMethod) {
return true;
}
if (! $constructorMethodReflection->isPublic()) {
if (! $reflectionMethod->isPublic()) {
return false;
}
// required parameters in constructor, nothing we can do
return ! (bool) $constructorMethodReflection->getNumberOfRequiredParameters();
return ! (bool) $reflectionMethod->getNumberOfRequiredParameters();
}
}

View File

@ -10,7 +10,8 @@ use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\Type\UnionTypeMethodReflection;
use Rector\Core\PHPStan\Reflection\CallReflectionResolver;
use Rector\Core\Rector\AbstractRector;
@ -65,16 +66,14 @@ final class RemoveExtraParametersRector extends AbstractRector
return null;
}
/** @var ParametersAcceptor $parametersAcceptor */
$parametersAcceptor = $this->callReflectionResolver->resolveParametersAcceptor(
$functionLikeReflection,
$node
);
if ($functionLikeReflection === null) {
return null;
}
$maximumAllowedParameterCount = $this->resolveMaximumAllowedParameterCount($functionLikeReflection);
$numberOfParameters = count($parametersAcceptor->getParameters());
$numberOfArguments = count($node->args);
for ($i = $numberOfParameters; $i <= $numberOfArguments; ++$i) {
for ($i = $maximumAllowedParameterCount; $i <= $numberOfArguments; ++$i) {
unset($node->args[$i]);
}
@ -100,20 +99,35 @@ final class RemoveExtraParametersRector extends AbstractRector
}
}
$parametersAcceptor = $this->callReflectionResolver->resolveParametersAcceptor(
$this->callReflectionResolver->resolveCall($node),
$node
);
if (! $parametersAcceptor instanceof ParametersAcceptor) {
$reflection = $this->callReflectionResolver->resolveCall($node);
if ($reflection === null) {
return true;
}
// can be any number of arguments → nothing to limit here
if ($parametersAcceptor->isVariadic()) {
if ($reflection->getVariants() === []) {
return true;
}
return count($parametersAcceptor->getParameters()) >= count($node->args);
foreach ($reflection->getVariants() as $parametersAcceptor) {
// can be any number of arguments → nothing to limit here
if ($parametersAcceptor->isVariadic()) {
return true;
}
}
return false;
}
/**
* @param MethodReflection|FunctionReflection $reflection
*/
private function resolveMaximumAllowedParameterCount(object $reflection): int
{
$parameterCounts = [0];
foreach ($reflection->getVariants() as $parametersAcceptor) {
$parameterCounts[] = count($parametersAcceptor->getParameters());
}
return (int) max($parameterCounts);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Rector\Php71\Tests\Rector\FuncCall\RemoveExtraParametersRector\Fixture;
final class SkipVersionCompare
{
/**
* @param string|null $value
* @return bool|int
*/
public function run($value)
{
if ($value === null) {
return version_compare('one', 'two');
}
return version_compare('one', 'two', $value);
}
}

View File

@ -68,15 +68,13 @@ final class StrncmpMatchAndRefactor implements StrStartWithMatchAndRefactorInter
)) {
return $this->strStartsWithFactory->createFromFuncCall($binaryOp->left, $isPositive);
}
if ($binaryOp->right instanceof FuncCall && $this->nodeNameResolver->isName(
$binaryOp->right,
self::FUNCTION_NAME
)) {
return $this->strStartsWithFactory->createFromFuncCall($binaryOp->right, $isPositive);
if (! $binaryOp->right instanceof FuncCall) {
return null;
}
return null;
if (! $this->nodeNameResolver->isName($binaryOp->right, self::FUNCTION_NAME)) {
return null;
}
return $this->strStartsWithFactory->createFromFuncCall($binaryOp->right, $isPositive);
}
public function refactorStrStartsWith(StrStartsWith $strStartsWith): ?Node

View File

@ -32,16 +32,16 @@ final class StrposMatchAndRefactor implements StrStartWithMatchAndRefactorInterf
/**
* @var StrStartsWithFuncCallFactory
*/
private $startsWithFuncCallFactory;
private $strStartsWithFuncCallFactory;
public function __construct(
NodeNameResolver $nodeNameResolver,
ValueResolver $valueResolver,
StrStartsWithFuncCallFactory $startsWithFuncCallFactory
StrStartsWithFuncCallFactory $strStartsWithFuncCallFactory
) {
$this->nodeNameResolver = $nodeNameResolver;
$this->valueResolver = $valueResolver;
$this->startsWithFuncCallFactory = $startsWithFuncCallFactory;
$this->strStartsWithFuncCallFactory = $strStartsWithFuncCallFactory;
}
/**
@ -88,6 +88,6 @@ final class StrposMatchAndRefactor implements StrStartWithMatchAndRefactorInterf
$strposFuncCall = $strStartsWith->getFuncCall();
$strposFuncCall->name = new Name('str_starts_with');
return $this->startsWithFuncCallFactory->createStrStartsWith($strStartsWith);
return $this->strStartsWithFuncCallFactory->createStrStartsWith($strStartsWith);
}
}

View File

@ -63,11 +63,6 @@ final class NewUniqueObjectToEntityFactoryRector extends AbstractRector implemen
*/
private $staticTypesInClassResolver;
/**
* @var array|mixed
*/
private $typesToServices;
public function __construct(PropertyNaming $propertyNaming, StaticTypesInClassResolver $staticTypesInClassResolver)
{
$this->propertyNaming = $propertyNaming;

View File

@ -63,9 +63,10 @@ final class RenameFunctionRector extends AbstractRector implements ConfigurableR
}
$node->name = $this->createName($newFunction);
return $node;
}
return $node;
return null;
}
/**

View File

@ -30,9 +30,10 @@ use Webmozart\Assert\Assert;
final class InferParamFromClassMethodReturnRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @api
* @var string
*/
public const PARAM_FROM_CLASS_METHOD_RETURNS = 'param_from_class_method_returns';
public const INFER_PARAMS_FROM_CLASS_METHOD_RETURNS = 'infer_param_from_class_method_returns';
/**
* @var InferParamFromClassMethodReturn[]
@ -100,7 +101,7 @@ class SomeClass
CODE_SAMPLE
,
[
self::PARAM_FROM_CLASS_METHOD_RETURNS => [
self::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => [
new InferParamFromClassMethodReturn('SomeClass', 'process', 'getNodeTypes'),
],
]
@ -160,7 +161,7 @@ CODE_SAMPLE
*/
public function configure(array $configuration): void
{
$inferParamsFromClassMethodReturns = $configuration[self::PARAM_FROM_CLASS_METHOD_RETURNS] ?? [];
$inferParamsFromClassMethodReturns = $configuration[self::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS] ?? [];
Assert::allIsInstanceOf($inferParamsFromClassMethodReturns, InferParamFromClassMethodReturn::class);
$this->inferParamFromClassMethodReturn = $inferParamsFromClassMethodReturns;

View File

@ -156,18 +156,18 @@ CODE_SAMPLE
}
$classReflection = $this->reflectionProvider->getClass($className);
$nativeClassReflection = $classReflection->getNativeReflection();
return $nativeClassReflection->getConstructor();
$reflectionClass = $classReflection->getNativeReflection();
return $reflectionClass->getConstructor();
}
private function resolveClassToInstantiateByParameterReflection(ReflectionParameter $reflectionParameter): ?string
{
$parameterType = $reflectionParameter->getType();
if (! $parameterType instanceof ReflectionType) {
$reflectionType = $reflectionParameter->getType();
if (! $reflectionType instanceof ReflectionType) {
return null;
}
$requiredType = (string) $parameterType;
$requiredType = (string) $reflectionType;
return $this->classToInstantiateByType[$requiredType] ?? null;
}

View File

@ -17,6 +17,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(InferParamFromClassMethodReturnRector::class)
->call('configure', [[
InferParamFromClassMethodReturnRector::PARAM_FROM_CLASS_METHOD_RETURNS => $configuration,
InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => $configuration,
]]);
};

View File

@ -263,9 +263,9 @@ CODE_SAMPLE
}
$classReflection = $this->reflectionProvider->getClass($className);
$nativeClassReflection = $classReflection->getNativeReflection();
$reflectionClass = $classReflection->getNativeReflection();
$constructorReflectionMethod = $nativeClassReflection->getConstructor();
$constructorReflectionMethod = $reflectionClass->getConstructor();
if (! $constructorReflectionMethod instanceof ReflectionMethod) {
return [];
}

View File

@ -121,9 +121,9 @@ final class TypeProvidingExprFromClassResolver
Scope $scope,
ObjectType $objectType
): ?PropertyFetch {
$nativeReflection = $classReflection->getNativeReflection();
$reflectionClass = $classReflection->getNativeReflection();
foreach ($nativeReflection->getProperties() as $reflectionProperty) {
foreach ($reflectionClass->getProperties() as $reflectionProperty) {
/** @var PhpPropertyReflection $phpPropertyReflection */
$phpPropertyReflection = $classReflection->getProperty($reflectionProperty->getName(), $scope);

View File

@ -97,12 +97,12 @@ final class CallTypeAnalyzer
}
$methodReflection = $classReflection->getMethod($methodName, $scope);
$functionVariant = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants());
$parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants());
$parameterTypes = [];
/** @var ParameterReflection $parameterReflection */
foreach ($functionVariant->getParameters() as $parameterReflection) {
foreach ($parametersAcceptor->getParameters() as $parameterReflection) {
$parameterTypes[] = $parameterReflection->getType();
}

View File

@ -111,26 +111,13 @@ final class PHPUnitDataProviderParamTypeInferer implements ParamTypeInfererInter
*/
private function resolveReturnStaticArrayTypeByParameterPosition(array $returns, int $parameterPosition): Type
{
$paramOnPositionTypes = [];
$firstReturnedExpr = $returns[0]->expr;
if (! $returns[0]->expr instanceof Array_) {
if (! $firstReturnedExpr instanceof Array_) {
throw new ShouldNotHappenException();
}
foreach ($returns[0]->expr->items as $singleDataProvidedSet) {
if (! $singleDataProvidedSet instanceof ArrayItem || ! $singleDataProvidedSet->value instanceof Array_) {
throw new ShouldNotHappenException();
}
foreach ($singleDataProvidedSet->value->items as $position => $singleDataProvidedSetItem) {
if ($position !== $parameterPosition || ! $singleDataProvidedSetItem instanceof ArrayItem) {
continue;
}
$paramOnPositionTypes[] = $this->nodeTypeResolver->resolve($singleDataProvidedSetItem->value);
}
}
$paramOnPositionTypes = $this->resolveParamOnPositionTypes($firstReturnedExpr, $parameterPosition);
if ($paramOnPositionTypes === []) {
return new MixedType();
}
@ -193,4 +180,31 @@ final class PHPUnitDataProviderParamTypeInferer implements ParamTypeInfererInter
return $this->phpDocInfoFactory->createFromNodeOrEmpty($parent);
}
/**
* @return Type[]
*/
private function resolveParamOnPositionTypes(Array_ $array, int $parameterPosition): array
{
$paramOnPositionTypes = [];
foreach ($array->items as $singleDataProvidedSet) {
if (! $singleDataProvidedSet instanceof ArrayItem || ! $singleDataProvidedSet->value instanceof Array_) {
throw new ShouldNotHappenException();
}
foreach ($singleDataProvidedSet->value->items as $position => $singleDataProvidedSetItem) {
if ($position !== $parameterPosition) {
continue;
}
if (! $singleDataProvidedSetItem instanceof ArrayItem) {
continue;
}
$paramOnPositionTypes[] = $this->nodeTypeResolver->resolve($singleDataProvidedSetItem->value);
}
}
return $paramOnPositionTypes;
}
}

View File

@ -20,6 +20,11 @@ use Rector\TypeDeclaration\Contract\TypeInferer\PropertyTypeInfererInterface;
final class DoctrineColumnPropertyTypeInferer implements PropertyTypeInfererInterface
{
/**
* @var string
*/
private const DATE_TIME_INTERFACE = 'DateTimeInterface';
/**
* @var Type[]
*
@ -72,11 +77,11 @@ final class DoctrineColumnPropertyTypeInferer implements PropertyTypeInfererInte
'varbinary' => new StringType(),
'set' => new StringType(),
// date time objects
'date' => new ObjectType('DateTimeInterface'),
'datetime' => new ObjectType('DateTimeInterface'),
'timestamp' => new ObjectType('DateTimeInterface'),
'time' => new ObjectType('DateTimeInterface'),
'year' => new ObjectType('DateTimeInterface'),
'date' => new ObjectType(self::DATE_TIME_INTERFACE),
'datetime' => new ObjectType(self::DATE_TIME_INTERFACE),
'timestamp' => new ObjectType(self::DATE_TIME_INTERFACE),
'time' => new ObjectType(self::DATE_TIME_INTERFACE),
'year' => new ObjectType(self::DATE_TIME_INTERFACE),
];
$this->phpDocInfoFactory = $phpDocInfoFactory;
}

View File

@ -11,6 +11,7 @@ use Rector\Core\HttpKernel\RectorKernel;
use Rector\Core\Stubs\PHPStanStubLoader;
use Rector\Core\Stubs\StubLoader;
use Rector\Core\ValueObject\Bootstrap\BootstrapConfigs;
use Symfony\Component\DependencyInjection\Container;
use Symplify\PackageBuilder\Console\Input\StaticInputDetector;
use Symplify\SmartFileSystem\SmartFileInfo;
@ -27,7 +28,8 @@ final class RectorContainerFactory
$environment = $this->createEnvironment($configFileInfos);
$rectorKernel = new RectorKernel($environment, $isDebug);
// mt_rand is needed to invalidate container cache in case of class changes to be registered as services
$rectorKernel = new RectorKernel($environment . mt_rand(0, 10000), $isDebug);
if ($configFileInfos !== []) {
$configFilePaths = $this->unpackRealPathsFromFileInfos($configFileInfos);
$rectorKernel->setConfigs($configFilePaths);