Skip to content

Commit 94c058d

Browse files
committed
Merge branch 2.1.x into 2.2.x
2 parents b655779 + 52704a4 commit 94c058d

File tree

14 files changed

+288
-8
lines changed

14 files changed

+288
-8
lines changed

src/Analyser/MutatingScope.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3221,9 +3221,34 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
32213221
if (array_key_exists($conditionalExprString, $conditions)) {
32223222
continue;
32233223
}
3224+
3225+
// Pass 1: Prefer exact matches
3226+
foreach ($conditionalExpressions as $conditionalExpression) {
3227+
foreach ($conditionalExpression->getConditionExpressionTypeHolders() as $holderExprString => $conditionalTypeHolder) {
3228+
if (
3229+
!array_key_exists($holderExprString, $specifiedExpressions)
3230+
|| !$conditionalTypeHolder->equals($specifiedExpressions[$holderExprString])
3231+
) {
3232+
continue 2;
3233+
}
3234+
}
3235+
3236+
$conditions[$conditionalExprString][] = $conditionalExpression;
3237+
$specifiedExpressions[$conditionalExprString] = $conditionalExpression->getTypeHolder();
3238+
}
3239+
3240+
if (array_key_exists($conditionalExprString, $conditions)) {
3241+
continue;
3242+
}
3243+
3244+
// Pass 2: Supertype match. Only runs when Pass 1 found no exact match for this expression.
32243245
foreach ($conditionalExpressions as $conditionalExpression) {
32253246
foreach ($conditionalExpression->getConditionExpressionTypeHolders() as $holderExprString => $conditionalTypeHolder) {
3226-
if (!array_key_exists($holderExprString, $specifiedExpressions) || !$specifiedExpressions[$holderExprString]->equals($conditionalTypeHolder)) {
3247+
if (
3248+
!array_key_exists($holderExprString, $specifiedExpressions)
3249+
|| !$conditionalTypeHolder->getCertainty()->equals($specifiedExpressions[$holderExprString]->getCertainty())
3250+
|| !$conditionalTypeHolder->getType()->isSuperTypeOf($specifiedExpressions[$holderExprString]->getType())->yes()
3251+
) {
32273252
continue 2;
32283253
}
32293254
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug10055;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
/**
10+
* @param 'value1'|'value2'|'value3' $param1
11+
* @param ($param1 is 'value3' ? bool : int) $param2
12+
*/
13+
function test(string $param1, int|bool $param2): void
14+
{
15+
match ($param1) {
16+
'value1' => assertType('int', $param2),
17+
'value2' => assertType('int', $param2),
18+
'value3' => assertType('bool', $param2),
19+
};
20+
}
21+
22+
function testScopeMerging(mixed $foo): void
23+
{
24+
$a = 0;
25+
if (\is_string($foo) || \is_int($foo)) {
26+
$a = 1;
27+
}
28+
29+
if (\is_int($foo)) {
30+
assertType('1', $a);
31+
}
32+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug10422;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
class TestClass {
10+
public function test(): void {}
11+
public function something(): bool { return true; }
12+
}
13+
14+
function test(?TestClass $test): void
15+
{
16+
$error = '';
17+
18+
if (!$test) {
19+
$error = 'missing test';
20+
} else if ($test->something()) {
21+
$error = 'another';
22+
}
23+
if ($error) {
24+
die('Done');
25+
}
26+
assertType('Bug10422\TestClass', $test);
27+
$test->test();
28+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug13591;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
function processHotel(int $hotelId): void {}
10+
11+
/**
12+
* @param 'get_rooms'|'get_hotels' $action
13+
*/
14+
function test(string $action, ?int $hotelId): void
15+
{
16+
if ($action === 'get_rooms' && $hotelId === null) {
17+
throw new \InvalidArgumentException('Hotel ID is required');
18+
}
19+
20+
if ($action === 'get_rooms') {
21+
assertType('int', $hotelId);
22+
processHotel($hotelId);
23+
}
24+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug4090;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
/** @param string[] $a */
10+
function foo(array $a): void
11+
{
12+
if (count($a) > 1) {
13+
echo implode(',', $a);
14+
} elseif (count($a) === 1) {
15+
assertType('string', current($a));
16+
echo trim(current($a));
17+
}
18+
}
19+
20+
/** @param string[] $a */
21+
function bar(array $a): void
22+
{
23+
$count = count($a);
24+
if ($count > 1) {
25+
echo implode(',', $a);
26+
} elseif ($count === 1) {
27+
assertType('string', current($a));
28+
echo trim(current($a));
29+
}
30+
}
31+
32+
/** @param string[] $a */
33+
function qux(array $a): void
34+
{
35+
switch (count($a)) {
36+
case 0:
37+
break;
38+
case 1:
39+
assertType('string', current($a));
40+
echo trim(current($a));
41+
break;
42+
default:
43+
echo implode(',', $a);
44+
break;
45+
}
46+
}

tests/PHPStan/Analyser/nsrt/bug-5051.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,35 +60,35 @@ public function testWithBooleans($data): void
6060
assertType('bool', $update);
6161
} else {
6262
assertType('1|2', $data);
63-
assertType('bool', $update);
63+
assertType('false', $update);
6464
}
6565

6666
if ($data === 1) {
67-
assertType('bool', $update);
68-
assertType('bool', $foo);
67+
assertType('false', $update);
68+
assertType('false', $foo);
6969
} else {
7070
assertType('bool', $update);
7171
assertType('bool', $foo);
7272
}
7373

7474
if ($data === 2) {
75-
assertType('bool', $update);
76-
assertType('bool', $foo);
75+
assertType('false', $update);
76+
assertType('false', $foo);
7777
} else {
7878
assertType('bool', $update);
7979
assertType('bool', $foo);
8080
}
8181

8282
if ($data === 3) {
83-
assertType('bool', $update);
83+
assertType('false', $update);
8484
assertType('true', $foo);
8585
} else {
8686
assertType('bool', $update);
8787
assertType('bool', $foo);
8888
}
8989

9090
if ($data === 1 || $data === 2) {
91-
assertType('bool', $update);
91+
assertType('false', $update);
9292
assertType('false', $foo);
9393
} else {
9494
assertType('bool', $update);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug6663;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
class X {}
10+
class Y {}
11+
12+
function test(mixed $xy, mixed $ab): void
13+
{
14+
if ($xy instanceof X || $ab instanceof X) {
15+
if ($xy instanceof Y) {
16+
assertType('Bug6663\Y', $xy);
17+
assertType('Bug6663\X', $ab);
18+
}
19+
if ($ab instanceof Y) {
20+
assertType('Bug6663\X', $xy);
21+
assertType('Bug6663\Y', $ab);
22+
}
23+
}
24+
}

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,4 +1277,11 @@ public function testBug14308(): void
12771277
$this->analyse([__DIR__ . '/data/bug-14308.php'], []);
12781278
}
12791279

1280+
public function testBug11218(): void
1281+
{
1282+
$this->reportPossiblyNonexistentConstantArrayOffset = true;
1283+
1284+
$this->analyse([__DIR__ . '/data/bug-11218.php'], []);
1285+
}
1286+
12801287
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11218Array;
4+
5+
function test(): void
6+
{
7+
$level = 'test';
8+
9+
$test = [];
10+
11+
for ($i = 0 ; $i <= 3 ; $i++) {
12+
if ($i === 0) {
13+
$test[$level] = 'this is a';
14+
} else {
15+
$test[$level] .= ' test';
16+
}
17+
}
18+
}

tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2913,4 +2913,14 @@ public function testBug4608(): void
29132913
]);
29142914
}
29152915

2916+
public function testBug13591(): void
2917+
{
2918+
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-13591.php'], []);
2919+
}
2920+
2921+
public function testBug4090(): void
2922+
{
2923+
$this->analyse([__DIR__ . '/../../Analyser/nsrt/bug-4090.php'], []);
2924+
}
2925+
29162926
}

0 commit comments

Comments
 (0)