Skip to content

Fix phpstan/phpstan#9634: Cannot use "T is never" in conditional return#5082

Merged
VincentLanglet merged 4 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-to7kx0v
Mar 29, 2026
Merged

Fix phpstan/phpstan#9634: Cannot use "T is never" in conditional return#5082
VincentLanglet merged 4 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-to7kx0v

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

@phpstan-bot phpstan-bot commented Feb 27, 2026

Summary

When using (T is never ? false : bool) as a conditional return type on a generic interface method, PHPStan incorrectly reported "Condition 'T is never' in conditional return type is always false." The fix allows never to correctly recognize that a template type T might be instantiated to never.

Changes

  • Added TemplateType handling in src/Type/NonAcceptingNeverType.phpisSuperTypeOf() now returns Maybe for template types instead of No
  • Added the same TemplateType handling in src/Type/NeverType.php for consistency
  • New rule test in tests/PHPStan/Rules/PhpDoc/data/bug-9634.php and test method in tests/PHPStan/Rules/PhpDoc/MethodConditionalReturnTypeRuleTest.php
  • New type inference test in tests/PHPStan/Analyser/nsrt/bug-9634.php verifying that Option<never>::isSome() resolves to false and Option<int>::isSome() resolves to bool

Root cause

NonAcceptingNeverType::isSuperTypeOf() (the type used for explicitly written never in PHPDoc) returned No for all types except NeverType and NonAcceptingNeverType. When ConditionalReturnTypeRuleHelper checked whether the condition T is never could ever be true by calling $targetType->isSuperTypeOf($subjectType), the never target returned No for the template type T, causing the rule to report the condition as "always false". However, a template type T can be instantiated to never (since never is a subtype of any bound), so the correct answer is Maybe.

Test

  • Rule test verifies that (T is never ? false : bool) no longer produces a false positive error
  • NSRT test verifies that when T is bound to never, the conditional resolves to false, and when T is bound to int, it resolves to bool

Fixes phpstan/phpstan#9634
Fixes phpstan/phpstan#10938

- Added TemplateType handling in NonAcceptingNeverType::isSuperTypeOf()
  and NeverType::isSuperTypeOf() to return Maybe instead of No, since a
  template type can be instantiated to never
- New regression tests in tests/PHPStan/Rules/PhpDoc/data/bug-9634.php
  and tests/PHPStan/Analyser/nsrt/bug-9634.php

Closes phpstan/phpstan#9634
@staabm staabm force-pushed the create-pull-request/patch-to7kx0v branch from 8118609 to 42c3d05 Compare March 27, 2026 07:11
@staabm staabm changed the title Fix #9634: Cannot use "T is never" in conditional return Fix phpstan/phpstan#9634: Cannot use "T is never" in conditional return Mar 27, 2026
@VincentLanglet VincentLanglet self-assigned this Mar 28, 2026
Copy link
Copy Markdown
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Add test for the issue phpstan/phpstan#10938
  • Use nsrt/bug-9634 file in testBug9634 and delete the duplicate
  • Be sure both NeverType and NonAcceptingNeverType are necessary/tested

phpstan-bot and others added 3 commits March 28, 2026 20:04
…uplicate data file

The rule test now references tests/PHPStan/Analyser/nsrt/bug-9634.php
instead of maintaining a separate duplicate in tests/PHPStan/Rules/PhpDoc/data/.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds nsrt and rule test coverage for the related issue where checking
if a collection is empty using (TValue is never ? true : bool)
conditional return type was not working.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…TemplateType

Tests both NeverType and NonAcceptingNeverType return Maybe when checking
isSuperTypeOf against a TemplateType, since a template type can be
instantiated to never. Tests with both unbound and bounded template types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@phpstan-bot
Copy link
Copy Markdown
Collaborator Author

All three review points have been addressed:

  1. Use nsrt/bug-9634 in testBug9634 and delete the duplicate - The rule test now references tests/PHPStan/Analyser/nsrt/bug-9634.php and the duplicate tests/PHPStan/Rules/PhpDoc/data/bug-9634.php has been deleted.

  2. Add test for issue #10938 - Created tests/PHPStan/Analyser/nsrt/bug-10938.php which tests a Collection interface with (TValue is never ? true : bool) conditional return type, and added testBug10938() in MethodConditionalReturnTypeRuleTest.

  3. Ensure both NeverType and NonAcceptingNeverType are necessary/tested - Added 4 test cases in TemplateTypeTest::dataIsSuperTypeOf that explicitly verify both NeverType::isSuperTypeOf(TemplateType) and NonAcceptingNeverType::isSuperTypeOf(TemplateType) return Maybe (with both unbound and bounded template types). Both changes are necessary: NonAcceptingNeverType overrides isSuperTypeOf completely and doesn't call the parent, so each class needs its own TemplateType check.

All tests pass (make tests: 11679 tests, 0 failures) and make phpstan reports no errors.

@VincentLanglet VincentLanglet requested a review from staabm March 28, 2026 20:35
@VincentLanglet VincentLanglet merged commit 1c822e2 into phpstan:2.1.x Mar 29, 2026
654 of 656 checks passed
@VincentLanglet VincentLanglet deleted the create-pull-request/patch-to7kx0v branch March 29, 2026 10:42
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.

3 participants