[`pylint`] Detect subclasses of builtin exceptions (`PLW0133`) (#21382)
<!--
Thank you for contributing to Ruff/ty! To help us out with reviewing,
please consider the following:
- Does this pull request include a summary of the change? (See below.)
- Does this pull request include a descriptive title? (Please prefix
with `[ty]` for ty pull
requests.)
- Does this pull request include references to any relevant issues?
-->
## Summary
<!-- What's the purpose of the change? What does it do, and why? -->
Closes #17347
Goal is to detect the useless exception statement not just for builtin
exceptions but also custom (user defined) ones.
## Test Plan
<!-- How was it tested? -->
I added test cases in the rule fixture and updated the insta snapshot.
Note that I first moved up a test case case which was at the bottom to
the correct "violation category".
I wasn't sure if I should create new test cases or just insert inside
those tests. I know that ideally each test case should test only one
thing, but here, duplicating twice 12 test cases seemed very verbose,
and actually less maintainable in the future. The drawback is that the
diff in the snapshot is hard to review, sorry. But you can see that the
snapshot gives 38 diagnostics, which is what we expect.
Alternatively, I also created this file for manual testing.
```py
# tmp/test_error.py
class MyException(Exception):
...
class MyBaseException(BaseException):
...
class MyValueError(ValueError):
...
class MyExceptionCustom(Exception):
...
class MyBaseExceptionCustom(BaseException):
...
class MyValueErrorCustom(ValueError):
...
class MyDeprecationWarning(DeprecationWarning):
...
class MyDeprecationWarningCustom(MyDeprecationWarning):
...
class MyExceptionGroup(ExceptionGroup):
...
class MyExceptionGroupCustom(MyExceptionGroup):
...
class MyBaseExceptionGroup(ExceptionGroup):
...
class MyBaseExceptionGroupCustom(MyBaseExceptionGroup):
...
def foo():
Exception("...")
BaseException("...")
ValueError("...")
RuntimeError("...")
DeprecationWarning("...")
GeneratorExit("...")
SystemExit("...")
ExceptionGroup("eg", [ValueError(1), TypeError(2), OSError(3), OSError(4)])
BaseExceptionGroup("eg", [ValueError(1), TypeError(2), OSError(3), OSError(4)])
MyException("...")
MyBaseException("...")
MyValueError("...")
MyExceptionCustom("...")
MyBaseExceptionCustom("...")
MyValueErrorCustom("...")
MyDeprecationWarning("...")
MyDeprecationWarningCustom("...")
MyExceptionGroup("...")
MyExceptionGroupCustom("...")
MyBaseExceptionGroup("...")
MyBaseExceptionGroupCustom("...")
```
and you can run this to check the PR:
```sh
target/debug/ruff check tmp/test_error.py --select PLW0133 --unsafe-fixes --diff --no-cache --isolated --target-version py310
target/debug/ruff check tmp/test_error.py --select PLW0133 --unsafe-fixes --diff --no-cache --isolated --target-version py314
```