mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-17 13:28:18 +01:00
improve ParamTypeDeclarationRector complexity
This commit is contained in:
parent
3e5dd77bb6
commit
4aac338d20
@ -16,6 +16,16 @@ use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
||||
|
||||
final class PHPStanStaticTypeMapper
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const KIND_PARAM = 'param';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const KIND_PROPERTY = 'property';
|
||||
|
||||
/**
|
||||
* @var TypeMapperInterface[]
|
||||
*/
|
||||
|
@ -35,6 +35,9 @@ final class ClassStringTypeMapper implements TypeMapperInterface
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassStringType $type
|
||||
*/
|
||||
public function mapToDocString(Type $type, ?Type $parentType = null): string
|
||||
{
|
||||
return $type->describe(VerbosityLevel::typeOnly());
|
||||
|
@ -16,7 +16,9 @@ parameters:
|
||||
- compiler/src
|
||||
|
||||
paths:
|
||||
- bin
|
||||
- src
|
||||
- rules
|
||||
- packages
|
||||
- tests
|
||||
- compiler/src
|
||||
@ -232,3 +234,7 @@ parameters:
|
||||
- '#Method Rector\\SOLID\\Reflection\\ParentConstantReflectionResolver\:\:(.*?)\(\) should return ReflectionClassConstant\|null but returns ReflectionClassConstant\|false#'
|
||||
- '#Parameter \#1 \$firstStmt of method Rector\\Core\\Rector\\MethodBody\\NormalToFluentRector\:\:isBothMethodCallMatch\(\) expects PhpParser\\Node\\Stmt\\Expression, PhpParser\\Node\\Stmt given#'
|
||||
- '#Method Rector\\Core\\Rector\\AbstractRector\:\:wrapToArg\(\) should return array<PhpParser\\Node\\Arg\> but returns array<PhpParser\\Node\\Arg\|PhpParser\\Node\\Expr\>#'
|
||||
- '#Property PhpParser\\Node\\Stmt\\ClassMethod\:\:\$returnType \(PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType\|null\) does not accept PhpParser\\Node#'
|
||||
|
||||
- '#Parameter \#1 \$possibleSubtype of method Rector\\TypeDeclaration\\PhpParserTypeAnalyzer\:\:isSubtypeOf\(\) expects PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType, PhpParser\\Node given#'
|
||||
- '#Parameter \#2 \$inferredReturnNode of method Rector\\TypeDeclaration\\Rector\\FunctionLike\\ReturnTypeDeclarationRector\:\:addReturnType\(\) expects PhpParser\\Node, PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType\|null given#'
|
||||
|
@ -10,6 +10,7 @@
|
||||
</php>
|
||||
<testsuites>
|
||||
<testsuite name="main">
|
||||
<directory>rules/*/tests</directory>
|
||||
<directory>packages/*/tests</directory>
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
@ -17,6 +18,7 @@
|
||||
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="false">
|
||||
<directory suffix=".php">rules/*/src</directory>
|
||||
<directory suffix=".php">packages/*/src</directory>
|
||||
<directory>src</directory>
|
||||
</whitelist>
|
||||
|
@ -15,6 +15,7 @@ use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||
use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer;
|
||||
use Rector\VendorLocker\VendorLockResolver;
|
||||
|
||||
@ -95,7 +96,10 @@ PHP
|
||||
return null;
|
||||
}
|
||||
|
||||
$propertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($varType, 'property');
|
||||
$propertyTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode(
|
||||
$varType,
|
||||
PHPStanStaticTypeMapper::KIND_PROPERTY
|
||||
);
|
||||
if ($propertyTypeNode === null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -5,18 +5,23 @@ declare(strict_types=1);
|
||||
namespace Rector\TypeDeclaration\Rector\FunctionLike;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\UnionType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||
|
||||
/**
|
||||
* @see \Rector\TypeDeclaration\Tests\Rector\FunctionLike\ParamTypeDeclarationRector\ParamTypeDeclarationRectorTest
|
||||
@ -114,70 +119,7 @@ PHP
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($node->params as $position => $paramNode) {
|
||||
// skip variadics
|
||||
if ($paramNode->variadic) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// already set → skip
|
||||
$hasNewType = false;
|
||||
if ($paramNode->type !== null) {
|
||||
$hasNewType = $paramNode->type->getAttribute(self::HAS_NEW_INHERITED_TYPE, false);
|
||||
if (! $hasNewType) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$paramNodeName = '$' . $this->getName($paramNode->var);
|
||||
|
||||
// no info about it
|
||||
if (! isset($paramWithTypes[$paramNodeName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$paramType = $paramWithTypes[$paramNodeName];
|
||||
$paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($paramType, 'param');
|
||||
if ($paramTypeNode === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$position = (int) $position;
|
||||
if ($node instanceof ClassMethod && $this->vendorLockResolver->isParamChangeVendorLockedIn(
|
||||
$node,
|
||||
$position
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($hasNewType) {
|
||||
// should override - is it subtype?
|
||||
$possibleOverrideNewReturnType = $paramTypeNode;
|
||||
if ($possibleOverrideNewReturnType !== null) {
|
||||
if ($paramNode->type === null) {
|
||||
$paramNode->type = $paramTypeNode;
|
||||
} elseif ($this->phpParserTypeAnalyzer->isSubtypeOf(
|
||||
$possibleOverrideNewReturnType,
|
||||
$paramNode->type
|
||||
)) {
|
||||
// allow override
|
||||
$paramNode->type = $paramTypeNode;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$paramNode->type = $paramTypeNode;
|
||||
|
||||
$paramNodeType = $paramNode->type instanceof NullableType ? $paramNode->type->type : $paramNode->type;
|
||||
// "resource" is valid phpdoc type, but it's not implemented in PHP
|
||||
if ($paramNodeType instanceof Name && reset($paramNodeType->parts) === 'resource') {
|
||||
$paramNode->type = null;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->populateChildren($node, $position, $paramType);
|
||||
}
|
||||
$this->refactorParams($node, $paramWithTypes);
|
||||
|
||||
return $node;
|
||||
}
|
||||
@ -250,4 +192,111 @@ PHP
|
||||
|
||||
$this->notifyNodeChangeFileInfo($paramNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Function_ $functionLike
|
||||
* @param Type[] $paramWithTypes
|
||||
*/
|
||||
private function refactorParams(FunctionLike $functionLike, array $paramWithTypes): void
|
||||
{
|
||||
foreach ($functionLike->params as $position => $param) {
|
||||
// to be sure
|
||||
$position = (int) $position;
|
||||
|
||||
if ($this->shouldSkipParam($param)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$hasNewType = false;
|
||||
|
||||
$docParamType = $this->matchParamNodeFromDoc($paramWithTypes, $param);
|
||||
if ($docParamType === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode(
|
||||
$docParamType,
|
||||
PHPStanStaticTypeMapper::KIND_PARAM
|
||||
);
|
||||
|
||||
if ($paramTypeNode === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($functionLike instanceof ClassMethod && $this->vendorLockResolver->isParamChangeVendorLockedIn(
|
||||
$functionLike,
|
||||
$position
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->changeParamNodeType($hasNewType, $paramTypeNode, $param);
|
||||
|
||||
$this->populateChildren($functionLike, $position, $docParamType);
|
||||
}
|
||||
}
|
||||
|
||||
private function isResourceType(Node $node): bool
|
||||
{
|
||||
if ($this->isName($node, 'resource')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($node instanceof NullableType) {
|
||||
return $this->isResourceType($node->type);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Identifier|Name|NullableType|UnionType $paramTypeNode
|
||||
*/
|
||||
private function changeParamNodeType(bool $hasNewType, Node $paramTypeNode, Param $param): void
|
||||
{
|
||||
if ($hasNewType) {
|
||||
// should override - is it subtype?
|
||||
if ($param->type === null) {
|
||||
$param->type = $paramTypeNode;
|
||||
} elseif ($this->phpParserTypeAnalyzer->isSubtypeOf($paramTypeNode, $param->type)) {
|
||||
// allow override
|
||||
$param->type = $paramTypeNode;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isResourceType($paramTypeNode)) {
|
||||
// "resource" is valid phpdoc type, but it's not implemented in PHP
|
||||
$param->type = null;
|
||||
} else {
|
||||
$param->type = $paramTypeNode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Type[] $paramWithTypes
|
||||
*/
|
||||
private function matchParamNodeFromDoc(array $paramWithTypes, Param $param): ?Type
|
||||
{
|
||||
$paramNodeName = '$' . $this->getName($param->var);
|
||||
|
||||
return $paramWithTypes[$paramNodeName] ?? null;
|
||||
}
|
||||
|
||||
private function shouldSkipParam(Param $param): bool
|
||||
{
|
||||
if ($param->variadic) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// already set → skip
|
||||
if ($param->type === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasNewType = $param->type->getAttribute(self::HAS_NEW_INHERITED_TYPE, false);
|
||||
|
||||
return ! $hasNewType;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user