From d3a9187eb5504fe15b517b1a2fd699b07f177573 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 31 Mar 2025 20:42:20 +0200 Subject: [PATCH 1/4] Disable purity check for non final methods --- src/Rules/Pure/FunctionPurityCheck.php | 5 +++ .../PHPStan/Rules/Pure/PureMethodRuleTest.php | 17 +++++++- tests/PHPStan/Rules/Pure/data/bug-12382.php | 36 ++++++++++++++++ .../Rules/Pure/data/pure-constructor.php | 6 +-- tests/PHPStan/Rules/Pure/data/pure-method.php | 42 +++++++++---------- 5 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 tests/PHPStan/Rules/Pure/data/bug-12382.php diff --git a/src/Rules/Pure/FunctionPurityCheck.php b/src/Rules/Pure/FunctionPurityCheck.php index ccc85a1c24..a799cd4351 100644 --- a/src/Rules/Pure/FunctionPurityCheck.php +++ b/src/Rules/Pure/FunctionPurityCheck.php @@ -92,6 +92,11 @@ public function check( count($throwPoints) === 0 && count($impurePoints) === 0 && count($functionReflection->getAsserts()->getAll()) === 0 + && ( + !$functionReflection instanceof ExtendedMethodReflection + || $functionReflection->isFinal()->yes() + || $functionReflection->getDeclaringClass()->isFinal() + ) ) { $errors[] = RuleErrorBuilder::message(sprintf( '%s is marked as impure but does not have any side effects.', diff --git a/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php b/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php index a483c6d580..7649d25035 100644 --- a/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php +++ b/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php @@ -215,9 +215,24 @@ public function testBug12048(): void public function testBug12224(): void { $this->treatPhpDocTypesAsCertain = true; - $this->analyse([__DIR__ . '/data/bug-12224.php'], [ + $this->analyse([__DIR__.'/data/bug-12224.php'], [ ['Method PHPStan\Rules\Pure\data\A::pureWithThrowsVoid() is marked as pure but returns void.', 47], ]); } + public function testBug12382(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->analyse([__DIR__ . '/data/bug-12382.php'], [ + [ + 'Method Bug12382\FinalHelloWorld1::dummy() is marked as impure but does not have any side effects.', + 25, + ], + [ + 'Method Bug12382\FinalHelloWorld2::dummy() is marked as impure but does not have any side effects.', + 33, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Pure/data/bug-12382.php b/tests/PHPStan/Rules/Pure/data/bug-12382.php new file mode 100644 index 0000000000..f9f3b2a40b --- /dev/null +++ b/tests/PHPStan/Rules/Pure/data/bug-12382.php @@ -0,0 +1,36 @@ +prop++; + return $this; + } +} + +final class FinalHelloWorld1 +{ + /** @phpstan-impure */ + public function dummy() : self{ + return $this; + } +} + +class FinalHelloWorld2 +{ + /** @phpstan-impure */ + final public function dummy() : self{ + return $this; + } +} diff --git a/tests/PHPStan/Rules/Pure/data/pure-constructor.php b/tests/PHPStan/Rules/Pure/data/pure-constructor.php index 71045fd3ed..baa1f755cf 100644 --- a/tests/PHPStan/Rules/Pure/data/pure-constructor.php +++ b/tests/PHPStan/Rules/Pure/data/pure-constructor.php @@ -2,7 +2,7 @@ namespace PureConstructor; -class Foo +final class Foo { private string $prop; @@ -21,7 +21,7 @@ public function __construct( } -class Bar +final class Bar { private string $prop; @@ -37,7 +37,7 @@ public function __construct( } -class AssignOtherThanThis +final class AssignOtherThanThis { private int $i = 0; diff --git a/tests/PHPStan/Rules/Pure/data/pure-method.php b/tests/PHPStan/Rules/Pure/data/pure-method.php index efa83c9191..7ac37bd82f 100644 --- a/tests/PHPStan/Rules/Pure/data/pure-method.php +++ b/tests/PHPStan/Rules/Pure/data/pure-method.php @@ -2,7 +2,7 @@ namespace PureMethod; -class Foo +final class Foo { /** @@ -92,7 +92,7 @@ public function doFoo5() } -class PureConstructor +final class PureConstructor { /** @@ -105,7 +105,7 @@ public function __construct() } -class ImpureConstructor +final class ImpureConstructor { /** @@ -118,7 +118,7 @@ public function __construct() } -class PossiblyImpureConstructor +final class PossiblyImpureConstructor { public function __construct() @@ -128,7 +128,7 @@ public function __construct() } -class TestConstructors +final class TestConstructors { /** @@ -144,7 +144,7 @@ public function doFoo(string $s) } -class ActuallyPure +final class ActuallyPure { /** @@ -157,7 +157,7 @@ public function doFoo() } -class ToBeExtended +final class ToBeExtended { /** @phpstan-pure */ @@ -175,7 +175,7 @@ public function impure(): int } -class ExtendingClass extends ToBeExtended +final class ExtendingClass extends ToBeExtended { public function pure(): int @@ -191,7 +191,7 @@ public function impure(): int } -class ClassWithVoidMethods +final class ClassWithVoidMethods { public function voidFunctionThatThrows(): void @@ -235,12 +235,12 @@ public function purePostGetAssign(array $post = [], array $get = []): int } -class NoMagicMethods +final class NoMagicMethods { } -class PureMagicMethods +final class PureMagicMethods { /** @@ -253,7 +253,7 @@ public function __toString(): string } -class MaybePureMagicMethods +final class MaybePureMagicMethods { public function __toString(): string @@ -263,7 +263,7 @@ public function __toString(): string } -class ImpureMagicMethods +final class ImpureMagicMethods { /** @@ -277,7 +277,7 @@ public function __toString(): string } -class TestMagicMethods +final class TestMagicMethods { /** @@ -298,12 +298,12 @@ public function doFoo( } -class NoConstructor +final class NoConstructor { } -class TestNoConstructor +final class TestNoConstructor { /** @@ -318,7 +318,7 @@ public function doFoo(): int } -class MaybeCallableFromUnion +final class MaybeCallableFromUnion { /** @@ -334,7 +334,7 @@ public function doFoo($p): int } -class VoidMethods +final class VoidMethods { private function doFoo(): void @@ -361,7 +361,7 @@ private function doBaz(): void } -class AssertingImpureVoidMethod +final class AssertingImpureVoidMethod { /** @@ -376,7 +376,7 @@ public function assertSth($value): void } -class StaticMethodAccessingStaticProperty +final class StaticMethodAccessingStaticProperty { /** @var int */ public static $a = 0; @@ -397,7 +397,7 @@ public static function getB(): int } } -class StaticMethodAssigningStaticProperty +final class StaticMethodAssigningStaticProperty { /** @var int */ public static $a = 0; From 778088190b2a88409d69810a75c43ef9a6883cb4 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 31 Mar 2025 23:00:59 +0200 Subject: [PATCH 2/4] Fix --- tests/PHPStan/Rules/Pure/data/pure-method.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Pure/data/pure-method.php b/tests/PHPStan/Rules/Pure/data/pure-method.php index 7ac37bd82f..ba2ce7e3be 100644 --- a/tests/PHPStan/Rules/Pure/data/pure-method.php +++ b/tests/PHPStan/Rules/Pure/data/pure-method.php @@ -157,7 +157,7 @@ public function doFoo() } -final class ToBeExtended +class ToBeExtended { /** @phpstan-pure */ From 385e116099e9bd5067944b9dde2359a29ab650d1 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 3 Apr 2025 19:53:37 +0200 Subject: [PATCH 3/4] Annotation test --- .../PHPStan/Rules/Pure/PureMethodRuleTest.php | 8 ++++++++ tests/PHPStan/Rules/Pure/data/bug-12382.php | 20 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php b/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php index 7649d25035..6018ef4f42 100644 --- a/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php +++ b/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php @@ -232,6 +232,14 @@ public function testBug12382(): void 'Method Bug12382\FinalHelloWorld2::dummy() is marked as impure but does not have any side effects.', 33, ], + [ + 'Method Bug12382\FinalHelloWorld3::dummy() is marked as impure but does not have any side effects.', + 42, + ], + [ + 'Method Bug12382\FinalHelloWorld4::dummy() is marked as impure but does not have any side effects.', + 53, + ], ]); } diff --git a/tests/PHPStan/Rules/Pure/data/bug-12382.php b/tests/PHPStan/Rules/Pure/data/bug-12382.php index f9f3b2a40b..7a66941da8 100644 --- a/tests/PHPStan/Rules/Pure/data/bug-12382.php +++ b/tests/PHPStan/Rules/Pure/data/bug-12382.php @@ -34,3 +34,23 @@ final public function dummy() : self{ return $this; } } + +/** @final */ +class FinalHelloWorld3 +{ + /** @phpstan-impure */ + public function dummy() : self{ + return $this; + } +} + +class FinalHelloWorld4 +{ + /** + * @final + * @phpstan-impure + */ + public function dummy() : self{ + return $this; + } +} From 15e8eceb250ef5a75fc9b5ab83c98ce351514455 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Tue, 6 May 2025 14:37:12 +0200 Subject: [PATCH 4/4] Fix lint --- tests/PHPStan/Rules/Pure/PureMethodRuleTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php b/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php index 6018ef4f42..c31874629d 100644 --- a/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php +++ b/tests/PHPStan/Rules/Pure/PureMethodRuleTest.php @@ -215,7 +215,7 @@ public function testBug12048(): void public function testBug12224(): void { $this->treatPhpDocTypesAsCertain = true; - $this->analyse([__DIR__.'/data/bug-12224.php'], [ + $this->analyse([__DIR__ . '/data/bug-12224.php'], [ ['Method PHPStan\Rules\Pure\data\A::pureWithThrowsVoid() is marked as pure but returns void.', 47], ]); }