mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-07 11:50:51 +00:00
[TypeDeclaration] Add AddParamTypeFromCallersRector (#5782)
This commit is contained in:
parent
956cac70a0
commit
031deda881
|
@ -33,7 +33,7 @@
|
|||
"doctrine/annotations": "^1.11",
|
||||
"doctrine/inflector": "^2.0",
|
||||
"jean85/pretty-package-versions": "^1.5.1|^2.0.1",
|
||||
"nette/robot-loader": "^3.2",
|
||||
"nette/robot-loader": "^3.2 <=3.3.1",
|
||||
"nette/utils": "^3.2",
|
||||
"nikic/php-parser": "^4.10.4",
|
||||
"phpstan/phpdoc-parser": "^0.4.9",
|
||||
|
|
|
@ -21,6 +21,7 @@ use Symfony\Component\Console\Style\SymfonyStyle;
|
|||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
|
||||
use Symplify\PackageBuilder\Console\Command\CommandNaming;
|
||||
use Symplify\PackageBuilder\Console\Style\SymfonyStyleFactory;
|
||||
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
||||
|
@ -56,7 +57,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
$services->alias(SymfonyApplication::class, ConsoleApplication::class);
|
||||
|
||||
$services->set(NoRectorsLoadedReporter::class);
|
||||
$services->set(\Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser::class);
|
||||
$services->set(SimpleCallableNodeTraverser::class);
|
||||
|
||||
$services->set(TextDescriptor::class);
|
||||
|
||||
|
@ -77,6 +78,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
$services->set(PrivatesCaller::class);
|
||||
$services->set(FinderSanitizer::class);
|
||||
$services->set(FileSystemFilter::class);
|
||||
|
||||
$services->set(ParameterProvider::class)
|
||||
->arg('$container', service('service_container'));
|
||||
|
||||
|
|
|
@ -2,20 +2,15 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\AddArrayParamDocTypeRector;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector;
|
||||
use Rector\TypeDeclaration\Rector\Closure\AddClosureReturnTypeRector;
|
||||
use Rector\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector;
|
||||
use Rector\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector;
|
||||
use Rector\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
return static function (
|
||||
\Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator $containerConfigurator
|
||||
): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(ParamTypeDeclarationRector::class);
|
||||
$services->set(ReturnTypeDeclarationRector::class);
|
||||
$services->set(PropertyTypeDeclarationRector::class);
|
||||
$services->set(AddClosureReturnTypeRector::class);
|
||||
$services->set(AddArrayParamDocTypeRector::class);
|
||||
$services->set(AddArrayReturnDocTypeRector::class);
|
||||
$services->set(\Rector\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector::class);
|
||||
$services->set(\Rector\TypeDeclaration\Rector\FunctionLike\ReturnTypeDeclarationRector::class);
|
||||
$services->set(\Rector\TypeDeclaration\Rector\Property\PropertyTypeDeclarationRector::class);
|
||||
$services->set(\Rector\TypeDeclaration\Rector\Closure\AddClosureReturnTypeRector::class);
|
||||
$services->set(\Rector\TypeDeclaration\Rector\ClassMethod\AddArrayParamDocTypeRector::class);
|
||||
$services->set(\Rector\TypeDeclaration\Rector\ClassMethod\AddArrayReturnDocTypeRector::class);
|
||||
// $services->set(\Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromCallersRector::class);
|
||||
};
|
||||
|
|
|
@ -49,7 +49,6 @@ use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
|
|||
use ReflectionMethod;
|
||||
|
||||
/**
|
||||
* @rector-doc
|
||||
* This service contains all the parsed nodes. E.g. all the functions, method call, classes, static calls etc.
|
||||
* It's useful in case of context analysis, e.g. find all the usage of class method to detect, if the method is used.
|
||||
*/
|
||||
|
@ -377,7 +376,6 @@ final class NodeRepository
|
|||
|
||||
/** @var string $method */
|
||||
$method = $classMethod->getAttribute(AttributeKey::METHOD_NAME);
|
||||
|
||||
return $this->findCallsByClassAndMethod($class, $method);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Rector\PHPStanStaticTypeMapper\TypeAnalyzer;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\BinaryOp;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
|
@ -27,6 +28,7 @@ final class UnionTypeCommonTypeNarrower
|
|||
* @var array<class-string<Node|\PHPStan\PhpDocParser\Ast\Node>, array<class-string<Node|\PHPStan\PhpDocParser\Ast\Node>>>
|
||||
*/
|
||||
private const PRIORITY_TYPES = [
|
||||
FunctionLike::class => [FunctionLike::class],
|
||||
BinaryOp::class => [BinaryOp::class, Expr::class],
|
||||
Expr::class => [Node::class, Expr::class],
|
||||
Stmt::class => [Node::class, Stmt::class],
|
||||
|
|
|
@ -75,6 +75,9 @@ final class ArrayTypeMapper implements TypeMapperInterface
|
|||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return ArrayType::class;
|
||||
|
|
|
@ -27,6 +27,9 @@ final class BooleanTypeMapper implements TypeMapperInterface
|
|||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return BooleanType::class;
|
||||
|
|
|
@ -31,6 +31,9 @@ final class CallableTypeMapper implements TypeMapperInterface
|
|||
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return CallableType::class;
|
||||
|
|
|
@ -23,6 +23,9 @@ final class ClassStringTypeMapper implements TypeMapperInterface, PHPStanStaticT
|
|||
*/
|
||||
private $phpStanStaticTypeMapper;
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return ClassStringType::class;
|
||||
|
|
|
@ -32,6 +32,9 @@ final class ClosureTypeMapper implements TypeMapperInterface, PHPStanStaticTypeM
|
|||
$this->callableTypeMapper = $callableTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return ClosureType::class;
|
||||
|
|
|
@ -27,6 +27,9 @@ final class FloatTypeMapper implements TypeMapperInterface
|
|||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return FloatType::class;
|
||||
|
|
|
@ -15,6 +15,9 @@ use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
|||
|
||||
final class HasOffsetTypeMapper implements TypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return HasOffsetType::class;
|
||||
|
|
|
@ -27,6 +27,9 @@ final class IntegerTypeMapper implements TypeMapperInterface
|
|||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return IntegerType::class;
|
||||
|
|
|
@ -27,6 +27,9 @@ final class IntersectionTypeMapper implements TypeMapperInterface
|
|||
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return IntersectionType::class;
|
||||
|
|
|
@ -44,6 +44,9 @@ final class IterableTypeMapper implements TypeMapperInterface
|
|||
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return IterableType::class;
|
||||
|
|
|
@ -14,6 +14,9 @@ use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
|||
|
||||
final class MixedTypeMapper implements TypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return MixedType::class;
|
||||
|
|
|
@ -13,6 +13,9 @@ use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
|||
|
||||
final class NeverTypeMapper implements TypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return NeverType::class;
|
||||
|
|
|
@ -15,6 +15,9 @@ use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
|||
|
||||
final class NonEmptyArrayTypeMapper implements TypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return NonEmptyArrayType::class;
|
||||
|
|
|
@ -16,6 +16,9 @@ use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
|||
|
||||
final class NullTypeMapper implements TypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return NullType::class;
|
||||
|
|
|
@ -42,6 +42,9 @@ final class ObjectTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMa
|
|||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return ObjectType::class;
|
||||
|
|
|
@ -36,6 +36,9 @@ final class ObjectWithoutClassTypeMapper implements TypeMapperInterface, PHPStan
|
|||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return ObjectWithoutClassType::class;
|
||||
|
|
|
@ -15,6 +15,9 @@ use Rector\StaticTypeMapper\ValueObject\Type\ParentStaticType;
|
|||
|
||||
final class ParentStaticTypeMapper implements TypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return ParentStaticType::class;
|
||||
|
|
|
@ -14,6 +14,9 @@ use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
|||
|
||||
final class ResourceTypeMapper implements TypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return ResourceType::class;
|
||||
|
|
|
@ -15,6 +15,9 @@ use Rector\StaticTypeMapper\ValueObject\Type\SelfObjectType;
|
|||
|
||||
final class SelfObjectTypeMapper implements TypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return SelfObjectType::class;
|
||||
|
|
|
@ -31,6 +31,9 @@ final class StaticTypeMapper implements TypeMapperInterface
|
|||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return StaticType::class;
|
||||
|
|
|
@ -19,6 +19,9 @@ final class StrictMixedTypeMapper implements TypeMapperInterface
|
|||
*/
|
||||
private const MIXED = 'mixed';
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return StrictMixedType::class;
|
||||
|
|
|
@ -27,6 +27,9 @@ final class StringTypeMapper implements TypeMapperInterface
|
|||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return StringType::class;
|
||||
|
|
|
@ -15,6 +15,9 @@ use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
|||
|
||||
final class ThisTypeMapper implements TypeMapperInterface
|
||||
{
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return ThisType::class;
|
||||
|
|
|
@ -24,6 +24,9 @@ final class TypeWithClassNameTypeMapper implements TypeMapperInterface
|
|||
$this->stringTypeMapper = $stringTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return TypeWithClassName::class;
|
||||
|
|
|
@ -85,6 +85,9 @@ final class UnionTypeMapper implements TypeMapperInterface
|
|||
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return UnionType::class;
|
||||
|
@ -107,7 +110,6 @@ final class UnionTypeMapper implements TypeMapperInterface
|
|||
}
|
||||
|
||||
$unionTypesNodes = array_unique($unionTypesNodes);
|
||||
|
||||
return new AttributeAwareUnionTypeNode($unionTypesNodes);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,9 @@ final class VoidTypeMapper implements TypeMapperInterface
|
|||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string<Type>
|
||||
*/
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return VoidType::class;
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Rector\PostRector\Application;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\NodeTraverser;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\Logging\CurrentRectorProvider;
|
||||
use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider;
|
||||
use Rector\PostRector\Contract\Rector\PostRectorInterface;
|
||||
use Symplify\Skipper\Skipper\Skipper;
|
||||
|
@ -29,14 +30,24 @@ final class PostFileProcessor
|
|||
*/
|
||||
private $currentFileInfoProvider;
|
||||
|
||||
/**
|
||||
* @var CurrentRectorProvider
|
||||
*/
|
||||
private $currentRectorProvider;
|
||||
|
||||
/**
|
||||
* @param PostRectorInterface[] $postRectors
|
||||
*/
|
||||
public function __construct(Skipper $skipper, CurrentFileInfoProvider $currentFileInfoProvider, array $postRectors)
|
||||
{
|
||||
public function __construct(
|
||||
Skipper $skipper,
|
||||
CurrentFileInfoProvider $currentFileInfoProvider,
|
||||
CurrentRectorProvider $currentRectorProvider,
|
||||
array $postRectors
|
||||
) {
|
||||
$this->postRectors = $this->sortByPriority($postRectors);
|
||||
$this->skipper = $skipper;
|
||||
$this->currentFileInfoProvider = $currentFileInfoProvider;
|
||||
$this->currentRectorProvider = $currentRectorProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,6 +61,8 @@ final class PostFileProcessor
|
|||
continue;
|
||||
}
|
||||
|
||||
$this->currentRectorProvider->changeCurrentRector($postRector);
|
||||
|
||||
$nodeTraverser = new NodeTraverser();
|
||||
$nodeTraverser->addVisitor($postRector);
|
||||
$nodes = $nodeTraverser->traverse($nodes);
|
||||
|
|
|
@ -47,7 +47,6 @@ parameters:
|
|||
- stubs
|
||||
|
||||
reportUnmatchedIgnoredErrors: false
|
||||
|
||||
checkGenericClassInNonGenericObjectType: false
|
||||
|
||||
excludes_analyse:
|
||||
|
|
|
@ -4,11 +4,10 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\Arguments\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Arguments\ValueObject\ArgumentAdder;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
|
||||
|
@ -40,26 +39,21 @@ final class ArgumentAddingScope
|
|||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|MethodCall|StaticCall $node
|
||||
* @param MethodCall|StaticCall $expr
|
||||
*/
|
||||
public function isInCorrectScope(Node $node, ArgumentAdder $argumentAdder): bool
|
||||
public function isInCorrectScope(Expr $expr, ArgumentAdder $argumentAdder): bool
|
||||
{
|
||||
if ($argumentAdder->getScope() === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$scope = $argumentAdder->getScope();
|
||||
|
||||
if ($node instanceof ClassMethod) {
|
||||
return $scope === self::SCOPE_CLASS_METHOD;
|
||||
}
|
||||
|
||||
if ($node instanceof StaticCall) {
|
||||
if (! $node->class instanceof Name) {
|
||||
if ($expr instanceof StaticCall) {
|
||||
if (! $expr->class instanceof Name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->nodeNameResolver->isName($node->class, 'parent')) {
|
||||
if ($this->nodeNameResolver->isName($expr->class, 'parent')) {
|
||||
return $scope === self::SCOPE_PARENT_CALL;
|
||||
}
|
||||
|
||||
|
|
|
@ -212,6 +212,7 @@ CODE_SAMPLE
|
|||
|
||||
return $this->isName($node->params[$position], $argumentName);
|
||||
}
|
||||
|
||||
// already added?
|
||||
if (! isset($node->args[$position])) {
|
||||
// is correct scope?
|
||||
|
|
|
@ -8,6 +8,7 @@ use Nette\Utils\Strings;
|
|||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
|
@ -120,23 +121,23 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
/**
|
||||
* @param MethodCall|StaticCall $node
|
||||
* @param MethodCall|StaticCall $expr
|
||||
*/
|
||||
private function processArgs(Node $node, ArgumentDefaultValueReplacer $argumentDefaultValueReplacer): void
|
||||
private function processArgs(Expr $expr, ArgumentDefaultValueReplacer $argumentDefaultValueReplacer): void
|
||||
{
|
||||
$position = $argumentDefaultValueReplacer->getPosition();
|
||||
|
||||
$argValue = $this->valueResolver->getValue($node->args[$position]->value);
|
||||
$argValue = $this->valueResolver->getValue($expr->args[$position]->value);
|
||||
|
||||
if (is_scalar(
|
||||
$argumentDefaultValueReplacer->getValueBefore()
|
||||
) && $argValue === $argumentDefaultValueReplacer->getValueBefore()) {
|
||||
$node->args[$position] = $this->normalizeValueToArgument($argumentDefaultValueReplacer->getValueAfter());
|
||||
$expr->args[$position] = $this->normalizeValueToArgument($argumentDefaultValueReplacer->getValueAfter());
|
||||
} elseif (is_array($argumentDefaultValueReplacer->getValueBefore())) {
|
||||
$newArgs = $this->processArrayReplacement($node->args, $argumentDefaultValueReplacer);
|
||||
$newArgs = $this->processArrayReplacement($expr->args, $argumentDefaultValueReplacer);
|
||||
|
||||
if ($newArgs) {
|
||||
$node->args = $newArgs;
|
||||
$expr->args = $newArgs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ use Rector\NodeTypeResolver\NodeTypeResolver;
|
|||
final class ValueObjectClassAnalyzer
|
||||
{
|
||||
/**
|
||||
* @var bool[]
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $valueObjectStatusByClassName = [];
|
||||
|
||||
|
@ -98,7 +98,7 @@ final class ValueObjectClassAnalyzer
|
|||
return true;
|
||||
}
|
||||
|
||||
private function analyseWithoutConstructor(Class_ $class, ?string $className): bool
|
||||
private function analyseWithoutConstructor(Class_ $class, string $className): bool
|
||||
{
|
||||
// A. has all properties with serialize?
|
||||
if ($this->hasAllPropertiesWithSerialize($class)) {
|
||||
|
|
|
@ -309,7 +309,7 @@ CODE_SAMPLE
|
|||
/**
|
||||
* @param array<int, Node|null> $multiNodes
|
||||
*/
|
||||
private function getSameVarName(array $multiNodes, Node $node): ?Variable
|
||||
private function getSameVarName(array $multiNodes, Variable $variable): ?Variable
|
||||
{
|
||||
foreach ($multiNodes as $multiNode) {
|
||||
if ($multiNode === null) {
|
||||
|
@ -317,12 +317,12 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
/** @var Variable|null $found */
|
||||
$found = $this->betterNodeFinder->findFirst($multiNode, function (Node $n) use ($node): bool {
|
||||
$found = $this->betterNodeFinder->findFirst($multiNode, function (Node $n) use ($variable): bool {
|
||||
$n = $this->mayBeArrayDimFetch($n);
|
||||
if (! $n instanceof Variable) {
|
||||
return false;
|
||||
}
|
||||
return $this->isName($n, (string) $this->getName($node));
|
||||
return $this->isName($n, (string) $this->getName($variable));
|
||||
});
|
||||
|
||||
if ($found !== null) {
|
||||
|
|
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\CodeQuality\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
|
||||
final class ClassLikeAnalyzer
|
||||
|
@ -22,11 +22,11 @@ final class ClassLikeAnalyzer
|
|||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function resolvePropertyNames(ClassLike $classLike): array
|
||||
public function resolvePropertyNames(Class_ $class): array
|
||||
{
|
||||
$propertyNames = [];
|
||||
|
||||
foreach ($classLike->getProperties() as $property) {
|
||||
foreach ($class->getProperties() as $property) {
|
||||
$propertyNames[] = $this->nodeNameResolver->getName($property);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ use PhpParser\Node\Expr\PreInc;
|
|||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Stmt\For_;
|
||||
use PhpParser\Node\Stmt\Unset_;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\NodeManipulator\AssignManipulator;
|
||||
use Rector\Core\PhpParser\Comparing\NodeComparator;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
|
@ -137,7 +136,7 @@ final class ForAnalyzer
|
|||
);
|
||||
}
|
||||
|
||||
public function isAssignmentWithArrayDimFetchAsVariableInsideForStatements(For_ $for, ?string $keyValueName): bool
|
||||
public function isAssignmentWithArrayDimFetchAsVariableInsideForStatements(For_ $for, string $keyValueName): bool
|
||||
{
|
||||
return (bool) $this->betterNodeFinder->findFirst(
|
||||
$for->stmts,
|
||||
|
@ -150,10 +149,6 @@ final class ForAnalyzer
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($keyValueName === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$arrayDimFetch = $node->var;
|
||||
if ($arrayDimFetch->dim === null) {
|
||||
return false;
|
||||
|
|
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\CodeQuality\NodeFactory;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Expr\ClosureUse;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
|
@ -50,9 +50,9 @@ final class AnonymousFunctionFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Variable|PropertyFetch $node
|
||||
* @param Variable|PropertyFetch $expr
|
||||
*/
|
||||
public function create(PhpMethodReflection $phpMethodReflection, Node $node): Closure
|
||||
public function create(PhpMethodReflection $phpMethodReflection, Expr $expr): Closure
|
||||
{
|
||||
/** @var FunctionVariantWithPhpDocs $functionVariantWithPhpDoc */
|
||||
$functionVariantWithPhpDoc = $phpMethodReflection->getVariants()[0];
|
||||
|
@ -62,7 +62,7 @@ final class AnonymousFunctionFactory
|
|||
|
||||
$anonymousFunction->params = $newParams;
|
||||
|
||||
$innerMethodCall = new MethodCall($node, $phpMethodReflection->getName());
|
||||
$innerMethodCall = new MethodCall($expr, $phpMethodReflection->getName());
|
||||
$innerMethodCall->args = $this->nodeFactory->createArgsFromParams($newParams);
|
||||
|
||||
if (! $functionVariantWithPhpDoc->getReturnType() instanceof MixedType) {
|
||||
|
@ -80,8 +80,8 @@ final class AnonymousFunctionFactory
|
|||
$anonymousFunction->stmts[] = new Expression($innerMethodCall);
|
||||
}
|
||||
|
||||
if ($node instanceof Variable && ! $this->nodeNameResolver->isName($node, 'this')) {
|
||||
$anonymousFunction->uses[] = new ClosureUse($node);
|
||||
if ($expr instanceof Variable && ! $this->nodeNameResolver->isName($expr, 'this')) {
|
||||
$anonymousFunction->uses[] = new ClosureUse($expr);
|
||||
}
|
||||
|
||||
return $anonymousFunction;
|
||||
|
|
|
@ -101,13 +101,13 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Array_|List_ $node
|
||||
* @param Array_|List_ $expr
|
||||
* @return Assign[]
|
||||
*/
|
||||
private function createStandaloneAssigns(Node $node, Array_ $rightArray): array
|
||||
private function createStandaloneAssigns(Expr $expr, Array_ $rightArray): array
|
||||
{
|
||||
$standaloneAssigns = [];
|
||||
foreach ($node->items as $key => $leftArrayItem) {
|
||||
foreach ($expr->items as $key => $leftArrayItem) {
|
||||
if ($leftArrayItem === null) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -180,7 +180,7 @@ final class ShortNameResolver
|
|||
|
||||
/**
|
||||
* @param Node[] $stmts
|
||||
* @return string[]
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function resolveFromDocBlocks(array $stmts): array
|
||||
{
|
||||
|
@ -192,6 +192,7 @@ final class ShortNameResolver
|
|||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
|
||||
|
||||
foreach ($phpDocInfo->getPhpDocNode()->children as $phpDocChildNode) {
|
||||
/** @var PhpDocChildNode $phpDocChildNode */
|
||||
$shortTagName = $this->resolveShortTagNameFromPhpDocChildNode($phpDocChildNode);
|
||||
if ($shortTagName === null) {
|
||||
continue;
|
||||
|
|
|
@ -95,7 +95,23 @@ CODE_SAMPLE
|
|||
]);
|
||||
}
|
||||
|
||||
public function isNullableParam(Param $param, FunctionLike $functionLike): bool
|
||||
/**
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if ($node->params === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($node->params as $param) {
|
||||
$this->refactorParam($param, $node);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function isNullableParam(Param $param, FunctionLike $functionLike): bool
|
||||
{
|
||||
if ($param->variadic) {
|
||||
return false;
|
||||
|
@ -119,22 +135,6 @@ CODE_SAMPLE
|
|||
return $this->getDifferentParamTypeFromAncestorClass($param, $functionLike) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod|Function_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if ($node->params === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($node->params as $param) {
|
||||
$this->refactorParam($param, $node);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getDifferentParamTypeFromAncestorClass(Param $param, FunctionLike $functionLike): ?string
|
||||
{
|
||||
$scope = $functionLike->getAttribute(AttributeKey::SCOPE);
|
||||
|
|
163
rules/type-declaration/src/NodeAnalyzer/CallTypesResolver.php
Normal file
163
rules/type-declaration/src/NodeAnalyzer/CallTypesResolver.php
Normal file
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\ThisType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\NodeCollector\ValueObject\ArrayCallable;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
|
||||
final class CallTypesResolver
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const STRICTNESS_TYPE_DECLARATION = 'type_declaration';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const STRICTNESS_DOCBLOCK = 'docblock';
|
||||
|
||||
/**
|
||||
* @var NodeTypeResolver
|
||||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
/**
|
||||
* @var TypeFactory
|
||||
*/
|
||||
private $typeFactory;
|
||||
|
||||
public function __construct(NodeTypeResolver $nodeTypeResolver, TypeFactory $typeFactory)
|
||||
{
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
$this->typeFactory = $typeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls
|
||||
* @return Type[]
|
||||
*/
|
||||
public function resolveStrictTypesFromCalls(array $calls): array
|
||||
{
|
||||
return $this->resolveTypesFromCalls($calls, self::STRICTNESS_TYPE_DECLARATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls
|
||||
* @return Type[]
|
||||
*/
|
||||
public function resolveWeakTypesFromCalls(array $calls): array
|
||||
{
|
||||
return $this->resolveTypesFromCalls($calls, self::STRICTNESS_DOCBLOCK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls
|
||||
* @return Type[]
|
||||
*/
|
||||
private function resolveTypesFromCalls(array $calls, string $strictnessLevel): array
|
||||
{
|
||||
$staticTypesByArgumentPosition = [];
|
||||
|
||||
foreach ($calls as $call) {
|
||||
if (! $call instanceof StaticCall && ! $call instanceof MethodCall) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($call->args as $position => $arg) {
|
||||
$argValueType = $this->resolveArgValueType($strictnessLevel, $arg);
|
||||
$staticTypesByArgumentPosition[$position][] = $argValueType;
|
||||
}
|
||||
}
|
||||
|
||||
// unite to single type
|
||||
return $this->unionToSingleType($staticTypesByArgumentPosition);
|
||||
}
|
||||
|
||||
private function resolveArgValueType(string $strictnessLevel, Arg $arg): Type
|
||||
{
|
||||
if ($strictnessLevel === self::STRICTNESS_TYPE_DECLARATION) {
|
||||
$argValueType = $this->nodeTypeResolver->getNativeType($arg->value);
|
||||
} else {
|
||||
$argValueType = $this->nodeTypeResolver->resolve($arg->value);
|
||||
}
|
||||
|
||||
// "self" in another object is not correct, this make it independent
|
||||
return $this->correctSelfType($argValueType);
|
||||
}
|
||||
|
||||
private function correctSelfType(Type $argValueType): Type
|
||||
{
|
||||
if ($argValueType instanceof ThisType) {
|
||||
return new ObjectType($argValueType->getClassName());
|
||||
}
|
||||
|
||||
return $argValueType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, Type[]> $staticTypesByArgumentPosition
|
||||
* @return array<int, Type>
|
||||
*/
|
||||
private function unionToSingleType(array $staticTypesByArgumentPosition): array
|
||||
{
|
||||
$staticTypeByArgumentPosition = [];
|
||||
foreach ($staticTypesByArgumentPosition as $position => $staticTypes) {
|
||||
$unionedType = $this->typeFactory->createMixedPassedOrUnionType($staticTypes);
|
||||
|
||||
// narrow parents to most child type
|
||||
$unionedType = $this->narrowParentObjectTreeToSingleObjectChildType($unionedType);
|
||||
$staticTypeByArgumentPosition[$position] = $unionedType;
|
||||
}
|
||||
|
||||
return $staticTypeByArgumentPosition;
|
||||
}
|
||||
|
||||
private function narrowParentObjectTreeToSingleObjectChildType(Type $type): Type
|
||||
{
|
||||
if (! $type instanceof UnionType) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
if (! $this->isTypeWithClassNameOnly($type)) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
/** @var TypeWithClassName $firstUnionedType */
|
||||
$firstUnionedType = $type->getTypes()[0];
|
||||
|
||||
foreach ($type->getTypes() as $unionedType) {
|
||||
if (! $unionedType instanceof TypeWithClassName) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
if (! is_a($firstUnionedType->getClassName(), $unionedType->getClassName(), true)) {
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
|
||||
return $firstUnionedType;
|
||||
}
|
||||
|
||||
private function isTypeWithClassNameOnly(UnionType $unionType): bool
|
||||
{
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
if (! $unionedType instanceof TypeWithClassName) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\StaticTypeMapper\StaticTypeMapper;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodParamVendorLockResolver;
|
||||
|
||||
final class ClassMethodParamTypeCompleter
|
||||
{
|
||||
/**
|
||||
* @var StaticTypeMapper
|
||||
*/
|
||||
private $staticTypeMapper;
|
||||
|
||||
/**
|
||||
* @var ClassMethodParamVendorLockResolver
|
||||
*/
|
||||
private $classMethodParamVendorLockResolver;
|
||||
|
||||
public function __construct(
|
||||
StaticTypeMapper $staticTypeMapper,
|
||||
ClassMethodParamVendorLockResolver $classMethodParamVendorLockResolver
|
||||
) {
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
$this->classMethodParamVendorLockResolver = $classMethodParamVendorLockResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, Type> $classParameterTypes
|
||||
*/
|
||||
public function complete(ClassMethod $classMethod, array $classParameterTypes): ?ClassMethod
|
||||
{
|
||||
$hasChanged = false;
|
||||
|
||||
foreach ($classParameterTypes as $position => $argumentStaticType) {
|
||||
if ($this->shouldSkipArgumentStaticType($classMethod, $argumentStaticType, $position)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$phpParserTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($argumentStaticType);
|
||||
if ($phpParserTypeNode === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// update parameter
|
||||
$classMethod->params[$position]->type = $phpParserTypeNode;
|
||||
$hasChanged = true;
|
||||
}
|
||||
|
||||
if ($hasChanged) {
|
||||
return $classMethod;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function shouldSkipArgumentStaticType(
|
||||
ClassMethod $classMethod,
|
||||
Type $argumentStaticType,
|
||||
int $position
|
||||
): bool {
|
||||
if ($argumentStaticType instanceof MixedType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! isset($classMethod->params[$position])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->classMethodParamVendorLockResolver->isVendorLocked($classMethod, $position)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$parameter = $classMethod->params[$position];
|
||||
if ($parameter->type === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$parameterStaticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($parameter->type);
|
||||
// already completed → skip
|
||||
return $parameterStaticType->equals($argumentStaticType);
|
||||
}
|
||||
}
|
|
@ -5,16 +5,10 @@ declare(strict_types=1);
|
|||
namespace Rector\TypeDeclaration\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\ThisType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\NodeCollector\ValueObject\ArrayCallable;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
use Rector\TypeDeclaration\NodeAnalyzer\CallTypesResolver;
|
||||
use Rector\TypeDeclaration\NodeAnalyzer\ClassMethodParamTypeCompleter;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
|
@ -26,13 +20,21 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
|||
final class AddMethodCallBasedStrictParamTypeRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var TypeFactory
|
||||
* @var CallTypesResolver
|
||||
*/
|
||||
private $typeFactory;
|
||||
private $callTypesResolver;
|
||||
|
||||
public function __construct(TypeFactory $typeFactory)
|
||||
{
|
||||
$this->typeFactory = $typeFactory;
|
||||
/**
|
||||
* @var ClassMethodParamTypeCompleter
|
||||
*/
|
||||
private $classMethodParamTypeCompleter;
|
||||
|
||||
public function __construct(
|
||||
CallTypesResolver $callTypesResolver,
|
||||
ClassMethodParamTypeCompleter $classMethodParamTypeCompleter
|
||||
) {
|
||||
$this->callTypesResolver = $callTypesResolver;
|
||||
$this->classMethodParamTypeCompleter = $classMethodParamTypeCompleter;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
|
@ -99,86 +101,13 @@ CODE_SAMPLE
|
|||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if ($node->params === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classMethodCalls = $this->nodeRepository->findCallsByClassMethod($node);
|
||||
$classParameterTypes = $this->getCallTypesByPosition($classMethodCalls);
|
||||
$classMethodParameterTypes = $this->callTypesResolver->resolveStrictTypesFromCalls($classMethodCalls);
|
||||
|
||||
foreach ($classParameterTypes as $position => $argumentStaticType) {
|
||||
if ($this->shouldSkipArgumentStaticType($node, $argumentStaticType, $position)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$phpParserTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($argumentStaticType);
|
||||
if ($phpParserTypeNode === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// update parameter
|
||||
$node->params[$position]->type = $phpParserTypeNode;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MethodCall[]|StaticCall[]|ArrayCallable[] $calls
|
||||
* @return Type[]
|
||||
*/
|
||||
private function getCallTypesByPosition(array $calls): array
|
||||
{
|
||||
$staticTypesByArgumentPosition = [];
|
||||
|
||||
foreach ($calls as $call) {
|
||||
if (! $call instanceof StaticCall && ! $call instanceof MethodCall) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($call->args as $position => $arg) {
|
||||
$argValueType = $this->nodeTypeResolver->getNativeType($arg->value);
|
||||
|
||||
// "self" in another object is not correct, this make it independent
|
||||
$argValueType = $this->correctSelfType($argValueType);
|
||||
$staticTypesByArgumentPosition[$position][] = $argValueType;
|
||||
}
|
||||
}
|
||||
|
||||
// unite to single type
|
||||
$staticTypeByArgumentPosition = [];
|
||||
foreach ($staticTypesByArgumentPosition as $position => $staticTypes) {
|
||||
$staticTypeByArgumentPosition[$position] = $this->typeFactory->createMixedPassedOrUnionType($staticTypes);
|
||||
}
|
||||
|
||||
return $staticTypeByArgumentPosition;
|
||||
}
|
||||
|
||||
private function shouldSkipArgumentStaticType(
|
||||
ClassMethod $classMethod,
|
||||
Type $argumentStaticType,
|
||||
int $position
|
||||
): bool {
|
||||
if ($argumentStaticType instanceof MixedType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! isset($classMethod->params[$position])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$parameter = $classMethod->params[$position];
|
||||
if ($parameter->type === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$parameterStaticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($parameter->type);
|
||||
// already completed → skip
|
||||
return $parameterStaticType->equals($argumentStaticType);
|
||||
}
|
||||
|
||||
private function correctSelfType(Type $argValueType): Type
|
||||
{
|
||||
if ($argValueType instanceof ThisType) {
|
||||
return new ObjectType($argValueType->getClassName());
|
||||
}
|
||||
|
||||
return $argValueType;
|
||||
return $this->classMethodParamTypeCompleter->complete($node, $classMethodParameterTypes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\TypeDeclaration\NodeAnalyzer\CallTypesResolver;
|
||||
use Rector\TypeDeclaration\NodeAnalyzer\ClassMethodParamTypeCompleter;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
||||
/**
|
||||
* @see https://github.com/symplify/phpstan-rules/blob/master/docs/rules_overview.md#checktypehintcallertyperule
|
||||
*
|
||||
* @see \Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\AddParamTypeFromCallersRectorTest
|
||||
*
|
||||
* Less strict version of \Rector\TypeDeclaration\Rector\ClassMethod\AddMethodCallBasedStrictParamTypeRector,
|
||||
* that can work with docblocks too
|
||||
*/
|
||||
final class AddParamTypeFromCallersRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var CallTypesResolver
|
||||
*/
|
||||
private $callTypesResolver;
|
||||
|
||||
/**
|
||||
* @var ClassMethodParamTypeCompleter
|
||||
*/
|
||||
private $classMethodParamTypeCompleter;
|
||||
|
||||
public function __construct(
|
||||
CallTypesResolver $callTypesResolver,
|
||||
ClassMethodParamTypeCompleter $classMethodParamTypeCompleter
|
||||
) {
|
||||
$this->callTypesResolver = $callTypesResolver;
|
||||
$this->classMethodParamTypeCompleter = $classMethodParamTypeCompleter;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Add param type based on called types in that particular method', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
{
|
||||
public function run(Return_ $return)
|
||||
{
|
||||
$this->print($return);
|
||||
}
|
||||
|
||||
public function print($return)
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
final class SomeClass
|
||||
{
|
||||
public function run(Return_ $return)
|
||||
{
|
||||
$this->print($return);
|
||||
}
|
||||
|
||||
public function print(Return_ $return)
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [ClassMethod::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if ($node->params === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$calls = $this->nodeRepository->findCallsByClassMethod($node);
|
||||
if ($calls === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classMethodParameterTypes = $this->callTypesResolver->resolveWeakTypesFromCalls($calls);
|
||||
return $this->classMethodParamTypeCompleter->complete($node, $classMethodParameterTypes);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class AddParamTypeFromCallersRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function provideConfigFilePath(): string
|
||||
{
|
||||
return __DIR__ . '/config/configured_rule.php';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Source\SomeInterface;
|
||||
|
||||
final class SkipByInterface implements SomeInterface
|
||||
{
|
||||
public function run(Return_ $return)
|
||||
{
|
||||
$this->print($return);
|
||||
}
|
||||
|
||||
public function print(\PhpParser\Node $return)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
|
||||
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
|
||||
final class SkipFunctionLike
|
||||
{
|
||||
/**
|
||||
* @param ClassMethod|Function_ $functionLike
|
||||
*/
|
||||
public function run(FunctionLike $functionLike)
|
||||
{
|
||||
$this->print($functionLike);
|
||||
}
|
||||
|
||||
public function print(\PhpParser\Node\FunctionLike $functionLike)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
|
||||
final class SomeClass
|
||||
{
|
||||
public function run(Return_ $return)
|
||||
{
|
||||
$this->print($return);
|
||||
}
|
||||
|
||||
public function print($return)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
|
||||
final class SomeClass
|
||||
{
|
||||
public function run(Return_ $return)
|
||||
{
|
||||
$this->print($return);
|
||||
}
|
||||
|
||||
public function print(\PhpParser\Node\Stmt\Return_ $return)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddParamTypeFromCallersRector\Source;
|
||||
|
||||
use PhpParser\Node;
|
||||
|
||||
interface SomeInterface
|
||||
{
|
||||
public function print(Node $node);
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeFromCallersRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
$parameters->set(Option::PHP_VERSION_FEATURES, PhpVersionFeature::UNION_TYPES - 1);
|
||||
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(AddParamTypeFromCallersRector::class);
|
||||
};
|
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\Core\NodeAnalyzer;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\BinaryOp;
|
||||
use PhpParser\Node\Expr\BooleanNot;
|
||||
|
@ -19,21 +18,21 @@ final class CallAnalyzer
|
|||
*/
|
||||
private const OBJECT_CALLS = [MethodCall::class, NullsafeMethodCall::class, StaticCall::class];
|
||||
|
||||
public function isObjectCall(Node $node): bool
|
||||
public function isObjectCall(Expr $expr): bool
|
||||
{
|
||||
if ($node instanceof BooleanNot) {
|
||||
$node = $node->expr;
|
||||
if ($expr instanceof BooleanNot) {
|
||||
$expr = $expr->expr;
|
||||
}
|
||||
|
||||
if ($node instanceof BinaryOp) {
|
||||
$isObjectCallLeft = $this->isObjectCall($node->left);
|
||||
$isObjectCallRight = $this->isObjectCall($node->right);
|
||||
if ($expr instanceof BinaryOp) {
|
||||
$isObjectCallLeft = $this->isObjectCall($expr->left);
|
||||
$isObjectCallRight = $this->isObjectCall($expr->right);
|
||||
|
||||
return $isObjectCallLeft || $isObjectCallRight;
|
||||
}
|
||||
|
||||
foreach (self::OBJECT_CALLS as $objectCall) {
|
||||
if (is_a($node, $objectCall, true)) {
|
||||
if (is_a($expr, $objectCall, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user