Repository navigation
Pick the ternary's final scope from the native condition type when treatPhpDocTypesAsCertain is off - #6822
Open
phpstan-bot wants to merge 1 commit into
Open
Pick the ternary's final scope from the native condition type when treatPhpDocTypesAsCertain is off#6822phpstan-bot wants to merge 1 commit into
treatPhpDocTypesAsCertain is off#6822phpstan-bot wants to merge 1 commit into
Conversation
…reatPhpDocTypesAsCertain` is off - TernaryHandler decided whether to keep only the truthy or only the falsey branch scope from the PHPDoc condition type. With a PHPDoc-only `@return Expr`, `$x === null` is PHPDoc-false, so the truthy branch (where the native type is `null`) was dropped and the native type stayed `mixed~null` after the ternary, making a repeated `$x === null` "always false" even with treatPhpDocTypesAsCertain: false. - Inject the `treatPhpDocTypesAsCertain` parameter and read the native condition type when it is off, like IfHandler, ForHandler, WhileHandler, DoWhileHandler and MatchHandler already do. - Port the change to the turbo-ext C++ twin (TernaryHandler.cpp + regenerated declarations header). - Drop the stale line-20 expectation in ImpossibleInstanceOfRuleWithUncertainPhpDocTypesTest: it was produced by the same scope drop after the line-19 ternary. - Probed if/elseif, switch, match, &&, ||, ??, while and short ternary; only the ternary (long and short) was affected.
VincentLanglet
approved these changes
Oct 24, 2026
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.
Summary
With
treatPhpDocTypesAsCertain: false, a repeated$x === null ? ... : ...on a value whose type comes only from PHPDoc (@return Expr, no native type) was reported asidentical.alwaysFalse. After the first ternary, the native type of$xwasmixed~null. The branch where it isnullhad been thrown away.The ternary now picks its final scope from the native condition type when PHPDoc types are not treated as certain.
Changes
src/Analyser/ExprHandler/TernaryHandler.php: new#[AutowiredParameter] bool $treatPhpDocTypesAsCertainconstructor parameter. The "condition is always true/false, keep a single branch scope" decision now readsgetNativeType()when it is off.turbo-ext/src/TernaryHandler.cpp,turbo-ext/src/generated/TernaryHandler.h: the same change in the native twin (new property slot, constructor parameter, native-type read).if/elseif,switch,match,&&,||,??,whileand the short ternary?:. All except the ternary (long and short, which share this code) already behaved correctly.IfHandler,ForHandler,WhileHandler,DoWhileHandlerandMatchHandleralready use the native type in this mode.Root cause
TernaryHandler::processExpr()used$ternaryCondResult->getType()to decide whether the condition is always true or false. If it was, the handler kept only one branch scope instead of merging both. The PHPDoc typeExprmakes$x === nullalways false, so only the falsey scope was kept. That scope's native type for$xismixed~null. The nativenullfrom the truthy branch was lost, and PHPDoc certainty leaked into the native type. The statement handlers already gate this decision ontreatPhpDocTypesAsCertain; the ternary did not.Test
StrictComparisonOfDifferentTypesRuleWithUncertainPhpDocTypesTest(loadsuncertain-phpdoc-types.neon) analysingtests/PHPStan/Rules/Comparison/data/bug-15403.php.!==variantreturnexpression on demand. It is kept in the file as written in the issue.ImpossibleInstanceOfRuleWithUncertainPhpDocTypesTest::testTernaryElseDoNotReportPhpDoc: removed the line-20 expectation. Line 20 repeats the PHPDoc-onlyinstanceofcheck from line 19, which is correctly not reported. The line-19 ternary dropped its falsey scope, which is this same bug, so the expectation was wrong.side-by-side.phpandsmoke.phppass.Fixes phpstan/phpstan#15403
🤖 Generated with Claude Code