Skip to content

Commit 3b25234

Browse files
committed
Remember narrowed readonly types in anonymous functions
1 parent 8d619a6 commit 3b25234

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
@@ -3707,19 +3707,40 @@ private function enterAnonymousFunctionWithoutReflection(
37073707

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

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)