# Exception Handling

## Single Exception

```py
import re

try:
    help()
except NameError as e:
    reveal_type(e)  # revealed: NameError
except re.error as f:
    reveal_type(f)  # revealed: error
```

## Unknown type in except handler does not cause spurious diagnostic

```py
from nonexistent_module import foo  # error: [unresolved-import]

try:
    help()
except foo as e:
    reveal_type(foo)  # revealed: Unknown
    reveal_type(e)  # revealed: Unknown
```

## Multiple Exceptions in a Tuple

```py
EXCEPTIONS = (AttributeError, TypeError)

try:
    help()
except (RuntimeError, OSError) as e:
    reveal_type(e)  # revealed: RuntimeError | OSError
except EXCEPTIONS as f:
    reveal_type(f)  # revealed: AttributeError | TypeError
```

## Dynamic exception types

```py
def foo(
    x: type[AttributeError],
    y: tuple[type[OSError], type[RuntimeError]],
    z: tuple[type[BaseException], ...],
):
    try:
        help()
    except x as e:
        reveal_type(e)  # revealed: AttributeError
    except y as f:
        reveal_type(f)  # revealed: OSError | RuntimeError
    except z as g:
        # TODO: should be `BaseException`
        reveal_type(g)  # revealed: @Todo(full tuple[...] support)
```

## Invalid exception handlers

```py
try:
    pass
# error: [invalid-exception-caught] "Cannot catch object of type `Literal[3]` in an exception handler (must be a `BaseException` subclass or a tuple of `BaseException` subclasses)"
except 3 as e:
    reveal_type(e)  # revealed: Unknown

try:
    pass
# error: [invalid-exception-caught] "Cannot catch object of type `Literal["foo"]` in an exception handler (must be a `BaseException` subclass or a tuple of `BaseException` subclasses)"
# error: [invalid-exception-caught] "Cannot catch object of type `Literal[b"bar"]` in an exception handler (must be a `BaseException` subclass or a tuple of `BaseException` subclasses)"
except (ValueError, OSError, "foo", b"bar") as e:
    reveal_type(e)  # revealed: ValueError | OSError | Unknown

def foo(
    x: type[str],
    y: tuple[type[OSError], type[RuntimeError], int],
    z: tuple[type[str], ...],
):
    try:
        help()
    # error: [invalid-exception-caught]
    except x as e:
        reveal_type(e)  # revealed: Unknown
    # error: [invalid-exception-caught]
    except y as f:
        reveal_type(f)  # revealed: OSError | RuntimeError | Unknown
    except z as g:
        # TODO: should emit a diagnostic here:
        reveal_type(g)  # revealed: @Todo(full tuple[...] support)
```

## Object raised is not an exception

```py
try:
    raise AttributeError()  # fine
except:
    ...

try:
    raise FloatingPointError  # fine
except:
    ...

try:
    raise 1  # error: [invalid-raise]
except:
    ...

try:
    raise int  # error: [invalid-raise]
except:
    ...

def _(e: Exception | type[Exception]):
    raise e  # fine

def _(e: Exception | type[Exception] | None):
    raise e  # error: [invalid-raise]
```

## Exception cause is not an exception

```py
try:
    raise EOFError() from GeneratorExit  # fine
except:
    ...

try:
    raise StopIteration from MemoryError()  # fine
except:
    ...

try:
    raise BufferError() from None  # fine
except:
    ...

try:
    raise ZeroDivisionError from False  # error: [invalid-raise]
except:
    ...

try:
    raise SystemExit from bool()  # error: [invalid-raise]
except:
    ...

try:
    raise
except KeyboardInterrupt as e:  # fine
    reveal_type(e)  # revealed: KeyboardInterrupt
    raise LookupError from e  # fine

try:
    raise
except int as e:  # error: [invalid-exception-caught]
    reveal_type(e)  # revealed: Unknown
    raise KeyError from e

def _(e: Exception | type[Exception]):
    raise ModuleNotFoundError from e  # fine

def _(e: Exception | type[Exception] | None):
    raise IndexError from e  # fine

def _(e: int | None):
    raise IndexError from e  # error: [invalid-raise]
```
