diff --git a/crates/ty_python_semantic/resources/mdtest/binary/booleans.md b/crates/ty_python_semantic/resources/mdtest/binary/booleans.md index 6bfca0ecce..e0bcca56e9 100644 --- a/crates/ty_python_semantic/resources/mdtest/binary/booleans.md +++ b/crates/ty_python_semantic/resources/mdtest/binary/booleans.md @@ -103,3 +103,65 @@ def _(a: bool): reveal_type(x / y) # revealed: int | float reveal_type(x % y) # revealed: int ``` + +## Bitwise operations with a variable + +```py +import random + +def _(a: bool): + def lhs_is_int(x: int): + reveal_type(x | a) # revealed: int + reveal_type(x & a) # revealed: int + reveal_type(x ^ a) # revealed: int + + def rhs_is_int(x: int): + reveal_type(a | x) # revealed: int + reveal_type(a & x) # revealed: int + reveal_type(a ^ x) # revealed: int + + def lhs_is_int_literal(): + reveal_type(0 | a) # revealed: int + reveal_type(0 & a) # revealed: int + reveal_type(0 ^ a) # revealed: int + + reveal_type(1 | a) # revealed: int + reveal_type(1 & a) # revealed: int + reveal_type(1 ^ a) # revealed: int + + def lhs_is_true(): + reveal_type(True | a) # revealed: bool + reveal_type(True & a) # revealed: bool + reveal_type(True ^ a) # revealed: bool + + def rhs_is_true(): + reveal_type(a | True) # revealed: bool + reveal_type(a & True) # revealed: bool + reveal_type(a ^ True) # revealed: bool + + def lhs_is_false(): + reveal_type(False | a) # revealed: bool + reveal_type(False & a) # revealed: bool + reveal_type(False ^ a) # revealed: bool + + def rhs_is_false(): + reveal_type(a | False) # revealed: bool + reveal_type(a & False) # revealed: bool + reveal_type(a ^ False) # revealed: bool + + def both_are_bool(x: bool, y: bool): + reveal_type(x | y) # revealed: bool + reveal_type(x & y) # revealed: bool + reveal_type(x ^ y) # revealed: bool + + def lhs_is_int_literal_rhs_is_bool_literal(): + reveal_type(0 & True) # revealed: Literal[0] + reveal_type(0 | True) # revealed: Literal[1] + reveal_type(3 & True) # revealed: Literal[1] + reveal_type(3 | True) # revealed: Literal[3] + + reveal_type(0 & False) # revealed: Literal[0] + reveal_type(0 | False) # revealed: Literal[0] + reveal_type(3 & False) # revealed: Literal[0] + reveal_type(3 | False) # revealed: Literal[3] +``` diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index b2f941da31..4f7180ee31 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -6893,22 +6893,22 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { (Type::BooleanLiteral(b1), Type::BooleanLiteral(b2), ast::Operator::BitXor) => { Some(Type::BooleanLiteral(b1 ^ b2)) } - - (Type::BooleanLiteral(bool_value), right, op) => self.infer_binary_expression_type( - node, - emitted_division_by_zero_diagnostic, - Type::IntLiteral(i64::from(bool_value)), - right, - op, - ), - (left, Type::BooleanLiteral(bool_value), op) => self.infer_binary_expression_type( - node, - emitted_division_by_zero_diagnostic, - left, - Type::IntLiteral(i64::from(bool_value)), - op, - ), - + (Type::BooleanLiteral(b1), Type::BooleanLiteral(_) | Type::IntLiteral(_), op) => self + .infer_binary_expression_type( + node, + emitted_division_by_zero_diagnostic, + Type::IntLiteral(i64::from(b1)), + right_ty, + op, + ), + (Type::IntLiteral(_), Type::BooleanLiteral(b2), op) => self + .infer_binary_expression_type( + node, + emitted_division_by_zero_diagnostic, + left_ty, + Type::IntLiteral(i64::from(b2)), + op, + ), (Type::Tuple(lhs), Type::Tuple(rhs), ast::Operator::Add) => { // Note: this only works on heterogeneous tuples. let lhs_elements = lhs.elements(self.db()); @@ -6927,6 +6927,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // fall back on looking for dunder methods on one of the operand types. ( Type::FunctionLiteral(_) + | Type::BooleanLiteral(_) | Type::Callable(..) | Type::BoundMethod(_) | Type::WrapperDescriptor(_) @@ -6954,6 +6955,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { | Type::TypeVar(_) | Type::TypeIs(_), Type::FunctionLiteral(_) + | Type::BooleanLiteral(_) | Type::Callable(..) | Type::BoundMethod(_) | Type::WrapperDescriptor(_)