Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,36 @@ parameters:
count: 1
path: src/Analyser/RuleErrorTransformer.php

-
rawMessage: Doing instanceof PHPStan\Type\IntersectionType is error-prone and deprecated.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ondrejmirtes I wonder why this errors show up now, but it seems they were not reported when 1:1 the same code was part of MutatingScope class. I could not find ignore-error rules or something which would swallow them either.


besides that, I think this PR is ready to merge.
I would do more *Traverser-extractions in separate PRs, as we have too many of them.

identifier: phpstanApi.instanceofType
count: 1
path: src/Analyser/Traverser/CloneTypeTraverser.php

-
rawMessage: 'Doing instanceof PHPStan\Type\Constant\ConstantStringType is error-prone and deprecated. Use Type::getConstantStrings() instead.'
identifier: phpstanApi.instanceofType
count: 1
path: src/Analyser/Traverser/InstanceOfClassTypeTraverser.php

-
rawMessage: 'Doing instanceof PHPStan\Type\Generic\GenericClassStringType is error-prone and deprecated. Use Type::isClassStringType() and Type::getClassStringObjectType() instead.'
identifier: phpstanApi.instanceofType
count: 1
path: src/Analyser/Traverser/InstanceOfClassTypeTraverser.php

-
rawMessage: Doing instanceof PHPStan\Type\IntersectionType is error-prone and deprecated.
identifier: phpstanApi.instanceofType
count: 1
path: src/Analyser/Traverser/InstanceOfClassTypeTraverser.php

-
rawMessage: Doing instanceof PHPStan\Type\IntersectionType is error-prone and deprecated.
identifier: phpstanApi.instanceofType
count: 1
path: src/Analyser/Traverser/VoidToNullTraverser.php

-
rawMessage: 'Doing instanceof PHPStan\Type\ConstantScalarType is error-prone and deprecated. Use Type::isConstantScalarValue() or Type::getConstantScalarTypes() or Type::getConstantScalarValues() instead.'
identifier: phpstanApi.instanceofType
Expand Down Expand Up @@ -1689,6 +1719,12 @@ parameters:
count: 2
path: src/Type/StringType.php

-
rawMessage: 'Doing instanceof PHPStan\Type\CallableType is error-prone and deprecated. Use Type::isCallable() and Type::getCallableParametersAcceptors() instead.'
identifier: phpstanApi.instanceofType
count: 1
path: src/Type/Traverser/LateResolvableTraverser.php

-
rawMessage: 'Doing instanceof PHPStan\Type\ArrayType is error-prone and deprecated. Use Type::isArray() or Type::getArrays() instead.'
identifier: phpstanApi.instanceofType
Expand Down
95 changes: 17 additions & 78 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\NodeFinder;
use PHPStan\Analyser\Traverser\CloneTypeTraverser;
use PHPStan\Analyser\Traverser\ConstructorClassTemplateTraverser;
use PHPStan\Analyser\Traverser\GenericTypeTemplateTraverser;
use PHPStan\Analyser\Traverser\InstanceOfClassTypeTraverser;
use PHPStan\Analyser\Traverser\TransformStaticTypeTraverser;
use PHPStan\Analyser\Traverser\VoidToNullTraverser;
use PHPStan\Node\ExecutionEndNode;
use PHPStan\Node\Expr\AlwaysRememberedExpr;
use PHPStan\Node\Expr\ExistingArrayDimFetch;
Expand Down Expand Up @@ -107,7 +113,6 @@
use PHPStan\Type\ExpressionTypeResolverExtensionRegistry;
use PHPStan\Type\FloatType;
use PHPStan\Type\GeneralizePrecision;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\Generic\GenericStaticType;
use PHPStan\Type\Generic\TemplateType;
Expand Down Expand Up @@ -1139,23 +1144,9 @@ private function resolveType(string $exprString, Expr $node): Type
}
} else {
$classType = $this->getType($node->class);
$classType = TypeTraverser::map($classType, static function (Type $type, callable $traverse) use (&$uncertainty): Type {
if ($type instanceof UnionType || $type instanceof IntersectionType) {
return $traverse($type);
}
if ($type->getObjectClassNames() !== []) {
$uncertainty = true;
return $type;
}
if ($type instanceof GenericClassStringType) {
$uncertainty = true;
return $type->getGenericType();
}
if ($type instanceof ConstantStringType) {
return new ObjectType($type->getValue());
}
return new MixedType();
});
$traverser = new InstanceOfClassTypeTraverser();
$classType = TypeTraverser::map($classType, $traverser);
$uncertainty = $traverser->getUncertainty();
}

