diff --git a/src/Type/InspectorTypeExtension.php b/src/Type/InspectorTypeExtension.php index 6f190574..d090bc62 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 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);