Skip to content

[6.x] Support the PHP 8.5 #[\NoDiscard] attribute#11892

Draft
alies-dev wants to merge 3 commits into
vimeo:6.xfrom
alies-dev:support-php85-nodiscard
Draft

[6.x] Support the PHP 8.5 #[\NoDiscard] attribute#11892
alies-dev wants to merge 3 commits into
vimeo:6.xfrom
alies-dev:support-php85-nodiscard

Conversation

@alies-dev

@alies-dev alies-dev commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

PHP 8.5 adds the #[\NoDiscard] attribute, which marks a function or method whose return value is meant to be used. Ignoring the result produces a runtime E_WARNING, and the new (void) cast is the documented way to opt out at a specific call site. This teaches Psalm to model both.

When a function-like is annotated with #[\NoDiscard], the attribute is recorded on FunctionLikeStorage during scanning, and discarding the call's return value now reports the existing UnusedFunctionCall / UnusedMethodCall issue. The check covers plain functions, instance methods and static methods (including inherited concrete methods, trait methods and nullsafe calls) and fires independently of purity and findUnusedVariables, matching PHP's runtime behaviour. Reusing the existing issues, rather than introducing a new one, keeps the user facing meaning ("this return value is not used") consistent with the existing pure-call detection. A function that is both pure and #[\NoDiscard] is reported once.

Behaviour was verified against PHP 8.5.7, which informed a few subtleties:

  • PHP does not inherit #[\NoDiscard] onto an implementation, so a call resolved to an interface or abstract method declaration does not report. The implementing or overriding declaration must carry the attribute itself.
  • Applying the attribute to a void or never return type is a fatal error in PHP ("A void function does not return a value, but #[\NoDiscard] requires a return value"), so such a declaration is reported as InvalidAttribute, checked against the native signature return type.
  • Reporting is gated on the analysis PHP version being 8.5 or higher, where the attribute is enforced and the (void) escape hatch parses. Below 8.5 the attribute is inert and @psalm-suppress remains the way to silence the report.

Supporting the (void) cast is a prerequisite: it was previously reported as UnrecognizedExpression. It is now analysed like the other general-use casts, which both fixes that and provides the escape hatch, since (void) f(); analyses its operand as used and therefore raises no issue. nikic/php-parser accepts the cast in value positions that PHP rejects at parse time (for example $x = (void) f();), so using its result is reported as InvalidCast.

Not covered here: PHP 8.5 marks a number of native functions (such as flock()) with #[\NoDiscard], but those reach Psalm through the call map rather than as scanned attributes, so discarding them is not yet caught. Annotating the call map is a natural follow-up.

Fixes #11381.

The (void) cast was previously unrecognised and reported as
UnrecognizedExpression. Analyse it like the other general-use casts:
evaluate the operand under inside_general_use and type the expression as
void. The parser only emits this node when targeting PHP 8.5+, so no
explicit version guard is needed.

The (void) cast is also the documented escape hatch for #[\NoDiscard].
…ethods

PHP 8.5's #[\NoDiscard] attribute marks a function or method whose return
value must be used; ignoring it triggers a runtime E_WARNING. Record the
attribute on FunctionLikeStorage during scanning and report the existing
UnusedFunctionCall / UnusedMethodCall at call sites (functions, instance
methods and static methods) when the result is discarded.

Unlike the existing pure-call check, this fires regardless of purity and
the find-unused-variables setting, matching PHP's runtime behaviour. It is
gated on analysis PHP version 8.5, where the attribute is enforced and the
(void) escape hatch is available. void/never returns and first-class
callable creation are excluded, and a function that is both pure and
#[\NoDiscard] is reported once.

Fixes vimeo#11381
@alies-dev alies-dev changed the title Support the PHP 8.5 #[\NoDiscard] attribute [6.x] Support the PHP 8.5 #[\NoDiscard] attribute Jun 23, 2026
@alies-dev alies-dev marked this pull request as draft June 23, 2026 13:42
Verified against PHP 8.5.7 and corrects four behaviours:

- #[\NoDiscard] on a void or never return type is a fatal error in PHP
  ("A void function does not return a value, but #[\NoDiscard] requires a
  return value"), so report the declaration as InvalidAttribute, checked
  on the native signature return type, instead of silently accepting it.
- PHP does not inherit the attribute onto an implementation, so calls
  resolved to an interface or abstract method declaration no longer report.
- The discard check now keys off the native signature return type rather
  than the docblock-inclusive return type.
- nikic/php-parser accepts the (void) cast in value positions that PHP
  rejects at parse time; using its result is now reported as InvalidCast.
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.

Add support for #[\NoDiscard]

1 participant