diff --git a/crates/red_knot_python_semantic/resources/mdtest/assignment/annotations.md b/crates/red_knot_python_semantic/resources/mdtest/assignment/annotations.md index 1e912f7f248b3..923fca96ac53b 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/assignment/annotations.md +++ b/crates/red_knot_python_semantic/resources/mdtest/assignment/annotations.md @@ -22,3 +22,13 @@ x: int = "foo" # error: [invalid-assignment] "Object of type `Literal["foo"]` i x: int x = "foo" # error: [invalid-assignment] "Object of type `Literal["foo"]` is not assignable to `int`" ``` + +## PEP-604 annotations not yet supported + +```py +def f() -> str | None: + return None + +# TODO: should be `str | None` (but Todo is better than `Unknown`) +reveal_type(f()) # revealed: @Todo +``` diff --git a/crates/red_knot_python_semantic/src/types.rs b/crates/red_knot_python_semantic/src/types.rs index b953f2865119f..d445b694be249 100644 --- a/crates/red_knot_python_semantic/src/types.rs +++ b/crates/red_knot_python_semantic/src/types.rs @@ -211,7 +211,13 @@ fn declarations_ty<'db>( let declared_ty = if let Some(second) = all_types.next() { let mut builder = UnionBuilder::new(db).add(first); for other in [second].into_iter().chain(all_types) { - if !first.is_equivalent_to(db, other) { + // Make sure not to emit spurious errors relating to `Type::Todo`, + // since we only infer this type due to a limitation in our current model. + // + // `Unknown` is different here, since we might infer `Unknown` + // for one of these due to a variable being defined in one possible + // control-flow branch but not another one. + if !first.is_equivalent_to(db, other) && !first.is_todo() && !other.is_todo() { conflicting.push(other); } builder = builder.add(other); @@ -292,6 +298,10 @@ impl<'db> Type<'db> { matches!(self, Type::Never) } + pub const fn is_todo(&self) -> bool { + matches!(self, Type::Todo) + } + pub const fn into_class_literal_type(self) -> Option> { match self { Type::ClassLiteral(class_type) => Some(class_type), diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index f726caffea106..d2d307b7b49b9 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -3471,6 +3471,17 @@ impl<'db> TypeInferenceBuilder<'db> { Type::Todo } + // TODO PEP-604 unions + ast::Expr::BinOp(binary) => { + self.infer_binary_expression(binary); + match binary.op { + // PEP-604 unions are okay + ast::Operator::BitOr => Type::Todo, + // anything else is an invalid annotation: + _ => Type::Unknown, + } + } + // Forms which are invalid in the context of annotation expressions: we infer their // nested expressions as normal expressions, but the type of the top-level expression is // always `Type::Unknown` in these cases. @@ -3482,10 +3493,6 @@ impl<'db> TypeInferenceBuilder<'db> { self.infer_named_expression(named); Type::Unknown } - ast::Expr::BinOp(binary) => { - self.infer_binary_expression(binary); - Type::Unknown - } ast::Expr::UnaryOp(unary) => { self.infer_unary_expression(unary); Type::Unknown