Remove dynamic type checks #3 (#5942)

This commit is contained in:
Tomas Votruba 2021-03-22 00:07:40 +01:00 committed by GitHub
parent ae1c6b90f7
commit 4f27f39e70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 232 additions and 170 deletions

View File

@ -26,7 +26,6 @@ use Rector\ChangesReporting\Collector\RectorChangeCollector;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Exception\NotImplementedYetException;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Util\StaticInstanceOf;
use Rector\StaticTypeMapper\StaticTypeMapper;
/**
@ -486,16 +485,21 @@ final class PhpDocInfo
return $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($phpDocTagValueNode, $this->node);
}
/**
* @param class-string $type
*/
private function ensureTypeIsTagValueNode(string $type, string $location): void
{
/** @var array<class-string> $desiredTypes */
/** @var array<class-string<\PhpParser\Node>> $desiredTypes */
$desiredTypes = array_merge([
PhpDocTagValueNode::class,
PhpDocTagNode::class,
], NodeTypes::TYPE_AWARE_NODES);
if (StaticInstanceOf::isOneOf($type, $desiredTypes)) {
return;
foreach ($desiredTypes as $desiredType) {
if (is_a($type, $desiredType, true)) {
return;
}
}
throw new ShouldNotHappenException(sprintf(
@ -509,7 +513,8 @@ final class PhpDocInfo
private function resolveNameForPhpDocTagValueNode(PhpDocTagValueNode $phpDocTagValueNode): string
{
foreach (self::TAGS_TYPES_TO_NAMES as $tagValueNodeType => $name) {
if ($phpDocTagValueNode instanceof $tagValueNodeType) {
/** @var class-string<PhpDocTagNode> $tagValueNodeType */
if (is_a($phpDocTagValueNode, $tagValueNodeType, true)) {
return $name;
}
}

View File

@ -167,6 +167,7 @@ final class ParsedNodeCollector
public function isCollectableNode(Node $node): bool
{
foreach (self::COLLECTABLE_NODE_TYPES as $collectableNodeType) {
/** @var class-string<Node> $collectableNodeType */
if (is_a($node, $collectableNodeType, true)) {
return true;
}

View File

@ -13,7 +13,7 @@ use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\NodeTypeResolver;
@ -55,7 +55,7 @@ final class ParsedPropertyFetchNodeCollector
}
// make sure name is valid
if (StaticInstanceOf::isOneOf($node->name, [StaticCall::class, MethodCall::class])) {
if (StaticNodeInstanceOf::isOneOf($node->name, [StaticCall::class, MethodCall::class])) {
return;
}

View File

@ -8,6 +8,9 @@ use PhpParser\Node;
interface NodeNameResolverInterface
{
/**
* @return class-string<Node>
*/
public function getNode(): string;
public function resolve(Node $node): ?string;

View File

@ -17,7 +17,7 @@ use Rector\CodingStyle\Naming\ClassNaming;
use Rector\Core\Contract\Rector\RectorInterface;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeNameResolver\Contract\NodeNameResolverInterface;
use Rector\NodeNameResolver\Regex\RegexPatternDetector;
use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider;
@ -220,7 +220,7 @@ final class NodeNameResolver
private function isCallOrIdentifier(Node $node): bool
{
return StaticInstanceOf::isOneOf($node, [MethodCall::class, StaticCall::class, Identifier::class]);
return StaticNodeInstanceOf::isOneOf($node, [MethodCall::class, StaticCall::class, Identifier::class]);
}
/**

View File

@ -24,6 +24,9 @@ final class ClassConstFetchNameResolver implements NodeNameResolverInterface
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return ClassConstFetch::class;

View File

@ -24,6 +24,9 @@ final class ClassConstNameResolver implements NodeNameResolverInterface
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return ClassConst::class;

View File

@ -25,6 +25,9 @@ final class ClassNameResolver implements NodeNameResolverInterface
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return ClassLike::class;

View File

@ -10,6 +10,9 @@ use Rector\NodeNameResolver\Contract\NodeNameResolverInterface;
final class EmptyNameResolver implements NodeNameResolverInterface
{
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return Empty_::class;

View File

@ -24,6 +24,9 @@ final class FuncCallNameResolver implements NodeNameResolverInterface
$this->reflectionProvider = $reflectionProvider;
}
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return FuncCall::class;

View File

@ -12,6 +12,9 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
final class FunctionNameResolver implements NodeNameResolverInterface
{
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return Function_::class;

View File

@ -23,6 +23,9 @@ final class NameNameResolver implements NodeNameResolverInterface
$this->funcCallNameResolver = $funcCallNameResolver;
}
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return Name::class;

View File

@ -24,6 +24,9 @@ final class ParamNameResolver implements NodeNameResolverInterface
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return Param::class;

View File

@ -24,6 +24,9 @@ final class PropertyNameResolver implements NodeNameResolverInterface
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return Property::class;

View File

@ -24,6 +24,9 @@ final class UseNameResolver implements NodeNameResolverInterface
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return Use_::class;

View File

@ -14,6 +14,9 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
final class VariableNameResolver implements NodeNameResolverInterface
{
/**
* @return class-string<Node>
*/
public function getNode(): string
{
return Variable::class;

View File

@ -15,7 +15,7 @@ use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Switch_;
use PhpParser\Node\Stmt\While_;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
final class ContextAnalyzer
{
@ -49,7 +49,7 @@ final class ContextAnalyzer
return false;
}
return StaticInstanceOf::isOneOf($firstParent, self::LOOP_NODES);
return StaticNodeInstanceOf::isOneOf($firstParent, self::LOOP_NODES);
}
public function isInIf(Node $node): bool

View File

@ -48,10 +48,11 @@ final class ScopeAwareNodeFinder
/**
* Find node based on $callable or null, when the nesting scope is broken
* @param class-string[] $allowedTypes
* @param array<class-string<Node>> $allowedTypes
*/
public function findParent(Node $node, callable $callable, array $allowedTypes): ?Node
{
/** @var array<class-string<Node>> $parentNestingBreakTypes */
$parentNestingBreakTypes = array_diff(ControlStructure::BREAKING_SCOPE_NODE_TYPES, $allowedTypes);
$this->isBreakingNodeFoundFirst = false;

View File

@ -11,7 +11,7 @@ use PhpParser\Node\Stmt\Expression;
use Rector\ChangesReporting\Collector\RectorChangeCollector;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PostRector\Contract\Collector\NodeCollectorInterface;
@ -128,7 +128,7 @@ final class NodesToAddCollector implements NodeCollectorInterface
private function resolveNearestExpressionPosition(Node $node): string
{
if (StaticInstanceOf::isOneOf($node, [Expression::class, Stmt::class])) {
if (StaticNodeInstanceOf::isOneOf($node, [Expression::class, Stmt::class])) {
return spl_object_hash($node);
}

View File

@ -19,7 +19,6 @@ use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\Core\Exception\NotImplementedYetException;
use Rector\Core\Util\StaticInstanceOf;
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
use Rector\StaticTypeMapper\Mapper\PhpParserNodeMapper;
use Rector\StaticTypeMapper\Naming\NameScopeFactory;
@ -99,11 +98,16 @@ final class StaticTypeMapper
return $this->phpDocTypeMapper->mapToPHPStanType($phpDocTagValueNode->bound, $node, $nameScope);
}
if (StaticInstanceOf::isOneOf(
$phpDocTagValueNode,
[ReturnTagValueNode::class, ParamTagValueNode::class, VarTagValueNode::class, ThrowsTagValueNode::class]
)) {
if ($phpDocTagValueNode instanceof ReturnTagValueNode) {
return $this->mapPHPStanPhpDocTypeNodeToPHPStanType($phpDocTagValueNode->type, $node);
}
if ($phpDocTagValueNode instanceof ParamTagValueNode) {
return $this->mapPHPStanPhpDocTypeNodeToPHPStanType($phpDocTagValueNode->type, $node);
}
if ($phpDocTagValueNode instanceof VarTagValueNode) {
return $this->mapPHPStanPhpDocTypeNodeToPHPStanType($phpDocTagValueNode->type, $node);
}
if ($phpDocTagValueNode instanceof ThrowsTagValueNode) {
return $this->mapPHPStanPhpDocTypeNodeToPHPStanType($phpDocTagValueNode->type, $node);
}

View File

@ -4,7 +4,9 @@ declare(strict_types=1);
namespace Rector\StaticTypeMapper\ValueObject\Type;
use PHPStan\TrinaryLogic;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
final class ShortenedObjectType extends ObjectType
{
@ -23,6 +25,12 @@ final class ShortenedObjectType extends ObjectType
$this->fullyQualifiedName = $fullyQualifiedName;
}
public function isSuperTypeOf(Type $type): TrinaryLogic
{
$fullyQualifiedObjectType = new ObjectType($this->fullyQualifiedName);
return $fullyQualifiedObjectType->isSuperTypeOf($type);
}
public function getShortName(): string
{
return $this->getClassName();

View File

@ -519,12 +519,16 @@ parameters:
message: '#Do not inherit from abstract class, better use composition#'
path: utils/phpstan-extensions/src/Rule/NoInstanceOfStaticReflectionRule.php
# first wave resolved, merge PR
- '#Instead of "(.*?)" use ReflectionProvider service (.*?) for static reflection to work#'
# known internal types on correct location
-
message: '#Instead of "(.*?)" use ReflectionProvider service (.*?) for static reflection to work#'
paths:
- src/Application/RectorApplication.php
- src/Console/Command/ProcessCommand.php
# annotations
-
message: '#Instead of "instanceof/is_a\(\)" use ReflectionProvider service or "\(new ObjectType\(<desired_type\>\)\)\-\>isSuperTypeOf\(<element_type\>\)" for static reflection to work#'
paths:
- packages/BetterPhpDocParser/AnnotationReader/NodeAnnotationReader.php
- packages/BetterPhpDocParser/PhpDocNodeFactory/*NodeFactory.php

View File

@ -6,6 +6,9 @@ use Rector\Tests\Transform\Rector\ClassMethod\SingleToManyMethodRector\Source\On
class SomeClass implements OneToManyInterface
{
/**
* @return class-string<\PhpParser\Node>
*/
public function getNode(): string
{
return 'Echo_';

View File

@ -6,6 +6,9 @@ use Rector\Tests\Transform\Rector\ClassMethod\SingleToManyMethodRector\Source\On
class MultiReturn implements OneToManyInterface
{
/**
* @return class-string<\PhpParser\Node>
*/
public function getNode(): string
{
if (true) {

View File

@ -6,6 +6,9 @@ use Rector\Tests\Transform\Rector\ClassMethod\SingleToManyMethodRector\Source\On
class ReturnClassConstFetch implements OneToManyInterface
{
/**
* @return class-string<\PhpParser\Node>
*/
public function getNode(): string
{
return self::class;

View File

@ -20,7 +20,7 @@ use PhpParser\Node\Stmt\Unset_;
use Rector\Core\NodeManipulator\AssignManipulator;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -105,7 +105,7 @@ final class ForAnalyzer
/** @var PreInc|PostInc $prePostInc */
$prePostInc = $loopExprs[0];
if (StaticInstanceOf::isOneOf($prePostInc, [PreInc::class, PostInc::class])) {
if (StaticNodeInstanceOf::isOneOf($prePostInc, [PreInc::class, PostInc::class])) {
return $this->nodeNameResolver->isName($prePostInc->var, $keyValueName);
}

View File

@ -15,7 +15,7 @@ use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Return_;
use Rector\Core\NodeManipulator\ForeachManipulator;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -127,7 +127,7 @@ CODE_SAMPLE
}
$innerNode = $node->stmts[0] instanceof Expression ? $node->stmts[0]->expr : $node->stmts[0];
if (StaticInstanceOf::isOneOf($innerNode, [Assign::class, Return_::class])) {
if (StaticNodeInstanceOf::isOneOf($innerNode, [Assign::class, Return_::class])) {
return $innerNode;
}

View File

@ -18,7 +18,7 @@ use PhpParser\Node\Expr\Cast\String_;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Stmt\Expression;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -104,7 +104,7 @@ CODE_SAMPLE
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
// result of function or probably used
if (StaticInstanceOf::isOneOf($parentNode, [Expr::class, Arg::class])) {
if (StaticNodeInstanceOf::isOneOf($parentNode, [Expr::class, Arg::class])) {
return null;
}

View File

@ -6,7 +6,6 @@ namespace Rector\CodeQualityStrict\TypeAnalyzer;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use Rector\StaticTypeMapper\ValueObject\Type\ShortenedObjectType;
final class SubTypeAnalyzer
{
@ -20,14 +19,12 @@ final class SubTypeAnalyzer
return false;
}
$checkedClassName = $checkedType instanceof ShortenedObjectType ? $checkedType->getFullyQualifiedName() : $checkedType->getClassName();
$mainClassName = $mainType instanceof ShortenedObjectType ? $mainType->getFullyQualifiedName() : $mainType->getClassName();
if (is_a($checkedClassName, $mainClassName, true)) {
// parent type to all objects
if ($mainType->getClassName() === 'stdClass') {
return true;
}
// child of every object
return $mainClassName === 'stdClass';
return $mainType->isSuperTypeOf($checkedType)
->yes();
}
}

View File

@ -30,7 +30,7 @@ use PhpParser\Node\Expr\UnaryPlus;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Scalar;
use PhpParser\Node\Stmt\Expression;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\PostRector\Collector\NodesToAddCollector;
final class LivingCodeManipulator
@ -63,7 +63,7 @@ final class LivingCodeManipulator
return [];
}
if (StaticInstanceOf::isOneOf($expr, [Closure::class, Scalar::class, ConstFetch::class])) {
if (StaticNodeInstanceOf::isOneOf($expr, [Closure::class, Scalar::class, ConstFetch::class])) {
return [];
}
@ -88,7 +88,7 @@ final class LivingCodeManipulator
$this->keepLivingCodeFromExpr($expr->dim)
);
}
if (StaticInstanceOf::isOneOf($expr, [ClassConstFetch::class, StaticPropertyFetch::class])) {
if (StaticNodeInstanceOf::isOneOf($expr, [ClassConstFetch::class, StaticPropertyFetch::class])) {
/** @var ClassConstFetch|StaticPropertyFetch $expr */
return array_merge(
$this->keepLivingCodeFromExpr($expr->class),

View File

@ -15,7 +15,7 @@ use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Expression;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeNestingScope\ScopeNestingComparator;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -100,7 +100,7 @@ CODE_SAMPLE
private function isCall(Expr $expr): bool
{
return StaticInstanceOf::isOneOf($expr, [FuncCall::class, StaticCall::class, MethodCall::class]);
return StaticNodeInstanceOf::isOneOf($expr, [FuncCall::class, StaticCall::class, MethodCall::class]);
}
private function shouldSkipForDifferentScope(Assign $assign, Expression $expression): bool

View File

@ -18,7 +18,7 @@ use PhpParser\Node\Expr\BinaryOp\Mul;
use PhpParser\Node\Expr\BinaryOp\Plus;
use PhpParser\Node\Expr\UnaryMinus;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -139,7 +139,7 @@ CODE_SAMPLE
private function processBinaryOp(Node $node): ?Expr
{
if (StaticInstanceOf::isOneOf($node, [Plus::class, Minus::class])) {
if (StaticNodeInstanceOf::isOneOf($node, [Plus::class, Minus::class])) {
/** @var Plus|Minus $node */
return $this->processBinaryPlusAndMinus($node);
}

View File

@ -16,7 +16,7 @@ use PHPStan\Analyser\MutatingScope;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\Defluent\Reflection\MethodCallToClassMethodParser;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -239,7 +239,7 @@ final class FluentChainMethodCallNodeAnalyzer
private function isCall(Expr $expr): bool
{
return StaticInstanceOf::isOneOf($expr, [MethodCall::class, StaticCall::class]);
return StaticNodeInstanceOf::isOneOf($expr, [MethodCall::class, StaticCall::class]);
}
private function isMethodCallCreatingNewInstance(MethodCall $methodCall): bool

View File

@ -13,7 +13,7 @@ use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Return_;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\Defluent\ValueObject\AssignAndRootExpr;
use Rector\Defluent\ValueObject\FluentCallsKind;
use Rector\Naming\Naming\PropertyNaming;
@ -86,7 +86,7 @@ final class FluentChainMethodCallRootExtractor
}
foreach ($methodCalls as $methodCall) {
if (StaticInstanceOf::isOneOf($methodCall->var, [Variable::class, PropertyFetch::class])) {
if (StaticNodeInstanceOf::isOneOf($methodCall->var, [Variable::class, PropertyFetch::class])) {
return $this->createAssignAndRootExprForVariableOrPropertyFetch($methodCall);
}
if ($methodCall->var instanceof New_) {

View File

@ -16,7 +16,7 @@ use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\DowngradePhp73\Tokenizer\FollowedByCommaAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -102,7 +102,12 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
if (StaticInstanceOf::isOneOf($node, [MethodCall::class, FuncCall::class, StaticCall::class, New_::class])) {
if (StaticNodeInstanceOf::isOneOf($node, [
MethodCall::class,
FuncCall::class,
StaticCall::class,
New_::class,
])) {
/** @var MethodCall|FuncCall|StaticCall|New_ $node */
return $this->processArgs($node);
}

View File

@ -22,7 +22,7 @@ use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use Rector\Core\Exception\NotImplementedYetException;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Stringy\Stringy;
@ -89,7 +89,7 @@ final class VariableNaming
return $this->resolveFromPropertyFetch($node);
}
if ($node !== null && StaticInstanceOf::isOneOf(
if ($node !== null && StaticNodeInstanceOf::isOneOf(
$node,
[MethodCall::class, NullsafeMethodCall::class, StaticCall::class])) {
return $this->resolveFromMethodCall($node);

View File

@ -27,7 +27,7 @@ use PhpParser\Node\Stmt\Unset_;
use PhpParser\NodeTraverser;
use PHPStan\Analyser\Scope;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -213,7 +213,7 @@ CODE_SAMPLE
)) {
return true;
}
if (StaticInstanceOf::isOneOf($parentNode, [Unset_::class, UnsetCast::class])) {
if (StaticNodeInstanceOf::isOneOf($parentNode, [Unset_::class, UnsetCast::class])) {
return true;
}
@ -253,6 +253,6 @@ CODE_SAMPLE
private function isListAssign(Node $node): bool
{
$parentParentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
return StaticInstanceOf::isOneOf($parentParentNode, [List_::class, Array_::class]);
return StaticNodeInstanceOf::isOneOf($parentParentNode, [List_::class, Array_::class]);
}
}

View File

@ -25,7 +25,7 @@ use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use Rector\Core\Exception\NotImplementedYetException;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Stringy\Stringy;
@ -181,7 +181,7 @@ final class VariableNaming
return $this->resolveFromPropertyFetch($node);
}
if ($node !== null && StaticInstanceOf::isOneOf(
if ($node !== null && StaticNodeInstanceOf::isOneOf(
$node,
[MethodCall::class, NullsafeMethodCall::class, StaticCall::class])) {

View File

@ -18,7 +18,7 @@ use PhpParser\NodeTraverser;
use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer;
use Rector\Core\NodeManipulator\ClassManipulator;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Privatization\NodeReplacer\PropertyFetchWithVariableReplacer;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
@ -222,7 +222,7 @@ CODE_SAMPLE
private function isScopeChangingNode(Node $node): bool
{
return StaticInstanceOf::isOneOf($node, self::SCOPE_CHANGING_NODE_TYPES);
return StaticNodeInstanceOf::isOneOf($node, self::SCOPE_CHANGING_NODE_TYPES);
}
private function refactorIf(If_ $if, string $privatePropertyName): ?bool

View File

@ -12,7 +12,7 @@ use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeNameResolver\NodeNameResolver;
use Webmozart\Assert\Assert;
@ -81,7 +81,7 @@ final class IdentifierManipulator
*/
private function resolveOldMethodName(Node $node): ?string
{
if (StaticInstanceOf::isOneOf($node, [StaticCall::class, MethodCall::class])) {
if (StaticNodeInstanceOf::isOneOf($node, [StaticCall::class, MethodCall::class])) {
return $this->nodeNameResolver->getName($node->name);
}

View File

@ -17,7 +17,7 @@ use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\PhpDocTypeRenamer;
use Rector\Renaming\ValueObject\PseudoNamespaceToNamespace;
@ -138,7 +138,7 @@ CODE_SAMPLE
private function refactorStmts(array $stmts): array
{
$this->traverseNodesWithCallable($stmts, function (Node $node): ?Node {
if (! StaticInstanceOf::isOneOf(
if (! StaticNodeInstanceOf::isOneOf(
$node,
[Name::class, Identifier::class, Property::class, FunctionLike::class])) {
return null;

View File

@ -16,7 +16,7 @@ use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer;
use Rector\Core\NodeManipulator\MagicPropertyFetchAnalyzer;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -117,7 +117,7 @@ CODE_SAMPLE
public function refactor(Node $node): ?Node
{
if ($node instanceof Assign) {
if (StaticInstanceOf::isOneOf($node->var, [PropertyFetch::class, StaticPropertyFetch::class])) {
if (StaticNodeInstanceOf::isOneOf($node->var, [PropertyFetch::class, StaticPropertyFetch::class])) {
return $this->processMagicSet($node);
}
return null;

View File

@ -133,7 +133,7 @@ final class CallTypesResolver
return $type;
}
if (! is_a($firstUnionedType->getClassName(), $unionedType->getClassName(), true)) {
if ($unionedType->isSuperTypeOf($firstUnionedType)->yes()) {
return $type;
}
}

View File

@ -25,10 +25,6 @@ final class ObjectTypeComparator
return true;
}
if ($this->isBothIterableIteratorGeneratorTraversable($currentType, $newType)) {
return true;
}
if (! $currentType instanceof ObjectType) {
return false;
}
@ -37,12 +33,8 @@ final class ObjectTypeComparator
return false;
}
return is_a($currentType->getClassName(), $newType->getClassName(), true);
}
private function isClosure(Type $type): bool
{
return $type instanceof ObjectType && $type->getClassName() === 'Closure';
return $newType->isSuperTypeOf($currentType)
->yes();
}
private function isBothCallable(Type $currentType, Type $newType): bool
@ -54,29 +46,9 @@ final class ObjectTypeComparator
return $newType instanceof CallableType && $this->isClosure($currentType);
}
private function isBothIterableIteratorGeneratorTraversable(Type $currentType, Type $newType): bool
private function isClosure(Type $type): bool
{
if (! $currentType instanceof ObjectType) {
return false;
}
if (! $newType instanceof ObjectType) {
return false;
}
if ($currentType->getClassName() === 'iterable' && $this->isTraversableGeneratorIterator($newType)) {
return true;
}
if ($newType->getClassName() !== 'iterable') {
return false;
}
return $this->isTraversableGeneratorIterator($currentType);
}
private function isTraversableGeneratorIterator(ObjectType $objectType): bool
{
return in_array($objectType->getClassName(), ['Traversable', 'Generator', 'Iterator'], true);
$closureObjectType = new ObjectType('Closure');
return $closureObjectType->equals($type);
}
}

View File

@ -18,6 +18,7 @@ use PHPStan\Type\Type;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedGenericObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
@ -66,7 +67,15 @@ final class YieldNodesReturnTypeInferer implements ReturnTypeInfererInterface
continue;
}
$types[] = $this->nodeTypeResolver->getStaticType($value);
$resolvedType = $this->nodeTypeResolver->getStaticType($value);
if ($resolvedType instanceof MixedType) {
continue;
}
$types[] = $resolvedType;
}
if ($types === []) {
return new FullyQualifiedObjectType('Iterator');
}
$types = $this->typeFactory->createMixedPassedOrUnionType($types);

View File

@ -27,7 +27,7 @@ use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\Core\PHPStan\Reflection\CallReflectionResolver;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
@ -307,7 +307,7 @@ final class ClassMethodAssignManipulator
return false;
}
if (StaticInstanceOf::isOneOf($node, [Arg::class, ClosureUse::class, Param::class])) {
if (StaticNodeInstanceOf::isOneOf($node, [Arg::class, ClosureUse::class, Param::class])) {
return $node->byRef;
}

View File

@ -11,7 +11,7 @@ use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\NodeTraverser;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeNameResolver\NodeNameResolver;
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
@ -59,7 +59,7 @@ final class ClassMethodPropertyFetchManipulator
return null;
}
if (StaticInstanceOf::isOneOf($node->expr, [MethodCall::class, StaticCall::class])) {
if (StaticNodeInstanceOf::isOneOf($node->expr, [MethodCall::class, StaticCall::class])) {
return null;
}

View File

@ -10,7 +10,7 @@ use PhpParser\Node\Expr\NullsafeMethodCall;
use PhpParser\Node\Expr\NullsafePropertyFetch;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Identifier;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class NullsafeManipulator
@ -36,7 +36,7 @@ final class NullsafeManipulator
$parentIdentifier = $nextExprIdentifier->getAttribute(AttributeKey::PARENT_NODE);
if (StaticInstanceOf::isOneOf($parentIdentifier, [MethodCall::class, NullsafeMethodCall::class])) {
if (StaticNodeInstanceOf::isOneOf($parentIdentifier, [MethodCall::class, NullsafeMethodCall::class])) {
return new NullsafeMethodCall($expr, $nextExprIdentifier);
}

View File

@ -14,7 +14,7 @@ use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\Expression;
use PhpParser\NodeFinder;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\PackageBuilder\Php\TypeChecker;
@ -99,7 +99,7 @@ final class BetterNodeFinder
}
do {
if (StaticInstanceOf::isOneOf($parent, $types)) {
if (StaticNodeInstanceOf::isOneOf($parent, $types)) {
return $parent;
}

View File

@ -16,7 +16,7 @@ use PhpParser\Node\Stmt;
use PhpParser\Parser;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\Core\Util\StaticInstanceOf;
use Rector\Core\Util\StaticNodeInstanceOf;
use Rector\NodeTypeResolver\NodeScopeAndMetadataDecorator;
final class InlineCodeParser
@ -91,7 +91,7 @@ final class InlineCodeParser
return $this->stringify($expr->left) . $this->stringify($expr->right);
}
if (StaticInstanceOf::isOneOf($expr, [Variable::class, PropertyFetch::class, StaticPropertyFetch::class])) {
if (StaticNodeInstanceOf::isOneOf($expr, [Variable::class, PropertyFetch::class, StaticPropertyFetch::class])) {
return $this->betterStandardPrinter->print($expr);
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Util;
/**
* @see \Rector\Core\Tests\Util\StaticInstanceOfTest
*/
final class StaticInstanceOf
{
/**
* @param string|object|null $element
* @param class-string[] $array
*/
public static function isOneOf($element, array $array): bool
{
if ($element === null) {
return false;
}
foreach ($array as $singleArray) {
if (is_a($element, $singleArray, true)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Util;
use PhpParser\Node;
use Rector\Core\Exception\ShouldNotHappenException;
/**
* @see \Rector\Core\Tests\Util\StaticNodeInstanceOfTest
*/
final class StaticNodeInstanceOf
{
/**
* @param string|object|null $element
* @param array<class-string<Node>> $nodeTypes
*/
public static function isOneOf($element, array $nodeTypes): bool
{
if ($element === null) {
return false;
}
// at least 2 types; use instanceof otherwise
if (count($nodeTypes) < 2) {
throw new ShouldNotHappenException();
}
foreach ($nodeTypes as $nodeType) {
if (is_a($element, $nodeType, true)) {
return true;
}
}
return false;
}
}

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Tests\Util;
use DateTime;
use Iterator;
use PHPUnit\Framework\TestCase;
use Rector\Core\Util\StaticInstanceOf;
use stdClass;
final class StaticInstanceOfTest extends TestCase
{
/**
* @dataProvider provideIsOneOf()
* @param class-string[] $array
* @param DateTime|stdClass|null $object
*/
public function testIsOneOf(?object $object, array $array, bool $expected): void
{
$this->assertSame($expected, StaticInstanceOf::isOneOf($object, $array));
}
public function provideIsOneOf(): Iterator
{
yield [new DateTime('now'), [DateTime::class, stdClass::class], true];
yield [new stdClass(), [DateTime::class, Iterator::class], false];
yield [null, [DateTime::class, Iterator::class], false];
}
}

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Tests\Util;
use DateTime;
use Iterator;
use PhpParser\Node;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Nop;
use PHPUnit\Framework\TestCase;
use Rector\Core\Util\StaticNodeInstanceOf;
use stdClass;
final class StaticNodeInstanceOfTest extends TestCase
{
/**
* @dataProvider provideIsOneOf()
* @param array<class-string<Node>> $array
* @param DateTime|stdClass|null $object
*/
public function testIsOneOf(?object $object, array $array, bool $expected): void
{
$this->assertSame($expected, StaticNodeInstanceOf::isOneOf($object, $array));
}
public function provideIsOneOf(): Iterator
{
yield [new String_('hey'), [LNumber::class, String_::class], true];
yield [null, [Nop::class], false];
}
}