Skip to content

Commit f87b890

Browse files
dmytro-dymarchukondrejmirtes
authored andcommitted
Fix scope in enum match arm body
1 parent b80968f commit f87b890

File tree

3 files changed

+56
-17
lines changed

3 files changed

+56
-17
lines changed

src/Analyser/NodeScopeResolver.php

+29-17
Original file line numberDiff line numberDiff line change
@@ -3644,6 +3644,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
36443644

36453645
$condNodes = [];
36463646
$conditionCases = [];
3647+
$conditionExprs = [];
36473648
foreach ($arm->conds as $cond) {
36483649
if (!$cond instanceof Expr\ClassConstFetch) {
36493650
continue 2;
@@ -3701,6 +3702,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
37013702
$armConditionScope,
37023703
$cond->getStartLine(),
37033704
);
3705+
$conditionExprs[] = $cond;
37043706

37053707
unset($unusedIndexedEnumCases[$loweredFetchedClassName][$caseName]);
37063708
}
@@ -3714,10 +3716,11 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
37143716
$conditionCaseType = new UnionType($conditionCases);
37153717
}
37163718

3719+
$filteringExpr = $this->getFilteringExprForMatchArm($expr, $conditionExprs);
37173720
$matchArmBodyScope = $matchScope->addTypeToExpression(
37183721
$expr->cond,
37193722
$conditionCaseType,
3720-
);
3723+
)->filterByTruthyValue($filteringExpr);
37213724
$matchArmBody = new MatchExpressionArmBody($matchArmBodyScope, $arm->body);
37223725
$armNodes[$i] = new MatchExpressionArm($matchArmBody, $condNodes, $arm->getStartLine());
37233726

@@ -3793,22 +3796,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
37933796
$filteringExprs[] = $armCond;
37943797
}
37953798

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

38133801
$bodyScope = $this->processExprNode($stmt, $filteringExpr, $matchScope, static function (): void {
38143802
}, $deepContext)->getTruthyScope();
@@ -6591,4 +6579,28 @@ private function getNextUnreachableStatements(array $nodes, bool $earlyBinding):
65916579
return $stmts;
65926580
}
65936581

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

tests/PHPStan/Analyser/NodeScopeResolverTest.php

+1
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';
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)