psalm
psalm copied to clipboard
Throws types validation
Recently, I have faced the problem that it is impossible to control what kind of exceptions may be thrown by interface implementations. It would be great to restrict to throw an exception that not declared in the interface doc block (or in any overrode method).
For example, Java supports the possibility to declare what exceptions can be thrown by the method using throws in the method declaration. Java also validates that the overridden method should throw only those exceptions that declared in the parent method or exceptions of compatible types.
In the following example, Psalm should point that WrongImplementation::get()
throws an exception that isn't supported by the interface SomeInterface::get()
:
<?php
class SomeException extends Exception {}
interface SomeInterface
{
/**
* @throws SomeException
*/
public function get() : int;
}
class WrongImplementation implements SomeInterface
{
/**
* {@inheritDoc}
* @throws RuntimeException
*/
public function get() : int
{
// ...
throw new RuntimeException();
// ...
}
}
class CorrectImplementation implements SomeInterface
{
/**
* {@inheritDoc}
*/
public function get() : int
{
// ...
try {
throw new RuntimeException();
} catch (RuntimeException $e) {
// we should wrap not supported exception
throw new SomeException('', 0, $e);
}
return 0;
// ...
}
}
https://psalm.dev/r/ecf23dae78
Example of implementation: https://github.com/pepakriz/phpstan-exception-rules/blob/master/tests/src/Rules/data/throws-inheritance.php#L90
May be related to #6940.
Looks like @psalm-type
declarations are not visible in @throws
either.
Update: I see that I had actually been here before. Why was my comment marked as off-topic? Aren't the errors I am getting (crashing my builds) related to type validation in @throws
?
Please, I would very much like to have this. Is there any plan or even intention? Is this a feature that is already decided to be OK?
I found these snippets:
https://psalm.dev/r/70c8a71e28
<?php
declare(strict_types=1);
use RangeException;
use RuntimeException;
/**
* @psalm-type EMyFailure = RuntimeException
*/
interface MyInterface
{
/**
* Psalm has no problem understanding the aliased return type.
*
* @return EMyFailure
*/
public function getStuff();
/**
* Psalm is unable to see that this method throws
* a previously declared type that works with `@return`.
*
* @throws EMyFailure If response invalid.
*/
public function throwStuff(): Throwable;
}
Psalm output (using commit 5baf85e):
ERROR: UndefinedDocblockClass - 24:16 - Docblock-defined class, interface or enum named EMyFailure does not exist