@@ -3581,7 +3581,7 @@ def visit_raise_stmt(self, s: RaiseStmt) -> None:
3581
3581
if s .expr :
3582
3582
self .type_check_raise (s .expr , s )
3583
3583
if s .from_expr :
3584
- self .type_check_raise (s .from_expr , s , True )
3584
+ self .type_check_raise (s .from_expr , s , optional = True )
3585
3585
self .binder .unreachable ()
3586
3586
3587
3587
def type_check_raise (self , e : Expression , s : RaiseStmt ,
@@ -3590,24 +3590,94 @@ def type_check_raise(self, e: Expression, s: RaiseStmt,
3590
3590
if isinstance (typ , DeletedType ):
3591
3591
self .msg .deleted_as_rvalue (typ , e )
3592
3592
return
3593
+
3594
+ if self .options .python_version [0 ] == 2 :
3595
+ # Since `raise` has very different rule on python2, we use a different helper.
3596
+ # https://github.com/python/mypy/pull/11289
3597
+ self ._type_check_raise_python2 (e , s , typ )
3598
+ return
3599
+
3600
+ # Python3 case:
3593
3601
exc_type = self .named_type ('builtins.BaseException' )
3594
- expected_type = UnionType ( [exc_type , TypeType (exc_type )])
3602
+ expected_type_items = [exc_type , TypeType (exc_type )]
3595
3603
if optional :
3596
- expected_type .items .append (NoneType ())
3597
- if self .options .python_version [0 ] == 2 :
3598
- # allow `raise type, value, traceback`
3599
- # https://docs.python.org/2/reference/simple_stmts.html#the-raise-statement
3600
- # TODO: Also check tuple item types.
3601
- any_type = AnyType (TypeOfAny .implementation_artifact )
3602
- tuple_type = self .named_type ('builtins.tuple' )
3603
- expected_type .items .append (TupleType ([any_type , any_type ], tuple_type ))
3604
- expected_type .items .append (TupleType ([any_type , any_type , any_type ], tuple_type ))
3605
- self .check_subtype (typ , expected_type , s , message_registry .INVALID_EXCEPTION )
3604
+ # This is used for `x` part in a case like `raise e from x`,
3605
+ # where we allow `raise e from None`.
3606
+ expected_type_items .append (NoneType ())
3607
+
3608
+ self .check_subtype (
3609
+ typ , UnionType .make_union (expected_type_items ), s ,
3610
+ message_registry .INVALID_EXCEPTION ,
3611
+ )
3606
3612
3607
3613
if isinstance (typ , FunctionLike ):
3608
3614
# https://github.com/python/mypy/issues/11089
3609
3615
self .expr_checker .check_call (typ , [], [], e )
3610
3616
3617
+ def _type_check_raise_python2 (self , e : Expression , s : RaiseStmt , typ : ProperType ) -> None :
3618
+ # Python2 has two possible major cases:
3619
+ # 1. `raise expr`, where `expr` is some expression, it can be:
3620
+ # - Exception typ
3621
+ # - Exception instance
3622
+ # - Old style class (not supported)
3623
+ # - Tuple, where 0th item is exception type or instance
3624
+ # 2. `raise exc, msg, traceback`, where:
3625
+ # - `exc` is exception type (not instance!)
3626
+ # - `traceback` is `types.TracebackType | None`
3627
+ # Important note: `raise exc, msg` is not the same as `raise (exc, msg)`
3628
+ # We call `raise exc, msg, traceback` - legacy mode.
3629
+ exc_type = self .named_type ('builtins.BaseException' )
3630
+ exc_inst_or_type = UnionType ([exc_type , TypeType (exc_type )])
3631
+
3632
+ if (not s .legacy_mode and (isinstance (typ , TupleType ) and typ .items
3633
+ or (isinstance (typ , Instance ) and typ .args
3634
+ and typ .type .fullname == 'builtins.tuple' ))):
3635
+ # `raise (exc, ...)` case:
3636
+ item = typ .items [0 ] if isinstance (typ , TupleType ) else typ .args [0 ]
3637
+ self .check_subtype (
3638
+ item , exc_inst_or_type , s ,
3639
+ 'When raising a tuple, first element must by derived from BaseException' ,
3640
+ )
3641
+ return
3642
+ elif s .legacy_mode :
3643
+ # `raise Exception, msg` case
3644
+ # `raise Exception, msg, traceback` case
3645
+ # https://docs.python.org/2/reference/simple_stmts.html#the-raise-statement
3646
+ assert isinstance (typ , TupleType ) # Is set in fastparse2.py
3647
+ if (len (typ .items ) >= 2
3648
+ and isinstance (get_proper_type (typ .items [1 ]), NoneType )):
3649
+ expected_type : Type = exc_inst_or_type
3650
+ else :
3651
+ expected_type = TypeType (exc_type )
3652
+ self .check_subtype (
3653
+ typ .items [0 ], expected_type , s ,
3654
+ 'Argument 1 must be "{}" subtype' .format (expected_type ),
3655
+ )
3656
+
3657
+ # Typecheck `traceback` part:
3658
+ if len (typ .items ) == 3 :
3659
+ # Now, we typecheck `traceback` argument if it is present.
3660
+ # We do this after the main check for better error message
3661
+ # and better ordering: first about `BaseException` subtype,
3662
+ # then about `traceback` type.
3663
+ traceback_type = UnionType .make_union ([
3664
+ self .named_type ('types.TracebackType' ),
3665
+ NoneType (),
3666
+ ])
3667
+ self .check_subtype (
3668
+ typ .items [2 ], traceback_type , s ,
3669
+ 'Argument 3 must be "{}" subtype' .format (traceback_type ),
3670
+ )
3671
+ else :
3672
+ expected_type_items = [
3673
+ # `raise Exception` and `raise Exception()` cases:
3674
+ exc_type , TypeType (exc_type ),
3675
+ ]
3676
+ self .check_subtype (
3677
+ typ , UnionType .make_union (expected_type_items ),
3678
+ s , message_registry .INVALID_EXCEPTION ,
3679
+ )
3680
+
3611
3681
def visit_try_stmt (self , s : TryStmt ) -> None :
3612
3682
"""Type check a try statement."""
3613
3683
# Our enclosing frame will get the result if the try/except falls through.
0 commit comments