Repository: python/mypy

Issue:
Confusing behavior with `raise Class`
```python
from typing import Type, Mapping, TypeVar


class Foo(Exception):
    pass


class Bar(Foo):
    pass


m = {1: Bar, 2: Foo}  # Mapping[int, Type[Foo]]


def f(i):
    # type: (int) -> Type[Foo]
    return m[i]


def x():
    # type: () -> None
    reveal_type(f(1))
    raise f(1)


def y():
    # type: () -> None
    reveal_type(m[1])
    raise m[1]


# for completeness's sake
def huh(*args, **kwargs):
    # type: (*object, **object) -> Foo
    return Bar()


def z():
    # type: () -> None
    reveal_type(huh)
    raise huh
```


The first `reveal_type` shows that `f(1)` returns a `Type[Foo]`; `x()` does not typecheck, with the error "Exception must be derived from BaseException".

The second `reveal_type` shows that `m[1]` is `def (*args: builtins.object, **kwargs: builtins.object) -> foo.Foo`. `y()` typechecks.

The third `reveal_type` shows that `huh` is `def (*args: builtins.object, **kwargs: builtins.object) -> foo.Foo`. `z()` does not typecheck (same error as `x()`).


Discussion:
Could you describe in English what you think is wrong and what you would
expect instead? It's a rather long example to try and understand.

Here's a really short repro:
```
$ python3.6 -m mypy -c 'from typing import Type; X: Type[Exception]; raise X'
<string>:1: error: Exception must be derived from BaseException
```
`mypy -c 'raise Exception'` works fine though.

mypy seems to not realize that `Type[Exception]` is compatible with `raise`.
@gvanrossum sorry, though @JelleZijlstra has basically got it.

If I have a `Type[Exception]` (as returned by `f`), I ought to be able to raise it, but instead, mypy complains.

mypy does *not* complain if I just write `raise Bar`, but it also thinks (according to `reveal_type`) that `Bar` is a function type returning a an instance of `Bar`. It will, however, complain if I type `raise huh`, even though `reveal_type(huh)` and `reveal_type(Bar)` have identical output.
Ah, this is definitely a bug. Do you want to give fixing it a try?
Sure, but I could do with a pointer as to where to start.
@bwo -- A good starting point is often to grep for the error message -- you can then see where it's called, attach a breakpoint, etc.

To save you a little bit of work, you'll probably want to start with the `type_check_raise` method in `mypy/checker.py`.

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -2357,6 +2357,10 @@ def visit_raise_stmt(self, s: RaiseStmt) -> None:
     def type_check_raise(self, e: Expression, s: RaiseStmt,
                          optional: bool = False) -> None:
         typ = self.expr_checker.accept(e)
+        if isinstance(typ, TypeType):
+            if isinstance(typ.item, AnyType):
+                return
+            typ = typ.item
         if isinstance(typ, FunctionLike):
             if typ.is_type_obj():
                 # Cases like "raise/from ExceptionClass".


Test patch:
diff --git a/test-data/unit/check-statements.test b/test-data/unit/check-statements.test
--- a/test-data/unit/check-statements.test
+++ b/test-data/unit/check-statements.test
@@ -410,8 +410,19 @@ raise object # E: Exception must be derived from BaseException
 raise f # E: Exception must be derived from BaseException
 [builtins fixtures/exception.pyi]
 
-[case testRaiseFromStatement]
+[case testRaiseExceptionType]
+import typing
+x = None # type: typing.Type[BaseException]
+raise x
+[builtins fixtures/exception.pyi]
+
+[case testRaiseNonExceptionTypeFails]
+import typing
+x = int # type: typing.Type[int]
+raise x # E: Exception must be derived from BaseException
+[builtins fixtures/exception.pyi]
 
+[case testRaiseFromStatement]
 e = None # type: BaseException
 f = None # type: MyError
 a = None # type: A
@@ -628,6 +639,16 @@ except ((E1, E2)): pass
 except (((E1, E2))): pass
 [builtins fixtures/exception.pyi]
 
+[case testExceptWithTypeType]
+import typing
+E = BaseException  # type: typing.Type[BaseException]
+
+try:
+    pass
+except E:
+    pass
+[builtins fixtures/exception.pyi]
+
 [case testExceptWithMultipleTypes2]
 import typing
 class E1(BaseException): pass
@@ -1615,4 +1636,3 @@ N = TypedDict('N', {'x': int})
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-full.pyi]
 [out]
-