mirror of
https://github.com/rectorphp/rector.git
synced 2025-04-20 15:31:58 +02:00
Add BetterNodeFinderReturnTypeExtension
This commit is contained in:
parent
7c8a88cf73
commit
def4db7a8f
@ -159,7 +159,6 @@ CODE_SAMPLE
|
||||
$paramNames[] = $this->getName($paramNode);
|
||||
}
|
||||
|
||||
/** @var Variable[] $variableNodes */
|
||||
$variableNodes = $this->betterNodeFinder->findInstanceOf($nodes, Variable::class);
|
||||
|
||||
$filteredVariables = [];
|
||||
@ -169,11 +168,12 @@ CODE_SAMPLE
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($this->getName($variableNode), $paramNames, true)) {
|
||||
$variableName = $this->getName($variableNode);
|
||||
if (in_array($variableName, $paramNames, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$filteredVariables[$this->getName($variableNode)] = $variableNode;
|
||||
$filteredVariables[$variableName] = $variableNode;
|
||||
}
|
||||
|
||||
return $filteredVariables;
|
||||
|
@ -12,7 +12,7 @@ use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\PhpParser\Node\Maintainer\ClassMaintainer;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
@ -23,13 +23,13 @@ use Rector\RectorDefinition\RectorDefinition;
|
||||
final class Php4ConstructorRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
* @var ClassMaintainer
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
private $classMaintainer;
|
||||
|
||||
public function __construct(BetterNodeFinder $betterNodeFinder)
|
||||
public function __construct(ClassMaintainer $classMaintainer)
|
||||
{
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
$this->classMaintainer = $classMaintainer;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
@ -73,9 +73,7 @@ CODE_SAMPLE
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$namespace = $node->getAttribute(Attribute::NAMESPACE_NAME);
|
||||
// catch only classes without namespace
|
||||
if ($namespace) {
|
||||
if ($this->shouldSkip($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -84,21 +82,16 @@ CODE_SAMPLE
|
||||
return null;
|
||||
}
|
||||
|
||||
// anonymous class → skip
|
||||
if ($classNode->name === null || $node->isAbstract() || $node->isStatic()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// process parent call references first
|
||||
$this->processClassMethodStatementsForParentConstructorCalls($node);
|
||||
|
||||
// not PSR-4 constructor
|
||||
if (! $this->isNameInsensitive($classNode, (string) $node->name)) {
|
||||
if (! $this->isNameInsensitive($classNode, $this->getName($node))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// does it already have a __construct method?
|
||||
if (! in_array('__construct', $this->getClassMethodNames($classNode), true)) {
|
||||
if (! $this->classMaintainer->hasClassMethod($classNode, '__construct')) {
|
||||
$node->name = new Identifier('__construct');
|
||||
}
|
||||
|
||||
@ -120,6 +113,26 @@ CODE_SAMPLE
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function shouldSkip(ClassMethod $classMethod): bool
|
||||
{
|
||||
$namespace = $classMethod->getAttribute(Attribute::NAMESPACE_NAME);
|
||||
// catch only classes without namespace
|
||||
if ($namespace !== null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($classMethod->isAbstract() || $classMethod->isStatic()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$classNode = $classMethod->getAttribute(Attribute::CLASS_NODE);
|
||||
if ($classNode instanceof Class_ && $classNode->name === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function processClassMethodStatementsForParentConstructorCalls(ClassMethod $classMethodNode): void
|
||||
{
|
||||
if (! is_iterable($classMethodNode->stmts)) {
|
||||
@ -140,22 +153,6 @@ CODE_SAMPLE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getClassMethodNames(Class_ $classNode): array
|
||||
{
|
||||
$classMethodNames = [];
|
||||
|
||||
/** @var ClassMethod[] $classMethodNodes */
|
||||
$classMethodNodes = $this->betterNodeFinder->findInstanceOf($classNode->stmts, ClassMethod::class);
|
||||
foreach ($classMethodNodes as $classMethodNode) {
|
||||
$classMethodNames[] = $this->getName($classMethodNode);
|
||||
}
|
||||
|
||||
return $classMethodNames;
|
||||
}
|
||||
|
||||
private function isThisConstructCall(Node $node): bool
|
||||
{
|
||||
if (! $node instanceof MethodCall) {
|
||||
@ -191,16 +188,16 @@ CODE_SAMPLE
|
||||
}
|
||||
|
||||
// rename ParentClass
|
||||
if ((string) $node->class === $parentClassName) {
|
||||
if ($this->isName($node->class, $parentClassName)) {
|
||||
$node->class = new Name('parent');
|
||||
}
|
||||
|
||||
if ((string) $node->class !== 'parent') {
|
||||
if ($this->isName($node->class, 'parent') === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// it's not a parent PHP 4 constructor call
|
||||
if (! $this->isNameInsensitive($node, $parentClassName)) {
|
||||
if ($this->isNameInsensitive($node, $parentClassName) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Nop;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\Stmt\TraitUse;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\PhpParser\Node\NodeFactory;
|
||||
use Rector\PhpParser\Node\Resolver\NameResolver;
|
||||
use Rector\PhpParser\Node\VariableInfo;
|
||||
@ -32,14 +33,21 @@ final class ClassMaintainer
|
||||
*/
|
||||
private $childAndParentClassMaintainer;
|
||||
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
public function __construct(
|
||||
NameResolver $nameResolver,
|
||||
NodeFactory $nodeFactory,
|
||||
ChildAndParentClassMaintainer $childAndParentClassMaintainer
|
||||
ChildAndParentClassMaintainer $childAndParentClassMaintainer,
|
||||
BetterNodeFinder $betterNodeFinder
|
||||
) {
|
||||
$this->nodeFactory = $nodeFactory;
|
||||
$this->nameResolver = $nameResolver;
|
||||
$this->childAndParentClassMaintainer = $childAndParentClassMaintainer;
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
}
|
||||
|
||||
public function addConstructorDependency(Class_ $classNode, VariableInfo $variableInfo): void
|
||||
@ -208,6 +216,13 @@ final class ClassMaintainer
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasClassMethod(Class_ $classNode, string $methodName): bool
|
||||
{
|
||||
$methodNames = $this->getClassMethodNames($classNode);
|
||||
|
||||
return in_array($methodName, $methodNames, true);
|
||||
}
|
||||
|
||||
private function tryInsertBeforeFirstMethod(Class_ $classNode, Stmt $node): bool
|
||||
{
|
||||
foreach ($classNode->stmts as $key => $classElementNode) {
|
||||
@ -283,6 +298,21 @@ final class ClassMaintainer
|
||||
$classMethodNode->stmts[] = new Expression($propertyAssignNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function getClassMethodNames(Class_ $classNode): array
|
||||
{
|
||||
$classMethodNames = [];
|
||||
|
||||
$classMethodNodes = $this->betterNodeFinder->findInstanceOf($classNode->stmts, ClassMethod::class);
|
||||
foreach ($classMethodNodes as $classMethodNode) {
|
||||
$classMethodNames[] = $this->nameResolver->resolve($classMethodNode);
|
||||
}
|
||||
|
||||
return $classMethodNames;
|
||||
}
|
||||
|
||||
private function hasMethodParameter(ClassMethod $classMethodNode, VariableInfo $variableInfo): bool
|
||||
{
|
||||
foreach ($classMethodNode->params as $constructorParameter) {
|
||||
|
@ -7,7 +7,6 @@ use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Configuration\Rector\Architecture\DependencyInjection\VariablesToPropertyFetchCollection;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
@ -107,11 +106,12 @@ CODE_SAMPLE
|
||||
return null;
|
||||
}
|
||||
|
||||
private function isInControllerActionMethod(Node $node): bool
|
||||
private function isInControllerActionMethod(Variable $node): bool
|
||||
{
|
||||
$className = $node->getAttribute(Attribute::CLASS_NAME);
|
||||
|
||||
if ($className === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! Strings::endsWith($className, 'Controller')) {
|
||||
|
@ -122,7 +122,6 @@ CODE_SAMPLE
|
||||
$smartFileInfo->getRealPath()
|
||||
);
|
||||
|
||||
/** @var Namespace_[] $namespaceNodes */
|
||||
$namespaceNodes = $this->betterNodeFinder->findInstanceOf($newStmts, Namespace_::class);
|
||||
|
||||
if ($this->shouldSkip($smartFileInfo, $newStmts, $namespaceNodes)) {
|
||||
|
@ -1,5 +1,8 @@
|
||||
services:
|
||||
- Rector\PHPStanExtensions\Utils\ValueResolver
|
||||
|
||||
- { 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] }
|
||||
|
||||
@ -7,3 +10,6 @@ services:
|
||||
- { class: Rector\PHPStanExtensions\Rector\Type\NameResolverReturnTypeExtension, tags: [phpstan.broker.dynamicMethodReturnTypeExtension] }
|
||||
# $nameResolverTrait->getName() => in some cases always string
|
||||
- { class: Rector\PHPStanExtensions\Rector\Type\NameResolverTraitReturnTypeExtension, tags: [phpstan.broker.dynamicMethodReturnTypeExtension] }
|
||||
|
||||
# $betterNodeFinder->findByInstance(..., $1) => $1[]
|
||||
- { class: Rector\PHPStanExtensions\Rector\Type\BetterNodeFinderReturnTypeExtension, tags: [phpstan.broker.dynamicMethodReturnTypeExtension] }
|
||||
|
@ -0,0 +1,58 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStanExtensions\Rector\Type;
|
||||
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Reflection\ParametersAcceptorSelector;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\DynamicMethodReturnTypeExtension;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\PHPStanExtensions\Utils\ValueResolver;
|
||||
|
||||
final class BetterNodeFinderReturnTypeExtension implements DynamicMethodReturnTypeExtension
|
||||
{
|
||||
/**
|
||||
* @var ValueResolver
|
||||
*/
|
||||
private $valueResolver;
|
||||
|
||||
public function __construct(ValueResolver $valueResolver)
|
||||
{
|
||||
$this->valueResolver = $valueResolver;
|
||||
}
|
||||
|
||||
public function getClass(): string
|
||||
{
|
||||
return BetterNodeFinder::class;
|
||||
}
|
||||
|
||||
public function isMethodSupported(MethodReflection $methodReflection): bool
|
||||
{
|
||||
return $methodReflection->getName() === 'findInstanceOf';
|
||||
}
|
||||
|
||||
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
|
||||
{
|
||||
$returnType = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getReturnType();
|
||||
|
||||
$secondArgumentNode = $methodCall->args[1]->value;
|
||||
if (! $secondArgumentNode instanceof ClassConstFetch) {
|
||||
return $returnType;
|
||||
}
|
||||
|
||||
if (! $secondArgumentNode->class instanceof Name) {
|
||||
return $returnType;
|
||||
}
|
||||
|
||||
$class = $secondArgumentNode->class->toString();
|
||||
|
||||
return new ArrayType(new MixedType(), new ObjectType($class));
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Php\PhpTypeSupport;
|
||||
use Rector\Php\TypeAnalyzer;
|
||||
use Rector\PHPStanExtensions\Utils\ValueResolver;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
|
||||
final class GetAttributeReturnTypeExtension implements DynamicMethodReturnTypeExtension
|
||||
@ -53,6 +54,15 @@ final class GetAttributeReturnTypeExtension implements DynamicMethodReturnTypeEx
|
||||
'Rector\NodeTypeResolver\Node\Attribute::CLASS_NAME' => 'string',
|
||||
'Rector\NodeTypeResolver\Node\Attribute::METHOD_NAME' => 'string',
|
||||
];
|
||||
/**
|
||||
* @var ValueResolver
|
||||
*/
|
||||
private $valueResolver;
|
||||
|
||||
public function __construct(ValueResolver $valueResolver)
|
||||
{
|
||||
$this->valueResolver = $valueResolver;
|
||||
}
|
||||
|
||||
public function getClass(): string
|
||||
{
|
||||
@ -92,22 +102,10 @@ final class GetAttributeReturnTypeExtension implements DynamicMethodReturnTypeEx
|
||||
|
||||
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 $this->valueResolver->resolveClassConstFetch($node);
|
||||
}
|
||||
|
||||
return $value;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
29
utils/phpstan/src/Utils/ValueResolver.php
Normal file
29
utils/phpstan/src/Utils/ValueResolver.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStanExtensions\Utils;
|
||||
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
|
||||
final class ValueResolver
|
||||
{
|
||||
public function resolveClassConstFetch(ClassConstFetch $classConstFetch): ?string
|
||||
{
|
||||
$value = null;
|
||||
|
||||
if ($classConstFetch->class instanceof Name) {
|
||||
$value = $classConstFetch->class->toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($classConstFetch->name instanceof Identifier) {
|
||||
$value .= '::' . $classConstFetch->name->toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user