Check @phpstan-assert $this narrowing against prototype declaring class in overriding methods#5644
Open
phpstan-bot wants to merge 1 commit into
Open
Conversation
…lass in overriding methods - When a child class overrides a method and repeats a `@phpstan-assert` on `$this`, `$this->property`, or `$this->method()`, the assertion was checked against the child class type, which may already satisfy it. Now checks against the prototype's declaring class type when the method is an override, suppressing false "does not narrow down the type" and "can never happen" errors. - Fix applies to all assertion variants: `@phpstan-assert`, `@phpstan-assert-if-true`, `@phpstan-assert-if-false`, and negated assertions. - Also handles interface hierarchy: when an interface declares the assertion and the implementing class satisfies it. - Also handles `$this->property` and `$this->method()` assertions where the prototype's property type or method return type is wider.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a child class overrides a method and explicitly repeats a
@phpstan-asserton$this(or$this->property/$this->method()), PHPStan incorrectly reports "does not narrow down the type" or "can never happen" because the child class's$thistype already satisfies the assertion. The assertion is valid in the parent class context and should not be flagged.Changes
src/Rules/PhpDoc/AssertRuleHelper.php: when checking whether a$thisassertion narrows the type, if the method overrides a parent method (prototype has a different declaring class), re-evaluate the assertion against the prototype's declaring class type. If the assertion would narrow the prototype's type (result ismaybe), suppress the error.tests/PHPStan/Rules/PhpDoc/data/bug-10787.phpcovering:@phpstan-assert FooInterface $this(basic case)@phpstan-assert-if-true FooInterface $this@phpstan-assert-if-false FooInterface $this@phpstan-assert !FooInterface $this(negated)@phpstan-assert int $this->value(property assertion)@phpstan-assert-if-true int $this->getValue()(method return type assertion)Root cause
AssertRuleHelper::check()uses$reflection->getDeclaringClass()to determine the type of$this. When a child class overrides a method, the declaring class is the child class. If the child class already satisfies the asserted type (e.g., implements the asserted interface), the assertion appears to not narrow the type. But the assertion is valid in the parent class context, where$thishas a wider type.The fix checks the method's prototype (via
$reflection->getPrototype()->getDeclaringClass()). If the prototype's declaring class is different, the assertion is re-evaluated against the prototype's$thistype. If the assertion narrows the prototype's type, the error is suppressed.Test
tests/PHPStan/Rules/PhpDoc/MethodAssertRuleTest::testBug10787— covers the reported bug and all analogous cases listed above. All seven assertion variants were verified to produce false errors without the fix and no errors with the fix.FunctionAssertRule— not affected (functions don't have$this)incompatible-assert-type-with-method-return-typetest — still correctly reports error (no prototype difference since both methods declared in same class)Fixes phpstan/phpstan#10787