From 2e9c7638590a09e1a5dd80ee8fc35f0e28b6059f Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Sat, 3 Oct 2026 22:35:12 +0900 Subject: [PATCH 1/2] Report impure methods overriding @pure-unless-parameter-passed methods --- src/Rules/Methods/MethodSignatureRule.php | 12 ++++ .../Rules/Methods/MethodSignatureRuleTest.php | 13 +++++ ...signature-pure-unless-parameter-passed.php | 55 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 tests/PHPStan/Rules/Methods/data/method-signature-pure-unless-parameter-passed.php diff --git a/src/Rules/Methods/MethodSignatureRule.php b/src/Rules/Methods/MethodSignatureRule.php index 4f410a7b321..5d0cafb6024 100644 --- a/src/Rules/Methods/MethodSignatureRule.php +++ b/src/Rules/Methods/MethodSignatureRule.php @@ -94,6 +94,18 @@ public function processNode(Node $node, Scope $scope): array $parentMethodDeclaringClass->getDisplayName(), $parentMethod->getName(), ))->identifier('method.impureOverridePureUnlessCallable')->build(); + } elseif ( + $this->reportMethodPurityOverride + && $method->isPure()->no() + && count($parentMethod->getPureUnlessParameterPassedParameters()) > 0 + ) { + $errors[] = RuleErrorBuilder::message(sprintf( + 'Impure method %s::%s() overrides method %s::%s() marked @pure-unless-parameter-passed.', + $method->getDeclaringClass()->getDisplayName(), + $method->getName(), + $parentMethodDeclaringClass->getDisplayName(), + $parentMethod->getName(), + ))->identifier('method.impureOverridePureUnlessParameterPassed')->build(); } $parentVariants = $parentMethod->getVariants(); diff --git a/tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php b/tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php index 109b85fd3c9..403abab8187 100644 --- a/tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php +++ b/tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php @@ -659,4 +659,17 @@ public function testPureUnlessCallableIsImpureOverride(): void ]); } + public function testPureUnlessParameterPassedOverride(): void + { + $this->reportMaybes = true; + $this->reportStatic = true; + $this->reportMethodPurityOverride = true; + $this->analyse([__DIR__ . '/data/method-signature-pure-unless-parameter-passed.php'], [ + [ + 'Impure method MethodSignaturePureUnlessParameterPassed\ImpureChild::replace() overrides method MethodSignaturePureUnlessParameterPassed\PureUnlessParent::replace() marked @pure-unless-parameter-passed.', + 22, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Methods/data/method-signature-pure-unless-parameter-passed.php b/tests/PHPStan/Rules/Methods/data/method-signature-pure-unless-parameter-passed.php new file mode 100644 index 00000000000..4a0d1cd2457 --- /dev/null +++ b/tests/PHPStan/Rules/Methods/data/method-signature-pure-unless-parameter-passed.php @@ -0,0 +1,55 @@ + Date: Sun, 4 Oct 2026 02:42:32 +0900 Subject: [PATCH 2/2] Cover class-level purity tags on overrides of @pure-unless-parameter-passed methods --- .../Rules/Methods/MethodSignatureRuleTest.php | 4 +++ ...signature-pure-unless-parameter-passed.php | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php b/tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php index 403abab8187..f24317d6b61 100644 --- a/tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php +++ b/tests/PHPStan/Rules/Methods/MethodSignatureRuleTest.php @@ -669,6 +669,10 @@ public function testPureUnlessParameterPassedOverride(): void 'Impure method MethodSignaturePureUnlessParameterPassed\ImpureChild::replace() overrides method MethodSignaturePureUnlessParameterPassed\PureUnlessParent::replace() marked @pure-unless-parameter-passed.', 22, ], + [ + 'Impure method MethodSignaturePureUnlessParameterPassed\AllMethodsImpureChild::replace() overrides method MethodSignaturePureUnlessParameterPassed\PureUnlessParent::replace() marked @pure-unless-parameter-passed.', + 76, + ], ]); } diff --git a/tests/PHPStan/Rules/Methods/data/method-signature-pure-unless-parameter-passed.php b/tests/PHPStan/Rules/Methods/data/method-signature-pure-unless-parameter-passed.php index 4a0d1cd2457..600a91a91b9 100644 --- a/tests/PHPStan/Rules/Methods/data/method-signature-pure-unless-parameter-passed.php +++ b/tests/PHPStan/Rules/Methods/data/method-signature-pure-unless-parameter-passed.php @@ -53,3 +53,32 @@ public function replace(string $subject, int &$count = 0): string } } + +/** + * @phpstan-all-methods-pure + */ +class AllMethodsPureChild implements PureUnlessParent +{ + + public function replace(string $subject, int &$count = 0): string + { + return $subject; + } + +} + +/** + * @phpstan-all-methods-impure + */ +class AllMethodsImpureChild implements PureUnlessParent +{ + + public function replace(string $subject, int &$count = 0): string + { + echo 'side effect'; + $count = 1; + + return $subject; + } + +}