Skip to content

Commit 239c64c

Browse files
committed
Fix #12875
1 parent 6b63691 commit 239c64c

4 files changed

Lines changed: 98 additions & 2 deletions

File tree

src/Analyser/MutatingScope.php

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2653,7 +2653,7 @@ public function enterAnonymousFunction(
26532653
$scope->getNamespace(),
26542654
$scope->expressionTypes,
26552655
$scope->nativeExpressionTypes,
2656-
[],
2656+
$scope->conditionalExpressions,
26572657
$scope->inClosureBindScopeClasses,
26582658
$anonymousFunctionReflection,
26592659
true,
@@ -2703,12 +2703,14 @@ private function enterAnonymousFunctionWithoutReflection(
27032703
}
27042704

27052705
$nonRefVariableNames = [];
2706+
$useVariableNames = [];
27062707
foreach ($closure->uses as $use) {
27072708
if (!is_string($use->var->name)) {
27082709
throw new ShouldNotHappenException();
27092710
}
27102711
$variableName = $use->var->name;
27112712
$paramExprString = '$' . $use->var->name;
2713+
$useVariableNames[$paramExprString] = true;
27122714
if ($use->byRef) {
27132715
$holder = ExpressionTypeHolder::createYes($use->var, new MixedType());
27142716
$expressionTypes[$paramExprString] = $holder;
@@ -2774,14 +2776,33 @@ private function enterAnonymousFunctionWithoutReflection(
27742776
}
27752777
}
27762778

2779+
$filteredConditionalExpressions = [];
2780+
foreach ($this->conditionalExpressions as $conditionalExprString => $holders) {
2781+
if (!array_key_exists($conditionalExprString, $useVariableNames)) {
2782+
continue;
2783+
}
2784+
$filteredHolders = [];
2785+
foreach ($holders as $holder) {
2786+
foreach ($holder->getConditionExpressionTypeHolders() as $holderExprString => $conditionalTypeHolder) {
2787+
if (!array_key_exists($holderExprString, $useVariableNames)) {
2788+
continue 2;
2789+
}
2790+
}
2791+
$filteredHolders[] = $holder;
2792+
}
2793+
if ($filteredHolders !== []) {
2794+
$filteredConditionalExpressions[$conditionalExprString] = $filteredHolders;
2795+
}
2796+
}
2797+
27772798
return $this->scopeFactory->create(
27782799
$this->context,
27792800
$this->isDeclareStrictTypes(),
27802801
$this->getFunction(),
27812802
$this->getNamespace(),
27822803
array_merge($this->getConstantTypes(), $expressionTypes),
27832804
array_merge($this->getNativeConstantTypes(), $nativeTypes),
2784-
[],
2805+
$filteredConditionalExpressions,
27852806
$this->inClosureBindScopeClasses,
27862807
new ClosureType(),
27872808
true,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php declare(strict_types = 1); // lint >= 8.0
2+
3+
namespace Bug12875;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
interface HasFoo
8+
{
9+
public function foo(): int;
10+
}
11+
12+
interface HasBar
13+
{
14+
public function bar(): int;
15+
}
16+
17+
class HelloWorld
18+
{
19+
/**
20+
* @param "foo"|"bar" $method
21+
* @param ($method is "foo" ? HasFoo : HasBar) $a
22+
* @param ($method is "foo" ? HasFoo : HasBar) $b
23+
*/
24+
public function add(string $method, HasFoo|HasBar $a, HasFoo|HasBar $b): void
25+
{
26+
assertType('int', $a->{$method}());
27+
assertType('int', $b->{$method}());
28+
29+
$addInArrow = fn () => assertType('int', $a->{$method}());
30+
31+
$addInAnonymous = function () use ($a, $b, $method): void {
32+
assertType('int', $a->{$method}());
33+
assertType('int', $b->{$method}());
34+
};
35+
}
36+
}

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3827,4 +3827,13 @@ public function testDiscussion14038(): void
38273827
$this->analyse([__DIR__ . '/data/discussion-14038.php'], []);
38283828
}
38293829

3830+
public function testBug12875(): void
3831+
{
3832+
$this->checkThisOnly = false;
3833+
$this->checkNullables = true;
3834+
$this->checkUnionTypes = true;
3835+
$this->checkExplicitMixed = true;
3836+
$this->analyse([__DIR__ . '/data/bug-12875.php'], []);
3837+
}
3838+
38303839
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1); // lint >= 8.0
2+
3+
namespace Bug12875;
4+
5+
interface HasFoo
6+
{
7+
public function foo(): int;
8+
}
9+
10+
interface HasBar
11+
{
12+
public function bar(): int;
13+
}
14+
15+
class HelloWorld
16+
{
17+
/**
18+
* @param "foo"|"bar" $method
19+
* @param ($method is "foo" ? HasFoo : HasBar) $a
20+
* @param ($method is "foo" ? HasFoo : HasBar) $b
21+
*/
22+
public function add(string $method, HasFoo|HasBar $a, HasFoo|HasBar $b): void
23+
{
24+
$add = $a->{$method}() + $b->{$method}();
25+
$addInArrow = fn () => $a->{$method}() + $b->{$method}();
26+
$addInAnonymous = function () use ($a, $b, $method): int {
27+
return $a->{$method}() + $b->{$method}();
28+
};
29+
}
30+
}

0 commit comments

Comments
 (0)