if ($classType->isSuperTypeOf(new MixedType())->yes()) {
Expand Down Expand Up @@ -1289,17 +1280,7 @@ private function resolveType(string $exprString, Expr $node): Type

if ($node instanceof Expr\Clone_) {
$cloneType = TypeCombinator::intersect($this->getType($node->expr), new ObjectWithoutClassType());

return TypeTraverser::map($cloneType, static function (Type $type, callable $traverse): Type {
if ($type instanceof UnionType || $type instanceof IntersectionType) {
return $traverse($type);
}
if ($type instanceof ThisType) {
return new StaticType($type->getClassReflection(), $type->getSubtractedType());
}

return $type;
});
return TypeTraverser::map($cloneType, new CloneTypeTraverser());
}

if ($node instanceof Node\Scalar\Int_) {
Expand Down Expand Up @@ -2571,17 +2552,7 @@ private function transformVoidToNull(Type $type, Node $node): Type
return $type;
}

return TypeTraverser::map($type, static function (Type $type, callable $traverse): Type {
if ($type instanceof UnionType || $type instanceof IntersectionType) {
return $traverse($type);
}

if ($type->isVoid()->yes()) {
return new NullType();
}

return $type;
});
return TypeTraverser::map($type, new VoidToNullTraverser());
}

/**
Expand Down Expand Up @@ -3246,21 +3217,7 @@ public function enterPropertyHook(

private function transformStaticType(Type $type): Type
{
return TypeTraverser::map($type, function (Type $type, callable $traverse): Type {
if (!$this->isInClass()) {
return $type;
}
if ($type instanceof StaticType) {
$classReflection = $this->getClassReflection();
$changedType = $type->changeBaseClass($classReflection);
if ($classReflection->isFinal() && !$type instanceof ThisType) {
$changedType = $changedType->getStaticObjectType();
}
return $traverse($changedType);
}

return $traverse($type);
});
return TypeTraverser::map($type, new TransformStaticTypeTraverser($this));
}

/**
Expand Down Expand Up @@ -5987,19 +5944,12 @@ private function exactInstantiation(New_ $node, Name $className): Type
$constructorVariant = $constructorVariants[0];
$classTemplateTypes = $classReflection->getTemplateTypeMap()->getTypes();
$originalClassTemplateTypes = $classTemplateTypes;
foreach ($constructorVariant->getParameters() as $parameter) {
TypeTraverser::map($parameter->getType(), static function (Type $type, callable $traverse) use (&$classTemplateTypes): Type {
if ($type instanceof TemplateType && array_key_exists($type->getName(), $classTemplateTypes)) {
$classTemplateType = $classTemplateTypes[$type->getName()];
if ($classTemplateType instanceof TemplateType && $classTemplateType->getScope()->equals($type->getScope())) {
unset($classTemplateTypes[$type->getName()]);
}
return $type;
}

return $traverse($type);
});
$traverser = new ConstructorClassTemplateTraverser($classTemplateTypes);
foreach ($constructorVariant->getParameters() as $parameter) {
TypeTraverser::map($parameter->getType(), $traverser);
}
$classTemplateTypes = $traverser->getClassTemplateTypes();

if (count($classTemplateTypes) === count($originalClassTemplateTypes)) {
$propertyType = TypeCombinator::removeNull($this->getType($assignedToProperty));
Expand Down Expand Up @@ -6168,18 +6118,7 @@ classReflection: $classReflection->withTypes($types)->asFinal(),
[],
);
}
return TypeTraverser::map($newGenericType, static function (Type $type, callable $traverse) use ($resolvedTemplateTypeMap): Type {
if ($type instanceof TemplateType && !$type->isArgument()) {
$newType = $resolvedTemplateTypeMap->getType($type->getName());
if ($newType === null || $newType instanceof ErrorType) {
return $type->getDefault() ?? $type->getBound();
}

return TemplateTypeHelper::generalizeInferredTemplateType($type, $newType);
}

return $traverse($type);
});
return TypeTraverser::map($newGenericType, new GenericTypeTemplateTraverser($resolvedTemplateTypeMap));
}

private function filterTypeWithMethod(Type $typeWithMethod, string $methodName): ?Type
Expand Down
29 changes: 29 additions & 0 deletions src/Analyser/Traverser/CloneTypeTraverser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser\Traverser;

use PHPStan\Type\IntersectionType;
use PHPStan\Type\StaticType;
use PHPStan\Type\ThisType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;

final class CloneTypeTraverser
{

/**
* @param callable(Type): Type $traverse
*/
public function __invoke(Type $type, callable $traverse): Type
{
if ($type instanceof UnionType || $type instanceof IntersectionType) {
return $traverse($type);
}
if ($type instanceof ThisType) {
return new StaticType($type->getClassReflection(), $type->getSubtractedType());
}

return $type;
}

}
45 changes: 45 additions & 0 deletions src/Analyser/Traverser/ConstructorClassTemplateTraverser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser\Traverser;

