Skip to content

Commit 03ea9b2

Browse files
authored
[ReturnTypeInferer] Drop this/static docblock type check on ReturnTypeInferer (#6270)
* [ReturnTypeInferer] Drop this/static type check on ReturnTypeInferer * use phpstan TypeCombinator
1 parent 1eb04d6 commit 03ea9b2

File tree

3 files changed

+5
-343
lines changed

3 files changed

+5
-343
lines changed

rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php

Lines changed: 0 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -4,62 +4,11 @@
44

55
namespace Rector\TypeDeclaration\TypeAnalyzer;
66

7-
use PHPStan\Reflection\ReflectionProvider;
8-
use PHPStan\Type\ArrayType;
9-
use PHPStan\Type\ClassStringType;
10-
use PHPStan\Type\Constant\ConstantIntegerType;
11-
use PHPStan\Type\Constant\ConstantStringType;
127
use PHPStan\Type\Generic\GenericClassStringType;
13-
use PHPStan\Type\MixedType;
14-
use PHPStan\Type\ObjectType;
15-
use PHPStan\Type\StringType;
16-
use PHPStan\Type\Type;
17-
use PHPStan\Type\TypeTraverser;
188
use PHPStan\Type\UnionType;
19-
use Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeAnalyzer;
20-
use Rector\TypeDeclaration\NodeTypeAnalyzer\DetailedTypeAnalyzer;
219

2210
final readonly class GenericClassStringTypeNormalizer
2311
{
24-
public function __construct(
25-
private ReflectionProvider $reflectionProvider,
26-
private DetailedTypeAnalyzer $detailedTypeAnalyzer,
27-
private UnionTypeAnalyzer $unionTypeAnalyzer
28-
) {
29-
}
30-
31-
public function normalize(Type $type): ArrayType | UnionType | Type
32-
{
33-
$type = TypeTraverser::map($type, function (Type $type, $callback): Type {
34-
if (! $type instanceof ConstantStringType) {
35-
return $callback($type);
36-
}
37-
38-
$value = $type->getValue();
39-
40-
// skip string that look like classe
41-
if ($value === 'error') {
42-
return $callback($type);
43-
}
44-
45-
if (! $this->reflectionProvider->hasClass($value)) {
46-
return $callback($type);
47-
}
48-
49-
return $this->resolveStringType($value);
50-
});
51-
52-
if ($type instanceof UnionType && ! $this->unionTypeAnalyzer->isNullable($type, true)) {
53-
return $this->resolveClassStringInUnionType($type);
54-
}
55-
56-
if ($type instanceof ArrayType && $type->getKeyType() instanceof UnionType) {
57-
return $this->resolveArrayTypeWithUnionKeyType($type);
58-
}
59-
60-
return $type;
61-
}
62-
6312
public function isAllGenericClassStringType(UnionType $unionType): bool
6413
{
6514
foreach ($unionType->getTypes() as $type) {
@@ -70,69 +19,4 @@ public function isAllGenericClassStringType(UnionType $unionType): bool
7019

7120
return true;
7221
}
73-
74-
private function resolveArrayTypeWithUnionKeyType(ArrayType $arrayType): ArrayType
75-
{
76-
$itemType = $arrayType->getItemType();
77-
78-
if (! $itemType instanceof UnionType) {
79-
return $arrayType;
80-
}
81-
82-
$keyType = $arrayType->getKeyType();
83-
$isAllGenericClassStringType = $this->isAllGenericClassStringType($itemType);
84-
85-
if (! $isAllGenericClassStringType) {
86-
return new ArrayType($keyType, new MixedType());
87-
}
88-
89-
if ($this->detailedTypeAnalyzer->isTooDetailed($itemType)) {
90-
return new ArrayType($keyType, new ClassStringType());
91-
}
92-
93-
return $arrayType;
94-
}
95-
96-
private function resolveClassStringInUnionType(UnionType $type): UnionType | ArrayType
97-
{
98-
$unionTypes = $type->getTypes();
99-
100-
foreach ($unionTypes as $unionType) {
101-
if (! $unionType instanceof ArrayType) {
102-
return $type;
103-
}
104-
105-
$keyType = $unionType->getKeyType();
106-
$itemType = $unionType->getItemType();
107-
108-
if (! $keyType instanceof MixedType && ! $keyType instanceof ConstantIntegerType) {
109-
return $type;
110-
}
111-
112-
if ($itemType instanceof ArrayType) {
113-
$arrayType = new ArrayType(new MixedType(), new MixedType());
114-
return new ArrayType($keyType, $arrayType);
115-
}
116-
117-
if (! $itemType instanceof ClassStringType) {
118-
return $type;
119-
}
120-
}
121-
122-
return new ArrayType(new MixedType(), new ClassStringType());
123-
}
124-
125-
private function resolveStringType(string $value): GenericClassStringType | StringType
126-
{
127-
$classReflection = $this->reflectionProvider->getClass($value);
128-
if ($classReflection->isBuiltin()) {
129-
return new GenericClassStringType(new ObjectType($value));
130-
}
131-
132-
if (str_contains($value, '\\')) {
133-
return new GenericClassStringType(new ObjectType($value));
134-
}
135-
136-
return new StringType();
137-
}
13822
}

rules/TypeDeclaration/TypeInferer/ReturnTypeInferer.php

Lines changed: 2 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,13 @@
44

55
namespace Rector\TypeDeclaration\TypeInferer;
66

7-
use PhpParser\Node;
8-
use PhpParser\Node\Expr;
97
use PhpParser\Node\Expr\Closure;
10-
use PhpParser\Node\Expr\Yield_;
11-
use PhpParser\Node\FunctionLike;
128
use PhpParser\Node\Stmt\ClassMethod;
139
use PhpParser\Node\Stmt\Function_;
14-
use PhpParser\Node\Stmt\Return_;
15-
use PHPStan\Reflection\ClassReflection;
16-
use PHPStan\Reflection\ReflectionProvider;
17-
use PHPStan\Type\BenevolentUnionType;
1810
use PHPStan\Type\MixedType;
19-
use PHPStan\Type\ThisType;
2011
use PHPStan\Type\Type;
21-
use PHPStan\Type\TypeWithClassName;
22-
use PHPStan\Type\UnionType;
23-
use Rector\Enum\ObjectReference;
24-
use Rector\Exception\ShouldNotHappenException;
25-
use Rector\NodeTypeResolver\NodeTypeResolver;
26-
use Rector\Php\PhpVersionProvider;
27-
use Rector\PhpParser\Node\BetterNodeFinder;
28-
use Rector\Reflection\ReflectionResolver;
29-
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
30-
use Rector\TypeDeclaration\TypeAnalyzer\GenericClassStringTypeNormalizer;
3112
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer\ReturnedNodesReturnTypeInfererTypeInferer;
3213
use Rector\TypeDeclaration\TypeNormalizer;
33-
use Rector\ValueObject\PhpVersionFeature;
3414

3515
/**
3616
* @internal
@@ -39,207 +19,17 @@
3919
{
4020
public function __construct(
4121
private TypeNormalizer $typeNormalizer,
42-
private ReturnedNodesReturnTypeInfererTypeInferer $returnedNodesReturnTypeInfererTypeInferer,
43-
private GenericClassStringTypeNormalizer $genericClassStringTypeNormalizer,
44-
private PhpVersionProvider $phpVersionProvider,
45-
private BetterNodeFinder $betterNodeFinder,
46-
private ReflectionResolver $reflectionResolver,
47-
private ReflectionProvider $reflectionProvider,
48-
private NodeTypeResolver $nodeTypeResolver
22+
private ReturnedNodesReturnTypeInfererTypeInferer $returnedNodesReturnTypeInfererTypeInferer
4923
) {
5024
}
5125

5226
public function inferFunctionLike(ClassMethod|Function_|Closure $functionLike): Type
5327
{
54-
$isSupportedStaticReturnType = $this->phpVersionProvider->isAtLeastPhpVersion(
55-
PhpVersionFeature::STATIC_RETURN_TYPE
56-
);
57-
5828
$originalType = $this->returnedNodesReturnTypeInfererTypeInferer->inferFunctionLike($functionLike);
5929
if ($originalType instanceof MixedType) {
6030
return new MixedType();
6131
}
6232

63-
$type = $this->typeNormalizer->normalizeArrayTypeAndArrayNever($originalType);
64-
65-
// in case of void, check return type of children methods
66-
if ($type instanceof MixedType) {
67-
return new MixedType();
68-
}
69-
70-
$type = $this->verifyStaticType($type, $isSupportedStaticReturnType);
71-
if (! $type instanceof Type) {
72-
return new MixedType();
73-
}
74-
75-
$type = $this->verifyThisType($type, $functionLike);
76-
77-
// normalize ConstStringType to ClassStringType
78-
$resolvedType = $this->genericClassStringTypeNormalizer->normalize($type);
79-
return $this->resolveTypeWithVoidHandling($functionLike, $resolvedType);
80-
}
81-
82-
private function verifyStaticType(Type $type, bool $isSupportedStaticReturnType): ?Type
83-
{
84-
if ($this->isStaticType($type)) {
85-
/** @var TypeWithClassName $type */
86-
return $this->resolveStaticType($isSupportedStaticReturnType, $type);
87-
}
88-
89-
if ($type instanceof UnionType) {
90-
return $this->resolveUnionStaticTypes($type, $isSupportedStaticReturnType);
91-
}
92-
93-
return $type;
94-
}
95-
96-
private function verifyThisType(Type $type, FunctionLike $functionLike): Type
97-
{
98-
if (! $type instanceof ThisType) {
99-
return $type;
100-
}
101-
102-
$classReflection = $this->reflectionResolver->resolveClassReflection($functionLike);
103-
$objectType = $type->getStaticObjectType();
104-
$objectTypeClassName = $objectType->getClassName();
105-
106-
if (! $classReflection instanceof ClassReflection || ! $classReflection->isClass()) {
107-
return $type;
108-
}
109-
110-
if ($classReflection->getName() === $objectTypeClassName) {
111-
return $type;
112-
}
113-
114-
return new MixedType();
115-
}
116-
117-
private function resolveTypeWithVoidHandling(
118-
ClassMethod|Function_|Closure $functionLike,
119-
Type $resolvedType
120-
): Type {
121-
if ($resolvedType->isVoid()->yes()) {
122-
$hasReturnValue = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped(
123-
$functionLike,
124-
static function (Node $subNode): bool {
125-
if (! $subNode instanceof Return_) {
126-
// yield return is handled on speicific rule: AddReturnTypeDeclarationFromYieldsRector
127-
return $subNode instanceof Yield_;
128-
}
129-
130-
return $subNode->expr instanceof Expr;
131-
}
132-
);
133-
134-
if ($hasReturnValue) {
135-
return new MixedType();
136-
}
137-
}
138-
139-
if ($resolvedType instanceof UnionType) {
140-
$benevolentUnionTypeIntegerType = $this->resolveBenevolentUnionTypeInteger($functionLike, $resolvedType);
141-
if ($benevolentUnionTypeIntegerType->isInteger()->yes()) {
142-
return $benevolentUnionTypeIntegerType;
143-
}
144-
}
145-
146-
return $resolvedType;
147-
}
148-
149-
private function resolveBenevolentUnionTypeInteger(
150-
ClassMethod|Function_|Closure $functionLike,
151-
UnionType $unionType
152-
): Type {
153-
$types = $unionType->getTypes();
154-
$countTypes = count($types);
155-
156-
if ($countTypes !== 2) {
157-
return $unionType;
158-
}
159-
160-
if (! ($types[0]->isInteger()->yes() && $types[1]->isString()->yes())) {
161-
return $unionType;
162-
}
163-
164-
$returns = $this->betterNodeFinder->findReturnsScoped($functionLike);
165-
$returnsWithExpr = array_filter(
166-
$returns,
167-
static fn (Return_ $return): bool => $return->expr instanceof Expr
168-
);
169-
170-
if ($returns !== $returnsWithExpr) {
171-
return $unionType;
172-
}
173-
174-
if ($returnsWithExpr === []) {
175-
return $unionType;
176-
}
177-
178-
foreach ($returnsWithExpr as $returnWithExpr) {
179-
/** @var Expr $expr */
180-
$expr = $returnWithExpr->expr;
181-
$type = $this->nodeTypeResolver->getNativeType($expr);
182-
183-
if (! $type instanceof BenevolentUnionType) {
184-
return $unionType;
185-
}
186-
}
187-
188-
return $types[0];
189-
}
190-
191-
private function isStaticType(Type $type): bool
192-
{
193-
if (! $type instanceof TypeWithClassName) {
194-
return false;
195-
}
196-
197-
return $type->getClassName() === ObjectReference::STATIC;
198-
}
199-
200-
private function resolveUnionStaticTypes(UnionType $unionType, bool $isSupportedStaticReturnType): UnionType|null
201-
{
202-
$resolvedTypes = [];
203-
$hasStatic = false;
204-
205-
foreach ($unionType->getTypes() as $unionedType) {
206-
if ($this->isStaticType($unionedType)) {
207-
/** @var FullyQualifiedObjectType $unionedType */
208-
$classReflection = $this->reflectionProvider->getClass($unionedType->getClassName());
209-
210-
$resolvedTypes[] = new ThisType($classReflection);
211-
$hasStatic = true;
212-
continue;
213-
}
214-
215-
$resolvedTypes[] = $unionedType;
216-
}
217-
218-
if (! $hasStatic) {
219-
return $unionType;
220-
}
221-
222-
// has static, but it is not supported
223-
if (! $isSupportedStaticReturnType) {
224-
return null;
225-
}
226-
227-
return new UnionType($resolvedTypes);
228-
}
229-
230-
private function resolveStaticType(
231-
bool $isSupportedStaticReturnType,
232-
TypeWithClassName $typeWithClassName
233-
): ?ThisType {
234-
if (! $isSupportedStaticReturnType) {
235-
return null;
236-
}
237-
238-
$classReflection = $typeWithClassName->getClassReflection();
239-
if (! $classReflection instanceof ClassReflection) {
240-
throw new ShouldNotHappenException();
241-
}
242-
243-
return new ThisType($classReflection);
33+
return $this->typeNormalizer->normalizeArrayTypeAndArrayNever($originalType);
24434
}
24535
}

0 commit comments

Comments
 (0)