Repository navigation
Resolve type arguments of generic static types inside the class, in properties and in class constants - #6826
Merged
Merged
Conversation
GenericStaticType::getStaticObjectType() handed the class reflection of the declaring class, whose template types are unresolved, to the GenericObjectType it builds, and getClassReflection() returned that reflection too. Members of a `static<int, string>` then resolved against `TKey`/`TValue` instead of `int`/`string` whenever the type was used inside the class - `$this->concrete()->all()`, a `@param static<...>` parameter, `static::` calls and properties, `new static(...)` with inferred type arguments, promoted properties typed by the constructor's `@param`, and every `static<...>` of a final class. getClassReflection() now returns the reflection with the type arguments, like GenericObjectType does, and getStaticObjectType() builds on it. getAncestorWithClassName() keeps the type arguments the static object type has for the ancestor class: through changeBaseClass(), which can only carry them down to a subclass, an ancestor got its template bounds instead. This replaces the workaround in CalledOnTypeUnresolvedMethodPrototypeReflection that rebuilt the object type without the reflection for method calls from outside the class (#6695); its tests are kept. Three expectations pinned the bug: - bug-4590: `return new static(["ok" => "hello"])` from a method returning `static<array<string, string>>` now infers `T` from the return type, like `return new OkResponse([...])` in the same file already did. A real mismatch is added instead. - pr-5880: after `@phpstan-assert-if-true static<A> $this`, `$this->get()` is `A` and `->a()` exists. - bug-12575: after `@phpstan-self-out static<T&U>`, `$this->getT()` is `A&T`. The native GenericStaticType declares its properties from the generated declarations now, so the new memo property keeps the slots in step with the PHP twin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011i2xTiyrXgfVkG1tmwMtUM
CalledOnTypeUnresolvedPropertyPrototypeReflection replaced every StaticType, GenericStaticType included, with the fetched-on type, so the type arguments written in `@var static<...>` were dropped: on `NodeCursor<BNode>`, a property declared `static<ANode>` was read as `NodeCursor<BNode>`. This handles GenericStaticType like CalledOnTypeUnresolvedMethodPrototypeReflection does for method return types. The bug-15378 regression test comes from #6799. Co-Authored-By: HawkForge <75696486+HawkForge@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011i2xTiyrXgfVkG1tmwMtUM
A class constant's declared type kept `static` bound to the declaring class: `$child::IMPL` with `@var class-string<static>` declared on the parent was `class-string<static(Base)>`, and so was `static::IMPLS` inside the child. Unlike properties and methods, constants had no counterpart of the called-on prototype reflections that resolve `static` against the class the member is accessed on. `static` now becomes the fetched-on type: an object type for `$object::X`, a `static` of the current class for `static::X` and `$this::X`, and a `static<...>` takes its type arguments over to the fetched-on class. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011i2xTiyrXgfVkG1tmwMtUM
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.
static<...>lost its type arguments in several places. This fixes them at the root and extends the fix to properties and class constants.Generic static type's class reflection –
GenericStaticType::getStaticObjectType()andgetClassReflection()handed out the declaring class's reflection with unresolved template types. Members of astatic<int, string>then resolved againstTKey/TValuewhenever the type was used inside the class. This covers$this->concrete()->all(),@param static<...>,static::calls and properties,new static(...)with inferred type arguments, promoted properties typed by the constructor's@param, and final classes.getAncestorWithClassName()now keeps the type arguments for parent classes and interfaces, where it used to fall back to their bounds.This reverts the source change from #6695 (a workaround for method calls from outside the class) and keeps its tests. Three existing expectations pinned the bug (bug-4590, pr-5880, bug-12575); the first commit's message describes each one.
Properties –
CalledOnTypeUnresolvedPropertyPrototypeReflectionreplaced astatic<ANode>property type with the fetched-onNodeCursor<BNode>. It now handlesGenericStaticTypethe same way the method counterpart does. This brings in the test from #6799 (HawkForge is credited as co-author) and supersedes that PR.Class constants –
staticin a constant's PHPDoc type stayed bound to the declaring class. For example,$child::IMPLdeclared asclass-string<static>on the parent wasclass-string<static(Base)>. It is now resolved against the class the constant is fetched on, like properties and methods.The turbo C++ mirrors (
GenericStaticType, the two prototype reflections andInitializerExprTypeResolver) are ported. Analysis output is byte-identical with the extension on and off.Closes phpstan/phpstan#15378
Closes phpstan/phpstan#12680
🤖 Generated with Claude Code
https://claude.ai/code/session_011i2xTiyrXgfVkG1tmwMtUM