Check the subtracted type when a subtracted mixed accepts a value - #6167
Check the subtracted type when a subtracted mixed accepts a value#6167zonuexe wants to merge 2 commits into
Conversation
e8428d1 to
a5e98b4
Compare
| ) | ||
| ) { | ||
| return new StrictMixedType(); | ||
| return new StrictMixedType($type->getSubtractedType()); |
There was a problem hiding this comment.
Good catch — yes, two more. RuleLevelHelper::findTypeToCheck() dropped it for a plain MixedType, and TemplateMixedType::toStrictMixedType() reset the template bound to a bare StrictMixedType (that one is also reached from transformCommonType() and the intersection branch of findTypeToCheck()). Both are observable at level 9 with checkNonStringableDynamicAccess: after if ($mixed === null) { return; }, self::{$mixed} reported ... but mixed was given while the neighbouring property.nameNotString message already said mixed~null, and the template case said T of mixed instead of T of mixed~null. Both now pass the subtraction through, with a regression test in ClassConstantRuleTest; I kept the subtraction on the template's bound rather than on TemplateStrictMixedType itself, since TemplateTypeTrait already delegates all subtraction handling to the bound.
|
This pull request has been marked as ready for review. |

Closes phpstan/phpstan#15033
MixedType::accepts(andStrictMixedType, reached onceRuleLevelHelperrewrites an explicitMixedTypeforcheckExplicitMixed) accepted every value unconditionally, so a subtracted mixed likenon-empty-mixedrejected nothing at argument or return boundaries, even though the same subtraction already powered narrowing and reachability (identical.alwaysFalse,if.alwaysTrue, etc.).Acceptance now turns to
Noonly on a definite hit (subtractedType->isSuperTypeOf($given)->yes()), so partial overlaps (e.g. a generalstringintonon-empty-mixed, which may or may not be'') stay accepted — this preservesmixed's usual looseness and is why the fix isn'tisSuperTypeOf(...)->toAcceptsResult().NeverTypeis exempted, mirroringMixedType::isSuperTypeOf.RuleLevelHelper::transformCommonTypenow carries the subtraction through when it converts an explicitMixedTypeintoStrictMixedType, instead of discarding it — otherwise level max stayed silent even withMixedTypefixed.VerbosityLevel::getRecommendedLevelByTypeescalates toprecise()when a subtracted (Strict)MixedType is involved, so messages render the subtraction instead of a bare, uninformativemixed.