use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Type;
use function array_key_exists;

final class ConstructorClassTemplateTraverser
{

/**
* @param array<string, Type> $classTemplateTypes
*/
public function __construct(
private array $classTemplateTypes,
)
{
}

/**
* @param callable(Type): Type $traverse
*/
public function __invoke(Type $type, callable $traverse): Type
{
if ($type instanceof TemplateType && array_key_exists($type->getName(), $this->classTemplateTypes)) {
$classTemplateType = $this->classTemplateTypes[$type->getName()];
if ($classTemplateType instanceof TemplateType && $classTemplateType->getScope()->equals($type->getScope())) {
unset($this->classTemplateTypes[$type->getName()]);
}
return $type;
}

return $traverse($type);
}

/**
* @return array<string, Type>
*/
public function getClassTemplateTypes(): array
{
return $this->classTemplateTypes;
}

}
37 changes: 37 additions & 0 deletions src/Analyser/Traverser/GenericTypeTemplateTraverser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser\Traverser;

use PHPStan\Type\ErrorType;
use PHPStan\Type\Generic\TemplateType;
use PHPStan\Type\Generic\TemplateTypeHelper;
use PHPStan\Type\Generic\TemplateTypeMap;
use PHPStan\Type\Type;

final class GenericTypeTemplateTraverser
{

public function __construct(
private TemplateTypeMap $resolvedTemplateTypeMap,
)
{
}

/**
* @param callable(Type): Type $traverse
*/
public function __invoke(Type $type, callable $traverse): Type
{
if ($type instanceof TemplateType && !$type->isArgument()) {
$newType = $this->resolvedTemplateTypeMap->getType($type->getName());
if ($newType === null || $newType instanceof ErrorType) {
return $type->getDefault() ?? $type->getBound();
}

return TemplateTypeHelper::generalizeInferredTemplateType($type, $newType);
}

return $traverse($type);
}

}
46 changes: 46 additions & 0 deletions src/Analyser/Traverser/InstanceOfClassTypeTraverser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser\Traverser;

use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Generic\GenericClassStringType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;

final class InstanceOfClassTypeTraverser
{

private bool $uncertainty = false;

/**
* @param callable(Type): Type $traverse
*/
public function __invoke(Type $type, callable $traverse): Type
{
if ($type instanceof UnionType || $type instanceof IntersectionType) {
return $traverse($type);
}

if ($type->getObjectClassNames() !== []) {
$this->uncertainty = true;
return $type;
}
if ($type instanceof GenericClassStringType) {
$this->uncertainty = true;
return $type->getGenericType();
}
if ($type instanceof ConstantStringType) {
return new ObjectType($type->getValue());
}
return new MixedType();
}

public function getUncertainty(): bool
{
return $this->uncertainty;
}

}
Loading
Loading