Skip to content

Fix nullsafe.neverNull false positive when the ?-> operand is nullable#6065

Closed
clarg18 wants to merge 1 commit into
phpstan:2.2.xfrom
clarg18:fix-14311-nullsafe-neverNull-coalesce
Closed

Fix nullsafe.neverNull false positive when the ?-> operand is nullable#6065
clarg18 wants to merge 1 commit into
phpstan:2.2.xfrom
clarg18:fix-14311-nullsafe-neverNull-coalesce

Conversation

@clarg18

@clarg18 clarg18 commented Jul 18, 2026

Copy link
Copy Markdown

Fixes phpstan/phpstan#14311

$a?->b on the left of ??, or inside isset()/empty(), was reported as an unnecessary nullsafe access whenever the whole $a?->b expression came out nullable, without looking at whether $a itself could be null. When $a is nullable the ?-> is exactly what produces the null the surrounding construct then handles, so it is required. Following the advice and switching to -> fatals at runtime when $a is null.

Small repro:

function resolve(bool $flag): ?Node { return $flag ? new Node() : null; }

$node = resolve($flag);   // Node|null
return $node?->id ?? 0;   // reported as "unnecessary. Use -> instead."

The check in IssetCheck now only reports when the operand is never null. When the operand is itself a nullsafe fetch with a never-null operand of its own ($nonNull?->nullable?->x), it recurses so that inner redundant link is still reported, which keeps parity with NullsafePropertyFetchRule outside these contexts. I also added the treatPhpDocTypesAsCertain tip that the sibling rule emits.

bug-7109 had assertions that encoded the old behaviour (its $this->get()?->aaa ?? 6, where get(): ?HelloWorld, was expected to be reported), so I corrected those and added bug-14311 for the fix.

…lable

$a?->b on the left of ??, or inside isset()/empty(), was reported as
redundant whenever $a?->b as a whole was nullable, without checking $a
itself. When $a is nullable the ?-> is what yields the null those
constructs handle, so it's required, and switching to -> as advised would
fatal at runtime.

Report only when the operand is never null, and recurse into a nullsafe
operand so an inner never-null link ($nonNull?->nullable?->x) is still
caught, matching NullsafePropertyFetchRule outside these contexts. Adds the
same treatPhpDocTypesAsCertain tip, and fixes the bug-7109 expectations
that had baked in the false positive.

Fixes phpstan/phpstan#14311
@ondrejmirtes

Copy link
Copy Markdown
Member

Don't fix what's not broken :) phpstan/phpstan#14311 (comment)

@clarg18

clarg18 commented Jul 18, 2026

Copy link
Copy Markdown
Author

You're right, thanks. I hadn't clocked that ??/isset()/empty() suppress the null-read warning, so -> and ?-> are genuinely identical inside them and the rule is correct to flag it as redundant. Closing.

@clarg18 clarg18 closed this Jul 18, 2026
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.

nullsafe.neverNull false positive: nullsafe on variable assigned from array<K, V> with ??, isset(), or array_key_exists()

2 participants