Skip to content

Substitute generic return types when the native hint is nullable#152

Open
MrSrsen wants to merge 1 commit into
PHPantom-dev:mainfrom
MrSrsen:fix/nullable-native-generic-return
Open

Substitute generic return types when the native hint is nullable#152
MrSrsen wants to merge 1 commit into
PHPantom-dev:mainfrom
MrSrsen:fix/nullable-native-generic-return

Conversation

@MrSrsen

@MrSrsen MrSrsen commented Jun 7, 2026

Copy link
Copy Markdown

Fixes: #151

should_override_type_typed used unwrap_nullable(), which only strips the ?Foo (Nullable) form and not the Foo|null (Union-with-null) form. A nullable-union native such as object|null therefore reached the union branch with its null member attached; since object and null are both scalar names, the branch judged the type unrefinable and discarded a generic docblock return like @psalm-return ?T, leaving the bare native.

Use non_null_type() (strips null from both Nullable and Union forms) so the non-null part is analysed, matching the function's documented Foo|null → Foo intent. Fixes inherited generic returns such as Doctrine's ServiceEntityRepository<T>::find(): ?T resolving to object|null.

Adds an assert_type regression fixture.

`should_override_type_typed` used `unwrap_nullable()`, which only strips the
`?Foo` (Nullable) form and not the `Foo|null` (Union-with-null) form. A
nullable-union native such as `object|null` therefore reached the union
branch with its `null` member attached; since `object` and `null` are both
scalar names, the branch judged the type unrefinable and discarded a generic
docblock return like `@psalm-return ?T`, leaving the bare native.

Use `non_null_type()` (strips null from both Nullable and Union forms) so the
non-null part is analysed, matching the function's documented `Foo|null → Foo`
intent. Fixes inherited generic returns such as Doctrine's
`ServiceEntityRepository<T>::find(): ?T` resolving to `object|null`.

Adds an assert_type regression fixture.
@MrSrsen

MrSrsen commented Jun 8, 2026

Copy link
Copy Markdown
Author

@AJenbo I think CI won't pass unless you run the CI because my fork has no access to project secrets. Or, can I do something about the CI myself?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants