Skip to content

Allow @throws annotation in base classes for exceptions not actually thrown #13258

@czirkoszoltan

Description

@czirkoszoltan

Bug report

A method in a base class may be annotated with @throws SomeException to express that derived classes are allowed to throw such exceptions:

abstract class Base {
    /**
     * @throws \LogicException
     */
    public function f(): void {
    }
}

class Derived extends Base {
    public function f(): void {
        throw new \LogicException();
    }
}

This informs users of the base class that code calling the method has to expect the exception:

function g(Base $obj): void {
    try {
        $obj->f();
    } catch (\LogicException $e) {
        // Not a dead catch: Base::f itself doesn't throw,
        // but it may be overridden to throw
    }
}

However, PhpStan reports the @throws annotation as an error:

Method Base::f() has LogicException in PHPDoc @throws tag but it's not thrown.

Some thoughts around this:

  • A class which is not final is a slight hint that this situation may happen. 'Slight hint' refers to the fact that most code bases do not use final consistently. I think this is the most important curlpit here.
  • A class which is tagged as abstract is a stronger hint that this may happen.
  • A class tagged as final would disallow overriding the method, making the error message valid.
  • Same applies to final on the method.
  • And to private methods.

Code snippet that reproduces the problem

https://phpstan.org/r/f7f8735c-2620-4cfa-88e1-f4442f36214f

Expected output

No error reported.

Did PHPStan help you today? Did it make you happy in any way?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions