From cbb796380815485a9986a0945f1c5b6657a60ba1 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Fri, 14 Jan 2022 15:49:25 +0100 Subject: [PATCH] Readonly properties cannot be invalidated --- src/Analyser/MutatingScope.php | 14 ++++++++- .../Analyser/NodeScopeResolverTest.php | 5 +++ .../data/invalidate-readonly-properties.php | 31 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Analyser/data/invalidate-readonly-properties.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index b52590b91e..f337f2981e 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -3827,7 +3827,19 @@ public function invalidateExpression(Expr $expressionToInvalidate, bool $require if (!$expr instanceof Node\Stmt\Expression) { throw new ShouldNotHappenException(); } - $found = $nodeFinder->findFirst([$expr->expr], function (Node $node) use ($expressionToInvalidateClass, $exprStringToInvalidate): bool { + + $exprExpr = $expr->expr; + if ($exprExpr instanceof PropertyFetch) { + $propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($exprExpr, $this); + if ($propertyReflection !== null) { + $nativePropertyReflection = $propertyReflection->getNativeReflection(); + if ($nativePropertyReflection !== null && $nativePropertyReflection->isReadOnly()) { + continue; + } + } + } + + $found = $nodeFinder->findFirst([$exprExpr], function (Node $node) use ($expressionToInvalidateClass, $exprStringToInvalidate): bool { if (!$node instanceof $expressionToInvalidateClass) { return false; } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index dcd8e99282..5a41492b5e 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -602,6 +602,11 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-2806.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5328.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3044.php'); + + if (PHP_VERSION_ID >= 80100) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/invalidate-readonly-properties.php'); + } + yield from $this->gatherAssertTypes(__DIR__ . '/data/weird-array_key_exists-issue.php'); } diff --git a/tests/PHPStan/Analyser/data/invalidate-readonly-properties.php b/tests/PHPStan/Analyser/data/invalidate-readonly-properties.php new file mode 100644 index 0000000000..5eb178a8a8 --- /dev/null +++ b/tests/PHPStan/Analyser/data/invalidate-readonly-properties.php @@ -0,0 +1,31 @@ += 8.1 + +namespace InvalidateReadonlyProperties; + +use function PHPStan\Testing\assertType; + +class Foo +{ + + private readonly int $foo; + + public function __construct(int $foo) + { + $this->foo = $foo; + } + + public function doFoo(): void + { + if ($this->foo === 1) { + assertType('1', $this->foo); + $this->doBar(); + assertType('1', $this->foo); + } + } + + public function doBar(): void + { + + } + +}