[6.x] Support the PHP 8.5 #[\NoDiscard] attribute#11892
Draft
alies-dev wants to merge 3 commits into
Draft
Conversation
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
#[\NoDiscard] attribute
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 runtimeE_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 onFunctionLikeStorageduring scanning, and discarding the call's return value now reports the existingUnusedFunctionCall/UnusedMethodCallissue. The check covers plain functions, instance methods and static methods (including inherited concrete methods, trait methods and nullsafe calls) and fires independently of purity andfindUnusedVariables, 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:
#[\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.voidorneverreturn 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 asInvalidAttribute, checked against the native signature return type.(void)escape hatch parses. Below 8.5 the attribute is inert and@psalm-suppressremains the way to silence the report.Supporting the
(void)cast is a prerequisite: it was previously reported asUnrecognizedExpression. 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-parseraccepts the cast in value positions that PHP rejects at parse time (for example$x = (void) f();), so using its result is reported asInvalidCast.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.