Type check a binary operator expression.
(self, e: OpExpr)
| 3553 | return self.named_type("builtins.ellipsis") |
| 3554 | |
| 3555 | def visit_op_expr(self, e: OpExpr) -> Type: |
| 3556 | """Type check a binary operator expression.""" |
| 3557 | if e.analyzed: |
| 3558 | # It's actually a type expression X | Y. |
| 3559 | return self.accept(e.analyzed) |
| 3560 | if e.op == "and" or e.op == "or": |
| 3561 | return self.check_boolean_op(e) |
| 3562 | if e.op == "*" and isinstance(e.left, ListExpr): |
| 3563 | # Expressions of form [...] * e get special type inference. |
| 3564 | return self.check_list_multiply(e) |
| 3565 | if e.op == "%": |
| 3566 | if isinstance(e.left, BytesExpr): |
| 3567 | return self.strfrm_checker.check_str_interpolation(e.left, e.right) |
| 3568 | if isinstance(e.left, StrExpr): |
| 3569 | return self.strfrm_checker.check_str_interpolation(e.left, e.right) |
| 3570 | left_type = self.accept(e.left) |
| 3571 | |
| 3572 | proper_left_type = get_proper_type(left_type) |
| 3573 | if isinstance(proper_left_type, TupleType) and e.op == "+": |
| 3574 | left_add_method = proper_left_type.partial_fallback.type.get("__add__") |
| 3575 | if left_add_method and left_add_method.fullname == "builtins.tuple.__add__": |
| 3576 | proper_right_type = get_proper_type(self.accept(e.right)) |
| 3577 | if isinstance(proper_right_type, TupleType): |
| 3578 | right_radd_method = proper_right_type.partial_fallback.type.get("__radd__") |
| 3579 | if right_radd_method is None: |
| 3580 | # One cannot have two variadic items in the same tuple. |
| 3581 | if ( |
| 3582 | find_unpack_in_list(proper_left_type.items) is None |
| 3583 | or find_unpack_in_list(proper_right_type.items) is None |
| 3584 | ): |
| 3585 | return self.concat_tuples(proper_left_type, proper_right_type) |
| 3586 | elif ( |
| 3587 | PRECISE_TUPLE_TYPES in self.chk.options.enable_incomplete_feature |
| 3588 | and isinstance(proper_right_type, Instance) |
| 3589 | and self.chk.type_is_iterable(proper_right_type) |
| 3590 | ): |
| 3591 | # Handle tuple[X, Y] + tuple[Z, ...] = tuple[X, Y, *tuple[Z, ...]]. |
| 3592 | right_radd_method = proper_right_type.type.get("__radd__") |
| 3593 | if ( |
| 3594 | right_radd_method is None |
| 3595 | and proper_left_type.partial_fallback.type.fullname == "builtins.tuple" |
| 3596 | and find_unpack_in_list(proper_left_type.items) is None |
| 3597 | ): |
| 3598 | item_type = self.chk.iterable_item_type(proper_right_type, e) |
| 3599 | mapped = self.chk.named_generic_type("builtins.tuple", [item_type]) |
| 3600 | return proper_left_type.copy_modified( |
| 3601 | items=proper_left_type.items + [UnpackType(mapped)] |
| 3602 | ) |
| 3603 | |
| 3604 | use_reverse: UseReverse = USE_REVERSE_DEFAULT |
| 3605 | if e.op == "|": |
| 3606 | if is_named_instance(proper_left_type, "builtins.dict"): |
| 3607 | # This is a special case for `dict | TypedDict`. |
| 3608 | # 1. Find `dict | TypedDict` case |
| 3609 | # 2. Switch `dict.__or__` to `TypedDict.__ror__` (the same from both runtime and typing perspective) |
| 3610 | proper_right_type = get_proper_type(self.accept(e.right)) |
| 3611 | if isinstance(proper_right_type, TypedDictType): |
| 3612 | use_reverse = USE_REVERSE_ALWAYS |
nothing calls this directly
no test coverage detected