diff --git a/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals_not.md b/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals_not.md new file mode 100644 index 0000000000..0cf341496c --- /dev/null +++ b/crates/red_knot_python_semantic/resources/mdtest/narrow/conditionals_not.md @@ -0,0 +1,33 @@ +# Narrowing for `not` conditionals + +The `not` operator negates a constraint. + +## `not is None` + +```py +def bool_instance() -> bool: + return True + +x = None if bool_instance() else 1 + +if not x is None: + reveal_type(x) # revealed: Literal[1] +else: + reveal_type(x) # revealed: None + +reveal_type(x) # revealed: None | Literal[1] +``` + +## `not isinstance` + +```py +def bool_instance() -> bool: + return True + +x = 1 if bool_instance() else "a" + +if not isinstance(x, (int)): + reveal_type(x) # revealed: Literal["a"] +else: + reveal_type(x) # revealed: Literal[1] +``` diff --git a/crates/red_knot_python_semantic/src/types/narrow.rs b/crates/red_knot_python_semantic/src/types/narrow.rs index 58a314523a..57798a9427 100644 --- a/crates/red_knot_python_semantic/src/types/narrow.rs +++ b/crates/red_knot_python_semantic/src/types/narrow.rs @@ -129,13 +129,30 @@ impl<'db> NarrowingConstraintsBuilder<'db> { } fn evaluate_expression_constraint(&mut self, expression: Expression<'db>, is_positive: bool) { - match expression.node_ref(self.db).node() { + let expression_node = expression.node_ref(self.db).node(); + self.evaluate_expression_node_constraint(expression_node, expression, is_positive); + } + + fn evaluate_expression_node_constraint( + &mut self, + expression_node: &ruff_python_ast::Expr, + expression: Expression<'db>, + is_positive: bool, + ) { + match expression_node { ast::Expr::Compare(expr_compare) => { self.add_expr_compare(expr_compare, expression, is_positive); } ast::Expr::Call(expr_call) => { self.add_expr_call(expr_call, expression, is_positive); } + ast::Expr::UnaryOp(unary_op) if unary_op.op == ast::UnaryOp::Not => { + self.evaluate_expression_node_constraint( + &unary_op.operand, + expression, + !is_positive, + ); + } _ => {} // TODO other test expression kinds } }