Skip to content

Commit 48e3ee2

Browse files
committed
Fix specifying types for left side of null coalesce
1 parent f654282 commit 48e3ee2

File tree

7 files changed

+137
-0
lines changed

7 files changed

+137
-0
lines changed

src/Analyser/TypeSpecifier.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,6 +1279,15 @@ private function createForExpr(
12791279
$expr = NullsafeOperatorHelper::getNullsafeShortcircuitedExpr($expr);
12801280
}
12811281

1282+
if (
1283+
$scope !== null
1284+
&& $context->true()
1285+
&& $expr instanceof Expr\BinaryOp\Coalesce
1286+
&& $type->isSuperTypeOf($scope->getType($expr->right))->no()
1287+
) {
1288+
$expr = $expr->left;
1289+
}
1290+
12821291
if (
12831292
$expr instanceof FuncCall
12841293
&& $expr->name instanceof Name

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,12 @@ public function dataFileAsserts(): iterable
10191019

10201020
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7921.php');
10211021
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7928.php');
1022+
1023+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7949.php');
1024+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7639.php');
1025+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5304.php');
1026+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7244.php');
1027+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7501.php');
10221028
}
10231029

10241030
/**
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Bug5304;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @param array{foo?: string} $in
9+
*/
10+
function takesArg(array $in): void
11+
{
12+
if(is_string($in['foo'] ?? null)) {
13+
assertType('array{foo: string}', $in);
14+
}
15+
16+
if(isset($in['foo'])) {
17+
assertType('array{foo: string}', $in);
18+
}
19+
20+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug7244;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class HelloWorld
8+
{
9+
/**
10+
* @param array<string, mixed> $arguments
11+
*/
12+
public function getFormat(array $arguments): string {
13+
$value = \is_string($arguments['format'] ?? null) ? $arguments['format'] : 'Y-m-d';
14+
assertType('string', $value);
15+
return $value;
16+
}
17+
18+
/**
19+
* @param array<string, mixed> $arguments
20+
*/
21+
public function getFormatWithoutFallback(array $arguments): string {
22+
$value = \is_string($arguments['format']) ? $arguments['format'] : 'Y-m-d';
23+
assertType('string', $value);
24+
return $value;
25+
}
26+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug7501;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @param array{key?: mixed} $a
9+
*/
10+
function f(array $a): void
11+
{
12+
if (isset($a['key']) && is_int($a['key'])) {
13+
assertType('array{key: int}', $a);
14+
}
15+
16+
if (is_int($a['key'] ?? null)) {
17+
assertType('array{key: int}', $a);
18+
}
19+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug7639;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
abstract class TestCase
8+
{
9+
/**
10+
* @return static|null
11+
*/
12+
public static function getTestFromBacktrace()
13+
{
14+
foreach (debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS | \DEBUG_BACKTRACE_PROVIDE_OBJECT) as $frame) {
15+
if (($frame['object'] ?? null) instanceof static) {
16+
assertType('static(Bug7639\\TestCase)', $frame['object']);
17+
return $frame['object'];
18+
}
19+
}
20+
21+
return null;
22+
}
23+
24+
/**
25+
* @return static|null
26+
*/
27+
public static function getTestFromBacktraceWithoutNullCoalesce()
28+
{
29+
foreach (debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS | \DEBUG_BACKTRACE_PROVIDE_OBJECT) as $frame) {
30+
if (isset($frame['object']) && $frame['object'] instanceof static) {
31+
assertType('static(Bug7639\\TestCase)', $frame['object']);
32+
return $frame['object'];
33+
}
34+
}
35+
36+
return null;
37+
}
38+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug7949;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class HelloWorld
8+
{
9+
public function bar(?string $price): void
10+
{
11+
$price = strlen($price ?? '') > 0 ? $price : '0';
12+
assertType('non-empty-string', $price);
13+
14+
$this->foo($price);
15+
}
16+
17+
public function foo(string $test): void {
18+
}
19+
}

0 commit comments

Comments
 (0)