mirror of
https://github.com/rectorphp/rector.git
synced 2025-04-20 23:41:57 +02:00
add GetAttributeReturnTypeExtension
This commit is contained in:
parent
753a478e7a
commit
45df82424c
@ -59,6 +59,7 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Rector\\PHPStanExtensions\\": "utils/phpstan/src",
|
||||
"Rector\\Tests\\": "tests",
|
||||
"Rector\\NodeTypeResolver\\Tests\\": "packages/NodeTypeResolver/tests",
|
||||
"Rector\\CakePHP\\Tests\\": "packages/CakePHP/tests",
|
||||
|
13
phpstan.neon
13
phpstan.neon
@ -5,7 +5,7 @@ includes:
|
||||
|
||||
parameters:
|
||||
# to allow intalling with various phsptan versions without reporting old errors here
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
# reportUnmatchedIgnoredErrors: false
|
||||
level: 7
|
||||
|
||||
excludes_analyse:
|
||||
@ -63,7 +63,6 @@ parameters:
|
||||
- '#Access to an undefined property PhpParser\\Node\\Expr\\MethodCall\|PhpParser\\Node\\Stmt\\ClassMethod::\$params#'
|
||||
- '#Cannot call method getName\(\) on PHPStan\\Reflection\\ClassReflection\|null#'
|
||||
|
||||
- '#Cannot call method getAttribute\(\) on PhpParser\\Node\\Name\|null#'
|
||||
- '#Cannot call method getText\(\) on PhpParser\\Comment\\Doc\|null#'
|
||||
- '#Method Rector\\PhpParser\\Node\\Maintainer\\PropertyMaintainer::getAllPropertyFetch\(\) should return array<PhpParser\\Node\\Expr\\PropertyFetch> but returns array<PhpParser\\Node>#'
|
||||
|
||||
@ -101,8 +100,10 @@ parameters:
|
||||
- '#Strict comparison using === between PhpParser\\Node\\Expr and null will always evaluate to false#'
|
||||
- '#Cannot cast array<string>\|string\|null to string#'
|
||||
|
||||
# false positive on anonymous classes
|
||||
- '#http\:\/\/bit\.ly\/typehintarray#'
|
||||
|
||||
services:
|
||||
-
|
||||
class: Symplify\PHPStanExtensions\Type\SplFileInfoTolerantDynamicMethodReturnTypeExtension
|
||||
tags:
|
||||
- phpstan.broker.dynamicMethodReturnTypeExtension
|
||||
- { class: Symplify\PHPStanExtensions\Type\SplFileInfoTolerantDynamicMethodReturnTypeExtension, tags: [phpstan.broker.dynamicMethodReturnTypeExtension] }
|
||||
# $node->geAttribute($1) => Type|null by $1
|
||||
- { class: Rector\PHPStanExtensions\Rector\Type\GetAttributeReturnTypeExtension, tags: [phpstan.broker.dynamicMethodReturnTypeExtension] }
|
||||
|
@ -0,0 +1,113 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStanExtensions\Rector\Type;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Reflection\ParametersAcceptorSelector;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\DynamicMethodReturnTypeExtension;
|
||||
use PHPStan\Type\IntegerType;
|
||||
use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Php\PhpTypeSupport;
|
||||
use Rector\Php\TypeAnalyzer;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
|
||||
final class GetAttributeReturnTypeExtension implements DynamicMethodReturnTypeExtension
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $argumentKeyToReturnType = [
|
||||
'Rector\NodeTypeResolver\Node\Attribute::FILE_INFO' => SmartFileInfo::class,
|
||||
'Rector\NodeTypeResolver\Node\Attribute::RESOLVED_NAME' => Name::class,
|
||||
'Rector\NodeTypeResolver\Node\Attribute::CLASS_NODE' => ClassLike::class,
|
||||
'Rector\NodeTypeResolver\Node\Attribute::METHOD_NODE' => ClassMethod::class,
|
||||
'Rector\NodeTypeResolver\Node\Attribute::CURRENT_EXPRESSION' => Expression::class,
|
||||
'Rector\NodeTypeResolver\Node\Attribute::PREVIOUS_EXPRESSION' => Expression::class,
|
||||
'Rector\NodeTypeResolver\Node\Attribute::SCOPE' => Scope::class,
|
||||
# Node
|
||||
'Rector\NodeTypeResolver\Node\Attribute::ORIGINAL_NODE' => Node::class,
|
||||
'Rector\NodeTypeResolver\Node\Attribute::PARENT_NODE' => Node::class,
|
||||
'Rector\NodeTypeResolver\Node\Attribute::NEXT_NODE' => Node::class,
|
||||
'Rector\NodeTypeResolver\Node\Attribute::PREVIOUS_NODE' => Node::class,
|
||||
'Rector\NodeTypeResolver\Node\Attribute::USE_NODES' => [Use_::class],
|
||||
# scalars
|
||||
'Rector\NodeTypeResolver\Node\Attribute::PARENT_CLASS_NAME' => 'string',
|
||||
'Rector\NodeTypeResolver\Node\Attribute::NAMESPACE_NAME' => 'string',
|
||||
'Rector\NodeTypeResolver\Node\Attribute::CLASS_NAME' => 'string',
|
||||
'Rector\NodeTypeResolver\Node\Attribute::METHOD_NAME' => 'string',
|
||||
];
|
||||
|
||||
public function getClass(): string
|
||||
{
|
||||
return Node::class;
|
||||
}
|
||||
|
||||
public function isMethodSupported(MethodReflection $methodReflection): bool
|
||||
{
|
||||
return $methodReflection->getName() === 'getAttribute';
|
||||
}
|
||||
|
||||
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
|
||||
{
|
||||
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
|
||||
|
||||
$argumentValue = $this->resolveArgumentValue($methodCall->args[0]->value);
|
||||
if ($argumentValue === null) {
|
||||
return $returnType;
|
||||
}
|
||||
|
||||
if (! isset($this->argumentKeyToReturnType[$argumentValue])) {
|
||||
return $returnType;
|
||||
}
|
||||
|
||||
$returnType = $this->argumentKeyToReturnType[$argumentValue];
|
||||
if ($returnType === 'string') {
|
||||
return new UnionType([new StringType(), new NullType()]);
|
||||
}
|
||||
|
||||
if (is_array($returnType) && count($returnType) === 1) {
|
||||
$arrayType = new ArrayType(new IntegerType(), new ObjectType($returnType[0]));
|
||||
return new UnionType([$arrayType, new NullType()]);
|
||||
}
|
||||
|
||||
return new UnionType([new ObjectType($returnType), new NullType()]);
|
||||
}
|
||||
|
||||
private function resolveArgumentValue(Expr $node): ?string
|
||||
{
|
||||
$value = null;
|
||||
|
||||
if ($node instanceof ClassConstFetch) {
|
||||
if ($node->class instanceof Name) {
|
||||
$value = $node->class->toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($node->name instanceof Identifier) {
|
||||
$value .= '::' . $node->name->toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user