From 7e2ab6645c0ff4829ba2675ddd27f5e27aaaf087 Mon Sep 17 00:00:00 2001 From: Niklan Date: Tue, 15 Apr 2025 20:53:09 +0500 Subject: [PATCH 1/2] Fix assertAll causes staticMethod.alreadyNarrowedType --- src/Type/InspectorTypeExtension.php | 20 +++++++ ...sibleCheckTypeStaticMethodCallRuleTest.php | 52 +++++++++++++++++++ tests/src/Rules/data/bug-857.php | 11 ++++ tests/src/Type/InspectorTypeExtensionTest.php | 1 + tests/src/Type/data/bug-857.php | 21 ++++++++ 5 files changed, 105 insertions(+) create mode 100644 tests/src/Rules/ImpossibleCheckTypeStaticMethodCallRuleTest.php create mode 100644 tests/src/Rules/data/bug-857.php create mode 100644 tests/src/Type/data/bug-857.php diff --git a/src/Type/InspectorTypeExtension.php b/src/Type/InspectorTypeExtension.php index 6f190574..346dfc20 100644 --- a/src/Type/InspectorTypeExtension.php +++ b/src/Type/InspectorTypeExtension.php @@ -107,6 +107,16 @@ private function specifyAssertAll(MethodReflection $staticMethodReflection, Stat return new SpecifiedTypes(); } + $traversable = $node->getArgs()[1]->value; + $traversableInfo = $scope->getType($traversable); + + // If it is already not mixed (narrowed by other code, like + // '::assertAllArray()'), we could not provide any additional + // information. We can only narrow this method to 'array'. + if (!$traversableInfo->equals(new MixedType())) { + return new SpecifiedTypes(); + } + // In a negation context, we cannot precisely narrow types because we do // not know the exact logic of the callable function. This means we // cannot safely return 'mixed~iterable' since the value might still be @@ -120,6 +130,16 @@ private function specifyAssertAll(MethodReflection $staticMethodReflection, Stat return new SpecifiedTypes(); } + $traversable = $node->getArgs()[1]->value; + $traversableInfo = $scope->getType($traversable); + + // If it is already not mixed (narrowed by other code, like + // '::assertAllArray()'), we could not provide any additional + // information. We can only narrow this method to 'array'. + if (!$traversableInfo instanceof MixedType) { + return new SpecifiedTypes(); + } + return $this->typeSpecifier->create( $node->getArgs()[1]->value, new IterableType(new MixedType(), new MixedType()), diff --git a/tests/src/Rules/ImpossibleCheckTypeStaticMethodCallRuleTest.php b/tests/src/Rules/ImpossibleCheckTypeStaticMethodCallRuleTest.php new file mode 100644 index 00000000..178a3799 --- /dev/null +++ b/tests/src/Rules/ImpossibleCheckTypeStaticMethodCallRuleTest.php @@ -0,0 +1,52 @@ +createReflectionProvider(), + $this->getTypeSpecifier(), + [], + $this->treatPhpDocTypesAsCertain, + false, + ), + true, + $this->treatPhpDocTypesAsCertain, + $this->reportAlwaysTrueInLastCondition, + true, + ); + } + + protected function shouldTreatPhpDocTypesAsCertain(): bool + { + return $this->treatPhpDocTypesAsCertain; + } + + + public function testBug857(): void + { + $this->treatPhpDocTypesAsCertain = true; + // It shouldn't report anything. + $this->analyse([__DIR__ . '/data/bug-857.php'], []); + } + +} diff --git a/tests/src/Rules/data/bug-857.php b/tests/src/Rules/data/bug-857.php new file mode 100644 index 00000000..d9b38c02 --- /dev/null +++ b/tests/src/Rules/data/bug-857.php @@ -0,0 +1,11 @@ +'. + Inspector::assertAllArrays($images); + Inspector::assertAll(fn (array $i): bool => $i['file'] instanceof Url, $images); +} diff --git a/tests/src/Type/InspectorTypeExtensionTest.php b/tests/src/Type/InspectorTypeExtensionTest.php index a3439678..355ba039 100644 --- a/tests/src/Type/InspectorTypeExtensionTest.php +++ b/tests/src/Type/InspectorTypeExtensionTest.php @@ -14,6 +14,7 @@ final class InspectorTypeExtensionTest extends TypeInferenceTestCase { public static function dataFileAsserts(): iterable { yield from self::gatherAssertTypes(__DIR__ . '/data/inspector.php'); yield from self::gatherAssertTypes(__DIR__ . '/data/bug-852.php'); + yield from self::gatherAssertTypes(__DIR__ . '/data/bug-857.php'); } /** diff --git a/tests/src/Type/data/bug-857.php b/tests/src/Type/data/bug-857.php new file mode 100644 index 00000000..43d48c89 --- /dev/null +++ b/tests/src/Type/data/bug-857.php @@ -0,0 +1,21 @@ + TRUE, $array)); +assertType('iterable', $array); + +// If the type is narrowed before '::assertAll()', it shouldn't change it. +$array = mixed_function(); +assertType('mixed', $array); +\assert(Inspector::assertAllArrays($array)); +assertType('iterable', $array); +\assert(Inspector::assertAll(fn (array $i): bool => TRUE, $array)); +assertType('iterable', $array); From 7607d9a696a65e852a4468d4af26a3af04b5ed1d Mon Sep 17 00:00:00 2001 From: Niklan Date: Tue, 15 Apr 2025 20:58:41 +0500 Subject: [PATCH 2/2] Fix duplicated part from wrong conflict resolve --- src/Type/InspectorTypeExtension.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Type/InspectorTypeExtension.php b/src/Type/InspectorTypeExtension.php index 346dfc20..d090bc62 100644 --- a/src/Type/InspectorTypeExtension.php +++ b/src/Type/InspectorTypeExtension.php @@ -130,16 +130,6 @@ private function specifyAssertAll(MethodReflection $staticMethodReflection, Stat return new SpecifiedTypes(); } - $traversable = $node->getArgs()[1]->value; - $traversableInfo = $scope->getType($traversable); - - // If it is already not mixed (narrowed by other code, like - // '::assertAllArray()'), we could not provide any additional - // information. We can only narrow this method to 'array'. - if (!$traversableInfo instanceof MixedType) { - return new SpecifiedTypes(); - } - return $this->typeSpecifier->create( $node->getArgs()[1]->value, new IterableType(new MixedType(), new MixedType()),