Skip to content

Commit 85698e2

Browse files
Fix scope in enum match arm body
1 parent 72a1607 commit 85698e2

File tree

3 files changed

+56
-17
lines changed

3 files changed

+56
-17
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3641,6 +3641,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
36413641

36423642
$condNodes = [];
36433643
$conditionCases = [];
3644+
$conditionExprs = [];
36443645
foreach ($arm->conds as $cond) {
36453646
if (!$cond instanceof Expr\ClassConstFetch) {
36463647
continue 2;
@@ -3698,6 +3699,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
36983699
$armConditionScope,
36993700
$cond->getStartLine(),
37003701
);
3702+
$conditionExprs[] = $cond;
37013703

37023704
unset($unusedIndexedEnumCases[$loweredFetchedClassName][$caseName]);
37033705
}
@@ -3711,10 +3713,11 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
37113713
$conditionCaseType = new UnionType($conditionCases);
37123714
}
37133715

3716+
$filteringExpr = $this->getFilteringExprForMatchArm($expr, $conditionExprs);
37143717
$matchArmBodyScope = $matchScope->addTypeToExpression(
37153718
$expr->cond,
37163719
$conditionCaseType,
3717-
);
3720+
)->filterByTruthyValue($filteringExpr);
37183721
$matchArmBody = new MatchExpressionArmBody($matchArmBodyScope, $arm->body);
37193722
$armNodes[$i] = new MatchExpressionArm($matchArmBody, $condNodes, $arm->getStartLine());
37203723

@@ -3790,22 +3793,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
37903793
$filteringExprs[] = $armCond;
37913794
}
37923795

3793-
if (count($filteringExprs) === 1) {
3794-
$filteringExpr = new BinaryOp\Identical($expr->cond, $filteringExprs[0]);
3795-
} else {
3796-
$items = [];
3797-
foreach ($filteringExprs as $filteringExpr) {
3798-
$items[] = new Node\ArrayItem($filteringExpr);
3799-
}
3800-
$filteringExpr = new FuncCall(
3801-
new Name\FullyQualified('in_array'),
3802-
[
3803-
new Arg($expr->cond),
3804-
new Arg(new Array_($items)),
3805-
new Arg(new ConstFetch(new Name\FullyQualified('true'))),
3806-
],
3807-
);
3808-
}
3796+
$filteringExpr = $this->getFilteringExprForMatchArm($expr, $filteringExprs);
38093797

38103798
$bodyScope = $this->processExprNode($stmt, $filteringExpr, $matchScope, static function (): void {
38113799
}, $deepContext)->getTruthyScope();
@@ -6598,4 +6586,28 @@ private function getNextUnreachableStatements(array $nodes, bool $earlyBinding):
65986586
return $stmts;
65996587
}
66006588

6589+
/**
6590+
* @param array<Expr> $conditions
6591+
*/
6592+
public function getFilteringExprForMatchArm(Expr\Match_ $expr, array $conditions): BinaryOp\Identical|FuncCall
6593+
{
6594+
if (count($conditions) === 1) {
6595+
return new BinaryOp\Identical($expr->cond, $conditions[0]);
6596+
}
6597+
6598+
$items = [];
6599+
foreach ($conditions as $filteringExpr) {
6600+
$items[] = new Node\ArrayItem($filteringExpr);
6601+
}
6602+
6603+
return new FuncCall(
6604+
new Name\FullyQualified('in_array'),
6605+
[
6606+
new Arg($expr->cond),
6607+
new Arg(new Array_($items)),
6608+
new Arg(new ConstFetch(new Name\FullyQualified('true'))),
6609+
],
6610+
);
6611+
}
6612+
66016613
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ private static function findTestFiles(): iterable
102102
define('TEST_ARRAY_CONSTANT', [true, false, null]);
103103
define('TEST_ENUM_CONSTANT', Foo::ONE);
104104
yield __DIR__ . '/data/new-in-initializers-runtime.php';
105+
yield __DIR__ . '/data/scope-in-enum-match-arm-body.php';
105106
}
106107

107108
yield __DIR__ . '/../Rules/Comparison/data/bug-6473.php';
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace ScopeInEnumMatchArmBody;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
enum Foo: int
8+
{
9+
case ALLOW_NULLABLE_INT = 1;
10+
case ALLOW_ONLY_INT = 2;
11+
12+
public function bar(?int $nullable): void
13+
{
14+
if ($nullable === null && $this === self::ALLOW_ONLY_INT) {
15+
throw new \LogicException('Cannot be null');
16+
}
17+
18+
match ($this) {
19+
self::ALLOW_ONLY_INT => assertType('int', $nullable),
20+
self::ALLOW_NULLABLE_INT => assertType('int|null', $nullable),
21+
};
22+
}
23+
}
24+
25+
26+

0 commit comments

Comments
 (0)