Skip to content
38 changes: 24 additions & 14 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we're missing a lot:

In the end, I'm not sure which are the skipped $expr

&& !$expr instanceof ArrayDimFetch
&& !$expr instanceof StaticPropertyFetch
) {
continue;
}

Expand Down
34 changes: 34 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-10040.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php declare(strict_types = 1); // lint >= 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);
}
15 changes: 15 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-11102.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php declare(strict_types = 1);

namespace Bug11102;

use DateTime;
use function PHPStan\Testing\assertType;

/** @var array{'start': DateTime|null} $details */
$details = ['start' => null];

$startIsADateTime = $details['start'] instanceof DateTime;

if ($startIsADateTime) {
assertType('DateTime', $details['start']);
}
14 changes: 14 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-11870.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php declare(strict_types = 1);

namespace Bug11870;

use function PHPStan\Testing\assertType;

function () {
assert(isset($v) && is_array($v) && isset($v['LANGUAGE']));

$language = $v['LANGUAGE'] !== 1 ? 'en' : '';
if ($language !== '') {
assertType('true', $v['LANGUAGE'] !== 1);
}
};
27 changes: 27 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-6120.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types = 1); // lint >= 8.0

namespace Bug6120;

use Generator;
use function PHPStan\Testing\assertType;

class HelloWorld
{
/**
* @var Generator<int, string> $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<int, string, mixed, mixed>', $this->gen);
echo $v;
$this->gen->next();
}
}
}
29 changes: 29 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-6991.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types = 1); // lint >= 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;
}
50 changes: 50 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-7716.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php declare(strict_types = 1);

namespace Bug7716;

use function PHPStan\Testing\assertType;

class HelloWorld
{
/**
* @param array{foo?: int, bar?: int} $array
*/
public function sayHello(array $array): int
{
$hasFoo = isset($array['foo']) && $array['foo'] > 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
32 changes: 32 additions & 0 deletions tests/PHPStan/Rules/Arrays/data/bug-12574c.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);

namespace Bug12574c;

class Galaxy
{
/** @var list<World> $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';
}
}
Loading