mirror of https://github.com/rectorphp/rector.git
Cleaning up NodeRepository dependency (#280)
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
parent
5daf16907a
commit
29e9bf5610
|
@ -7,6 +7,8 @@ namespace Rector\StaticTypeMapper;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\UnionType as PhpParserUnionType;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
|
@ -19,6 +21,7 @@ use PHPStan\Type\Generic\TemplateTypeMap;
|
|||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Core\Exception\NotImplementedYetException;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||
use Rector\StaticTypeMapper\Mapper\PhpParserNodeMapper;
|
||||
use Rector\StaticTypeMapper\Naming\NameScopeFactory;
|
||||
|
@ -36,7 +39,6 @@ final class StaticTypeMapper
|
|||
private PhpDocTypeMapper $phpDocTypeMapper,
|
||||
private PhpParserNodeMapper $phpParserNodeMapper
|
||||
) {
|
||||
// $this->nameScopeFactory->setStaticTypeMapper($this);
|
||||
}
|
||||
|
||||
public function mapPHPStanTypeToPHPStanPhpDocTypeNode(Type $phpStanType): TypeNode
|
||||
|
@ -78,7 +80,16 @@ final class StaticTypeMapper
|
|||
|
||||
public function mapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, Node $node): Type
|
||||
{
|
||||
if ($node instanceof Param) {
|
||||
$classMethod = $node->getAttribute(AttributeKey::PARENT_NODE);
|
||||
if ($classMethod instanceof ClassMethod) {
|
||||
// param does not hany any clue about template map, but class method has
|
||||
$node = $classMethod;
|
||||
}
|
||||
}
|
||||
|
||||
$nameScope = $this->nameScopeFactory->createNameScopeFromNode($node);
|
||||
|
||||
return $this->phpDocTypeMapper->mapToPHPStanType($typeNode, $node, $nameScope);
|
||||
}
|
||||
|
||||
|
|
|
@ -497,3 +497,6 @@ parameters:
|
|||
-
|
||||
message: '#Assign in loop is not allowed#'
|
||||
path: rules/RemovingStatic/Rector/ClassMethod/LocallyCalledStaticMethodToNonStaticRector.php
|
||||
|
||||
# generics single place
|
||||
- '#Method Rector\\Php80\\NodeResolver\\ArgumentSorter\:\:sortArgsByExpectedParamOrder\(\) should return array<T of PhpParser\\Node\\Arg\|PhpParser\\Node\\Param\> but returns array<PhpParser\\Node\\Arg\|PhpParser\\Node\\Param\>#'
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Param;
|
||||
|
||||
final class SkipTemplate
|
||||
{
|
||||
/**
|
||||
* @template TItem as Arg|Param
|
||||
* @param array<TItem> $args
|
||||
* @param array<int, string> $expectedOrderedParams
|
||||
* @return array<TItem>
|
||||
*/
|
||||
public function run(array $args): array
|
||||
{
|
||||
return $args;
|
||||
}
|
||||
}
|
|
@ -2,54 +2,33 @@
|
|||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector\Source\NestedGetData;
|
||||
|
||||
final class AddFromChild
|
||||
{
|
||||
public function getData(Nested $nested)
|
||||
public function getData(NestedGetData $nested)
|
||||
{
|
||||
return $nested->getData();
|
||||
}
|
||||
}
|
||||
|
||||
final class Nested
|
||||
{
|
||||
public function getData()
|
||||
{
|
||||
return [
|
||||
'key',
|
||||
'value'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector\Source\NestedGetData;
|
||||
|
||||
final class AddFromChild
|
||||
{
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getData(Nested $nested)
|
||||
public function getData(NestedGetData $nested)
|
||||
{
|
||||
return $nested->getData();
|
||||
}
|
||||
}
|
||||
|
||||
final class Nested
|
||||
{
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return [
|
||||
'key',
|
||||
'value'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector\Source;
|
||||
|
||||
final class NestedGetData
|
||||
{
|
||||
public function getData()
|
||||
{
|
||||
return [
|
||||
'key',
|
||||
'value'
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
|
||||
|
||||
use Rector\BetterPhpDocParser\ValueObject\Parser\BetterTokenIterator;
|
||||
|
||||
final class PrivatePropertyReflection
|
||||
{
|
||||
public function getTokens(BetterTokenIterator $betterTokenIterator): array
|
||||
{
|
||||
return $betterTokenIterator->getTokens();
|
||||
}
|
||||
}
|
|
@ -6,13 +6,15 @@ namespace Rector\Php80\NodeResolver;
|
|||
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Param;
|
||||
use PHPStan\Reflection\ParameterReflection;
|
||||
|
||||
final class ArgumentSorter
|
||||
{
|
||||
/**
|
||||
* @param array<int, Param> $expectedOrderedParams
|
||||
* @param Arg[] $args
|
||||
* @return Arg[]
|
||||
* @template T as Arg|Param
|
||||
* @param array<int, ParameterReflection> $expectedOrderedParams
|
||||
* @param T[] $args
|
||||
* @return T[]
|
||||
*/
|
||||
public function sortArgsByExpectedParamOrder(array $args, array $expectedOrderedParams): array
|
||||
{
|
||||
|
|
|
@ -4,25 +4,27 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\Php80\NodeResolver;
|
||||
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Reflection\FunctionReflection;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Reflection\ParameterReflection;
|
||||
|
||||
final class RequireOptionalParamResolver
|
||||
{
|
||||
/**
|
||||
* @param ClassMethod $functionLike
|
||||
* @return Param[]
|
||||
* @return ParameterReflection[]
|
||||
*/
|
||||
public function resolve(FunctionLike $functionLike): array
|
||||
public function resolveFromReflection(MethodReflection | FunctionReflection $functionLikeReflection): array
|
||||
{
|
||||
$parametersAcceptor = $functionLikeReflection->getVariants()[0];
|
||||
|
||||
$optionalParams = [];
|
||||
$requireParams = [];
|
||||
foreach ($functionLike->getParams() as $position => $param) {
|
||||
if ($param->default === null && ! $param->variadic) {
|
||||
$requireParams[$position] = $param;
|
||||
|
||||
foreach ($parametersAcceptor->getParameters() as $position => $parameterReflection) {
|
||||
if ($parameterReflection->getDefaultValue() === null && ! $parameterReflection->isVariadic()) {
|
||||
$requireParams[$position] = $parameterReflection;
|
||||
} else {
|
||||
$optionalParams[$position] = $param;
|
||||
$optionalParams[$position] = $parameterReflection;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,12 @@ use PhpParser\Node;
|
|||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use Rector\Core\PHPStan\Reflection\CallReflectionResolver;
|
||||
use Rector\Core\PHPStan\Reflection\ClassMethodReflectionResolver;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\ValueObject\MethodName;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\Php80\NodeResolver\ArgumentSorter;
|
||||
use Rector\Php80\NodeResolver\RequireOptionalParamResolver;
|
||||
use Rector\Php80\Reflection\MethodReflectionClassMethodResolver;
|
||||
|
@ -28,7 +30,9 @@ final class OptionalParametersAfterRequiredRector extends AbstractRector
|
|||
public function __construct(
|
||||
private RequireOptionalParamResolver $requireOptionalParamResolver,
|
||||
private ArgumentSorter $argumentSorter,
|
||||
private MethodReflectionClassMethodResolver $methodReflectionClassMethodResolver
|
||||
private MethodReflectionClassMethodResolver $methodReflectionClassMethodResolver,
|
||||
private CallReflectionResolver $callReflectionResolver,
|
||||
private ClassMethodReflectionResolver $classMethodReflectionResolver
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -88,12 +92,25 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
$expectedOrderParams = $this->requireOptionalParamResolver->resolve($classMethod);
|
||||
if ($classMethod->params === $expectedOrderParams) {
|
||||
$classMethodReflection = $this->classMethodReflectionResolver->resolve($classMethod);
|
||||
if (! $classMethodReflection instanceof MethodReflection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classMethod->params = $expectedOrderParams;
|
||||
$parametersAcceptor = $classMethodReflection->getVariants()[0];
|
||||
|
||||
$expectedOrderParameterReflections = $this->requireOptionalParamResolver->resolveFromReflection(
|
||||
$classMethodReflection
|
||||
);
|
||||
if ($parametersAcceptor->getParameters() === $expectedOrderParameterReflections) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$newParams = $this->argumentSorter->sortArgsByExpectedParamOrder(
|
||||
$classMethod->params,
|
||||
$expectedOrderParameterReflections
|
||||
);
|
||||
$classMethod->params = $newParams;
|
||||
|
||||
return $classMethod;
|
||||
}
|
||||
|
@ -117,8 +134,17 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
$expectedOrderedParams = $this->requireOptionalParamResolver->resolve($classMethod);
|
||||
if ($expectedOrderedParams === $classMethod->getParams()) {
|
||||
$classMethodReflection = $this->classMethodReflectionResolver->resolve($classMethod);
|
||||
if (! $classMethodReflection instanceof MethodReflection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parametersAcceptor = $classMethodReflection->getVariants()[0];
|
||||
|
||||
$expectedOrderedParameterReflections = $this->requireOptionalParamResolver->resolveFromReflection(
|
||||
$classMethodReflection
|
||||
);
|
||||
if ($expectedOrderedParameterReflections === $parametersAcceptor->getParameters()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -126,7 +152,10 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
$newArgs = $this->argumentSorter->sortArgsByExpectedParamOrder($new->args, $expectedOrderedParams);
|
||||
$newArgs = $this->argumentSorter->sortArgsByExpectedParamOrder(
|
||||
$new->args,
|
||||
$expectedOrderedParameterReflections
|
||||
);
|
||||
if ($new->args === $newArgs) {
|
||||
return null;
|
||||
}
|
||||
|
@ -138,32 +167,35 @@ CODE_SAMPLE
|
|||
|
||||
private function refactorMethodCall(MethodCall $methodCall): ?MethodCall
|
||||
{
|
||||
$classMethod = $this->nodeRepository->findClassMethodByMethodCall($methodCall);
|
||||
if (! $classMethod instanceof ClassMethod) {
|
||||
$callReflection = $this->callReflectionResolver->resolveCall($methodCall);
|
||||
if ($callReflection === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// because parameters can be already changed
|
||||
$originalClassMethod = $classMethod->getAttribute(AttributeKey::ORIGINAL_NODE);
|
||||
if (! $originalClassMethod instanceof ClassMethod) {
|
||||
$parametersAcceptor = $callReflection->getVariants()[0];
|
||||
|
||||
$expectedOrderedParameterReflections = $this->requireOptionalParamResolver->resolveFromReflection(
|
||||
$callReflection
|
||||
);
|
||||
if ($expectedOrderedParameterReflections === $parametersAcceptor->getParameters()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$expectedOrderedParams = $this->requireOptionalParamResolver->resolve($originalClassMethod);
|
||||
if ($expectedOrderedParams === $classMethod->getParams()) {
|
||||
if (count($methodCall->args) !== count($parametersAcceptor->getParameters())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (count($methodCall->args) !== count($classMethod->getParams())) {
|
||||
return null;
|
||||
}
|
||||
$newArgs = $this->argumentSorter->sortArgsByExpectedParamOrder(
|
||||
$methodCall->args,
|
||||
$expectedOrderedParameterReflections
|
||||
);
|
||||
|
||||
$newArgs = $this->argumentSorter->sortArgsByExpectedParamOrder($methodCall->args, $expectedOrderedParams);
|
||||
if ($methodCall->args === $newArgs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodCall->args = $newArgs;
|
||||
|
||||
return $methodCall;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,15 +17,15 @@ use PhpParser\Node\Stmt\ClassMethod;
|
|||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\Node\UnionType as PhpParserUnionType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\NullType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\PHPStan\Reflection\CallReflectionResolver;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\TypeDeclaration\NodeAnalyzer\TypeNodeUnwrapper;
|
||||
use Rector\TypeDeclaration\Reflection\ReflectionTypeResolver;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
|
@ -35,8 +35,8 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
|||
final class ReturnTypeFromStrictTypedCallRector extends AbstractRector
|
||||
{
|
||||
public function __construct(
|
||||
private ReflectionTypeResolver $reflectionTypeResolver,
|
||||
private TypeNodeUnwrapper $typeNodeUnwrapper
|
||||
private TypeNodeUnwrapper $typeNodeUnwrapper,
|
||||
private CallReflectionResolver $callReflectionResolver
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -162,12 +162,8 @@ CODE_SAMPLE
|
|||
|
||||
$returnedExpr = $return->expr;
|
||||
|
||||
if ($returnedExpr instanceof MethodCall) {
|
||||
if ($returnedExpr instanceof MethodCall || $returnedExpr instanceof StaticCall || $returnedExpr instanceof FuncCall) {
|
||||
$returnNode = $this->resolveMethodCallReturnNode($returnedExpr);
|
||||
} elseif ($returnedExpr instanceof StaticCall) {
|
||||
$returnNode = $this->resolveStaticCallReturnNode($returnedExpr);
|
||||
} elseif ($returnedExpr instanceof FuncCall) {
|
||||
$returnNode = $this->resolveFuncCallReturnNode($returnedExpr);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
@ -182,40 +178,16 @@ CODE_SAMPLE
|
|||
return $this->typeNodeUnwrapper->uniquateNodes($returnedStrictTypeNodes);
|
||||
}
|
||||
|
||||
private function resolveMethodCallReturnNode(MethodCall $methodCall): ?Node
|
||||
private function resolveMethodCallReturnNode(MethodCall | StaticCall | FuncCall $call): ?Node
|
||||
{
|
||||
$classMethod = $this->nodeRepository->findClassMethodByMethodCall($methodCall);
|
||||
if ($classMethod instanceof ClassMethod) {
|
||||
return $classMethod->returnType;
|
||||
}
|
||||
|
||||
$returnType = $this->reflectionTypeResolver->resolveMethodCallReturnType($methodCall);
|
||||
if (! $returnType instanceof Type) {
|
||||
$methodReflection = $this->callReflectionResolver->resolveCall($call);
|
||||
if ($methodReflection === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType);
|
||||
}
|
||||
|
||||
private function resolveStaticCallReturnNode(StaticCall $staticCall): ?Node
|
||||
{
|
||||
$classMethod = $this->nodeRepository->findClassMethodByStaticCall($staticCall);
|
||||
if ($classMethod instanceof ClassMethod) {
|
||||
return $classMethod->returnType;
|
||||
}
|
||||
|
||||
$returnType = $this->reflectionTypeResolver->resolveStaticCallReturnType($staticCall);
|
||||
if (! $returnType instanceof Type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType);
|
||||
}
|
||||
|
||||
private function resolveFuncCallReturnNode(FuncCall $funcCall): Name | NullableType | PhpParserUnionType | null
|
||||
{
|
||||
$returnType = $this->reflectionTypeResolver->resolveFuncCallReturnType($funcCall);
|
||||
if (! $returnType instanceof Type) {
|
||||
$parametersAcceptor = $methodReflection->getVariants()[0];
|
||||
$returnType = $parametersAcceptor->getReturnType();
|
||||
if ($returnType instanceof MixedType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -246,6 +246,7 @@ CODE_SAMPLE
|
|||
$hasExternalClassOrInterfaceOrTrait = $this->externalFullyQualifiedAnalyzer->hasExternalFullyQualifieds(
|
||||
$classLike
|
||||
);
|
||||
|
||||
return $functionLike->returnType === null && $hasExternalClassOrInterfaceOrTrait && $this->isName(
|
||||
$inferredReturnNode,
|
||||
'void'
|
||||
|
|
|
@ -4,14 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\TypeDeclaration\Reflection;
|
||||
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\Php\PhpFunctionReflection;
|
||||
use PHPStan\Reflection\Php\PhpMethodReflection;
|
||||
use PHPStan\Reflection\Php\PhpPropertyReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\Type;
|
||||
|
@ -19,7 +12,6 @@ use PHPStan\Type\TypeWithClassName;
|
|||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesCaller;
|
||||
|
||||
final class ReflectionTypeResolver
|
||||
{
|
||||
|
@ -27,40 +19,9 @@ final class ReflectionTypeResolver
|
|||
private NodeTypeResolver $nodeTypeResolver,
|
||||
private ReflectionProvider $reflectionProvider,
|
||||
private NodeNameResolver $nodeNameResolver,
|
||||
private PrivatesCaller $privatesCaller
|
||||
) {
|
||||
}
|
||||
|
||||
public function resolveMethodCallReturnType(MethodCall $methodCall): ?Type
|
||||
{
|
||||
$objectType = $this->nodeTypeResolver->resolve($methodCall->var);
|
||||
if (! $objectType instanceof TypeWithClassName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodName = $this->nodeNameResolver->getName($methodCall->name);
|
||||
if ($methodName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveNativeReturnTypeFromClassAndMethod($objectType->getClassName(), $methodName, $methodCall);
|
||||
}
|
||||
|
||||
public function resolveStaticCallReturnType(StaticCall $staticCall): ?Type
|
||||
{
|
||||
$className = $this->nodeNameResolver->getName($staticCall->class);
|
||||
if ($className === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodName = $this->nodeNameResolver->getName($staticCall->name);
|
||||
if ($methodName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveNativeReturnTypeFromClassAndMethod($className, $methodName, $staticCall);
|
||||
}
|
||||
|
||||
public function resolvePropertyFetchType(PropertyFetch $propertyFetch): ?Type
|
||||
{
|
||||
$objectType = $this->nodeTypeResolver->resolve($propertyFetch->var);
|
||||
|
@ -85,51 +46,4 @@ final class ReflectionTypeResolver
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function resolveFuncCallReturnType(FuncCall $funcCall): ?Type
|
||||
{
|
||||
$funcCallScope = $funcCall->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
$funcCallName = $funcCall->name;
|
||||
if ($funcCallName instanceof Expr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->reflectionProvider->hasFunction($funcCallName, $funcCallScope)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$functionReflection = $this->reflectionProvider->getFunction($funcCallName, $funcCallScope);
|
||||
if (! $functionReflection instanceof PhpFunctionReflection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->privatesCaller->callPrivateMethod($functionReflection, 'getNativeReturnType', []);
|
||||
}
|
||||
|
||||
private function resolveNativeReturnTypeFromClassAndMethod(string $className, string $methodName, Expr $expr): ?Type
|
||||
{
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
if (! $classReflection->hasMethod($methodName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$callerScope = $expr->getAttribute(AttributeKey::SCOPE);
|
||||
|
||||
// probably trait
|
||||
if (! $callerScope instanceof Scope) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodReflection = $classReflection->getMethod($methodName, $callerScope);
|
||||
if (! $methodReflection instanceof PhpMethodReflection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->privatesCaller->callPrivateMethod($methodReflection, 'getNativeReturnType', []);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,15 @@ use PhpParser\Node\Stmt\Interface_;
|
|||
use PhpParser\Node\Stmt\Return_;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Reflection\Php\PhpFunctionReflection;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\VoidType;
|
||||
use Rector\NodeCollector\NodeCollector\NodeRepository;
|
||||
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
|
||||
use Rector\Core\PHPStan\Reflection\CallReflectionResolver;
|
||||
use Rector\Core\Reflection\FunctionLikeReflectionParser;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
|
@ -37,7 +41,9 @@ final class ReturnedNodesReturnTypeInferer implements ReturnTypeInfererInterface
|
|||
private SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
|
||||
private TypeFactory $typeFactory,
|
||||
private SplArrayFixedTypeNarrower $splArrayFixedTypeNarrower,
|
||||
private NodeRepository $nodeRepository
|
||||
private CallReflectionResolver $callReflectionResolver,
|
||||
private FunctionLikeReflectionParser $functionLikeReflectionParser,
|
||||
private BetterStandardPrinter $betterStandardPrinter
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -137,17 +143,20 @@ final class ReturnedNodesReturnTypeInferer implements ReturnTypeInfererInterface
|
|||
return new MixedType();
|
||||
}
|
||||
|
||||
$classMethod = $this->nodeRepository->findClassMethodByMethodCall($return->expr);
|
||||
if (! $classMethod instanceof ClassMethod) {
|
||||
$callReflection = $this->callReflectionResolver->resolveCall($return->expr);
|
||||
if ($callReflection === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
// avoid infinite looping over self call
|
||||
if ($classMethod === $originalFunctionLike) {
|
||||
return new MixedType();
|
||||
if ($callReflection instanceof MethodReflection) {
|
||||
return $this->resolveClassMethod($callReflection, $originalFunctionLike);
|
||||
}
|
||||
|
||||
return $this->inferFunctionLike($classMethod);
|
||||
if ($callReflection instanceof PhpFunctionReflection) {
|
||||
return $this->resolveFunction($callReflection, $originalFunctionLike);
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
private function isArrayTypeMixed(Type $type): bool
|
||||
|
@ -167,12 +176,47 @@ final class ReturnedNodesReturnTypeInferer implements ReturnTypeInfererInterface
|
|||
{
|
||||
if ($resolvedType instanceof MixedType || $this->isArrayTypeMixed($resolvedType)) {
|
||||
$correctedType = $this->inferFromReturnedMethodCall($return, $functionLike);
|
||||
|
||||
// override only if has some extra value
|
||||
if (! $correctedType instanceof MixedType) {
|
||||
if (! $correctedType instanceof MixedType && ! $correctedType instanceof VoidType) {
|
||||
return $correctedType;
|
||||
}
|
||||
}
|
||||
|
||||
return $resolvedType;
|
||||
}
|
||||
|
||||
private function resolveClassMethod(MethodReflection $methodReflection, FunctionLike $originalFunctionLike): Type
|
||||
{
|
||||
$classMethod = $this->functionLikeReflectionParser->parseMethodReflection($methodReflection);
|
||||
if (! $classMethod instanceof ClassMethod) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$classMethodCacheKey = $this->betterStandardPrinter->print($classMethod);
|
||||
$functionLikeCacheKey = $this->betterStandardPrinter->print($originalFunctionLike);
|
||||
|
||||
if ($classMethodCacheKey === $functionLikeCacheKey) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return $this->inferFunctionLike($classMethod);
|
||||
}
|
||||
|
||||
private function resolveFunction(PhpFunctionReflection $phpFunctionReflection, FunctionLike $functionLike): Type
|
||||
{
|
||||
$function = $this->functionLikeReflectionParser->parseFunctionReflection($phpFunctionReflection);
|
||||
if (! $function instanceof Function_) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$classMethodCacheKey = $this->betterStandardPrinter->print($function);
|
||||
$functionLikeCacheKey = $this->betterStandardPrinter->print($functionLike);
|
||||
|
||||
if ($classMethodCacheKey === $functionLikeCacheKey) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
return $this->inferFunctionLike($function);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,13 @@ use PhpParser\Node\Expr\StaticCall;
|
|||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\NodeFinder;
|
||||
use PhpParser\Parser;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\MethodReflection;
|
||||
use PHPStan\Reflection\Php\PhpFunctionReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\ThisType;
|
||||
|
@ -67,6 +69,38 @@ final class FunctionLikeReflectionParser
|
|||
return $class->getMethod($methodReflection->getName());
|
||||
}
|
||||
|
||||
public function parseFunctionReflection(PhpFunctionReflection $phpFunctionReflection): ?Function_
|
||||
{
|
||||
$fileName = $phpFunctionReflection->getFileName();
|
||||
if ($fileName === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fileContent = $this->smartFileSystem->readFile($fileName);
|
||||
if (! is_string($fileContent)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$nodes = (array) $this->parser->parse($fileContent);
|
||||
|
||||
$smartFileInfo = new SmartFileInfo($fileName);
|
||||
$file = new File($smartFileInfo, $smartFileInfo->getContents());
|
||||
|
||||
$nodes = $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($file, $nodes);
|
||||
|
||||
/** @var Function_[] $functions */
|
||||
$functions = $this->nodeFinder->findInstanceOf($nodes, Function_::class);
|
||||
foreach ($functions as $function) {
|
||||
if (! $this->nodeNameResolver->isName($function, $phpFunctionReflection->getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $function;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MethodCall|StaticCall|Node $node
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue