Skip to content

Commit a8528fd

Browse files
committed
Remember narrowed readonly types in anonymous functions
1 parent ce6bef1 commit a8528fd

File tree

2 files changed

+75
-12
lines changed

2 files changed

+75
-12
lines changed

src/Analyser/MutatingScope.php

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3708,19 +3708,40 @@ private function enterAnonymousFunctionWithoutReflection(
37083708

37093709
foreach ($this->invalidateStaticExpressions($this->expressionTypes) as $exprString => $typeHolder) {
37103710
$expr = $typeHolder->getExpr();
3711-
if ($expr instanceof Variable) {
3712-
continue;
3713-
}
3714-
$variables = (new NodeFinder())->findInstanceOf([$expr], Variable::class);
3715-
if ($variables === [] && !$this->expressionTypeIsUnchangeable($typeHolder)) {
3716-
continue;
3717-
}
3718-
foreach ($variables as $variable) {
3719-
if (!is_string($variable->name)) {
3720-
continue 2;
3711+
3712+
if (
3713+
$expr instanceof PropertyFetch
3714+
&& $expr->name instanceof Node\Identifier
3715+
&& $expr->var instanceof Variable
3716+
&& is_string($expr->var->name)
3717+
&& $expr->var->name === 'this'
3718+
&& $this->phpVersion->supportsReadOnlyProperties()
3719+
) {
3720+
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr, $this);
3721+
if ($propertyReflection === null) {
3722+
continue;
37213723
}
3722-
if (!array_key_exists($variable->name, $nonRefVariableNames)) {
3723-
continue 2;
3724+
$nativePropertyReflection = $propertyReflection->getNativeReflection();
3725+
if ($nativePropertyReflection === null || !$nativePropertyReflection->isReadOnly()) {
3726+
continue;
3727+
}
3728+
} else {
3729+
if ($expr instanceof Variable) {
3730+
continue;
3731+
}
3732+
3733+
$variables = (new NodeFinder())->findInstanceOf([$expr], Variable::class);
3734+
if ($variables === [] && !$this->expressionTypeIsUnchangeable($typeHolder)) {
3735+
continue;
3736+
}
3737+
3738+
foreach ($variables as $variable) {
3739+
if (!is_string($variable->name)) {
3740+
continue 2;
3741+
}
3742+
if (!array_key_exists($variable->name, $nonRefVariableNames)) {
3743+
continue 2;
3744+
}
37243745
}
37253746
}
37263747

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Bug13321;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
public function __construct(public readonly string $value)
10+
{
11+
}
12+
}
13+
14+
class Bar
15+
{
16+
public function __construct(
17+
private readonly ?Foo $foo,
18+
private ?Foo $writableFoo = null,
19+
)
20+
{
21+
}
22+
23+
public function bar(): void
24+
{
25+
if ($this->foo === null) {
26+
return;
27+
}
28+
if ($this->writableFoo === null) {
29+
return;
30+
}
31+
32+
$test = function () {
33+
assertType(Foo::class, $this->foo);
34+
assertType(Foo::class.'|null', $this->writableFoo);
35+
36+
echo $this->foo->value;
37+
};
38+
39+
$test();
40+
}
41+
42+
}

0 commit comments

Comments
 (0)