Skip to content

Ask ExpressionTypeResolverExtensions for the scope state type of an untracked expression - #6688

Open
dg wants to merge 1 commit into
phpstan:2.3.xfrom
dg:fix-extension-narrowing
Open

dg wants to merge 1 commit into
phpstan:2.3.xfrom
dg:fix-extension-narrowing

Conversation

@dg

@dg dg commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Since 2.3, narrowing reads an expression's current type through MutatingScope::resolveScopeStateType(), which derives a property fetch's type from property reflection and skips ExpressionTypeResolverExtensions. For a property that only an extension knows (e.g. a @property-read tag on an interface, which ClassReflection ignores without __get), reflection finds nothing, so the narrowing starts from ErrorType:

assertType('string|null', $o->virtualProperty); // from the extension
if ($o->virtualProperty !== null) {
	assertType('string', $o->virtualProperty); // 2.3.0: *ERROR*
}

In 2.2 removeTypeFromExpression() started from getType(), which asks the extensions first. This makes the state read of an untracked expression ask them the same way resolveType() does; a tracked (already narrowed) type still wins without asking them.

@dg
dg force-pushed the fix-extension-narrowing branch 2 times, most recently from e3183d3 to 612b71b Compare October 6, 2026 14:10
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.

1 participant