Skip to content

Commit d7da40a

Browse files
ondrejmirtesphpstan-bot
authored andcommitted
Fix assignment inside match arm condition not recognized
- Variable assignments in match arm conditions (e.g. `is_dir($baseDir = ...)`) were lost after commit 3beb8c6 replaced processExprNode with filterByTruthyValue - filterByTruthyValue only narrows types without walking the AST, so assignments within condition expressions were not discovered for the arm body scope - Fix transfers newly-defined variables from the condition processing scope to the body scope after applying truthiness filtering - New regression test in tests/PHPStan/Rules/Variables/data/bug-13981.php Closes phpstan/phpstan#13981
1 parent 03e0417 commit d7da40a

File tree

3 files changed

+47
-0
lines changed

3 files changed

+47
-0
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@
208208
use UnhandledMatchError;
209209
use function array_fill_keys;
210210
use function array_filter;
211+
use function array_flip;
211212
use function array_key_exists;
212213
use function array_key_last;
213214
use function array_keys;
@@ -4355,6 +4356,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
43554356
$filteringExprs = [];
43564357
$armCondScope = $matchScope;
43574358
$condNodes = [];
4359+
$armCondResultScope = $matchScope;
43584360
foreach ($arm->conds as $j => $armCond) {
43594361
if (isset($armCondsToSkip[$i][$j])) {
43604362
continue;
@@ -4376,6 +4378,30 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
43764378

43774379
$filteringExpr = $this->getFilteringExprForMatchArm($expr, $filteringExprs);
43784380
$bodyScope = $matchScope->filterByTruthyValue($filteringExpr);
4381+
$condResultScope = $armCondResultScope;
4382+
$matchScopeKnownVars = array_flip(array_merge($matchScope->getDefinedVariables(), $matchScope->getMaybeDefinedVariables()));
4383+
foreach ($condResultScope->getDefinedVariables() as $varName) {
4384+
if (isset($matchScopeKnownVars[$varName])) {
4385+
continue;
4386+
}
4387+
$bodyScope = $bodyScope->assignVariable(
4388+
$varName,
4389+
$condResultScope->getVariableType($varName),
4390+
$condResultScope->getNativeType(new Variable($varName)),
4391+
$condResultScope->hasVariableType($varName),
4392+
);
4393+
}
4394+
foreach ($condResultScope->getMaybeDefinedVariables() as $varName) {
4395+
if (isset($matchScopeKnownVars[$varName])) {
4396+
continue;
4397+
}
4398+
$bodyScope = $bodyScope->assignVariable(
4399+
$varName,
4400+
$condResultScope->getVariableType($varName),
4401+
$condResultScope->getNativeType(new Variable($varName)),
4402+
$condResultScope->hasVariableType($varName),
4403+
);
4404+
}
43794405
$matchArmBody = new MatchExpressionArmBody($bodyScope, $arm->body);
43804406
$armNodes[$i] = new MatchExpressionArm($matchArmBody, $condNodes, $arm->getStartLine());
43814407

tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,4 +1257,15 @@ public function testBug12944(): void
12571257
$this->analyse([__DIR__ . '/data/bug-12944.php'], []);
12581258
}
12591259

1260+
#[RequiresPhp('>= 8.0')]
1261+
public function testBug13981(): void
1262+
{
1263+
$this->cliArgumentsVariablesRegistered = true;
1264+
$this->polluteScopeWithLoopInitialAssignments = true;
1265+
$this->checkMaybeUndefinedVariables = true;
1266+
$this->polluteScopeWithAlwaysIterableForeach = true;
1267+
1268+
$this->analyse([__DIR__ . '/data/bug-13981.php'], []);
1269+
}
1270+
12601271
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug13981;
6+
7+
$path = match (true) {
8+
is_dir($baseDir = dirname(__DIR__).'/lang') => $baseDir,
9+
default => '/translations',
10+
};

0 commit comments

Comments
 (0)