Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/php_linter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 3 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -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#'
1 change: 1 addition & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
SetList::PHP_72,
SetList::PHP_73,
SetList::EARLY_RETURN,
SetList::TYPE_DECLARATION_STRICT,
]);

$parameters->set(Option::PATHS, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
final class CompleteDynamicPropertiesRectorTest extends AbstractRectorTestCase
{
/**
* @requires PHP 8.0
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
final class RemoveDefaultArgumentValueRectorTest extends AbstractRectorTestCase
{
/**
* @requires PHP 8.0
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
final class AddDefaultValueForUndefinedVariableRectorTest extends AbstractRectorTestCase
{
/**
* @requires PHP 8.0
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@
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;
Expand All @@ -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
Expand Down Expand Up @@ -73,11 +86,11 @@ public function setAge(int $age)
*/
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
{
Expand All @@ -92,13 +105,16 @@ public function refactor(Node $node): ?Node
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) {
Expand All @@ -113,22 +129,29 @@ public function decorateParamWithType(ClassMethod $classMethod, Param $param): v
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) {
Expand All @@ -144,6 +167,6 @@ private function matchPropertyWithSingleType(PropertyFetch $propertyFetch): ?Pro
return null;
}

return $property;
return $property->type;
}
}
67 changes: 67 additions & 0 deletions rules/type-declaration/src/Reflection/ReflectionTypeResolver.php
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}

?>
Original file line number Diff line number Diff line change
@@ -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;
}

?>
Original file line number Diff line number Diff line change
@@ -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;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeFromStrictTypedPropertyRector\Source;

final class OutOfControlExternalClass
{
public string $name;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use Symplify\SmartFileSystem\SmartFileInfo;

/**
* @requires PHP < 8.0
* @requires PHP 8.0
*/
final class CompleteVarDocTypePropertyRectorTest extends AbstractRectorTestCase
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ namespace Rector\TypeDeclaration\Tests\Rector\Property\CompleteVarDocTypePropert
final class CallableType
{
/**
* @var callable|null
* @var null|callable
*/
private $code;
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ class Command
*/
private $applicationDefinitionMergedWithArgs = false;
/**
* @var callable|null
* @var null|callable
*/
private $code;
/**
Expand Down
Loading