mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-02 01:10:53 +00:00
[TypeDeclarationg] Add Tokens iterator to AddArrayReturnDocTypeRector and AddArrayParamDocTypeRector (#5796)
Co-authored-by: kaizen-ci <info@kaizen-ci.org>
This commit is contained in:
parent
5f70de5502
commit
80c4fc8aab
|
@ -49,6 +49,9 @@ final class MultilineTest extends AbstractPhpDocInfoPrinterTest
|
|||
yield [__DIR__ . '/Source/Multiline/multiline5.txt', new Nop()];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<string[]|Class_[]>
|
||||
*/
|
||||
public function provideDataClass(): Iterator
|
||||
{
|
||||
yield [__DIR__ . '/Source/Class_/some_entity_class.txt', new Class_(SomeEntityClass::class)];
|
||||
|
|
|
@ -37,6 +37,9 @@ final class TagValueNodeReprintTest extends AbstractPhpDocInfoTest
|
|||
$this->doTestPrintedPhpDocInfo($fileInfo, $tagValueNodeClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<mixed[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
foreach ($this->getDirectoriesByTagValueNodes() as $tagValueNode => $directory) {
|
||||
|
@ -50,7 +53,7 @@ final class TagValueNodeReprintTest extends AbstractPhpDocInfoTest
|
|||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @return array<class-string, string>
|
||||
*/
|
||||
private function getDirectoriesByTagValueNodes(): array
|
||||
{
|
||||
|
|
|
@ -60,6 +60,9 @@ final class TypeNodeAnalyzerTest extends AbstractKernelTestCase
|
|||
$this->assertSame($expectedIs, $isIntersection);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<IntersectionTypeNode[]|bool[]>
|
||||
*/
|
||||
public function provideDataForIntersectionAndNotNullable(): Iterator
|
||||
{
|
||||
yield [new IntersectionTypeNode([new IdentifierTypeNode(self::INT)]), true];
|
||||
|
|
|
@ -61,6 +61,9 @@ final class TagValueNodeConfigurationFactoryTest extends AbstractKernelTestCase
|
|||
$this->assertSame(':', $tagValueNodeConfiguration->getArrayEqualSign());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<string[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
yield ['(type="integer", nullable=true, options={"default":0})'];
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\NodeTypeCorrector;
|
||||
|
||||
use PHPStan\Type\Accessory\HasOffsetType;
|
||||
use PHPStan\Type\Accessory\NonEmptyArrayType;
|
||||
use PHPStan\Type\IntersectionType;
|
||||
use PHPStan\Type\Type;
|
||||
|
||||
final class HasOffsetTypeCorrector
|
||||
{
|
||||
/**
|
||||
* HasOffsetType breaks array mixed type, so we better get rid of it
|
||||
*/
|
||||
public function correct(Type $type): Type
|
||||
{
|
||||
if (! $type instanceof IntersectionType) {
|
||||
return $type;
|
||||
}
|
||||
|
||||
$clearTypes = [];
|
||||
foreach ($type->getTypes() as $intersectionedType) {
|
||||
if ($intersectionedType instanceof HasOffsetType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($intersectionedType instanceof NonEmptyArrayType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$clearTypes[] = $intersectionedType;
|
||||
}
|
||||
|
||||
if (count($clearTypes) === 1) {
|
||||
return $clearTypes[0];
|
||||
}
|
||||
|
||||
return new IntersectionType($clearTypes);
|
||||
}
|
||||
}
|
|
@ -10,10 +10,12 @@ use PhpParser\Node\Expr\ClassConstFetch;
|
|||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
|
@ -22,6 +24,7 @@ use PHPStan\Type\ArrayType;
|
|||
use PHPStan\Type\BooleanType;
|
||||
use PHPStan\Type\Constant\ConstantBooleanType;
|
||||
use PHPStan\Type\FloatType;
|
||||
use PHPStan\Type\Generic\GenericObjectType;
|
||||
use PHPStan\Type\IntegerType;
|
||||
use PHPStan\Type\IntersectionType;
|
||||
use PHPStan\Type\MixedType;
|
||||
|
@ -38,6 +41,8 @@ use Rector\Core\NodeAnalyzer\ClassAnalyzer;
|
|||
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeCorrector\GenericClassStringTypeCorrector;
|
||||
use Rector\NodeTypeResolver\NodeTypeCorrector\HasOffsetTypeCorrector;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver\IdentifierTypeResolver;
|
||||
use Rector\NodeTypeResolver\TypeAnalyzer\ArrayTypeAnalyzer;
|
||||
use Rector\StaticTypeMapper\TypeFactory\UnionTypeFactory;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
|
||||
|
@ -81,6 +86,16 @@ final class NodeTypeResolver
|
|||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
/**
|
||||
* @var HasOffsetTypeCorrector
|
||||
*/
|
||||
private $hasOffsetTypeCorrector;
|
||||
|
||||
/**
|
||||
* @var IdentifierTypeResolver
|
||||
*/
|
||||
private $identifierTypeResolver;
|
||||
|
||||
/**
|
||||
* @param NodeTypeResolverInterface[] $nodeTypeResolvers
|
||||
*/
|
||||
|
@ -90,6 +105,8 @@ final class NodeTypeResolver
|
|||
GenericClassStringTypeCorrector $genericClassStringTypeCorrector,
|
||||
UnionTypeFactory $unionTypeFactory,
|
||||
ReflectionProvider $reflectionProvider,
|
||||
HasOffsetTypeCorrector $hasOffsetTypeCorrector,
|
||||
IdentifierTypeResolver $identifierTypeResolver,
|
||||
array $nodeTypeResolvers
|
||||
) {
|
||||
foreach ($nodeTypeResolvers as $nodeTypeResolver) {
|
||||
|
@ -101,6 +118,8 @@ final class NodeTypeResolver
|
|||
$this->genericClassStringTypeCorrector = $genericClassStringTypeCorrector;
|
||||
$this->unionTypeFactory = $unionTypeFactory;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
$this->hasOffsetTypeCorrector = $hasOffsetTypeCorrector;
|
||||
$this->identifierTypeResolver = $identifierTypeResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -154,7 +173,7 @@ final class NodeTypeResolver
|
|||
{
|
||||
$type = $this->resolveByNodeTypeResolvers($node);
|
||||
if ($type !== null) {
|
||||
return $type;
|
||||
return $this->hasOffsetTypeCorrector->correct($type);
|
||||
}
|
||||
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
|
@ -163,6 +182,11 @@ final class NodeTypeResolver
|
|||
}
|
||||
|
||||
if (! $node instanceof Expr) {
|
||||
// scalar type, e.g. from param type name
|
||||
if ($node instanceof Identifier) {
|
||||
return $this->identifierTypeResolver->resolve($node);
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
|
@ -214,6 +238,10 @@ final class NodeTypeResolver
|
|||
return $this->resolve($node);
|
||||
}
|
||||
|
||||
if ($node instanceof Return_) {
|
||||
return $this->resolve($node);
|
||||
}
|
||||
|
||||
if (! $node instanceof Expr) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
@ -239,11 +267,15 @@ final class NodeTypeResolver
|
|||
}
|
||||
|
||||
$staticType = $scope->getType($node);
|
||||
if (! $staticType instanceof ObjectType) {
|
||||
if ($staticType instanceof GenericObjectType) {
|
||||
return $staticType;
|
||||
}
|
||||
|
||||
return $this->objectTypeSpecifier->narrowToFullyQualifiedOrAliasedObjectType($node, $staticType);
|
||||
if ($staticType instanceof ObjectType) {
|
||||
return $this->objectTypeSpecifier->narrowToFullyQualifiedOrAliasedObjectType($node, $staticType);
|
||||
}
|
||||
|
||||
return $staticType;
|
||||
}
|
||||
|
||||
public function isNumberType(Node $node): bool
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
use PhpParser\Node\Identifier;
|
||||
use PHPStan\Type\BooleanType;
|
||||
use PHPStan\Type\FloatType;
|
||||
use PHPStan\Type\IntegerType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\Type;
|
||||
|
||||
final class IdentifierTypeResolver
|
||||
{
|
||||
public function resolve(Identifier $identifier): Type
|
||||
{
|
||||
if ($identifier->toLowerString() === 'string') {
|
||||
return new StringType();
|
||||
}
|
||||
|
||||
if ($identifier->toLowerString() === 'bool') {
|
||||
return new BooleanType();
|
||||
}
|
||||
|
||||
if ($identifier->toLowerString() === 'int') {
|
||||
return new IntegerType();
|
||||
}
|
||||
|
||||
if ($identifier->toLowerString() === 'float') {
|
||||
return new FloatType();
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
}
|
|
@ -5,18 +5,19 @@ declare(strict_types=1);
|
|||
namespace Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\Php\PhpMethodReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
final class StaticCallTypeResolver implements NodeTypeResolverInterface
|
||||
final class StaticCallMethodCallTypeResolver implements NodeTypeResolverInterface
|
||||
{
|
||||
/**
|
||||
* @var NodeTypeResolver
|
||||
|
@ -42,7 +43,7 @@ final class StaticCallTypeResolver implements NodeTypeResolverInterface
|
|||
/**
|
||||
* @required
|
||||
*/
|
||||
public function autowireStaticCallTypeResolver(NodeTypeResolver $nodeTypeResolver): void
|
||||
public function autowireStaticCallMethodCallTypeResolver(NodeTypeResolver $nodeTypeResolver): void
|
||||
{
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
}
|
||||
|
@ -52,37 +53,55 @@ final class StaticCallTypeResolver implements NodeTypeResolverInterface
|
|||
*/
|
||||
public function getNodeClasses(): array
|
||||
{
|
||||
return [StaticCall::class];
|
||||
return [StaticCall::class, MethodCall::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StaticCall $node
|
||||
* @param StaticCall|MethodCall $node
|
||||
*/
|
||||
public function resolve(Node $node): Type
|
||||
{
|
||||
$classType = $this->nodeTypeResolver->resolve($node->class);
|
||||
if ($node instanceof MethodCall) {
|
||||
$callerType = $this->nodeTypeResolver->resolve($node->var);
|
||||
} else {
|
||||
$callerType = $this->nodeTypeResolver->resolve($node->class);
|
||||
}
|
||||
|
||||
$methodName = $this->nodeNameResolver->getName($node->name);
|
||||
|
||||
// no specific method found, return class types, e.g. <ClassType>::$method()
|
||||
if (! is_string($methodName)) {
|
||||
return $classType;
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
if (! $classType instanceof ObjectType) {
|
||||
return $classType;
|
||||
}
|
||||
|
||||
if (! $this->reflectionProvider->hasClass($classType->getClassName())) {
|
||||
return $classType;
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($classType->getClassName());
|
||||
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return $classType;
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$nodeReturnType = $scope->getType($node);
|
||||
if (! $nodeReturnType instanceof MixedType) {
|
||||
return $nodeReturnType;
|
||||
}
|
||||
|
||||
foreach ($callerType->getReferencedClasses() as $referencedClass) {
|
||||
$classMethodReturnType = $this->resolveClassMethodReturnType($referencedClass, $methodName, $scope);
|
||||
if (! $classMethodReturnType instanceof MixedType) {
|
||||
return $classMethodReturnType;
|
||||
}
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
private function resolveClassMethodReturnType(string $referencedClass, string $methodName, Scope $scope): Type
|
||||
{
|
||||
if (! $this->reflectionProvider->hasClass($referencedClass)) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($referencedClass);
|
||||
|
||||
foreach ($classReflection->getAncestors() as $ancestorClassReflection) {
|
||||
if (! $ancestorClassReflection->hasMethod($methodName)) {
|
||||
continue;
|
||||
|
@ -95,6 +114,6 @@ final class StaticCallTypeResolver implements NodeTypeResolverInterface
|
|||
}
|
||||
}
|
||||
|
||||
return $classType;
|
||||
return new MixedType();
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ final class StaticTypeAnalyzer
|
|||
}
|
||||
|
||||
if ($type instanceof ArrayType) {
|
||||
return false;
|
||||
return $this->isAlwaysTruableArrayType($type);
|
||||
}
|
||||
|
||||
if ($this->isNullable($type)) {
|
||||
|
@ -91,4 +91,10 @@ final class StaticTypeAnalyzer
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function isAlwaysTruableArrayType(ArrayType $arrayType): bool
|
||||
{
|
||||
$itemType = $arrayType->getItemType();
|
||||
return $itemType instanceof ConstantScalarType && $itemType->getValue();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\NodeTypeResolver\PHPStan\Type;
|
||||
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\BooleanType;
|
||||
use PHPStan\Type\Constant\ConstantArrayType;
|
||||
use PHPStan\Type\Constant\ConstantBooleanType;
|
||||
use PHPStan\Type\Constant\ConstantFloatType;
|
||||
use PHPStan\Type\Constant\ConstantIntegerType;
|
||||
|
@ -14,7 +16,7 @@ use PHPStan\Type\IntegerType;
|
|||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use PHPStan\Type\TypeUtils;
|
||||
use PHPStan\Type\VerbosityLevel;
|
||||
use Rector\StaticTypeMapper\TypeFactory\UnionTypeFactory;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
|
||||
|
@ -86,18 +88,19 @@ final class TypeFactory
|
|||
{
|
||||
// unwrap union types
|
||||
$unwrappedTypes = [];
|
||||
foreach ($types as $key => $type) {
|
||||
if ($type instanceof UnionType) {
|
||||
$unwrappedTypes = array_merge($unwrappedTypes, $type->getTypes());
|
||||
foreach ($types as $type) {
|
||||
$flattenTypes = TypeUtils::flattenTypes($type);
|
||||
|
||||
unset($types[$key]);
|
||||
foreach ($flattenTypes as $flattenType) {
|
||||
if ($flattenType instanceof ConstantArrayType) {
|
||||
$unwrappedTypes = array_merge($unwrappedTypes, $this->unwrapConstantArrayTypes($flattenType));
|
||||
} else {
|
||||
$unwrappedTypes[] = $flattenType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$types = array_merge($types, $unwrappedTypes);
|
||||
|
||||
// re-index
|
||||
return array_values($types);
|
||||
return $unwrappedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,4 +140,22 @@ final class TypeFactory
|
|||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Type[]
|
||||
*/
|
||||
private function unwrapConstantArrayTypes(ConstantArrayType $constantArrayType): array
|
||||
{
|
||||
$unwrappedTypes = [];
|
||||
|
||||
$flattenKeyTypes = TypeUtils::flattenTypes($constantArrayType->getKeyType());
|
||||
$flattenItemTypes = TypeUtils::flattenTypes($constantArrayType->getItemType());
|
||||
|
||||
foreach ($flattenItemTypes as $position => $nestedFlattenItemType) {
|
||||
$nestedFlattenKeyType = $flattenKeyTypes[$position];
|
||||
$unwrappedTypes[] = new ArrayType($nestedFlattenKeyType, $nestedFlattenItemType);
|
||||
}
|
||||
|
||||
return $unwrappedTypes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@ final class InterfaceTypeResolverTest extends AbstractNodeTypeResolverTest
|
|||
$this->assertEquals($expectedTypeWithClassName->getClassName(), $resolvedType->getClassName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<int[]|string[]|ObjectType[]>
|
||||
*/
|
||||
public function dataProvider(): Iterator
|
||||
{
|
||||
yield [
|
||||
|
|
|
@ -27,6 +27,9 @@ final class NameTypeResolverTest extends AbstractNodeTypeResolverTest
|
|||
$this->assertEquals($expectedType, $resolvedType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<int[]|string[]|ObjectType[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$expectedObjectType = new ObjectType(AnotherClass::class);
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\TraitTypeResolver;
|
|||
use Iterator;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\AbstractNodeTypeResolverTest;
|
||||
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\TraitTypeResolver\Source\AnotherTrait;
|
||||
use Rector\NodeTypeResolver\Tests\PerNodeTypeResolver\TraitTypeResolver\Source\TraitWithTrait;
|
||||
|
@ -28,6 +29,9 @@ final class TraitTypeResolverTest extends AbstractNodeTypeResolverTest
|
|||
$this->assertEquals($expectedType, $resolvedType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<int[]|string[]|UnionType[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$unionTypeFactory = new UnionTypeFactory();
|
||||
|
|
|
@ -88,6 +88,9 @@ final class StaticTypeMapperTest extends AbstractKernelTestCase
|
|||
$this->assertInstanceOf($expectedType, $phpStanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<class-string<IterableType>[]|Identifier[]>
|
||||
*/
|
||||
public function provideDataForMapPhpParserNodePHPStanType(): Iterator
|
||||
{
|
||||
yield [new Identifier('iterable'), IterableType::class];
|
||||
|
|
|
@ -41,6 +41,9 @@ final class ArrayTypeComparatorTest extends AbstractKernelTestCase
|
|||
$this->assertSame($areExpectedEqual, $areEqual);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<ArrayType[]|bool[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$unionTypeFactory = new UnionTypeFactory();
|
||||
|
|
|
@ -21,6 +21,7 @@ use Rector\PHPStanStaticTypeMapper\Contract\PHPStanStaticTypeMapperAwareInterfac
|
|||
use Rector\PHPStanStaticTypeMapper\Contract\TypeMapperInterface;
|
||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedGenericObjectType;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\SelfObjectType;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\ShortenedObjectType;
|
||||
|
@ -64,7 +65,9 @@ final class ObjectTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMa
|
|||
}
|
||||
|
||||
if ($type instanceof GenericObjectType) {
|
||||
if (Strings::contains($type->getClassName(), '\\')) {
|
||||
if ($type instanceof FullyQualifiedGenericObjectType) {
|
||||
$name = '\\' . $type->getClassName();
|
||||
} elseif (Strings::contains($type->getClassName(), '\\')) {
|
||||
$name = '\\' . $type->getClassName();
|
||||
} else {
|
||||
$name = $type->getClassName();
|
||||
|
@ -103,14 +106,22 @@ final class ObjectTypeMapper implements TypeMapperInterface, PHPStanStaticTypeMa
|
|||
if ($type instanceof FullyQualifiedObjectType) {
|
||||
return new FullyQualified($type->getClassName());
|
||||
}
|
||||
|
||||
if (! $type instanceof GenericObjectType) {
|
||||
// fallback
|
||||
return new FullyQualified($type->getClassName());
|
||||
}
|
||||
|
||||
if ($type->getClassName() === 'iterable') {
|
||||
// fallback
|
||||
return new Name('iterable');
|
||||
}
|
||||
|
||||
if ($type->getClassName() !== 'object') {
|
||||
// fallback
|
||||
return new FullyQualified($type->getClassName());
|
||||
}
|
||||
|
||||
return new Name('object');
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,9 @@ final class ArrayTypeMapperTest extends AbstractKernelTestCase
|
|||
$this->assertSame($expectedResult, (string) $actualTypeNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<string[]|ArrayType[]>
|
||||
*/
|
||||
public function provideDataWithoutKeys(): Iterator
|
||||
{
|
||||
$arrayType = new ArrayType(new MixedType(), new StringType());
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\StaticTypeMapper\ValueObject\Type;
|
||||
|
||||
use PHPStan\Type\Generic\GenericObjectType;
|
||||
|
||||
final class FullyQualifiedGenericObjectType extends GenericObjectType
|
||||
{
|
||||
}
|
|
@ -13,6 +13,9 @@ final class ShortenedObjectType extends ObjectType
|
|||
*/
|
||||
private $fullyQualifiedName;
|
||||
|
||||
/**
|
||||
* @param class-string $fullyQualifiedName
|
||||
*/
|
||||
public function __construct(string $shortName, string $fullyQualifiedName)
|
||||
{
|
||||
parent::__construct($shortName);
|
||||
|
|
|
@ -48,6 +48,9 @@ final class PhpDocTypeMapperTest extends AbstractKernelTestCase
|
|||
$this->assertInstanceOf($expectedPHPStanType, $phpStanType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<class-string<ArrayType>[]|ArrayShapeNode[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$arrayShapeNode = new ArrayShapeNode([new ArrayShapeItemNode(null, true, new IdentifierTypeNode('string'))]);
|
||||
|
|
|
@ -628,3 +628,11 @@ parameters:
|
|||
- '#Cognitive complexity for "Rector\\EarlyReturn\\Rector\\If_\\ChangeAndIfToEarlyReturnRector\:\:refactor\(\)" is 10, keep it under 9#'
|
||||
- '#Parameter \#2 \$returnedStrictTypeNode of method Rector\\TypeDeclaration\\Rector\\ClassMethod\\ReturnTypeFromStrictTypedCallRector\:\:refactorSingleReturnType\(\) expects PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType, PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PHPStan\\Type\\UnionType given#'
|
||||
- '#Method Rector\\DowngradePhp80\\Rector\\ClassMethod\\DowngradeTrailingCommasInParamUseRector\:\:processUses\(\) should return PhpParser\\Node\\Expr\\Closure but returns PhpParser\\Node#'
|
||||
- '#Cognitive complexity for "Rector\\NodeTypeResolver\\NodeTypeResolver\:\:getStaticType\(\)" is 11, keep it under 9#'
|
||||
|
||||
-
|
||||
message: '#There should be no empty class#'
|
||||
paths:
|
||||
- packages/static-type-mapper/src/ValueObject/Type/FullyQualifiedGenericObjectType.php
|
||||
|
||||
- '#(.*?) class\-string, string given#'
|
||||
|
|
|
@ -24,6 +24,9 @@ final class MoveEntitiesToEntityDirectoryRectorTest extends AbstractRectorTestCa
|
|||
$this->assertFileWithContentWasAdded($expectedAddedFileWithContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<AddedFileWithContent[]|SmartFileInfo[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$smartFileSystem = new SmartFileSystem();
|
||||
|
|
|
@ -103,7 +103,7 @@ CODE_SAMPLE
|
|||
$firstArgValue = $methodCall->args[0]->value;
|
||||
|
||||
foreach ($this->callsWithParamRenames as $callWithParamRename) {
|
||||
if (! $this->isObjectType($methodCall, $callWithParamRename->getOldObjectType())) {
|
||||
if (! $this->isObjectType($methodCall->var, $callWithParamRename->getOldObjectType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -113,6 +113,7 @@ CODE_SAMPLE
|
|||
if ($node->stmts === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$haveNodeChanged = false;
|
||||
foreach ($node->stmts as $key => $stmt) {
|
||||
if ($stmt instanceof Expression) {
|
||||
|
@ -163,7 +164,6 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
$propertyFetchType = $this->resolvePropertyFetchType($node->cond);
|
||||
|
||||
return $this->staticTypeAnalyzer->isAlwaysTruableType($propertyFetchType);
|
||||
}
|
||||
|
||||
|
|
|
@ -155,11 +155,12 @@ CODE_SAMPLE
|
|||
|
||||
foreach ($classMethod->stmts as $statement) {
|
||||
if ($statement instanceof Return_) {
|
||||
if (! $statement->expr instanceof Array_) {
|
||||
$returnedExpr = $statement->expr;
|
||||
if (! $returnedExpr instanceof Array_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $statement->expr;
|
||||
return $returnedExpr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ CODE_SAMPLE
|
|||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isObjectType($node, new ObjectType('Symfony\Component\Console\Style\SymfonyStyle'))) {
|
||||
if (! $this->isObjectType($node->var, new ObjectType('Symfony\Component\Console\Style\SymfonyStyle'))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ final class ExtraFilesTest extends AbstractRectorTestCase
|
|||
$this->doTestFileInfo($originalFileInfo, $extraFileInfos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<array<int, SmartFileInfo[]>|SmartFileInfo[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$extraFileInfos = [new SmartFileInfo(__DIR__ . '/Source/UseAbstract.php')];
|
||||
|
|
|
@ -200,7 +200,7 @@ CODE_SAMPLE
|
|||
private function matchMethodCall(MethodCall $methodCall): ?ObjectType
|
||||
{
|
||||
foreach ($this->callsToFluent as $callToFluent) {
|
||||
if (! $this->isObjectType($methodCall, $callToFluent->getObjectType())) {
|
||||
if (! $this->isObjectType($methodCall->var, $callToFluent->getObjectType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -159,14 +159,11 @@ CODE_SAMPLE
|
|||
|
||||
private function matchReturnMethodCall(Return_ $return): ?MethodCall
|
||||
{
|
||||
if ($return->expr === null) {
|
||||
$returnExpr = $return->expr;
|
||||
if (! $returnExpr instanceof MethodCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $return->expr instanceof MethodCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $return->expr;
|
||||
return $returnExpr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Rector\Defluent\Rector\Return_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
|
@ -108,7 +109,7 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
private function matchReturnMethodCall(Return_ $return): ?MethodCall
|
||||
private function matchReturnMethodCall(Return_ $return): ?Expr
|
||||
{
|
||||
if ($return->expr === null) {
|
||||
return null;
|
||||
|
|
|
@ -55,8 +55,10 @@ abstract class AbstractRootExpr implements RootExprAwareInterface, FirstCallFact
|
|||
return null;
|
||||
}
|
||||
|
||||
if ($currentStmt->expr instanceof Assign) {
|
||||
return $currentStmt->expr;
|
||||
$currentExpr = $currentStmt->expr;
|
||||
|
||||
if ($currentExpr instanceof Assign) {
|
||||
return $currentExpr;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -16,6 +16,7 @@ use PhpParser\Node\Stmt\ClassLike;
|
|||
use PHPStan\Type\ObjectType;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Defluent\NodeAnalyzer\FluentChainMethodCallNodeAnalyzer;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\TypeAnalyzer\ArrayTypeAnalyzer;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
|
@ -32,9 +33,17 @@ final class ChangeSetParametersArrayToArrayCollectionRector extends AbstractRect
|
|||
*/
|
||||
private $arrayTypeAnalyzer;
|
||||
|
||||
public function __construct(ArrayTypeAnalyzer $arrayTypeAnalyzer)
|
||||
{
|
||||
/**
|
||||
* @var FluentChainMethodCallNodeAnalyzer
|
||||
*/
|
||||
private $fluentChainMethodCallNodeAnalyzer;
|
||||
|
||||
public function __construct(
|
||||
ArrayTypeAnalyzer $arrayTypeAnalyzer,
|
||||
FluentChainMethodCallNodeAnalyzer $fluentChainMethodCallNodeAnalyzer
|
||||
) {
|
||||
$this->arrayTypeAnalyzer = $arrayTypeAnalyzer;
|
||||
$this->fluentChainMethodCallNodeAnalyzer = $fluentChainMethodCallNodeAnalyzer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,16 +140,22 @@ CODE_SAMPLE
|
|||
return true;
|
||||
}
|
||||
|
||||
//one of the cases when we are in the repo and it's extended from EntityRepository
|
||||
// one of the cases when we are in the repo and it's extended from EntityRepository
|
||||
if (! $this->isObjectType($classLike, new ObjectType('Doctrine\ORM\EntityRepository'))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $this->isObjectType($methodCall->var, new ObjectType('Doctrine\ORM\EntityRepository'))) {
|
||||
if (! $this->isName($methodCall->name, 'setParameters')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! $this->isName($methodCall->name, 'setParameters');
|
||||
// compare root variable
|
||||
$rootExpr = $this->fluentChainMethodCallNodeAnalyzer->resolveRootMethodCall($methodCall);
|
||||
if (! $rootExpr instanceof MethodCall) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! $this->isObjectType($rootExpr, new ObjectType('Doctrine\ORM\QueryBuilder'));
|
||||
}
|
||||
|
||||
private function getNewArrayCollectionFromSetParametersArgument(Arg $arg): New_
|
||||
|
|
|
@ -114,7 +114,10 @@ CODE_SAMPLE
|
|||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
foreach ($this->typeToTimeMethodsAndPositions as $typeToTimeMethodAndPosition) {
|
||||
if (! $this->isObjectType($node, $typeToTimeMethodAndPosition->getObjectType())) {
|
||||
if (! $this->isObjectType(
|
||||
$node instanceof MethodCall ? $node->var : $node->class,
|
||||
$typeToTimeMethodAndPosition->getObjectType()
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ CODE_SAMPLE
|
|||
private function shouldSkip(Node $node): bool
|
||||
{
|
||||
if ($node instanceof StaticCall) {
|
||||
return ! $this->nodeTypeResolver->isObjectTypes($node, $this->requestObjectTypes);
|
||||
return ! $this->nodeTypeResolver->isObjectTypes($node->class, $this->requestObjectTypes);
|
||||
}
|
||||
|
||||
$classLike = $node->getAttribute(AttributeKey::CLASS_NODE);
|
||||
|
|
|
@ -116,13 +116,17 @@ final class SingletonClassMethodAnalyzer
|
|||
return $expr->left;
|
||||
}
|
||||
}
|
||||
|
||||
// matching: "! self::$static"
|
||||
if (! $expr instanceof BooleanNot) {
|
||||
return null;
|
||||
}
|
||||
if (! $expr->expr instanceof StaticPropertyFetch) {
|
||||
|
||||
$negatedExpr = $expr->expr;
|
||||
if (! $negatedExpr instanceof StaticPropertyFetch) {
|
||||
return null;
|
||||
}
|
||||
return $expr->expr;
|
||||
|
||||
return $negatedExpr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,9 @@ final class PropertyRenameFactoryTest extends AbstractKernelTestCase
|
|||
$this->assertSame($currentName, $actualPropertyRename->getCurrentName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<string[]|SmartFileInfo[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
yield [new SmartFileInfo(__DIR__ . '/Fixture/skip_some_class.php.inc'), 'eliteManager', 'eventManager'];
|
||||
|
|
|
@ -85,12 +85,12 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isObjectType($node, new ObjectType('Tester\TestCase'))) {
|
||||
if ($node instanceof MethodCall) {
|
||||
$this->processUnderTestRun($node);
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($node instanceof MethodCall) {
|
||||
$this->processUnderTestRun($node);
|
||||
if (! $this->isObjectType($node, new ObjectType('Tester\TestCase'))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -110,6 +110,10 @@ CODE_SAMPLE
|
|||
|
||||
private function processUnderTestRun(MethodCall $methodCall): void
|
||||
{
|
||||
if (! $this->isObjectType($methodCall->var, new ObjectType('Tester\TestCase'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isName($methodCall->name, 'run')) {
|
||||
$this->removeNode($methodCall);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use PhpParser\Node\Stmt\Class_;
|
|||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocNode\Symfony\SymfonyRouteTagValueNode;
|
||||
use Rector\BetterPhpDocParser\ValueObjectFactory\PhpDocNode\Symfony\SymfonyRouteTagValueNodeFactory;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
|
@ -229,7 +228,7 @@ CODE_SAMPLE
|
|||
|
||||
if ($node->expr instanceof StaticCall) {
|
||||
// for custom static route factories
|
||||
return $this->isRouteStaticCallMatch($node->expr);
|
||||
return $this->nodeTypeResolver->isObjectType($node->expr, new ObjectType('Nette\Application\IRouter'));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -298,39 +297,6 @@ CODE_SAMPLE
|
|||
}
|
||||
}
|
||||
|
||||
private function isRouteStaticCallMatch(StaticCall $staticCall): bool
|
||||
{
|
||||
$className = $this->getName($staticCall->class);
|
||||
if ($className === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$methodName = $this->getName($staticCall->name);
|
||||
if ($methodName === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
if (! $classReflection->hasMethod($methodName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$methodReflection = $classReflection->getNativeMethod($methodName);
|
||||
$parametersAcceptor = $methodReflection->getVariants()[0];
|
||||
|
||||
$returnType = $parametersAcceptor->getReturnType();
|
||||
|
||||
if ($returnType instanceof TypeWithClassName) {
|
||||
return is_a($returnType->getClassName(), 'Nette\Application\IRouter', true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function shouldSkipClassMethod(ClassMethod $classMethod): bool
|
||||
{
|
||||
// not an action method
|
||||
|
|
|
@ -67,7 +67,7 @@ CODE_SAMPLE
|
|||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isObjectType($node, new ObjectType('Nette\Application\Request'))) {
|
||||
if (! $this->isObjectType($node->var, new ObjectType('Nette\Application\Request'))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ final class ConstantReferenceRouterFactory
|
|||
$routeList = new RouteList();
|
||||
|
||||
// case of single action controller, usually get() or __invoke() method
|
||||
$routeList[] = Route::get(self::SOME_PATH, ConstantReferenceSomePresenter::class);
|
||||
$routeList[] = new Route(self::SOME_PATH, ConstantReferenceSomePresenter::class);
|
||||
|
||||
return $routeList;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ final class ConstantReferenceRouterFactory
|
|||
final class ConstantReferenceSomePresenter
|
||||
{
|
||||
/**
|
||||
* @\Symfony\Component\Routing\Annotation\Route(path="/some-path", methods={"GET"})
|
||||
* @\Symfony\Component\Routing\Annotation\Route(path="/some-path")
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Rector\Nette\Rector\NotIdentical;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\BinaryOp;
|
||||
use PhpParser\Node\Expr\BinaryOp\Identical;
|
||||
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
|
||||
|
@ -86,25 +87,27 @@ CODE_SAMPLE
|
|||
return $containsStaticCall;
|
||||
}
|
||||
|
||||
private function matchStrposInComparisonToFalse(BinaryOp $binaryOp): ?FuncCall
|
||||
private function matchStrposInComparisonToFalse(BinaryOp $binaryOp): ?Expr
|
||||
{
|
||||
if ($this->valueResolver->isFalse($binaryOp->left)) {
|
||||
if (! $binaryOp->right instanceof FuncCall) {
|
||||
$rightExpr = $binaryOp->right;
|
||||
if (! $rightExpr instanceof FuncCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->isName($binaryOp->right, 'strpos')) {
|
||||
return $binaryOp->right;
|
||||
if ($this->isName($rightExpr, 'strpos')) {
|
||||
return $rightExpr;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->valueResolver->isFalse($binaryOp->right)) {
|
||||
if (! $binaryOp->left instanceof FuncCall) {
|
||||
$leftExpr = $binaryOp->left;
|
||||
if (! $leftExpr instanceof FuncCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->isName($binaryOp->left, 'strpos')) {
|
||||
return $binaryOp->left;
|
||||
if ($this->isName($leftExpr, 'strpos')) {
|
||||
return $leftExpr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,13 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\Restoration\Type;
|
||||
|
||||
use PHPStan\Type\Constant\ConstantArrayType;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\Constant\ConstantStringType;
|
||||
use PHPStan\Type\Generic\GenericClassStringType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeUtils;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
|
||||
|
@ -27,18 +28,37 @@ final class ConstantReturnToParamTypeConverter
|
|||
|
||||
public function convert(Type $type): Type
|
||||
{
|
||||
if ($type instanceof UnionType) {
|
||||
$flattenReturnTypes = TypeUtils::flattenTypes($type);
|
||||
$unionedTypes = [];
|
||||
foreach ($flattenReturnTypes as $flattenReturnType) {
|
||||
if ($flattenReturnType instanceof ArrayType) {
|
||||
$unionedTypes[] = $flattenReturnType->getItemType();
|
||||
}
|
||||
}
|
||||
|
||||
$resolvedTypes = [];
|
||||
foreach ($unionedTypes as $unionedType) {
|
||||
$resolvedTypes[] = $this->convert($unionedType);
|
||||
}
|
||||
|
||||
return new UnionType($resolvedTypes);
|
||||
}
|
||||
|
||||
if ($type instanceof ConstantStringType) {
|
||||
return $this->unwrapConstantTypeToObjectType($type);
|
||||
}
|
||||
if ($type instanceof ConstantArrayType) {
|
||||
|
||||
if ($type instanceof ArrayType) {
|
||||
return $this->unwrapConstantTypeToObjectType($type);
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
private function unwrapConstantTypeToObjectType(Type $type): Type
|
||||
{
|
||||
if ($type instanceof ConstantArrayType) {
|
||||
if ($type instanceof ArrayType) {
|
||||
return $this->unwrapConstantTypeToObjectType($type->getItemType());
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,10 @@ CODE_SAMPLE
|
|||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isObjectType($node, new ObjectType('Symfony\Component\DependencyInjection\ContainerBuilder'))) {
|
||||
if (! $this->isObjectType(
|
||||
$node->var,
|
||||
new ObjectType('Symfony\Component\DependencyInjection\ContainerBuilder')
|
||||
)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ CODE_SAMPLE
|
|||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
foreach ($this->methodCallRenamesWithAddedArguments as $methodCallRenameWithAddedArgument) {
|
||||
if (! $this->isObjectType($node, $methodCallRenameWithAddedArgument->getObjectType())) {
|
||||
if (! $this->isObjectType($node->var, $methodCallRenameWithAddedArgument->getObjectType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ final class StaticCallToFuncCallRector extends AbstractRector implements Configu
|
|||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
foreach ($this->staticCallsToFunctions as $staticCallToFunction) {
|
||||
if (! $this->isObjectType($node, $staticCallToFunction->getObjectType())) {
|
||||
if (! $this->isObjectType($node->class, $staticCallToFunction->getObjectType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ CODE_SAMPLE
|
|||
private function processMethodCall(MethodCall $methodCall): ?Node
|
||||
{
|
||||
foreach ($this->methodNamesByType as $type => $methodName) {
|
||||
if (! $this->isObjectType($methodCall, new ObjectType($type))) {
|
||||
if (! $this->isObjectType($methodCall->var, new ObjectType($type))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@ final class CommunityTestCaseRectorTest extends AbstractRectorTestCase
|
|||
$this->assertFileWithContentWasAdded($addedFileWithContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<AddedFileWithContent[]|SmartFileInfo[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$smartFileSystem = new SmartFileSystem();
|
||||
|
|
|
@ -21,6 +21,9 @@ final class NativeTestCaseRectorTest extends AbstractRectorTestCase
|
|||
$this->assertFileWithContentWasAdded($addedFileWithContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<AddedFileWithContent[]|SmartFileInfo[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$smartFileSystem = new SmartFileSystem();
|
||||
|
|
|
@ -8,12 +8,11 @@ use Symplify\SymfonyPhpConfig\ValueObjectInliner;
|
|||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(StaticCallToFuncCallRector::class)
|
||||
->call('configure', [[
|
||||
StaticCallToFuncCallRector::STATIC_CALLS_TO_FUNCTIONS => ValueObjectInliner::inline([
|
||||
|
||||
new StaticCallToFuncCall(SomeOldStaticClass::class, 'render', 'view'),
|
||||
|
||||
]),
|
||||
]]);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\NodeTypeAnalyzer;
|
||||
|
||||
use PHPStan\Type\Constant\ConstantArrayType;
|
||||
use PHPStan\Type\Generic\GenericObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
|
||||
final class DetailedTypeAnalyzer
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private const MAX_NUMBER_OF_TYPES = 3;
|
||||
|
||||
public function isTooDetailed(Type $type): bool
|
||||
{
|
||||
if ($type instanceof UnionType) {
|
||||
return count($type->getTypes()) > self::MAX_NUMBER_OF_TYPES;
|
||||
}
|
||||
|
||||
if ($type instanceof ConstantArrayType) {
|
||||
return count($type->getValueTypes()) > self::MAX_NUMBER_OF_TYPES;
|
||||
}
|
||||
|
||||
if ($type instanceof GenericObjectType) {
|
||||
return $this->isTooDetailedGenericObjectType($type);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isTooDetailedGenericObjectType(GenericObjectType $genericObjectType): bool
|
||||
{
|
||||
if (count($genericObjectType->getTypes()) !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$genericType = $genericObjectType->getTypes()[0];
|
||||
return $this->isTooDetailed($genericType);
|
||||
}
|
||||
}
|
|
@ -9,9 +9,11 @@ use PhpParser\Node\Param;
|
|||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\DeadDocBlock\TagRemover\ParamTagRemover;
|
||||
use Rector\TypeDeclaration\TypeInferer\ParamTypeInferer;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
@ -33,10 +35,19 @@ final class AddArrayParamDocTypeRector extends AbstractRector
|
|||
*/
|
||||
private $phpDocTypeChanger;
|
||||
|
||||
public function __construct(ParamTypeInferer $paramTypeInferer, PhpDocTypeChanger $phpDocTypeChanger)
|
||||
{
|
||||
/**
|
||||
* @var ParamTagRemover
|
||||
*/
|
||||
private $paramTagRemover;
|
||||
|
||||
public function __construct(
|
||||
ParamTypeInferer $paramTypeInferer,
|
||||
PhpDocTypeChanger $phpDocTypeChanger,
|
||||
ParamTagRemover $paramTagRemover
|
||||
) {
|
||||
$this->paramTypeInferer = $paramTypeInferer;
|
||||
$this->phpDocTypeChanger = $phpDocTypeChanger;
|
||||
$this->paramTagRemover = $paramTagRemover;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
|
@ -105,17 +116,17 @@ CODE_SAMPLE
|
|||
continue;
|
||||
}
|
||||
|
||||
$type = $this->paramTypeInferer->inferParam($param);
|
||||
if ($type instanceof MixedType) {
|
||||
$paramType = $this->paramTypeInferer->inferParam($param);
|
||||
if ($paramType instanceof MixedType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$paramName = $this->getName($param);
|
||||
|
||||
$this->phpDocTypeChanger->changeParamType($phpDocInfo, $type, $param, $paramName);
|
||||
$this->phpDocTypeChanger->changeParamType($phpDocInfo, $paramType, $param, $paramName);
|
||||
}
|
||||
|
||||
if ($phpDocInfo->hasChanged()) {
|
||||
$this->paramTagRemover->removeParamTagsIfUseless($phpDocInfo, $node);
|
||||
return $node;
|
||||
}
|
||||
|
||||
|
@ -130,28 +141,34 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
// not an array type
|
||||
if (! $this->isName($param->type, 'array')) {
|
||||
$paramType = $this->nodeTypeResolver->resolve($param->type);
|
||||
|
||||
// weird case for maybe interface
|
||||
if ($paramType->isIterable()->maybe() && ($paramType instanceof ObjectType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// not an array type
|
||||
$paramStaticType = $this->getStaticType($param);
|
||||
if ($paramStaticType instanceof MixedType) {
|
||||
$isArrayable = $paramType->isIterable()
|
||||
->yes() || $paramType->isArray()
|
||||
->yes() || ($paramType->isIterable()->maybe() || $paramType->isArray()->maybe());
|
||||
if (! $isArrayable) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->isArrayExplicitMixed($paramType);
|
||||
}
|
||||
|
||||
private function isArrayExplicitMixed(Type $type): bool
|
||||
{
|
||||
if (! $type instanceof ArrayType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $paramStaticType instanceof ArrayType) {
|
||||
return true;
|
||||
$iterableValueType = $type->getIterableValueType();
|
||||
if (! $iterableValueType instanceof MixedType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $paramStaticType->getIterableValueType() instanceof MixedType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// is defined mixed[] explicitly
|
||||
/** @var MixedType $mixedType */
|
||||
$mixedType = $paramStaticType->getIterableValueType();
|
||||
|
||||
return $mixedType->isExplicitMixed();
|
||||
return $iterableValueType->isExplicitMixed();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,18 +10,18 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
|
|||
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\Constant\ConstantArrayType;
|
||||
use PHPStan\Type\IterableType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use PHPStan\Type\VoidType;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareArrayShapeNode;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareGenericTypeNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\DeadDocBlock\TagRemover\ReturnTagRemover;
|
||||
use Rector\Privatization\TypeManipulator\NormalizeTypeToRespectArrayScalarType;
|
||||
use Rector\TypeDeclaration\NodeTypeAnalyzer\DetailedTypeAnalyzer;
|
||||
use Rector\TypeDeclaration\TypeAnalyzer\AdvancedArrayAnalyzer;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer\ReturnTypeDeclarationReturnTypeInferer;
|
||||
|
@ -36,11 +36,6 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
|||
*/
|
||||
final class AddArrayReturnDocTypeRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private const MAX_NUMBER_OF_TYPES = 3;
|
||||
|
||||
/**
|
||||
* @var ReturnTypeInferer
|
||||
*/
|
||||
|
@ -66,18 +61,32 @@ final class AddArrayReturnDocTypeRector extends AbstractRector
|
|||
*/
|
||||
private $normalizeTypeToRespectArrayScalarType;
|
||||
|
||||
/**
|
||||
* @var ReturnTagRemover
|
||||
*/
|
||||
private $returnTagRemover;
|
||||
|
||||
/**
|
||||
* @var DetailedTypeAnalyzer
|
||||
*/
|
||||
private $detailedTypeAnalyzer;
|
||||
|
||||
public function __construct(
|
||||
ReturnTypeInferer $returnTypeInferer,
|
||||
ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard,
|
||||
AdvancedArrayAnalyzer $advancedArrayAnalyzer,
|
||||
PhpDocTypeChanger $phpDocTypeChanger,
|
||||
NormalizeTypeToRespectArrayScalarType $normalizeTypeToRespectArrayScalarType
|
||||
NormalizeTypeToRespectArrayScalarType $normalizeTypeToRespectArrayScalarType,
|
||||
ReturnTagRemover $returnTagRemover,
|
||||
DetailedTypeAnalyzer $detailedTypeAnalyzer
|
||||
) {
|
||||
$this->returnTypeInferer = $returnTypeInferer;
|
||||
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
|
||||
$this->advancedArrayAnalyzer = $advancedArrayAnalyzer;
|
||||
$this->phpDocTypeChanger = $phpDocTypeChanger;
|
||||
$this->normalizeTypeToRespectArrayScalarType = $normalizeTypeToRespectArrayScalarType;
|
||||
$this->returnTagRemover = $returnTagRemover;
|
||||
$this->detailedTypeAnalyzer = $detailedTypeAnalyzer;
|
||||
}
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
|
@ -136,6 +145,7 @@ CODE_SAMPLE
|
|||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
|
||||
|
||||
if ($this->shouldSkip($node, $phpDocInfo)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -150,6 +160,10 @@ CODE_SAMPLE
|
|||
$node->returnType
|
||||
);
|
||||
|
||||
if ($this->detailedTypeAnalyzer->isTooDetailed($inferredReturnType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$currentReturnType = $phpDocInfo->getReturnType();
|
||||
if ($this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethodOldTypeWithNewType(
|
||||
$currentReturnType,
|
||||
|
@ -163,6 +177,7 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
$this->phpDocTypeChanger->changeReturnType($phpDocInfo, $inferredReturnType);
|
||||
$this->returnTagRemover->removeReturnTagIfUseless($phpDocInfo, $node);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
@ -200,7 +215,7 @@ CODE_SAMPLE
|
|||
return true;
|
||||
}
|
||||
|
||||
if ($newType instanceof UnionType && $this->shouldSkipUnionType($newType)) {
|
||||
if ($this->detailedTypeAnalyzer->isTooDetailed($newType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -213,11 +228,7 @@ CODE_SAMPLE
|
|||
return true;
|
||||
}
|
||||
|
||||
if (! $newType instanceof ConstantArrayType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return count($newType->getValueTypes()) > self::MAX_NUMBER_OF_TYPES;
|
||||
return $this->detailedTypeAnalyzer->isTooDetailed($newType);
|
||||
}
|
||||
|
||||
private function shouldSkipClassMethod(ClassMethod $classMethod): bool
|
||||
|
@ -230,7 +241,7 @@ CODE_SAMPLE
|
|||
return false;
|
||||
}
|
||||
|
||||
return ! $this->isNames($classMethod->returnType, ['array', 'iterable']);
|
||||
return ! $this->isNames($classMethod->returnType, ['array', 'iterable', 'Iterator']);
|
||||
}
|
||||
|
||||
private function hasArrayShapeNode(ClassMethod $classMethod): bool
|
||||
|
@ -275,9 +286,4 @@ CODE_SAMPLE
|
|||
|
||||
return $this->advancedArrayAnalyzer->isMixedOfSpecificOverride($arrayType, $phpDocInfo);
|
||||
}
|
||||
|
||||
private function shouldSkipUnionType(UnionType $unionType): bool
|
||||
{
|
||||
return count($unionType->getTypes()) > self::MAX_NUMBER_OF_TYPES;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use PhpParser\Node\Stmt\ClassMethod;
|
|||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\UnionType as PhpParserUnionType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
|
@ -24,6 +23,7 @@ use Rector\TypeDeclaration\ChildPopulator\ChildReturnPopulator;
|
|||
use Rector\TypeDeclaration\PhpDocParser\NonInformativeReturnTagRemover;
|
||||
use Rector\TypeDeclaration\PhpParserTypeAnalyzer;
|
||||
use Rector\TypeDeclaration\TypeAlreadyAddedChecker\ReturnTypeAlreadyAddedChecker;
|
||||
use Rector\TypeDeclaration\TypeAnalyzer\ObjectTypeComparator;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer\ReturnTypeDeclarationReturnTypeInferer;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
|
||||
|
@ -77,6 +77,11 @@ final class ReturnTypeDeclarationRector extends AbstractRector
|
|||
*/
|
||||
private $phpParserTypeAnalyzer;
|
||||
|
||||
/**
|
||||
* @var ObjectTypeComparator
|
||||
*/
|
||||
private $objectTypeComparator;
|
||||
|
||||
public function __construct(
|
||||
ReturnTypeInferer $returnTypeInferer,
|
||||
ChildReturnPopulator $childReturnPopulator,
|
||||
|
@ -84,7 +89,8 @@ final class ReturnTypeDeclarationRector extends AbstractRector
|
|||
NonInformativeReturnTagRemover $nonInformativeReturnTagRemover,
|
||||
ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard,
|
||||
VendorLockResolver $vendorLockResolver,
|
||||
PhpParserTypeAnalyzer $phpParserTypeAnalyzer
|
||||
PhpParserTypeAnalyzer $phpParserTypeAnalyzer,
|
||||
ObjectTypeComparator $objectTypeComparator
|
||||
) {
|
||||
$this->returnTypeInferer = $returnTypeInferer;
|
||||
$this->returnTypeAlreadyAddedChecker = $returnTypeAlreadyAddedChecker;
|
||||
|
@ -93,6 +99,7 @@ final class ReturnTypeDeclarationRector extends AbstractRector
|
|||
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
|
||||
$this->vendorLockResolver = $vendorLockResolver;
|
||||
$this->phpParserTypeAnalyzer = $phpParserTypeAnalyzer;
|
||||
$this->objectTypeComparator = $objectTypeComparator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -241,7 +248,7 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
$currentType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($functionLike->returnType);
|
||||
if ($this->isCurrentObjectTypeSubType($currentType, $inferedType)) {
|
||||
if ($this->objectTypeComparator->isCurrentObjectTypeSubType($currentType, $inferedType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -272,22 +279,6 @@ CODE_SAMPLE
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* E.g. current E, new type A, E extends A → true
|
||||
*/
|
||||
private function isCurrentObjectTypeSubType(Type $currentType, Type $inferedType): bool
|
||||
{
|
||||
if (! $currentType instanceof ObjectType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $inferedType instanceof ObjectType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_a($currentType->getClassName(), $inferedType->getClassName(), true);
|
||||
}
|
||||
|
||||
private function isNullableTypeSubType(Type $currentType, Type $inferedType): bool
|
||||
{
|
||||
if (! $currentType instanceof UnionType) {
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\TypeAnalyzer;
|
||||
|
||||
use PHPStan\Type\CallableType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
|
||||
final class ObjectTypeComparator
|
||||
{
|
||||
/**
|
||||
* E.g. current E, new type A, E extends A → true
|
||||
* Also for closure/callable, iterable/Traversable/Iterator/Generator
|
||||
*/
|
||||
public function isCurrentObjectTypeSubType(Type $currentType, Type $newType): bool
|
||||
{
|
||||
if ($this->isBothCallable($currentType, $newType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isBothIterableIteratorGeneratorTraversable($currentType, $newType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $currentType instanceof ObjectType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $newType instanceof ObjectType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_a($currentType->getClassName(), $newType->getClassName(), true);
|
||||
}
|
||||
|
||||
private function isClosure(Type $type): bool
|
||||
{
|
||||
return $type instanceof ObjectType && $type->getClassName() === 'Closure';
|
||||
}
|
||||
|
||||
private function isBothCallable(Type $currentType, Type $newType): bool
|
||||
{
|
||||
if ($currentType instanceof CallableType && $this->isClosure($newType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $newType instanceof CallableType && $this->isClosure($currentType);
|
||||
}
|
||||
|
||||
private function isBothIterableIteratorGeneratorTraversable(Type $currentType, Type $newType): bool
|
||||
{
|
||||
if (! $currentType instanceof ObjectType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $newType instanceof ObjectType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($currentType->getClassName() === 'iterable' && $this->isTraversableGeneratorIterator($newType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($newType->getClassName() !== 'iterable') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isTraversableGeneratorIterator($currentType);
|
||||
}
|
||||
|
||||
private function isTraversableGeneratorIterator(ObjectType $objectType): bool
|
||||
{
|
||||
return in_array($objectType->getClassName(), ['Traversable', 'Generator', 'Iterator'], true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\TypeInferer\ParamTypeInferer;
|
||||
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ParamTypeInfererInterface;
|
||||
|
||||
final class KnownArrayParamTypeInferer implements ParamTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var ReflectionProvider
|
||||
*/
|
||||
private $reflectionProvider;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver, ReflectionProvider $reflectionProvider)
|
||||
{
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
}
|
||||
|
||||
public function inferParam(Param $param): Type
|
||||
{
|
||||
$classLike = $param->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if (! $classLike instanceof Class_) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$className = $this->nodeNameResolver->getName($classLike);
|
||||
if (! $className) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
if (! $this->reflectionProvider->hasClass($className)) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$classReflection = $this->reflectionProvider->getClass($className);
|
||||
$paramName = $this->nodeNameResolver->getName($param);
|
||||
|
||||
// @todo create map later
|
||||
if ($paramName === 'configs' && $classReflection->isSubclassOf(
|
||||
'Symfony\Component\DependencyInjection\Extension\Extension'
|
||||
)) {
|
||||
return new ArrayType(new MixedType(), new StringType());
|
||||
}
|
||||
|
||||
return new MixedType();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\TypeInferer\ParamTypeInferer;
|
||||
|
||||
use PhpParser\Node\Param;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ParamTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\TypeInferer\SplArrayFixedTypeNarrower;
|
||||
|
||||
final class SplFixedArrayParamTypeInferer implements ParamTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @var SplArrayFixedTypeNarrower
|
||||
*/
|
||||
private $splArrayFixedTypeNarrower;
|
||||
|
||||
/**
|
||||
* @var NodeTypeResolver
|
||||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
public function __construct(
|
||||
SplArrayFixedTypeNarrower $splArrayFixedTypeNarrower,
|
||||
NodeTypeResolver $nodeTypeResolver
|
||||
) {
|
||||
$this->splArrayFixedTypeNarrower = $splArrayFixedTypeNarrower;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
}
|
||||
|
||||
public function inferParam(Param $param): Type
|
||||
{
|
||||
if ($param->type === null) {
|
||||
return new MixedType();
|
||||
}
|
||||
|
||||
$paramType = $this->nodeTypeResolver->resolve($param->type);
|
||||
return $this->splArrayFixedTypeNarrower->narrow($paramType);
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ use Rector\NodeTypeResolver\NodeTypeResolver;
|
|||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
|
||||
use Rector\TypeDeclaration\TypeInferer\SilentVoidResolver;
|
||||
use Rector\TypeDeclaration\TypeInferer\SplArrayFixedTypeNarrower;
|
||||
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
|
||||
|
||||
final class ReturnedNodesReturnTypeInferer implements ReturnTypeInfererInterface
|
||||
|
@ -53,16 +54,23 @@ final class ReturnedNodesReturnTypeInferer implements ReturnTypeInfererInterface
|
|||
*/
|
||||
private $typeFactory;
|
||||
|
||||
/**
|
||||
* @var SplArrayFixedTypeNarrower
|
||||
*/
|
||||
private $splArrayFixedTypeNarrower;
|
||||
|
||||
public function __construct(
|
||||
SilentVoidResolver $silentVoidResolver,
|
||||
NodeTypeResolver $nodeTypeResolver,
|
||||
SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
|
||||
TypeFactory $typeFactory
|
||||
TypeFactory $typeFactory,
|
||||
SplArrayFixedTypeNarrower $splArrayFixedTypeNarrower
|
||||
) {
|
||||
$this->silentVoidResolver = $silentVoidResolver;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
|
||||
$this->typeFactory = $typeFactory;
|
||||
$this->splArrayFixedTypeNarrower = $splArrayFixedTypeNarrower;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,13 +98,10 @@ final class ReturnedNodesReturnTypeInferer implements ReturnTypeInfererInterface
|
|||
$hasSilentVoid = $this->silentVoidResolver->hasSilentVoid($functionLike);
|
||||
|
||||
foreach ($localReturnNodes as $localReturnNode) {
|
||||
if ($localReturnNode->expr === null) {
|
||||
$this->types[] = new VoidType();
|
||||
continue;
|
||||
}
|
||||
$returnedExprType = $this->nodeTypeResolver->getStaticType($localReturnNode);
|
||||
$returnedExprType = $this->splArrayFixedTypeNarrower->narrow($returnedExprType);
|
||||
|
||||
$exprType = $this->nodeTypeResolver->getStaticType($localReturnNode->expr);
|
||||
$this->types[] = $exprType;
|
||||
$this->types[] = $returnedExprType;
|
||||
}
|
||||
|
||||
if ($hasSilentVoid) {
|
||||
|
|
|
@ -13,25 +13,16 @@ use PhpParser\Node\FunctionLike;
|
|||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\IterableType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Core\Php\PhpVersionProvider;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedGenericObjectType;
|
||||
use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface;
|
||||
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
|
||||
|
||||
final class YieldNodesReturnTypeInferer implements ReturnTypeInfererInterface
|
||||
{
|
||||
/**
|
||||
* @var PhpVersionProvider
|
||||
*/
|
||||
private $phpVersionProvider;
|
||||
|
||||
/**
|
||||
* @var NodeTypeResolver
|
||||
*/
|
||||
|
@ -48,12 +39,10 @@ final class YieldNodesReturnTypeInferer implements ReturnTypeInfererInterface
|
|||
private $simpleCallableNodeTraverser;
|
||||
|
||||
public function __construct(
|
||||
PhpVersionProvider $phpVersionProvider,
|
||||
NodeTypeResolver $nodeTypeResolver,
|
||||
TypeFactory $typeFactory,
|
||||
SimpleCallableNodeTraverser $simpleCallableNodeTraverser
|
||||
) {
|
||||
$this->phpVersionProvider = $phpVersionProvider;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
$this->typeFactory = $typeFactory;
|
||||
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
|
||||
|
@ -77,18 +66,11 @@ final class YieldNodesReturnTypeInferer implements ReturnTypeInfererInterface
|
|||
continue;
|
||||
}
|
||||
|
||||
$yieldValueStaticType = $this->nodeTypeResolver->getStaticType($value);
|
||||
$types[] = new ArrayType(new MixedType(), $yieldValueStaticType);
|
||||
$types[] = $this->nodeTypeResolver->getStaticType($value);
|
||||
}
|
||||
|
||||
if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::ITERABLE_TYPE)) {
|
||||
// @see https://www.php.net/manual/en/language.types.iterable.php
|
||||
$types[] = new IterableType(new MixedType(), new MixedType());
|
||||
} else {
|
||||
$types[] = new ObjectType('Iterator');
|
||||
}
|
||||
|
||||
return $this->typeFactory->createMixedPassedOrUnionType($types);
|
||||
$types = $this->typeFactory->createMixedPassedOrUnionType($types);
|
||||
return new FullyQualifiedGenericObjectType('Iterator', [$types]);
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\TypeInferer;
|
||||
|
||||
use PHPStan\Type\Generic\GenericObjectType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
|
||||
final class SplArrayFixedTypeNarrower
|
||||
{
|
||||
public function narrow(Type $paramType): Type
|
||||
{
|
||||
if ($paramType->isSuperTypeOf(new ObjectType('SplArrayFixed'))->no()) {
|
||||
return $paramType;
|
||||
}
|
||||
|
||||
if (! $paramType instanceof TypeWithClassName) {
|
||||
return $paramType;
|
||||
}
|
||||
|
||||
if ($paramType instanceof GenericObjectType) {
|
||||
return $paramType;
|
||||
}
|
||||
|
||||
$types = [];
|
||||
|
||||
if ($paramType->getClassName() === 'PhpCsFixer\Tokenizer\Tokens') {
|
||||
$types[] = new ObjectType('PhpCsFixer\Tokenizer\Token');
|
||||
}
|
||||
|
||||
if ($paramType->getClassName() === 'PhpCsFixer\Doctrine\Annotation\Tokens') {
|
||||
$types[] = new ObjectType('PhpCsFixer\Doctrine\Annotation\Token');
|
||||
}
|
||||
|
||||
if ($types === []) {
|
||||
return $paramType;
|
||||
}
|
||||
|
||||
return new GenericObjectType($paramType->getClassName(), $types);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
|
||||
final class KnownParamArray extends Extension
|
||||
{
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
|
||||
final class KnownParamArray extends Extension
|
||||
{
|
||||
/**
|
||||
* @param string[] $configs
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
final class SkipAlreadyParam
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $rectorClass;
|
||||
|
||||
public function setRectorClass(string $rectorClass)
|
||||
{
|
||||
$this->rectorClass = $rectorClass;
|
||||
}
|
||||
|
||||
public function getRectorClass(): ?string
|
||||
{
|
||||
return $this->rectorClass;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
use PhpParser\Node;
|
||||
|
||||
final class SkipSetNode
|
||||
{
|
||||
/**
|
||||
* @var Node|null
|
||||
*/
|
||||
private $node;
|
||||
|
||||
public function setNode(Node $node): void
|
||||
{
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
public function getNode(): ?Node
|
||||
{
|
||||
return $this->node;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class TokenPhpCsFixer
|
||||
{
|
||||
public function __construct(Tokens $tokens)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class TokenPhpCsFixer
|
||||
{
|
||||
/**
|
||||
* @param \PhpCsFixer\Tokenizer\Tokens<\PhpCsFixer\Tokenizer\Token> $tokens
|
||||
*/
|
||||
public function __construct(Tokens $tokens)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
use PhpCsFixer\Doctrine\Annotation\Tokens;
|
||||
|
||||
final class TokenPhpCsFixerDoctrine
|
||||
{
|
||||
public function __construct(Tokens $tokens)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
use PhpCsFixer\Doctrine\Annotation\Tokens;
|
||||
|
||||
final class TokenPhpCsFixerDoctrine
|
||||
{
|
||||
/**
|
||||
* @param \PhpCsFixer\Doctrine\Annotation\Tokens<\PhpCsFixer\Doctrine\Annotation\Token> $tokens
|
||||
*/
|
||||
public function __construct(Tokens $tokens)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -36,7 +36,7 @@ final class RespectArrayReturnType
|
|||
private $items = [];
|
||||
|
||||
/**
|
||||
* @return mixed[]|array<string, int|mixed[]>
|
||||
* @return mixed[]|array<string, mixed[]>|array<string, int>
|
||||
*/
|
||||
public function process($message): array
|
||||
{
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class ReturnTokens
|
||||
{
|
||||
public function createTokens()
|
||||
{
|
||||
return new Tokens();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use PhpCsFixer\Tokenizer\Tokens;
|
||||
|
||||
final class ReturnTokens
|
||||
{
|
||||
/**
|
||||
* @return \PhpCsFixer\Tokenizer\Tokens<\PhpCsFixer\Tokenizer\Token>
|
||||
*/
|
||||
public function createTokens()
|
||||
{
|
||||
return new Tokens();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
final class ReturnYieldIterator
|
||||
{
|
||||
public function someMethod()
|
||||
{
|
||||
yield ['test', 'test2'];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
final class ReturnYieldIterator
|
||||
{
|
||||
/**
|
||||
* @return \Iterator<string[]>
|
||||
*/
|
||||
public function someMethod()
|
||||
{
|
||||
yield ['test', 'test2'];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use Iterator;
|
||||
|
||||
final class SkipBareIterator
|
||||
{
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
private function yieldFilesFromDirectory(string $string): Iterator
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Parser as NikicParser;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
final class SkipEmptyArray
|
||||
{
|
||||
/**
|
||||
* @var Stmt[][]
|
||||
*/
|
||||
private $nodesByFile = [];
|
||||
|
||||
/**
|
||||
* @var NikicParser
|
||||
*/
|
||||
private $nikicParser;
|
||||
|
||||
/**
|
||||
* @var SmartFileSystem
|
||||
*/
|
||||
private $smartFileSystem;
|
||||
|
||||
public function __construct(NikicParser $nikicParser, SmartFileSystem $smartFileSystem)
|
||||
{
|
||||
$this->nikicParser = $nikicParser;
|
||||
$this->smartFileSystem = $smartFileSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Stmt[]
|
||||
*/
|
||||
public function parseFileInfo(SmartFileInfo $smartFileInfo): array
|
||||
{
|
||||
$fileRealPath = $smartFileInfo->getRealPath();
|
||||
|
||||
if (isset($this->nodesByFile[$fileRealPath])) {
|
||||
return $this->nodesByFile[$fileRealPath];
|
||||
}
|
||||
|
||||
$fileContent = $this->smartFileSystem->readFile($fileRealPath);
|
||||
|
||||
$nodes = $this->nikicParser->parse($fileContent);
|
||||
if ($nodes === null) {
|
||||
$this->nodesByFile[$fileRealPath] = [];
|
||||
} else {
|
||||
$this->nodesByFile[$fileRealPath] = [];
|
||||
}
|
||||
|
||||
return $this->nodesByFile[$fileRealPath];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class SkipReturnArrayMerge
|
||||
{
|
||||
/**
|
||||
* @var array<string, SmartFileInfo[]>
|
||||
*/
|
||||
private $resolvedConfigFileInfos = [];
|
||||
|
||||
/**
|
||||
* @param SmartFileInfo[] $setFileInfos
|
||||
* @return SmartFileInfo[]
|
||||
*/
|
||||
public function run(SmartFileInfo $configFileInfo): array
|
||||
{
|
||||
$hash = sha1($configFileInfo->getRealPath());
|
||||
|
||||
if (isset($this->resolvedConfigFileInfos[$hash])) {
|
||||
return $this->resolvedConfigFileInfos[$hash];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use Iterator;
|
||||
|
||||
final class SkipTooManyIterator
|
||||
{
|
||||
public function provide(): Iterator
|
||||
{
|
||||
yield [
|
||||
__DIR__ . '/config/main_config_with_only_imports.php', [
|
||||
'old_2' => 'new_2',
|
||||
'old_1' => 'new_1',
|
||||
],
|
||||
];
|
||||
|
||||
yield [
|
||||
__DIR__ . '/config/one_set_with_own_rename.php', [
|
||||
'PHPUnit_Framework_MockObject_Stub' => 'PHPUnit\Framework\MockObject\Stub',
|
||||
'PHPUnit_Framework_MockObject_Stub_Return' => 'PHPUnit\Framework\MockObject\Stub\ReturnStub',
|
||||
'PHPUnit_Framework_MockObject_MockObject' => 'PHPUnit\Framework\MockObject\MockObject',
|
||||
'Old' => 'New',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use Iterator;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class SomeIterator
|
||||
{
|
||||
public function someMethod(): Iterator
|
||||
{
|
||||
return self::someIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Iterator<array<int, SmartFileInfo>>
|
||||
*/
|
||||
public static function someIterator(): Iterator
|
||||
{
|
||||
yield [100 => new SmartFileInfo('...')];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use Iterator;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class SomeIterator
|
||||
{
|
||||
/**
|
||||
* @return Iterator<mixed, \Symplify\SmartFileSystem\SmartFileInfo[]>
|
||||
*/
|
||||
public function someMethod(): Iterator
|
||||
{
|
||||
return self::someIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Iterator<array<int, SmartFileInfo>>
|
||||
*/
|
||||
public static function someIterator(): Iterator
|
||||
{
|
||||
yield [100 => new SmartFileInfo('...')];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use Iterator;
|
||||
use Rector\FileSystemRector\ValueObject\AddedFileWithContent;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
final class UniqueReturnIterator
|
||||
{
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$smartFileSystem = new SmartFileSystem();
|
||||
|
||||
yield [
|
||||
new SmartFileInfo(__DIR__ . '/Source/Entity/RandomInterface.php'),
|
||||
new AddedFileWithContent(
|
||||
'/Source/Contract/RandomInterface.php',
|
||||
$smartFileSystem->readFile(__DIR__ . '/Expected/ExpectedRandomInterface.php')
|
||||
)
|
||||
];
|
||||
|
||||
yield [
|
||||
new SmartFileInfo(__DIR__ . '/Source/Control/ControlFactory.php'),
|
||||
new AddedFileWithContent(
|
||||
'/Source/Control/ControlFactory.php',
|
||||
$smartFileSystem->readFile(__DIR__ . '/Source/Control/ControlFactory.php')
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use Iterator;
|
||||
use Rector\FileSystemRector\ValueObject\AddedFileWithContent;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
final class UniqueReturnIterator
|
||||
{
|
||||
/**
|
||||
* @return \Iterator<\Rector\FileSystemRector\ValueObject\AddedFileWithContent[]|\Symplify\SmartFileSystem\SmartFileInfo[]>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
$smartFileSystem = new SmartFileSystem();
|
||||
|
||||
yield [
|
||||
new SmartFileInfo(__DIR__ . '/Source/Entity/RandomInterface.php'),
|
||||
new AddedFileWithContent(
|
||||
'/Source/Contract/RandomInterface.php',
|
||||
$smartFileSystem->readFile(__DIR__ . '/Expected/ExpectedRandomInterface.php')
|
||||
)
|
||||
];
|
||||
|
||||
yield [
|
||||
new SmartFileInfo(__DIR__ . '/Source/Control/ControlFactory.php'),
|
||||
new AddedFileWithContent(
|
||||
'/Source/Control/ControlFactory.php',
|
||||
$smartFileSystem->readFile(__DIR__ . '/Source/Control/ControlFactory.php')
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -20,7 +20,7 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeR
|
|||
class YieldStrings
|
||||
{
|
||||
/**
|
||||
* @return string[]
|
||||
* @return \Iterator<string>
|
||||
*/
|
||||
public function getValues(): iterable
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@ function iterableFunction($value)
|
|||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture\IterableFunction;
|
||||
|
||||
function iterableFunction($value): iterable
|
||||
function iterableFunction($value): \Iterator
|
||||
{
|
||||
yield 1;
|
||||
yield 2;
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
|
||||
|
||||
use PhpParser\NodeVisitor;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
final class KeepAnonymousExtendedClass
|
||||
{
|
||||
public function getNodeVisitor(): NodeVisitorAbstract
|
||||
{
|
||||
return new class extends NodeVisitorAbstract
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
|
@ -4,15 +4,6 @@ namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclaration
|
|||
|
||||
class KnownStaticNullable
|
||||
{
|
||||
public function getMoreItems()
|
||||
{
|
||||
if ((bool) rand(0, 100)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getStringNull()
|
||||
{
|
||||
/** @var string|null $value */
|
||||
|
@ -30,15 +21,6 @@ namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclaration
|
|||
|
||||
class KnownStaticNullable
|
||||
{
|
||||
public function getMoreItems(): ?array
|
||||
{
|
||||
if ((bool) rand(0, 100)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getStringNull(): ?string
|
||||
{
|
||||
/** @var string|null $value */
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
|
||||
|
||||
class KnownStaticNullableArray
|
||||
{
|
||||
public function getMoreItems()
|
||||
{
|
||||
if ((bool) rand(0, 100)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
|
||||
|
||||
class KnownStaticNullableArray
|
||||
{
|
||||
public function getMoreItems(): ?array
|
||||
{
|
||||
if ((bool) rand(0, 100)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
|
||||
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
final class OverrideAnonymousExtendedClass
|
||||
{
|
||||
public function getNodeVisitor(): NodeVisitorAbstract
|
||||
{
|
||||
return new class extends NodeVisitorAbstract
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
|
||||
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
final class OverrideAnonymousExtendedClass
|
||||
{
|
||||
public function getNodeVisitor(): object
|
||||
{
|
||||
return new class extends NodeVisitorAbstract
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
|
||||
|
||||
use Symplify\PhpConfigPrinter\HttpKernel\PhpConfigPrinterKernel;
|
||||
use Symplify\PhpConfigPrinter\Printer\SmartPhpConfigPrinter;
|
||||
|
||||
final class SkipContainerGet
|
||||
{
|
||||
public function create(): SmartPhpConfigPrinter
|
||||
{
|
||||
$phpConfigPrinterKernel = new PhpConfigPrinterKernel('prod', true);
|
||||
$phpConfigPrinterKernel->setConfigs([__DIR__ . '/config/php-config-printer-config.php']);
|
||||
$phpConfigPrinterKernel->boot();
|
||||
$container = $phpConfigPrinterKernel->getContainer();
|
||||
|
||||
return $container->get(SmartPhpConfigPrinter::class);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
|
||||
|
||||
use Iterator;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\ArrayItem;
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
|
||||
final class SkipIteratorAlready
|
||||
{
|
||||
/**
|
||||
* @return Iterator<int[][]|Array_[]<string, int>[]|Array_[]>
|
||||
*/
|
||||
public function provideDataForArray(): Iterator
|
||||
{
|
||||
$array = new Array_();
|
||||
$array->items[] = new ArrayItem(new LNumber(1));
|
||||
yield [[1], $array];
|
||||
|
||||
$array = new Array_();
|
||||
$array->items[] = new ArrayItem(new LNumber(1), new String_('a'));
|
||||
yield [[
|
||||
'a' => 1,
|
||||
], $array];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
|
||||
|
||||
use Symfony\Component\Console\Input\StringInput;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesCaller;
|
||||
|
||||
final class SkipMagicCaller
|
||||
{
|
||||
public function run(): array
|
||||
{
|
||||
$privatesCaller = new PrivatesCaller();
|
||||
return $privatesCaller->callPrivateMethod(new StringInput(''), 'tokenize', []);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\PrettyPrinter\Standard;
|
||||
|
||||
final class SkipParentStandard extends Standard
|
||||
{
|
||||
protected function pExpr_Cast_String(String_ $node): string
|
||||
{
|
||||
return parent::pExpr_Cast_String($node);
|
||||
}
|
||||
}
|
|
@ -1,16 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture\Inheritance;
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\FixtureInheritance;
|
||||
|
||||
class A {
|
||||
class A
|
||||
{
|
||||
/** @return A */
|
||||
public function test() {
|
||||
public function test()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
public function test() {
|
||||
class B extends A
|
||||
{
|
||||
public function test()
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -19,16 +23,20 @@ class B extends A {
|
|||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture\Inheritance;
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\FixtureInheritance;
|
||||
|
||||
class A {
|
||||
public function test(): self {
|
||||
class A
|
||||
{
|
||||
public function test(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
public function test(): \Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\Fixture\Inheritance\A {
|
||||
class B extends A
|
||||
{
|
||||
public function test(): \Rector\TypeDeclaration\Tests\Rector\FunctionLike\ReturnTypeDeclarationRector\FixtureInheritance\A
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\Property\CompleteVarDocTypePropertyRector\Fixture;
|
||||
|
||||
final class CallableType
|
||||
final class SomeCallableType
|
||||
{
|
||||
private $code;
|
||||
/**
|
||||
|
@ -27,10 +27,10 @@ final class CallableType
|
|||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\Property\CompleteVarDocTypePropertyRector\Fixture;
|
||||
|
||||
final class CallableType
|
||||
final class SomeCallableType
|
||||
{
|
||||
/**
|
||||
* @var null|callable
|
||||
* @var callable|null
|
||||
*/
|
||||
private $code;
|
||||
/**
|
|
@ -623,7 +623,7 @@ class Command
|
|||
*/
|
||||
private $applicationDefinitionMergedWithArgs = false;
|
||||
/**
|
||||
* @var null|callable
|
||||
* @var callable|null
|
||||
*/
|
||||
private $code;
|
||||
/**
|
||||
|
|
|
@ -46,9 +46,12 @@ final class RectorConfigsResolver
|
|||
}
|
||||
|
||||
$setFileInfos = $this->setAwareConfigResolver->resolveFromParameterSetsFromConfigFiles([$configFileInfo]);
|
||||
|
||||
/** @var SmartFileInfo[] $configFileInfos */
|
||||
$configFileInfos = array_merge([$configFileInfo], $setFileInfos);
|
||||
|
||||
$this->resolvedConfigFileInfos[$hash] = $configFileInfos;
|
||||
|
||||
return $configFileInfos;
|
||||
}
|
||||
|
||||
|
|
|
@ -111,9 +111,11 @@ final class IfManipulator
|
|||
if ($if->stmts === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $if->cond instanceof NotIdentical) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isNotIdenticalNullCompare($if->cond)) {
|
||||
return null;
|
||||
}
|
||||
|
@ -122,11 +124,13 @@ final class IfManipulator
|
|||
if (! $insideIfNode instanceof Expression) {
|
||||
return null;
|
||||
}
|
||||
if (! $insideIfNode->expr instanceof Assign) {
|
||||
|
||||
$assignedExpr = $insideIfNode->expr;
|
||||
if (! $assignedExpr instanceof Assign) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $insideIfNode->expr;
|
||||
return $assignedExpr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -45,8 +45,13 @@ final class Parser
|
|||
}
|
||||
|
||||
$fileContent = $this->smartFileSystem->readFile($fileRealPath);
|
||||
$this->nodesByFile[$fileRealPath] = (array) $this->nikicParser->parse($fileContent);
|
||||
|
||||
$nodes = $this->nikicParser->parse($fileContent);
|
||||
if ($nodes === null) {
|
||||
$nodes = [];
|
||||
}
|
||||
|
||||
$this->nodesByFile[$fileRealPath] = $nodes;
|
||||
return $this->nodesByFile[$fileRealPath];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,4 +15,9 @@ class EntityRepository
|
|||
* @var EntityManager
|
||||
*/
|
||||
protected $_em;
|
||||
|
||||
public function createQueryBuilder(): QueryBuilder
|
||||
{
|
||||
return new QueryBuilder();
|
||||
}
|
||||
}
|
||||
|
|
14
stubs/Doctrine/ORM/QueryBuilder.php
Normal file
14
stubs/Doctrine/ORM/QueryBuilder.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
if (class_exists('Doctrine\ORM\QueryBuilder')) {
|
||||
return;
|
||||
}
|
||||
|
||||
final class QueryBuilder
|
||||
{
|
||||
|
||||
}
|
|
@ -38,6 +38,9 @@ final class NodeFactoryTest extends AbstractKernelTestCase
|
|||
$this->assertEquals($expectedArrayNode, $arrayNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<int[][]|array<string, int>|Array_[]>
|
||||
*/
|
||||
public function provideDataForArray(): Iterator
|
||||
{
|
||||
$array = new Array_();
|
||||
|
|
|
@ -15,6 +15,7 @@ final class StaticInstanceOfTest extends TestCase
|
|||
/**
|
||||
* @dataProvider provideIsOneOf()
|
||||
* @param class-string[] $array
|
||||
* @param DateTime|stdClass|null $object
|
||||
*/
|
||||
public function testIsOneOf(?object $object, array $array, bool $expected): void
|
||||
{
|
||||
|
|
|
@ -53,6 +53,9 @@ final class StaticRectorStringsTest extends TestCase
|
|||
$this->assertSame($expected, StaticRectorStrings::underscoreToCamelCase($content));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<string[]>
|
||||
*/
|
||||
public function provideDataForUnderscoreToCamelCase(): Iterator
|
||||
{
|
||||
yield ['simple_test', 'simpleTest'];
|
||||
|
@ -66,6 +69,9 @@ final class StaticRectorStringsTest extends TestCase
|
|||
$this->assertSame($expected, StaticRectorStrings::underscoreToPascalCase($content));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<string[]>
|
||||
*/
|
||||
public function provideDataForUnderscoreToPascalCase(): Iterator
|
||||
{
|
||||
yield ['simple_test', 'SimpleTest'];
|
||||
|
|
Loading…
Reference in New Issue
Block a user