Fix phpstan/phpstan#14138: errors for argument array template types no longer reported (argument.type)#5300
Conversation
…late types on promoted properties - Promoted property @var T types were not resolved against the class template type map when the constructor had no phpDoc of its own - Moved template type resolution of phpDocParameterTypes outside the `if ($resolvedPhpDoc !== null)` block in PhpClassReflectionExtension::createUserlandMethodReflection - New regression test in tests/PHPStan/Rules/Classes/data/bug-14138.php - The root cause was that the phpDoc inheritance rework (617a3a8) made resolvePhpDocForMethod return null for constructors without doc comments, causing the template resolution to be skipped
VincentLanglet
left a comment
There was a problem hiding this comment.
Check if there is more code to move out the if ($resolvedPhpDoc !== null) {
The class-level purity check (areAllMethodsPure/areAllMethodsImpure) reads from phpDocBlockClassReflection->getResolvedPhpDoc(), not from the method's resolvedPhpDoc. It should run even when resolvedPhpDoc is null (e.g. constructors with promoted properties and no method doc). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Done. I moved the class-level This was the only other code in the block that doesn't depend on Tests and static analysis both pass. |
VincentLanglet
left a comment
There was a problem hiding this comment.
Add a non regression test for the isPure condition you moved
…ties Verifies that @phpstan-all-methods-pure correctly propagates to methods in classes with promoted constructor properties, even when the method's $resolvedPhpDoc is null. This is a non-regression test for the isPure condition moved outside the `if ($resolvedPhpDoc !== null)` block. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Done. Added a regression test for the
|
VincentLanglet
left a comment
There was a problem hiding this comment.
The test testBug14138Pure is strill successful without the fix
|
You're out of extra usage · resets 4pm (UTC) |
VincentLanglet
left a comment
There was a problem hiding this comment.
The test testBug14138Pure is still successful without the fix
VincentLanglet
left a comment
There was a problem hiding this comment.
The test testBug14138Pure is still successful without the fix so it's not a real non regression test
The previous test passed even without the fix because PureMethodRule gets purity from NodeScopeResolver (which already had the class-level check outside the null block). The new test verifies the fix at the reflection layer: a @phpstan-pure method instantiating a class with @phpstan-all-methods-pure and promoted constructor properties. Without the fix, the constructor's isPure() returns "maybe" from reflection, causing a false "Possibly impure instantiation" error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Done. The issue was that the previous The new test instead verifies the reflection layer fix: a |
|
I confirm it fails without the fix |
Summary
Since 2.1.38, PHPStan stopped reporting
argument.typeerrors when constructing a class that extends a generic parent class with promoted property template types. For example,new Foo(["foo" => 1])whereFoo extends AbstractApiData<array{foo: int, bar: int}>should report a missingbarkey error but didn't.Changes
$phpDocParameterTypesoutside theif ($resolvedPhpDoc !== null)block insrc/Reflection/Php/PhpClassReflectionExtension.php(createUserlandMethodReflectionmethod)tests/PHPStan/Rules/Classes/data/bug-14138.phpRoot cause
The phpDoc inheritance rework (commit 617a3a8) changed
PhpDocInheritanceResolver::resolvePhpDocForMethod()to return?ResolvedPhpDocBlock(nullable). When a constructor has no doc comment of its own (only@var Ton promoted properties),$resolvedPhpDocis null. The template type resolution for$phpDocParameterTypeswas inside theif ($resolvedPhpDoc !== null)block, so promoted property types likeTwere never resolved against the class's active template type map (e.g.T => array{foo: int, bar: int}from@extends). The parameter type stayed as the rawTemplateTypewith boundarray, which accepted any array argument.Test
Added a rule test (
testBug14138) that verifies passing["foo" => 1]tonew Foo(...)(whereFoo extends AbstractApiData<array{foo: int, bar: int}>) correctly reports theargument.typeerror about the missingbarkey.Fixes phpstan/phpstan#14138