Updated Rector to commit 75d1dca2ef328d91cff2b642e79f1c442696d0a1

75d1dca2ef Don't get type from PropertyFetch for not natively typed properties (#3327)
This commit is contained in:
Tomas Votruba 2023-02-06 17:12:10 +00:00
parent e30c207496
commit ab40a52407
8 changed files with 143 additions and 40 deletions

View File

@ -0,0 +1,52 @@
<?php
declare (strict_types=1);
namespace Rector\TypeDeclaration\TypeAnalyzer;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Identifier;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\Php\PhpPropertyReflection;
use PHPStan\Reflection\PropertyReflection;
use PHPStan\Reflection\WrapperPropertyReflection;
use PHPStan\Type\MixedType;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class PropertyFetchTypeAnalyzer
{
public function isPropertyFetchExprNotNativelyTyped(Expr $expr) : bool
{
if (!$expr instanceof PropertyFetch) {
return \false;
}
if (!$expr->name instanceof Identifier) {
return \false;
}
$scope = $expr->getAttribute(AttributeKey::SCOPE);
if (!$scope instanceof Scope) {
return \false;
}
$propertyName = $expr->name->toString();
$propertyHolderType = $scope->getType($expr->var);
if (!$propertyHolderType->hasProperty($propertyName)->yes()) {
return \false;
}
$propertyReflection = $propertyHolderType->getProperty($propertyName, $scope);
$phpPropertyReflection = $this->getNativeReflectionForProperty($propertyReflection);
if ($phpPropertyReflection === null) {
return \false;
}
return $phpPropertyReflection->getNativeType() instanceof MixedType;
}
private function getNativeReflectionForProperty(PropertyReflection $propertyReflection) : ?PhpPropertyReflection
{
$reflection = $propertyReflection;
while ($reflection instanceof WrapperPropertyReflection) {
$reflection = $reflection->getOriginalReflection();
}
if (!$reflection instanceof PhpPropertyReflection) {
return null;
}
return $reflection;
}
}

View File

