Skip to content

Commit afd310d

Browse files
phpstan-botstaabmVincentLanglet
authored
Fix phpstan/phpstan#13981: Assignment inside match statement no longer recognized (#5120)
Co-authored-by: staabm <120441+staabm@users.noreply.github.com> Co-authored-by: Vincent Langlet <vincentlanglet@hotmail.fr>
1 parent 1b3396d commit afd310d

File tree

3 files changed

+91
-3
lines changed

3 files changed

+91
-3
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4438,6 +4438,8 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
44384438
$filteringExprs = [];
44394439
$armCondScope = $matchScope;
44404440
$condNodes = [];
4441+
$armCondResultScope = $matchScope;
4442+
$bodyScope = null;
44414443
foreach ($arm->conds as $j => $armCond) {
44424444
if (isset($armCondsToSkip[$i][$j])) {
44434445
continue;
@@ -4453,12 +4455,17 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
44534455
if ($armCondType->isTrue()->yes()) {
44544456
$hasAlwaysTrueCond = true;
44554457
}
4456-
$armCondScope = $armCondResult->getScope()->filterByFalseyValue($armCondExpr);
4458+
$armCondScope = $armCondResultScope->filterByFalseyValue($armCondExpr);
4459+
if ($bodyScope === null) {
4460+
$bodyScope = $armCondResultScope->filterByTruthyValue($armCondExpr);
4461+
} else {
4462+
$bodyScope = $bodyScope->mergeWith($armCondResultScope->filterByTruthyValue($armCondExpr));
4463+
}
44574464
$filteringExprs[] = $armCond;
44584465
}
44594466

44604467
$filteringExpr = $this->getFilteringExprForMatchArm($expr, $filteringExprs);
4461-
$bodyScope = $matchScope->filterByTruthyValue($filteringExpr);
4468+
$bodyScope ??= $matchScope->filterByTruthyValue($filteringExpr);
44624469
$matchArmBody = new MatchExpressionArmBody($bodyScope, $arm->body);
44634470
$armNodes[$i] = new MatchExpressionArm($matchArmBody, $condNodes, $arm->getStartLine());
44644471

@@ -4475,7 +4482,7 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
44754482
$hasYield = $hasYield || $armResult->hasYield();
44764483
$throwPoints = array_merge($throwPoints, $armResult->getThrowPoints());
44774484
$impurePoints = array_merge($impurePoints, $armResult->getImpurePoints());
4478-
$matchScope = $matchScope->filterByFalseyValue($filteringExpr);
4485+
$matchScope = $armCondScope->filterByFalseyValue($filteringExpr);
44794486
}
44804487

44814488
if (!$hasDefaultCond && !$hasAlwaysTrueCond && $condType->isBoolean()->yes() && $condType->isConstantScalarValue()->yes()) {

tests/PHPStan/Rules/Variables/DefinedVariableRuleTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,26 @@ public function testBug10909(): void
12351235
$this->analyse([__DIR__ . '/data/bug-10909.php'], []);
12361236
}
12371237

1238+
#[RequiresPhp('>= 8.0')]
1239+
public function testBug13981(): void
1240+
{
1241+
$this->cliArgumentsVariablesRegistered = true;
1242+
$this->polluteScopeWithLoopInitialAssignments = true;
1243+
$this->checkMaybeUndefinedVariables = true;
1244+
$this->polluteScopeWithAlwaysIterableForeach = true;
1245+
1246+
$this->analyse([__DIR__ . '/data/bug-13981.php'], [
1247+
[
1248+
'Undefined variable: $baseDir',
1249+
34,
1250+
],
1251+
[
1252+
'Variable $baseDir might not be defined.',
1253+
46,
1254+
],
1255+
]);
1256+
}
1257+
12381258
#[RequiresPhp('>= 8.0')]
12391259
public function testBug7705(): void
12401260
{
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug13981;
6+
7+
function foo(): string
8+
{
9+
$path = match (true) {
10+
is_dir($baseDir = dirname(__DIR__).'/lang') => $baseDir,
11+
default => '/translations',
12+
};
13+
14+
return $path;
15+
}
16+
17+
function foo2(): string
18+
{
19+
if (rand(0, 1)) {
20+
$baseDir = '';
21+
}
22+
23+
$path = match (true) {
24+
is_dir($baseDir = dirname(__DIR__).'/lang') => $baseDir,
25+
default => '/translations',
26+
};
27+
28+
return $path;
29+
}
30+
31+
function foo3(): string
32+
{
33+
$path = match (true) {
34+
is_dir(dirname(__DIR__).'/lang2') => $baseDir,
35+
is_dir($baseDir = dirname(__DIR__).'/lang') => $baseDir,
36+
default => '/translations',
37+
};
38+
39+
return $path;
40+
}
41+
42+
function foo4(): string
43+
{
44+
$path = match (true) {
45+
is_dir(dirname(__DIR__).'/lang2'),
46+
is_dir($baseDir = dirname(__DIR__).'/lang') => $baseDir,
47+
default => '/translations',
48+
};
49+
50+
return $path;
51+
}
52+
53+
function foo5(): string
54+
{
55+
$path = match (true) {
56+
is_dir($baseDir = dirname(__DIR__).'/lang') => '$baseDir',
57+
default => $baseDir,
58+
};
59+
60+
return $path;
61+
}

0 commit comments

Comments
 (0)