diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 7a4d38545e..c0058cb2ae 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -72,6 +72,36 @@ parameters: count: 1 path: src/Analyser/RuleErrorTransformer.php + - + rawMessage: Doing instanceof PHPStan\Type\IntersectionType is error-prone and deprecated. + 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 @@ -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 diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index ead7b24c5c..e6e23151e7 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -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; @@ -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; @@ -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()) { @@ -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_) { @@ -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()); } /** @@ -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)); } /** @@ -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)); @@ -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 diff --git a/src/Analyser/Traverser/CloneTypeTraverser.php b/src/Analyser/Traverser/CloneTypeTraverser.php new file mode 100644 index 0000000000..2403050204 --- /dev/null +++ b/src/Analyser/Traverser/CloneTypeTraverser.php @@ -0,0 +1,29 @@ +getClassReflection(), $type->getSubtractedType()); + } + + return $type; + } + +} diff --git a/src/Analyser/Traverser/ConstructorClassTemplateTraverser.php b/src/Analyser/Traverser/ConstructorClassTemplateTraverser.php new file mode 100644 index 0000000000..1f3a59f744 --- /dev/null +++ b/src/Analyser/Traverser/ConstructorClassTemplateTraverser.php @@ -0,0 +1,45 @@ + $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 + */ + public function getClassTemplateTypes(): array + { + return $this->classTemplateTypes; + } + +} diff --git a/src/Analyser/Traverser/GenericTypeTemplateTraverser.php b/src/Analyser/Traverser/GenericTypeTemplateTraverser.php new file mode 100644 index 0000000000..c0957b5b61 --- /dev/null +++ b/src/Analyser/Traverser/GenericTypeTemplateTraverser.php @@ -0,0 +1,37 @@ +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); + } + +} diff --git a/src/Analyser/Traverser/InstanceOfClassTypeTraverser.php b/src/Analyser/Traverser/InstanceOfClassTypeTraverser.php new file mode 100644 index 0000000000..88b34c48db --- /dev/null +++ b/src/Analyser/Traverser/InstanceOfClassTypeTraverser.php @@ -0,0 +1,46 @@ +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; + } + +} diff --git a/src/Analyser/Traverser/TransformStaticTypeTraverser.php b/src/Analyser/Traverser/TransformStaticTypeTraverser.php new file mode 100644 index 0000000000..c164f06366 --- /dev/null +++ b/src/Analyser/Traverser/TransformStaticTypeTraverser.php @@ -0,0 +1,39 @@ +scope->isInClass()) { + return $type; + } + if ($type instanceof StaticType) { + $classReflection = $this->scope->getClassReflection(); + $changedType = $type->changeBaseClass($classReflection); + if ($classReflection->isFinal() && !$type instanceof ThisType) { + $changedType = $changedType->getStaticObjectType(); + } + return $traverse($changedType); + } + + return $traverse($type); + } + +} diff --git a/src/Analyser/Traverser/VoidToNullTraverser.php b/src/Analyser/Traverser/VoidToNullTraverser.php new file mode 100644 index 0000000000..831e797964 --- /dev/null +++ b/src/Analyser/Traverser/VoidToNullTraverser.php @@ -0,0 +1,29 @@ +isVoid()->yes()) { + return new NullType(); + } + + return $type; + } + +} diff --git a/src/Cache/FileCacheStorage.php b/src/Cache/FileCacheStorage.php index b1c4c56174..78d6d48859 100644 --- a/src/Cache/FileCacheStorage.php +++ b/src/Cache/FileCacheStorage.php @@ -49,7 +49,7 @@ public function load(string $key, string $variableKey) { [,, $filePath] = $this->getFilePaths($key); - return (static function () use ($variableKey, $filePath) { + return (static function ($variableKey, $filePath) { $cacheItem = @include $filePath; if (!$cacheItem instanceof CacheItem) { return null; @@ -59,7 +59,7 @@ public function load(string $key, string $variableKey) } return $cacheItem->getData(); - })(); + })($variableKey, $filePath); } /** diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php index a599a83efa..612d1ed762 100644 --- a/src/Command/CommandHelper.php +++ b/src/Command/CommandHelper.php @@ -98,11 +98,8 @@ public static function begin( ): InceptionResult { $stdOutput = new SymfonyOutput($output, new SymfonyStyle(new ErrorsConsoleStyle($input, $output))); - - $errorOutput = (static function () use ($input, $output): Output { - $symfonyErrorOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; - return new SymfonyOutput($symfonyErrorOutput, new SymfonyStyle(new ErrorsConsoleStyle($input, $symfonyErrorOutput))); - })(); + $symfonyErrorOutput = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; + $errorOutput = new SymfonyOutput($symfonyErrorOutput, new SymfonyStyle(new ErrorsConsoleStyle($input, $symfonyErrorOutput))); if (!$allowXdebug) { $xdebug = new XdebugHandler('phpstan'); diff --git a/src/Type/Traverser/LateResolvableTraverser.php b/src/Type/Traverser/LateResolvableTraverser.php new file mode 100644 index 0000000000..23fba78980 --- /dev/null +++ b/src/Type/Traverser/LateResolvableTraverser.php @@ -0,0 +1,42 @@ +ignoreResolveUnresolvableTypesLevel = 0; + } + + /** + * @param callable(Type): Type $traverse + */ + public function __invoke(Type $type, callable $traverse): Type + { + while ($type instanceof LateResolvableType && (($this->resolveUnresolvableTypes && $this->ignoreResolveUnresolvableTypesLevel === 0) || $type->isResolvable())) { + $type = $type->resolve(); + } + + if ($type instanceof CallableType || $type instanceof ClosureType) { + $this->ignoreResolveUnresolvableTypesLevel++; + $result = $traverse($type); + $this->ignoreResolveUnresolvableTypesLevel--; + + return $result; + } + + return $traverse($type); + } + +} diff --git a/src/Type/TypeUtils.php b/src/Type/TypeUtils.php index d649cfe0a7..b87e714dd1 100644 --- a/src/Type/TypeUtils.php +++ b/src/Type/TypeUtils.php @@ -9,6 +9,7 @@ use PHPStan\Type\Generic\TemplateBenevolentUnionType; use PHPStan\Type\Generic\TemplateType; use PHPStan\Type\Generic\TemplateUnionType; +use PHPStan\Type\Traverser\LateResolvableTraverser; use function array_merge; /** @@ -233,24 +234,7 @@ public static function containsTemplateType(Type $type): bool public static function resolveLateResolvableTypes(Type $type, bool $resolveUnresolvableTypes = true): Type { - /** @var int $ignoreResolveUnresolvableTypesLevel */ - $ignoreResolveUnresolvableTypesLevel = 0; - - return TypeTraverser::map($type, static function (Type $type, callable $traverse) use ($resolveUnresolvableTypes, &$ignoreResolveUnresolvableTypesLevel): Type { - while ($type instanceof LateResolvableType && (($resolveUnresolvableTypes && $ignoreResolveUnresolvableTypesLevel === 0) || $type->isResolvable())) { - $type = $type->resolve(); - } - - if ($type instanceof CallableType || $type instanceof ClosureType) { - $ignoreResolveUnresolvableTypesLevel++; - $result = $traverse($type); - $ignoreResolveUnresolvableTypesLevel--; - - return $result; - } - - return $traverse($type); - }); + return TypeTraverser::map($type, new LateResolvableTraverser($resolveUnresolvableTypes)); } }