From 04b840344a5f403e9a48120d7b4dccb278cab2d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20=22Jeff=22=20Meinesz?= Date: Tue, 4 Jun 2024 15:30:17 +0100 Subject: [PATCH] Consider a ClosureType() as maybe impure by default when no impurePoints array provided --- src/PhpDoc/TypeNodeResolver.php | 2 +- src/Type/ClosureType.php | 13 +++++++++++-- tests/PHPStan/Type/ClosureTypeTest.php | 5 +++++ tests/PHPStan/Type/TypeCombinatorTest.php | 12 ++++++------ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/PhpDoc/TypeNodeResolver.php b/src/PhpDoc/TypeNodeResolver.php index 78cb4c9e47..6a8a409e56 100644 --- a/src/PhpDoc/TypeNodeResolver.php +++ b/src/PhpDoc/TypeNodeResolver.php @@ -360,7 +360,7 @@ private function resolveIdentifierTypeNode(IdentifierTypeNode $typeNode, NameSco return new CallableType(null, null, true, null, null, [], TrinaryLogic::createYes()); case 'pure-closure': - return new ClosureType(); + return ClosureType::createPure(); case 'resource': $type = $this->tryResolvePseudoTypeClassType($typeNode, $nameScope); diff --git a/src/Type/ClosureType.php b/src/Type/ClosureType.php index d86f117119..e7c48df9bb 100644 --- a/src/Type/ClosureType.php +++ b/src/Type/ClosureType.php @@ -75,12 +75,15 @@ class ClosureType implements TypeWithClassName, CallableParametersAcceptor private TemplateTypeVarianceMap $callSiteVarianceMap; + /** @var SimpleImpurePoint[] */ + private array $impurePoints; + /** * @api * @param array|null $parameters * @param array $templateTags * @param SimpleThrowPoint[] $throwPoints - * @param SimpleImpurePoint[] $impurePoints + * @param ?SimpleImpurePoint[] $impurePoints * @param InvalidateExprNode[] $invalidateExpressions * @param string[] $usedVariables */ @@ -93,7 +96,7 @@ public function __construct( ?TemplateTypeVarianceMap $callSiteVarianceMap = null, private array $templateTags = [], private array $throwPoints = [], - private array $impurePoints = [], + ?array $impurePoints = null, private array $invalidateExpressions = [], private array $usedVariables = [], ) @@ -105,6 +108,7 @@ public function __construct( $this->templateTypeMap = $templateTypeMap ?? TemplateTypeMap::createEmpty(); $this->resolvedTemplateTypeMap = $resolvedTemplateTypeMap ?? TemplateTypeMap::createEmpty(); $this->callSiteVarianceMap = $callSiteVarianceMap ?? TemplateTypeVarianceMap::createEmpty(); + $this->impurePoints = $impurePoints ?? [new SimpleImpurePoint('functionCall', 'call to an unknown Closure', false)]; } /** @@ -115,6 +119,11 @@ public function getTemplateTags(): array return $this->templateTags; } + public static function createPure(): self + { + return new self(null, null, true, null, null, null, [], [], []); + } + public function isPure(): TrinaryLogic { $impurePoints = $this->getImpurePoints(); diff --git a/tests/PHPStan/Type/ClosureTypeTest.php b/tests/PHPStan/Type/ClosureTypeTest.php index f8d029048e..ccaaa4ca78 100644 --- a/tests/PHPStan/Type/ClosureTypeTest.php +++ b/tests/PHPStan/Type/ClosureTypeTest.php @@ -23,6 +23,11 @@ public function dataIsSuperTypeOf(): array new ClosureType([], new MixedType(), false), TrinaryLogic::createYes(), ], + [ + new ClosureType([], new MixedType(), false, null, null, null, [], [], []), + new ClosureType([], new MixedType(), false), + TrinaryLogic::createMaybe(), + ], [ new ClosureType([], new UnionType([new IntegerType(), new StringType()]), false), new ClosureType([], new IntegerType(), false), diff --git a/tests/PHPStan/Type/TypeCombinatorTest.php b/tests/PHPStan/Type/TypeCombinatorTest.php index f17eb54265..b50517c033 100644 --- a/tests/PHPStan/Type/TypeCombinatorTest.php +++ b/tests/PHPStan/Type/TypeCombinatorTest.php @@ -2535,7 +2535,7 @@ public function dataUnion(): iterable yield [ [ new CallableType(null, null, true, null, null, [], TrinaryLogic::createYes()), - new ClosureType(), + ClosureType::createPure(), ], CallableType::class, 'pure-callable(): mixed', @@ -2553,7 +2553,7 @@ public function dataUnion(): iterable new ClosureType([], new MixedType(), true, null, null, null, [], [], [ new SimpleImpurePoint('functionCall', 'foo', true), ]), - new ClosureType(), + ClosureType::createPure(), ], UnionType::class, '(Closure(): mixed)|(pure-Closure)', @@ -2563,7 +2563,7 @@ public function dataUnion(): iterable new ClosureType([], new MixedType(), true, null, null, null, [], [], [ new SimpleImpurePoint('functionCall', 'foo', false), ]), - new ClosureType(), + ClosureType::createPure(), ], ClosureType::class, 'Closure(): mixed', @@ -4219,7 +4219,7 @@ public function dataIntersect(): iterable yield [ [ new CallableType(null, null, true, null, null, [], TrinaryLogic::createYes()), - new ClosureType(), + ClosureType::createPure(), ], ClosureType::class, 'pure-Closure', @@ -4237,7 +4237,7 @@ public function dataIntersect(): iterable new ClosureType([], new MixedType(), true, null, null, null, [], [], [ new SimpleImpurePoint('functionCall', 'foo', true), ]), - new ClosureType(), + ClosureType::createPure(), ], NeverType::class, '*NEVER*=implicit', @@ -4247,7 +4247,7 @@ public function dataIntersect(): iterable new ClosureType([], new MixedType(), true, null, null, null, [], [], [ new SimpleImpurePoint('functionCall', 'foo', false), ]), - new ClosureType(), + ClosureType::createPure(), ], ClosureType::class, 'pure-Closure',