diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 8a807ea745..c267e716cb 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -6776,14 +6776,19 @@ private function unwrapAssign(Expr $expr): Expr private function processSureTypesForConditionalExpressionsAfterAssign(Scope $scope, string $variableName, array $conditionalExpressions, SpecifiedTypes $specifiedTypes, Type $variableType): array { foreach ($specifiedTypes->getSureTypes() as $exprString => [$expr, $exprType]) { - if (!$expr instanceof Variable) { - continue; - } - if (!is_string($expr->name)) { - continue; - } + if ($expr instanceof Variable) { + if (!is_string($expr->name)) { + continue; + } - if ($expr->name === $variableName) { + if ($expr->name === $variableName) { + continue; + } + } elseif ( + !$expr instanceof PropertyFetch + && !$expr instanceof ArrayDimFetch + && !$expr instanceof StaticPropertyFetch + ) { continue; } @@ -6810,14 +6815,19 @@ private function processSureTypesForConditionalExpressionsAfterAssign(Scope $sco private function processSureNotTypesForConditionalExpressionsAfterAssign(Scope $scope, string $variableName, array $conditionalExpressions, SpecifiedTypes $specifiedTypes, Type $variableType): array { foreach ($specifiedTypes->getSureNotTypes() as $exprString => [$expr, $exprType]) { - if (!$expr instanceof Variable) { - continue; - } - if (!is_string($expr->name)) { - continue; - } + if ($expr instanceof Variable) { + if (!is_string($expr->name)) { + continue; + } - if ($expr->name === $variableName) { + if ($expr->name === $variableName) { + continue; + } + } elseif ( + !$expr instanceof PropertyFetch + && !$expr instanceof ArrayDimFetch + && !$expr instanceof StaticPropertyFetch + ) { continue; } diff --git a/tests/PHPStan/Analyser/nsrt/bug-10040.php b/tests/PHPStan/Analyser/nsrt/bug-10040.php new file mode 100644 index 0000000000..35b30ac80e --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-10040.php @@ -0,0 +1,34 @@ += 8.0 + +namespace Bug10040; + +use function PHPStan\Testing\assertType; + +class A +{ + public string|null $foo; +} + +/** + * @phpstan-assert-if-true !null $a->foo + */ +function assertStringNotNull(A $a): bool +{ + return true; +} + +$a1 = new A(); +if(assertStringNotNull($a1)){ + assertType('string', $a1->foo); +} + +$a2 = new A(); +$stringIsNotNull = assertStringNotNull($a2); +if($stringIsNotNull){ + assertType('string', $a2->foo); +} + +$a3 = new A(); +if($stringIsNotNull = assertStringNotNull($a3)){ + assertType('string', $a3->foo); +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-11102.php b/tests/PHPStan/Analyser/nsrt/bug-11102.php new file mode 100644 index 0000000000..0f32d86399 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-11102.php @@ -0,0 +1,15 @@ + null]; + +$startIsADateTime = $details['start'] instanceof DateTime; + +if ($startIsADateTime) { + assertType('DateTime', $details['start']); +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-11870.php b/tests/PHPStan/Analyser/nsrt/bug-11870.php new file mode 100644 index 0000000000..04727d679b --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-11870.php @@ -0,0 +1,14 @@ += 8.0 + +namespace Bug6120; + +use Generator; +use function PHPStan\Testing\assertType; + +class HelloWorld +{ + /** + * @var Generator $gen + */ + private ?Generator $gen = null; + + public function setGenerator(Generator $gen): void { + $this->gen = $gen; + } + + public function sayHello(): void + { + while ($v = $this->gen?->current()) { + assertType('Generator', $this->gen); + echo $v; + $this->gen->next(); + } + } +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-6991.php b/tests/PHPStan/Analyser/nsrt/bug-6991.php new file mode 100644 index 0000000000..4b6248aa2b --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-6991.php @@ -0,0 +1,29 @@ += 8.0 + +namespace Bug6991; + +use function PHPStan\Testing\assertType; + +class HelloWorld +{ + public int $more = 1; + + public ?string $key = null; +} + +class Other { + public ?HelloWorld $optional = null; +} + +function test(Other $object): int +{ + $key = $object->optional?->key; + + if (!$key) { + return 0; + } + + assertType('Bug6991\HelloWorld', $object->optional); + + return $object->optional->more * 100; +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-7716.php b/tests/PHPStan/Analyser/nsrt/bug-7716.php new file mode 100644 index 0000000000..7551bf69de --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-7716.php @@ -0,0 +1,50 @@ + 1; + $hasBar = isset($array['bar']) && $array['bar'] > 1; + + if ($hasFoo) { + assertType('int<2, max>', $array['foo']); + return $array['foo']; + } + + if ($hasBar) { + assertType('int<2, max>', $array['bar']); + return $array['bar']; + } + + return 0; + } + + /** + * @param array{foo?: int, bar?: int} $array + */ + public function sayHello2(array $array): int + { + $hasBar = isset($array['bar']) && $array['bar'] > 1; + $hasFoo = isset($array['foo']) && $array['foo'] > 1; + + if ($hasFoo) { + assertType('int<2, max>', $array['foo']); + return $array['foo']; + } + + if ($hasBar) { + assertType('int<2, max>', $array['bar']); + return $array['bar']; + } + + return 0; + } +} diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 879c86f791..a641163fe1 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -1038,6 +1038,13 @@ public function testBug12574b(): void $this->analyse([__DIR__ . '/data/bug-12574b.php'], []); } + public function testBug12574c(): void + { + $this->reportPossiblyNonexistentGeneralArrayOffset = true; + + $this->analyse([__DIR__ . '/data/bug-12574c.php'], []); + } + public function testBug12926(): void { $this->reportPossiblyNonexistentGeneralArrayOffset = true; diff --git a/tests/PHPStan/Rules/Arrays/data/bug-12574c.php b/tests/PHPStan/Rules/Arrays/data/bug-12574c.php new file mode 100644 index 0000000000..ec949ff2cc --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/bug-12574c.php @@ -0,0 +1,32 @@ + $worlds */ + public array $worlds = []; +} + +class World +{ + public int $x = 1; +} + + +function hello(Galaxy $a): void +{ + $notEmpty = isset($a->worlds[0]); + if ($notEmpty && $a->worlds[0]->x === 1) { + echo 'hello'; + } +} + +function hello2(Galaxy $a): void +{ + $worlds = $a->worlds; + $notEmpty = isset($worlds[0]); + if ($notEmpty && $worlds[0]->x === 1) { + echo 'hello'; + } +}