From a24f456be931dfb483d9a749b93913ce2d864ab3 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 13 Apr 2025 22:19:33 +0200 Subject: [PATCH 1/7] Fix MixedType->equals(ErrorType) --- src/Type/MixedType.php | 3 +- tests/PHPStan/Type/MixedTypeTest.php | 45 ++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/Type/MixedType.php b/src/Type/MixedType.php index 487a474827..5ca9003b3a 100644 --- a/src/Type/MixedType.php +++ b/src/Type/MixedType.php @@ -37,6 +37,7 @@ use PHPStan\Type\Traits\NonGeneralizableTypeTrait; use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait; +use function get_class; use function sprintf; /** @api */ @@ -310,7 +311,7 @@ public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope) public function equals(Type $type): bool { - if (!$type instanceof self) { + if (get_class($type) !== self::class) { return false; } diff --git a/tests/PHPStan/Type/MixedTypeTest.php b/tests/PHPStan/Type/MixedTypeTest.php index 3ffc8f9db7..7de8774af7 100644 --- a/tests/PHPStan/Type/MixedTypeTest.php +++ b/tests/PHPStan/Type/MixedTypeTest.php @@ -1164,4 +1164,49 @@ public function testSubtractedHasOffsetValueType(MixedType $mixedType, Type $typ ); } + /** @dataProvider dataEquals */ + public function testEquals(MixedType $mixedType, Type $typeToCompare, bool $expectedResult): void + { + $this->assertSame( + $expectedResult, + $mixedType->equals($typeToCompare), + ); + } + + public function dataEquals(): array + { + return [ + [ + new MixedType(), + new MixedType(), + true, + ], + [ + new MixedType(true), + new MixedType(), + true, + ], + [ + new MixedType(), + new MixedType(true), + true, + ], + [ + new MixedType(), + new MixedType(true, new IntegerType()), + false, + ], + [ + new MixedType(), + new ErrorType(), + false, + ], + [ + new MixedType(true), + new ErrorType(), + false, + ], + ]; + } + } From 4b3036c89b187ce2e6e92da0700f141213404272 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 13 Apr 2025 22:25:28 +0200 Subject: [PATCH 2/7] Update MixedType.php --- src/Type/MixedType.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Type/MixedType.php b/src/Type/MixedType.php index 5ca9003b3a..a30b41879a 100644 --- a/src/Type/MixedType.php +++ b/src/Type/MixedType.php @@ -38,6 +38,7 @@ use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait; use function get_class; +use function in_array; use function sprintf; /** @api */ @@ -311,7 +312,7 @@ public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope) public function equals(Type $type): bool { - if (get_class($type) !== self::class) { + if (!in_array(get_class($type), [self::class, TemplateMixedType::class], true)) { return false; } From 8d8db52c1799f490c674ab32195108321a291720 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 13 Apr 2025 22:29:47 +0200 Subject: [PATCH 3/7] simplify --- src/Type/MixedType.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Type/MixedType.php b/src/Type/MixedType.php index a30b41879a..6cdb2786a0 100644 --- a/src/Type/MixedType.php +++ b/src/Type/MixedType.php @@ -32,6 +32,7 @@ use PHPStan\Type\Constant\ConstantFloatType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Constant\ConstantStringType; +use PHPStan\Type\Generic\GenericObjectType; use PHPStan\Type\Generic\TemplateMixedType; use PHPStan\Type\Generic\TemplateType; use PHPStan\Type\Traits\NonGeneralizableTypeTrait; @@ -312,7 +313,11 @@ public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope) public function equals(Type $type): bool { - if (!in_array(get_class($type), [self::class, TemplateMixedType::class], true)) { + if (!$type instanceof self) { + return false; + } + + if ($type instanceof ErrorType) { return false; } From 76c05372a9dea0ec1e14352fe7d77da60477b0cc Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 13 Apr 2025 22:36:59 +0200 Subject: [PATCH 4/7] cs --- src/Type/MixedType.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Type/MixedType.php b/src/Type/MixedType.php index 6cdb2786a0..175762d000 100644 --- a/src/Type/MixedType.php +++ b/src/Type/MixedType.php @@ -32,14 +32,11 @@ use PHPStan\Type\Constant\ConstantFloatType; use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Constant\ConstantStringType; -use PHPStan\Type\Generic\GenericObjectType; use PHPStan\Type\Generic\TemplateMixedType; use PHPStan\Type\Generic\TemplateType; use PHPStan\Type\Traits\NonGeneralizableTypeTrait; use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait; -use function get_class; -use function in_array; use function sprintf; /** @api */ From 1124c960374f44f1e00a71ef78f98739506af727 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 14 Apr 2025 09:14:02 +0200 Subject: [PATCH 5/7] Update MixedTypeTest.php --- tests/PHPStan/Type/MixedTypeTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/PHPStan/Type/MixedTypeTest.php b/tests/PHPStan/Type/MixedTypeTest.php index 7de8774af7..85edf6bc4f 100644 --- a/tests/PHPStan/Type/MixedTypeTest.php +++ b/tests/PHPStan/Type/MixedTypeTest.php @@ -1170,6 +1170,7 @@ public function testEquals(MixedType $mixedType, Type $typeToCompare, bool $expe $this->assertSame( $expectedResult, $mixedType->equals($typeToCompare), + sprintf('%s -> equals(%s)', $mixedType->describe(VerbosityLevel::precise()), $typeToCompare->describe(VerbosityLevel::precise())), ); } From 59e5e5766ed1879771546a2aaf24da3ecdf3388a Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 14 Apr 2025 09:29:33 +0200 Subject: [PATCH 6/7] Support arrays in fast-path --- src/Analyser/MutatingScope.php | 2 +- tests/PHPStan/Analyser/nsrt/conditional-vars.php | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 42034f0ae3..77a265078a 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -4498,7 +4498,7 @@ public function addTypeToExpression(Expr $expr, Type $type): self if ($originalExprType->equals($nativeType)) { $newType = TypeCombinator::intersect($type, $originalExprType); - if ($newType->isConstantScalarValue()->yes() && $newType->equals($originalExprType)) { + if ($newType->isObject()->no() && $newType->equals($originalExprType)) { // don't add the same type over and over again to improve performance return $this; } diff --git a/tests/PHPStan/Analyser/nsrt/conditional-vars.php b/tests/PHPStan/Analyser/nsrt/conditional-vars.php index 568c6a8b7f..467ca05aae 100644 --- a/tests/PHPStan/Analyser/nsrt/conditional-vars.php +++ b/tests/PHPStan/Analyser/nsrt/conditional-vars.php @@ -10,10 +10,10 @@ class HelloWorld public function conditionalVarInTernary(array $innerHits): void { if (array_key_exists('nearest_premise', $innerHits) || array_key_exists('matching_premises', $innerHits)) { - assertType('non-empty-array', $innerHits); + assertType('non-empty-array', $innerHits); $x = array_key_exists('nearest_premise', $innerHits) ? assertType("non-empty-array&hasOffset('nearest_premise')", $innerHits) - : assertType('non-empty-array', $innerHits); + : assertType('non-empty-array', $innerHits); assertType('non-empty-array', $innerHits); } @@ -24,11 +24,11 @@ public function conditionalVarInTernary(array $innerHits): void public function conditionalVarInIf(array $innerHits): void { if (array_key_exists('nearest_premise', $innerHits) || array_key_exists('matching_premises', $innerHits)) { - assertType('non-empty-array', $innerHits); + assertType('non-empty-array', $innerHits); if (array_key_exists('nearest_premise', $innerHits)) { assertType("non-empty-array&hasOffset('nearest_premise')", $innerHits); } else { - assertType('non-empty-array', $innerHits); + assertType('non-empty-array', $innerHits); } assertType('non-empty-array', $innerHits); From 9e6f98eb1961341eb3ff7973b4ccff1ca89b83d9 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 15 Apr 2025 16:06:30 +0200 Subject: [PATCH 7/7] Update conditional-vars.php --- tests/PHPStan/Analyser/nsrt/conditional-vars.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/PHPStan/Analyser/nsrt/conditional-vars.php b/tests/PHPStan/Analyser/nsrt/conditional-vars.php index 467ca05aae..568c6a8b7f 100644 --- a/tests/PHPStan/Analyser/nsrt/conditional-vars.php +++ b/tests/PHPStan/Analyser/nsrt/conditional-vars.php @@ -10,10 +10,10 @@ class HelloWorld public function conditionalVarInTernary(array $innerHits): void { if (array_key_exists('nearest_premise', $innerHits) || array_key_exists('matching_premises', $innerHits)) { - assertType('non-empty-array', $innerHits); + assertType('non-empty-array', $innerHits); $x = array_key_exists('nearest_premise', $innerHits) ? assertType("non-empty-array&hasOffset('nearest_premise')", $innerHits) - : assertType('non-empty-array', $innerHits); + : assertType('non-empty-array', $innerHits); assertType('non-empty-array', $innerHits); } @@ -24,11 +24,11 @@ public function conditionalVarInTernary(array $innerHits): void public function conditionalVarInIf(array $innerHits): void { if (array_key_exists('nearest_premise', $innerHits) || array_key_exists('matching_premises', $innerHits)) { - assertType('non-empty-array', $innerHits); + assertType('non-empty-array', $innerHits); if (array_key_exists('nearest_premise', $innerHits)) { assertType("non-empty-array&hasOffset('nearest_premise')", $innerHits); } else { - assertType('non-empty-array', $innerHits); + assertType('non-empty-array', $innerHits); } assertType('non-empty-array', $innerHits);