Skip to content

Commit ecd37c8

Browse files
VincentLangletphpstan-bot
authored andcommitted
Fix phpstan/phpstan#12597: variable might not be defined after in_array check
- Added subtype matching for conditional expressions in MutatingScope::processSpecifiedTypes - After exact-match conditional resolution, a second pass checks if specified types are subtypes of guard types for maybe-defined variables - Limited to Variable targets with Maybe certainty to avoid interfering with type narrowing - New regression test in tests/PHPStan/Rules/Variables/data/bug-12597.php
1 parent ba4c9e4 commit ecd37c8

File tree

3 files changed

+57
-0
lines changed

3 files changed

+57
-0
lines changed

src/Analyser/MutatingScope.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3229,6 +3229,32 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
32293229
}
32303230
}
32313231

3232+
foreach ($scope->conditionalExpressions as $conditionalExprString => $conditionalExpressions) {
3233+
if (array_key_exists($conditionalExprString, $conditions)) {
3234+
continue;
3235+
}
3236+
if (
3237+
!array_key_exists($conditionalExprString, $scope->expressionTypes)
3238+
|| $scope->expressionTypes[$conditionalExprString]->getCertainty()->yes()
3239+
|| !$scope->expressionTypes[$conditionalExprString]->getExpr() instanceof Variable
3240+
) {
3241+
continue;
3242+
}
3243+
foreach ($conditionalExpressions as $conditionalExpression) {
3244+
foreach ($conditionalExpression->getConditionExpressionTypeHolders() as $holderExprString => $conditionalTypeHolder) {
3245+
if (
3246+
!array_key_exists($holderExprString, $specifiedExpressions)
3247+
|| !$specifiedExpressions[$holderExprString]->getCertainty()->equals($conditionalTypeHolder->getCertainty())
3248+
|| !$conditionalTypeHolder->getType()->isSuperTypeOf($specifiedExpressions[$holderExprString]->getType())->yes()
3249+
) {
3250+
continue 2;
3251+
}
3252+
}
3253+
3254+
$conditions[$conditionalExprString][] = $conditionalExpression;
3255+
}
3256+
}
3257+
32323258
foreach ($conditions as $conditionalExprString => $expressions) {
32333259
$certainty = TrinaryLogic::lazyExtremeIdentity($expressions, static fn (ConditionalExpressionHolder $holder) => $holder->getTypeHolder()->getCertainty());
32343260
if ($certainty->no()) {

tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,15 @@ public function testBug8212(): void
905905
$this->analyse([__DIR__ . '/data/bug-8212.php'], []);
906906
}
907907

908+
public function testBug12597(): void
909+
{
910+
$this->cliArgumentsVariablesRegistered = true;
911+
$this->polluteScopeWithLoopInitialAssignments = true;
912+
$this->checkMaybeUndefinedVariables = true;
913+
$this->polluteScopeWithAlwaysIterableForeach = true;
914+
$this->analyse([__DIR__ . '/data/bug-12597.php'], []);
915+
}
916+
908917
public function testBug4173(): void
909918
{
910919
$this->cliArgumentsVariablesRegistered = true;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug12597;
4+
5+
class HelloWorld
6+
{
7+
private const TYPE_1 = 1;
8+
private const TYPE_2 = 2;
9+
10+
public function test(int $type): void
11+
{
12+
if (in_array($type, [self::TYPE_1, self::TYPE_2], true)) {
13+
$message = 'Hello!';
14+
}
15+
16+
if ($type === self::TYPE_1) {
17+
$this->message($message);
18+
}
19+
}
20+
21+
public function message(string $message): void {}
22+
}

0 commit comments

Comments
 (0)