@ -27,6 +27,7 @@ use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector;
use Rector\TypeDeclaration\AlreadyAssignDetector\NullTypeAssignDetector;
use Rector\TypeDeclaration\AlreadyAssignDetector\PropertyDefaultAssignDetector;
use Rector\TypeDeclaration\Matcher\PropertyAssignMatcher;
use Rector\TypeDeclaration\TypeAnalyzer\PropertyFetchTypeAnalyzer;
/**
* @deprecated
* @todo Split into many narrow-focused rules
@ -83,7 +84,12 @@ final class AssignToPropertyTypeInferer
* @var \Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer
*/
private $propertyFetchAnalyzer;
public function __construct(ConstructorAssignDetector $constructorAssignDetector, PropertyAssignMatcher $propertyAssignMatcher, PropertyDefaultAssignDetector $propertyDefaultAssignDetector, NullTypeAssignDetector $nullTypeAssignDetector, SimpleCallableNodeTraverser $simpleCallableNodeTraverser, TypeFactory $typeFactory, NodeTypeResolver $nodeTypeResolver, ExprAnalyzer $exprAnalyzer, ValueResolver $valueResolver, PropertyFetchAnalyzer $propertyFetchAnalyzer)
/**
* @readonly
* @var \Rector\TypeDeclaration\TypeAnalyzer\PropertyFetchTypeAnalyzer
*/
private $propertyFetchTypeAnalyzer;
public function __construct(ConstructorAssignDetector $constructorAssignDetector, PropertyAssignMatcher $propertyAssignMatcher, PropertyDefaultAssignDetector $propertyDefaultAssignDetector, NullTypeAssignDetector $nullTypeAssignDetector, SimpleCallableNodeTraverser $simpleCallableNodeTraverser, TypeFactory $typeFactory, NodeTypeResolver $nodeTypeResolver, ExprAnalyzer $exprAnalyzer, ValueResolver $valueResolver, PropertyFetchAnalyzer $propertyFetchAnalyzer, PropertyFetchTypeAnalyzer $propertyFetchTypeAnalyzer)
{
$this->constructorAssignDetector = $constructorAssignDetector;
$this->propertyAssignMatcher = $propertyAssignMatcher;
@ -95,37 +101,14 @@ final class AssignToPropertyTypeInferer
$this->exprAnalyzer = $exprAnalyzer;
$this->valueResolver = $valueResolver;
$this->propertyFetchAnalyzer = $propertyFetchAnalyzer;
$this->propertyFetchTypeAnalyzer = $propertyFetchTypeAnalyzer;
}
public function inferPropertyInClassLike(Property $property, string $propertyName, ClassLike $classLike) : ?Type
{
$assignedExprTypes = [];
$hasAssignDynamicPropertyValue = \false;
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($classLike->stmts, function (Node $node) use($propertyName, &$assignedExprTypes, &$hasAssignDynamicPropertyValue) : ?int {
if (!$node instanceof Assign) {
return null;
}
$expr = $this->propertyAssignMatcher->matchPropertyAssignExpr($node, $propertyName);
if (!$expr instanceof Expr) {
if (!$this->propertyFetchAnalyzer->isLocalPropertyFetch($node->var)) {
return null;
}
/** @var PropertyFetch|StaticPropertyFetch $assignVar */
$assignVar = $node->var;
if (!$assignVar->name instanceof Identifier) {
$hasAssignDynamicPropertyValue = \true;
return NodeTraverser::STOP_TRAVERSAL;
}
return null;
}
if ($this->exprAnalyzer->isNonTypedFromParam($node->expr)) {
return null;
}
$assignedExprTypes[] = $this->resolveExprStaticTypeIncludingDimFetch($node);
return null;
});
if ($hasAssignDynamicPropertyValue) {
if ($this->hasAssignDynamicPropertyValue($classLike, $propertyName)) {
return null;
}
$assignedExprTypes = $this->getAssignedExprTypes($classLike, $propertyName);
if ($this->shouldAddNullType($classLike, $propertyName, $assignedExprTypes)) {
$assignedExprTypes[] = new NullType();
}
@ -194,4 +177,53 @@ final class AssignToPropertyTypeInferer
}
return !$hasPropertyDefaultValue;
}
private function hasAssignDynamicPropertyValue(ClassLike $classLike, string $propertyName) : bool
{
$hasAssignDynamicPropertyValue = \false;
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($classLike->stmts, function (Node $node) use($propertyName, &$hasAssignDynamicPropertyValue) : ?int {
if (!$node instanceof Assign) {
return null;
}
$expr = $this->propertyAssignMatcher->matchPropertyAssignExpr($node, $propertyName);
if (!$expr instanceof Expr) {
if (!$this->propertyFetchAnalyzer->isLocalPropertyFetch($node->var)) {
return null;
}
/** @var PropertyFetch|StaticPropertyFetch $assignVar */
$assignVar = $node->var;
if (!$assignVar->name instanceof Identifier) {
$hasAssignDynamicPropertyValue = \true;
return NodeTraverser::STOP_TRAVERSAL;
}
return null;
}
return null;
});
return $hasAssignDynamicPropertyValue;
}
/**
* @return array<Type>
*/
private function getAssignedExprTypes(ClassLike $classLike, string $propertyName) : array
{
$assignedExprTypes = [];
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($classLike->stmts, function (Node $node) use($propertyName, &$assignedExprTypes) : ?int {
if (!$node instanceof Assign) {
return null;
}
$expr = $this->propertyAssignMatcher->matchPropertyAssignExpr($node, $propertyName);
if (!$expr instanceof Expr) {
return null;
}
if ($this->propertyFetchAnalyzer->isPropertyFetch($node->expr) && $this->propertyFetchTypeAnalyzer->isPropertyFetchExprNotNativelyTyped($node->expr)) {
return null;
}
if ($this->exprAnalyzer->isNonTypedFromParam($node->expr)) {
return null;
}
$assignedExprTypes[] = $this->resolveExprStaticTypeIncludingDimFetch($node);
return null;
});
return $assignedExprTypes;
}
}

View File

