Skip to content

Fix phpstan/phpstan#14281: Regression: type arrowed too much after identical comparison#5221

Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-mnyz94n
Closed

Fix phpstan/phpstan#14281: Regression: type arrowed too much after identical comparison#5221
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-mnyz94n

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When a generic child class (e.g., TestCollection extends Collection) was instantiated without constructor arguments, and the constructor was defined in the parent class with a default empty array parameter (array $elements = []), PHPStan resolved the template type to *NEVER* instead of mixed. This caused method return types like TElement|null to resolve to null, which then made === comparisons with known non-null values impossible, narrowing the compared array to *NEVER* (dead code).

Changes

  • Added a NeverType check in src/Analyser/ExprHandler/NewHandler.php (line ~550) when mapping parent constructor resolved template types to the child class. When a resolved template is NeverType (typically from an empty default array), it is now skipped, allowing the template to fall back to its bound (usually mixed).
  • New regression test in tests/PHPStan/Analyser/nsrt/bug-14281.php

Root cause

Commit a063119ee ("Fix inferring type of new with generic type with constructor in parent class") added logic to simulate new ParentClass(args) and map the resolved parent template types back to the child class. When called with no arguments (e.g., new TestCollection()), the parent constructor's default [] parameter resolved TElement to *NEVER* (since an empty array has element type never). This was then mapped to the child, making TestCollection<*NEVER*> instead of the pre-fix behavior of TestCollection<mixed>.

The fix skips NeverType entries when building the resolved type map from the parent constructor, so templates resolved from empty defaults correctly fall back to their bounds.

Test

The regression test reproduces the exact scenario from the issue: a Collection<TElement> with a TestCollection child class, where assert($data[X] === $collection->get(X)) should not narrow $data to *NEVER*.

Fixes phpstan/phpstan#14281

…parison

- Root cause: commit a063119 introduced parent constructor template inference
  that resolved TElement to *NEVER* from empty default array parameters
- When new TestCollection() was called without arguments, TElement was inferred
  as *NEVER* (from default []), causing get() to return null instead of mixed
- This made assert($data[1] === $collection->get(1)) resolve as assert(0 === null),
  narrowing $data to *NEVER* (dead code)
- Fix: skip NeverType when mapping parent constructor template types to child class
- New regression test in tests/PHPStan/Analyser/nsrt/bug-14281.php
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants