[TypeDeclaration] Add external types to ParamTypeFromStrictTypedPropertyRector (#5560)

This commit is contained in:
Tomas Votruba 2021-02-15 20:31:53 +01:00 committed by GitHub
parent 642110750f
commit 1d80ef3df6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 228 additions and 22 deletions

View File

@ -11,7 +11,7 @@ jobs:
actions:
-
php_version: 7.3
run: vendor/bin/parallel-lint src bin/rector config tests packages rules --colors --exclude packages/rector-generator/templates --exclude rules/psr4/tests/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Source --exclude rules/autodiscovery/tests/Rector/FileNode/MoveInterfacesToContractNamespaceDirectoryRector/Expected --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithNativeProps.php --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithNativePropsPhp80.php --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithTypedPropertyTypes.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/Source/ExpectedSomeClassCopyEvent.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceMagicPropertyEventWithEventClassRector/Source/ExpectedFileManagerUploadEvent.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceMagicPropertyEventWithEventClassRector/Source/ExpectedDuplicatedEventParamsUploadEvent.php
run: vendor/bin/parallel-lint src bin/rector config tests packages rules --colors --exclude packages/rector-generator/templates --exclude rules/psr4/tests/Rector/Namespace_/MultipleClassFileToPsr4ClassesRector/Source --exclude rules/autodiscovery/tests/Rector/FileNode/MoveInterfacesToContractNamespaceDirectoryRector/Expected --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithNativeProps.php --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithNativePropsPhp80.php --exclude packages/node-type-resolver/tests/PerNodeTypeResolver/PropertyFetchTypeResolver/Source/ClassWithTypedPropertyTypes.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceEventManagerWithEventSubscriberRector/Source/ExpectedSomeClassCopyEvent.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceMagicPropertyEventWithEventClassRector/Source/ExpectedFileManagerUploadEvent.php --exclude rules/nette-kdyby/tests/Rector/MethodCall/ReplaceMagicPropertyEventWithEventClassRector/Source/ExpectedDuplicatedEventParamsUploadEvent.php --exclude rules/type-declaration/tests/Rector/ClassMethod/ParamTypeFromStrictTypedPropertyRector/Source/OutOfControlExternalClass.php
-
php_version: 8.0

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.69, <0.12.70",
"phpstan/phpstan": "^0.12.69",
"phpstan/phpstan-phpunit": "^0.12.17",
"psr/simple-cache": "^1.0",
"sebastian/diff": "^4.0.4",

View File

@ -541,3 +541,6 @@ parameters:
# known values
- '#Method Rector\\Testing\\Finder\\RectorsFinder\:\:findClassesInDirectoriesByName\(\) should return array<class\-string\> but returns array<int, \(int\|string\)\>#'
- '#Content of method "collectVariableFromAssign\(\)" is duplicated with method "collectVariableFromAssign\(\)" in "Rector\\NetteToSymfony\\NodeAnalyzer\\ClassMethodRenderAnalyzer" class\. Use unique content or abstract service instead#'
- '#Property PhpParser\\Node\\Param\:\:\$type \(PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType\|null\) does not accept PhpParser\\Node#'
- '#Binary operation "\." between array\|string\|false and (.*?) results in an error#'
- '#Parameter \#3 \.\.\.\$rest of function array_uintersect expects array, Closure\(PhpParser\\Node\\Param, PhpParser\\Node\\Param\)\: int given#'

View File

@ -56,6 +56,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
SetList::PHP_72,
SetList::PHP_73,
SetList::EARLY_RETURN,
SetList::TYPE_DECLARATION_STRICT,
]);
$parameters->set(Option::PATHS, [

View File

@ -11,6 +11,7 @@ use Symplify\SmartFileSystem\SmartFileInfo;
final class CompleteDynamicPropertiesRectorTest extends AbstractRectorTestCase
{
/**
* @requires PHP 8.0
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void

View File

@ -12,6 +12,7 @@ use Symplify\SmartFileSystem\SmartFileInfo;
final class RemoveDefaultArgumentValueRectorTest extends AbstractRectorTestCase
{
/**
* @requires PHP 8.0
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void

View File

@ -12,6 +12,7 @@ use Symplify\SmartFileSystem\SmartFileInfo;
final class AddDefaultValueForUndefinedVariableRectorTest extends AbstractRectorTestCase
{
/**
* @requires PHP 8.0
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void

View File

@ -5,17 +5,22 @@ declare(strict_types=1);
namespace Rector\TypeDeclaration\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\NullableType;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\UnionType;
use PhpParser\NodeTraverser;
use PHPStan\Type\Type;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\TypeDeclaration\Reflection\ReflectionTypeResolver;
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -30,9 +35,17 @@ final class ParamTypeFromStrictTypedPropertyRector extends AbstractRector
*/
private $simpleCallableNodeTraverser;
public function __construct(SimpleCallableNodeTraverser $simpleCallableNodeTraverser)
{
/**
* @var ReflectionTypeResolver
*/
private $reflectionTypeResolver;
public function __construct(
SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
ReflectionTypeResolver $reflectionTypeResolver
) {
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
$this->reflectionTypeResolver = $reflectionTypeResolver;
}
public function getRuleDefinition(): RuleDefinition
@ -73,11 +86,11 @@ CODE_SAMPLE
*/
public function getNodeTypes(): array
{
return [ClassMethod::class];
return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class];
}
/**
* @param ClassMethod $node
* @param ClassMethod|Function_|Closure|ArrowFunction $node
*/
public function refactor(Node $node): ?Node
{
@ -92,13 +105,16 @@ CODE_SAMPLE
return $node;
}
public function decorateParamWithType(ClassMethod $classMethod, Param $param): void
/**
* @param ClassMethod|Function_|Closure|ArrowFunction $functionLike
*/
public function decorateParamWithType(FunctionLike $functionLike, Param $param): void
{
if ($param->type !== null) {
return;
}
$this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (
$this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $functionLike->getStmts(), function (
Node $node
) use ($param): ?int {
if (! $node instanceof Assign) {
@ -113,22 +129,29 @@ CODE_SAMPLE
return null;
}
$property = $this->matchPropertyWithSingleType($node->var);
if (! $property instanceof Property) {
$singlePropertyTypeNode = $this->matchPropertySingleTypeNode($node->var);
if (! $singlePropertyTypeNode instanceof Node) {
return null;
}
$param->type = $property->type;
$this->rectorChangeCollector->notifyNodeFileInfo($node);
$param->type = $singlePropertyTypeNode;
return NodeTraverser::STOP_TRAVERSAL;
});
}
private function matchPropertyWithSingleType(PropertyFetch $propertyFetch): ?Property
private function matchPropertySingleTypeNode(PropertyFetch $propertyFetch): ?Node
{
$property = $this->nodeRepository->findPropertyByPropertyFetch($propertyFetch);
if (! $property instanceof Property) {
return null;
// code from /vendor
$propertyFetchType = $this->reflectionTypeResolver->resolvePropertyFetchType($propertyFetch);
if (! $propertyFetchType instanceof Type) {
return null;
}
return $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($propertyFetchType);
}
if ($property->type === null) {
@ -144,6 +167,6 @@ CODE_SAMPLE
return null;
}
return $property;
return $property->type;
}
}

View File

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\Reflection;
use PhpParser\Node\Expr\PropertyFetch;
use PHPStan\Reflection\Php\PhpPropertyReflection;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
final class ReflectionTypeResolver
{
/**
* @var NodeTypeResolver
*/
private $nodeTypeResolver;
/**
* @var ReflectionProvider
*/
private $reflectionProvider;
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
public function __construct(
NodeTypeResolver $nodeTypeResolver,
ReflectionProvider $reflectionProvider,
NodeNameResolver $nodeNameResolver
) {
$this->nodeTypeResolver = $nodeTypeResolver;
$this->reflectionProvider = $reflectionProvider;
$this->nodeNameResolver = $nodeNameResolver;
}
public function resolvePropertyFetchType(PropertyFetch $propertyFetch): ?Type
{
$objectType = $this->nodeTypeResolver->resolve($propertyFetch->var);
if (! $objectType instanceof TypeWithClassName) {
return null;
}
$classReflection = $this->reflectionProvider->getClass($objectType->getClassName());
$propertyName = $this->nodeNameResolver->getName($propertyFetch);
if ($propertyName === null) {
return null;
}
if ($classReflection->hasProperty($propertyName)) {
$propertyFetchScope = $propertyFetch->getAttribute(AttributeKey::SCOPE);
$propertyReflection = $classReflection->getProperty($propertyName, $propertyFetchScope);
if ($propertyReflection instanceof PhpPropertyReflection) {
return $propertyReflection->getNativeType();
}
}
return null;
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeFromStrictTypedPropertyRector\Fixture;
final class EvenConstructor
{
private int $age;
public function __construct($age)
{
$this->age = $age;
}
}
?>
-----
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeFromStrictTypedPropertyRector\Fixture;
final class EvenConstructor
{
private int $age;
public function __construct(int $age)
{
$this->age = $age;
}
}
?>

View File

@ -0,0 +1,37 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeFromStrictTypedPropertyRector\Fixture;
final class ExternalType
{
public function setValues($age, ExternalClass $externalClass)
{
$externalClass->age = $age;
}
}
final class ExternalClass
{
public int $age;
}
?>
-----
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeFromStrictTypedPropertyRector\Fixture;
final class ExternalType
{
public function setValues(int $age, ExternalClass $externalClass)
{
$externalClass->age = $age;
}
}
final class ExternalClass
{
public int $age;
}
?>

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeFromStrictTypedPropertyRector\Fixture;
use Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeFromStrictTypedPropertyRector\Source\OutOfControlExternalClass;
final class VendorExternalType
{
public function setValues($age, OutOfControlExternalClass $outOfControlExternalClass)
{
$outOfControlExternalClass->name = $age;
}
}
?>
-----
<?php
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeFromStrictTypedPropertyRector\Fixture;
use Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeFromStrictTypedPropertyRector\Source\OutOfControlExternalClass;
final class VendorExternalType
{
public function setValues(string $age, OutOfControlExternalClass $outOfControlExternalClass)
{
$outOfControlExternalClass->name = $age;
}
}
?>

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeFromStrictTypedPropertyRector\Source;
final class OutOfControlExternalClass
{
public string $name;
}

View File

@ -10,7 +10,7 @@ use Rector\TypeDeclaration\Rector\Property\CompleteVarDocTypePropertyRector;
use Symplify\SmartFileSystem\SmartFileInfo;
/**
* @requires PHP < 8.0
* @requires PHP 8.0
*/
final class CompleteVarDocTypePropertyRectorTest extends AbstractRectorTestCase
{

View File

@ -30,7 +30,7 @@ namespace Rector\TypeDeclaration\Tests\Rector\Property\CompleteVarDocTypePropert
final class CallableType
{
/**
* @var callable|null
* @var null|callable
*/
private $code;
/**

View File

@ -623,7 +623,7 @@ class Command
*/
private $applicationDefinitionMergedWithArgs = false;
/**
* @var callable|null
* @var null|callable
*/
private $code;
/**

View File

@ -154,6 +154,11 @@ abstract class AbstractTemporaryRector extends NodeVisitorAbstract implements Ph
*/
protected $nodeRemover;
/**
* @var RectorChangeCollector
*/
protected $rectorChangeCollector;
/**
* @var SimpleCallableNodeTraverser
*/
@ -204,11 +209,6 @@ abstract class AbstractTemporaryRector extends NodeVisitorAbstract implements Ph
*/
private $propertyToAddCollector;
/**
* @var RectorChangeCollector
*/
private $rectorChangeCollector;
/**
* @var PropertyAdder
*/