@ -22,6 +22,7 @@ use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\UnionType;
use Rector\Core\NodeAnalyzer\ParamAnalyzer;
use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer;
use Rector\Core\NodeManipulator\ClassMethodPropertyFetchManipulator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\NodeNameResolver\NodeNameResolver;
@ -33,6 +34,7 @@ use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\TypeDeclaration\TypeAnalyzer\PropertyFetchTypeAnalyzer;
use Rector\TypeDeclaration\TypeInferer\AssignToPropertyTypeInferer;
/**
* @deprecated
@ -95,7 +97,17 @@ final class TrustedClassMethodPropertyTypeInferer
* @var \Rector\NodeTypeResolver\TypeComparator\TypeComparator
*/
private $typeComparator;
public function __construct(ClassMethodPropertyFetchManipulator $classMethodPropertyFetchManipulator, ReflectionProvider $reflectionProvider, NodeNameResolver $nodeNameResolver, SimpleCallableNodeTraverser $simpleCallableNodeTraverser, TypeFactory $typeFactory, StaticTypeMapper $staticTypeMapper, NodeTypeResolver $nodeTypeResolver, BetterNodeFinder $betterNodeFinder, ParamAnalyzer $paramAnalyzer, AssignToPropertyTypeInferer $assignToPropertyTypeInferer, TypeComparator $typeComparator)
/**
* @readonly
* @var \Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer
*/
private $propertyFetchAnalyzer;
/**
* @readonly
* @var \Rector\TypeDeclaration\TypeAnalyzer\PropertyFetchTypeAnalyzer
*/
private $propertyFetchTypeAnalyzer;
public function __construct(ClassMethodPropertyFetchManipulator $classMethodPropertyFetchManipulator, ReflectionProvider $reflectionProvider, NodeNameResolver $nodeNameResolver, SimpleCallableNodeTraverser $simpleCallableNodeTraverser, TypeFactory $typeFactory, StaticTypeMapper $staticTypeMapper, NodeTypeResolver $nodeTypeResolver, BetterNodeFinder $betterNodeFinder, ParamAnalyzer $paramAnalyzer, AssignToPropertyTypeInferer $assignToPropertyTypeInferer, TypeComparator $typeComparator, PropertyFetchAnalyzer $propertyFetchAnalyzer, PropertyFetchTypeAnalyzer $propertyFetchTypeAnalyzer)
{
$this->classMethodPropertyFetchManipulator = $classMethodPropertyFetchManipulator;
$this->reflectionProvider = $reflectionProvider;
@ -108,6 +120,8 @@ final class TrustedClassMethodPropertyTypeInferer
$this->paramAnalyzer = $paramAnalyzer;
$this->assignToPropertyTypeInferer = $assignToPropertyTypeInferer;
$this->typeComparator = $typeComparator;
$this->propertyFetchAnalyzer = $propertyFetchAnalyzer;
$this->propertyFetchTypeAnalyzer = $propertyFetchTypeAnalyzer;
}
public function inferProperty(Property $property, ClassMethod $classMethod) : Type
{
@ -126,6 +140,9 @@ final class TrustedClassMethodPropertyTypeInferer
$assignedExprs = $this->classMethodPropertyFetchManipulator->findAssignsToPropertyName($classMethod, $propertyName);
$resolvedTypes = [];
foreach ($assignedExprs as $assignedExpr) {
if ($this->propertyFetchAnalyzer->isPropertyFetch($assignedExpr) && $this->propertyFetchTypeAnalyzer->isPropertyFetchExprNotNativelyTyped($assignedExpr)) {
continue;
}
$resolvedTypes[] = $this->nodeTypeResolver->getType($assignedExpr);
}
if ($resolvedTypes === []) {

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '913ccba104bde2c858fba76ae56f1c4d7c35b067';
public const PACKAGE_VERSION = '75d1dca2ef328d91cff2b642e79f1c442696d0a1';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2023-02-06 16:33:15';
public const RELEASE_DATE = '2023-02-06 17:06:42';
/**
* @var int
*/

2
vendor/autoload.php vendored
View File

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

View File

@ -2790,6 +2790,7 @@ return array(
'Rector\\TypeDeclaration\\TypeAnalyzer\\AlwaysStrictBoolExprAnalyzer' => $baseDir . '/rules/TypeDeclaration/TypeAnalyzer/AlwaysStrictBoolExprAnalyzer.php',
'Rector\\TypeDeclaration\\TypeAnalyzer\\AlwaysStrictScalarExprAnalyzer' => $baseDir . '/rules/TypeDeclaration/TypeAnalyzer/AlwaysStrictScalarExprAnalyzer.php',
'Rector\\TypeDeclaration\\TypeAnalyzer\\GenericClassStringTypeNormalizer' => $baseDir . '/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php',
'Rector\\TypeDeclaration\\TypeAnalyzer\\PropertyFetchTypeAnalyzer' => $baseDir . '/rules/TypeDeclaration/TypeAnalyzer/PropertyFetchTypeAnalyzer.php',
'Rector\\TypeDeclaration\\TypeAnalyzer\\ReturnStrictTypeAnalyzer' => $baseDir . '/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php',
'Rector\\TypeDeclaration\\TypeAnalyzer\\StrictReturnClassConstReturnTypeAnalyzer' => $baseDir . '/rules/TypeDeclaration/TypeAnalyzer/StrictReturnClassConstReturnTypeAnalyzer.php',
'Rector\\TypeDeclaration\\TypeInferer\\AssignToPropertyTypeInferer' => $baseDir . '/rules/TypeDeclaration/TypeInferer/AssignToPropertyTypeInferer.php',

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit0a672635d29f42a014258712107ce131
class ComposerAutoloaderInit6ed8c1e889d5a966c6689f7b3517e8f3
{
private static $loader;
@ -22,17 +22,17 @@ class ComposerAutoloaderInit0a672635d29f42a014258712107ce131
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit0a672635d29f42a014258712107ce131', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit6ed8c1e889d5a966c6689f7b3517e8f3', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit0a672635d29f42a014258712107ce131', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit6ed8c1e889d5a966c6689f7b3517e8f3', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit0a672635d29f42a014258712107ce131::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit6ed8c1e889d5a966c6689f7b3517e8f3::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInit0a672635d29f42a014258712107ce131::$files;
$filesToLoad = \Composer\Autoload\ComposerStaticInit6ed8c1e889d5a966c6689f7b3517e8f3::$files;
$requireFile = \Closure::bind(static function ($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 ComposerStaticInit0a672635d29f42a014258712107ce131
class ComposerStaticInit6ed8c1e889d5a966c6689f7b3517e8f3
{
public static $files = array (
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
@ -3035,6 +3035,7 @@ class ComposerStaticInit0a672635d29f42a014258712107ce131
'Rector\\TypeDeclaration\\TypeAnalyzer\\AlwaysStrictBoolExprAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/TypeAnalyzer/AlwaysStrictBoolExprAnalyzer.php',
'Rector\\TypeDeclaration\\TypeAnalyzer\\AlwaysStrictScalarExprAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/TypeAnalyzer/AlwaysStrictScalarExprAnalyzer.php',
'Rector\\TypeDeclaration\\TypeAnalyzer\\GenericClassStringTypeNormalizer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php',
'Rector\\TypeDeclaration\\TypeAnalyzer\\PropertyFetchTypeAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/TypeAnalyzer/PropertyFetchTypeAnalyzer.php',
'Rector\\TypeDeclaration\\TypeAnalyzer\\ReturnStrictTypeAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/TypeAnalyzer/ReturnStrictTypeAnalyzer.php',
'Rector\\TypeDeclaration\\TypeAnalyzer\\StrictReturnClassConstReturnTypeAnalyzer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/TypeAnalyzer/StrictReturnClassConstReturnTypeAnalyzer.php',
'Rector\\TypeDeclaration\\TypeInferer\\AssignToPropertyTypeInferer' => __DIR__ . '/../..' . '/rules/TypeDeclaration/TypeInferer/AssignToPropertyTypeInferer.php',
@ -3086,9 +3087,9 @@ class ComposerStaticInit0a672635d29f42a014258712107ce131
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit0a672635d29f42a014258712107ce131::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit0a672635d29f42a014258712107ce131::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit0a672635d29f42a014258712107ce131::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit6ed8c1e889d5a966c6689f7b3517e8f3::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit6ed8c1e889d5a966c6689f7b3517e8f3::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit6ed8c1e889d5a966c6689f7b3517e8f3::$classMap;
}, null, ClassLoader::class);
}