Skip to content

Treat a property with a default value as always set in isset resolution - #6131

Merged
ondrejmirtes merged 2 commits into
2.2.xfrom
isset-property-default-value
Jul 28, 2026
Merged

Treat a property with a default value as always set in isset resolution#6131
ondrejmirtes merged 2 commits into
2.2.xfrom
isset-property-default-value

Conversation

@ondrejmirtes

@ondrejmirtes ondrejmirtes commented Jul 28, 2026

Copy link
Copy Markdown
Member

Extracted from the resolve-type-rewrite-2 branch (follows #6125, #6129, #6130).

MutatingScope::issetCheck() treated every native non-virtual property without a tracked expression type as possibly uninitialized — including properties with a default value, which can never be uninitialized. The guard now consults hasDefaultValue().

Effects, verified test-first (new nsrt/isset-property-default-value.php fails on 2.2.x with int|null, passes with the fix):

  • $a->value ?? $b->value narrows through an earlier is_null && is_null throw guard again (the coalesce only builds the left-is-null scope for its right side when the left is surely set).
  • isset($foo->hasDefaultValue) on a defaulted non-nullable property now infers true — one expectation updated in nsrt/isset-coalesce-empty-type.php, consistent with the adjacent assigned-before case; a no-default contrast case in the new fixture pins that uninitialized properties keep the conservative bool.

Full test suite green (17764 tests), self-analysis clean, code style clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_019wqGgaD7iqL44t1KgpJS7b


Note: the rule-side mirror of this logic in Rules\IssetCheck already consults hasDefaultValue() since 2f58f5f (#5327, phpstan/phpstan#14393) — this PR is the MutatingScope::issetCheck() catch-up, so type inference now agrees with what the isset/empty/coalesce rules already report.

Closes phpstan/phpstan#10786

ondrejmirtes and others added 2 commits July 28, 2026 17:11
issetCheck() treated every native non-virtual property without a tracked
expression type as possibly uninitialized. A property with a default
value cannot be uninitialized, so consult hasDefaultValue() in the
guard. This restores narrowing through ?? (the coalesce only builds the
left-is-null scope for its right side when the left is surely set) and
makes isset() on a defaulted non-nullable property infer true.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019wqGgaD7iqL44t1KgpJS7b
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019wqGgaD7iqL44t1KgpJS7b
@ondrejmirtes
ondrejmirtes merged commit 360b5dd into 2.2.x Jul 28, 2026
141 of 144 checks passed
@ondrejmirtes
ondrejmirtes deleted the isset-property-default-value branch July 28, 2026 15:19
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.

Invalid return type definition

1 participant