PHPStan 2.3.x-dev: variable.unused reports variables used to retain destructor-based scope guards
Bug report
While testing PHPStan 2.3.x-dev@455ae30, I found that the new unused-variable detection reports a variable used as a destructor-based scope guard.
PHPStan 2.3.x-dev reports:
Variable $guard is never read.
This use case follows the Scope Guard and RAII idioms. The object's lifetime is tied to the surrounding scope, and cleanup or state restoration is performed by its destructor.
The variable is intentionally not read. Assigning the object to $guard keeps it alive until the end of the function so that its destructor runs after the work performed by the function.
Removing the assignment changes observable runtime behavior. With the assignment, the output is:
enter
work
leaveIf it is replaced with an unassigned new ScopeGuard();, the temporary object is destroyed immediately and the output becomes:
enter
leave
workThe unused-variable diagnostic is behaving as designed because $guard is never read directly. However, retaining an object until the end of a scope is a legitimate use case, and this pattern newly produces a warning in PHPStan 2.3.x.
Automatically treating every class with a destructor as a Scope Guard would likely be too broad because the presence of a destructor does not necessarily mean that retaining the object is semantically significant.
One possible solution would be to introduce an explicit opt-in PHPDoc tag for Scope Guard classes. Such a tag could tell PHPStan that assigning an instance to a local variable is semantically significant because it controls the object's lifetime, even when the variable is never read.
The public Playground currently runs a version that does not report the error. I reproduced it with PHPStan 2.3.x-dev@455ae30. PHPStan 2.2.14 also reports no error.
This issue was generated by Cursor Agent.
Code snippet that reproduces the problem
https://phpstan.org/r/6dec52c7-02b5-45d2-9b55-3446f6945410
Expected output
PHPStan should provide an explicit way, such as an opt-in PHPDoc tag on the Scope Guard class, to express this legitimate use case without suppressing each assignment individually.
Did PHPStan help you today? Did it make you happy in any way?
Source: phpstan/phpstan