diff --git a/crates/red_knot_python_semantic/resources/mdtest/literal/literal.md b/crates/red_knot_python_semantic/resources/mdtest/literal/literal.md index 9aaf9ce7fb..92e76b45fd 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/literal/literal.md +++ b/crates/red_knot_python_semantic/resources/mdtest/literal/literal.md @@ -51,6 +51,8 @@ invalid1: Literal[3 + 4] invalid2: Literal[4 + 3j] # error: [invalid-literal-parameter] invalid3: Literal[(3, 4)] + +hello = "hello" invalid4: Literal[ 1 + 2, # error: [invalid-literal-parameter] "foo", diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index 59d3fa8023..1b33798a96 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -4443,11 +4443,18 @@ impl<'db> TypeInferenceBuilder<'db> { element_types.push(element_ty); } - if return_todo { + let ty = if return_todo { Type::Todo } else { Type::tuple(self.db, &element_types) - } + }; + + // Here, we store the type for the inner `int, str` tuple-expression, + // while the type for the outer `tuple[int, str]` slice-expression is + // stored in the surrounding `infer_type_expression` call: + self.store_expression_type(tuple_slice, ty); + + ty } single_element => { let single_element_ty = self.infer_type_expression(single_element); @@ -4463,8 +4470,8 @@ impl<'db> TypeInferenceBuilder<'db> { /// Given the slice of a `type[]` annotation, return the type that the annotation represents fn infer_subclass_of_type_expression(&mut self, slice: &ast::Expr) -> Type<'db> { match slice { - ast::Expr::Name(name) => { - let name_ty = self.infer_name_expression(name); + ast::Expr::Name(_) => { + let name_ty = self.infer_expression(slice); if let Some(ClassLiteralType { class }) = name_ty.into_class_literal() { Type::subclass_of(class) } else { @@ -4541,8 +4548,15 @@ impl<'db> TypeInferenceBuilder<'db> { ast::Expr::Subscript(ast::ExprSubscript { value, slice, .. }) => { let value_ty = self.infer_expression(value); if matches!(value_ty, Type::KnownInstance(KnownInstanceType::Literal)) { - self.infer_literal_parameter_type(slice)? + let ty = self.infer_literal_parameter_type(slice)?; + + // This branch deals with annotations such as `Literal[Literal[1]]`. + // Here, we store the type for the inner `Literal[1]` expression: + self.store_expression_type(parameters, ty); + ty } else { + self.store_expression_type(parameters, Type::Unknown); + return Err(vec![parameters]); } } @@ -4560,15 +4574,27 @@ impl<'db> TypeInferenceBuilder<'db> { } } if errors.is_empty() { - builder.build() + let union_type = builder.build(); + + // This branch deals with annotations such as `Literal[1, 2]`. Here, we + // store the type for the inner `1, 2` tuple-expression: + self.store_expression_type(parameters, union_type); + + union_type } else { + self.store_expression_type(parameters, Type::Unknown); + return Err(errors); } } - ast::Expr::StringLiteral(literal) => self.infer_string_literal_expression(literal), - ast::Expr::BytesLiteral(literal) => self.infer_bytes_literal_expression(literal), - ast::Expr::BooleanLiteral(literal) => self.infer_boolean_literal_expression(literal), + literal @ (ast::Expr::StringLiteral(_) + | ast::Expr::BytesLiteral(_) + | ast::Expr::BooleanLiteral(_) + | ast::Expr::NoneLiteral(_)) => self.infer_expression(literal), + literal @ ast::Expr::NumberLiteral(ref number) if number.value.is_int() => { + self.infer_expression(literal) + } // For enum values ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) => { let value_ty = self.infer_expression(value); @@ -4578,7 +4604,6 @@ impl<'db> TypeInferenceBuilder<'db> { .ignore_possibly_unbound() .unwrap_or(Type::Unknown) } - ast::Expr::NoneLiteral(_) => Type::none(self.db), // for negative and positive numbers ast::Expr::UnaryOp(ref u) if matches!(u.op, UnaryOp::USub | UnaryOp::UAdd) @@ -4586,10 +4611,8 @@ impl<'db> TypeInferenceBuilder<'db> { { self.infer_unary_expression(u) } - ast::Expr::NumberLiteral(ref number) if number.value.is_int() => { - self.infer_number_literal_expression(number) - } _ => { + self.infer_expression(parameters); return Err(vec![parameters]); } }) diff --git a/crates/red_knot_workspace/tests/check.rs b/crates/red_knot_workspace/tests/check.rs index 3a4460c351..8aae30b261 100644 --- a/crates/red_knot_workspace/tests/check.rs +++ b/crates/red_knot_workspace/tests/check.rs @@ -275,50 +275,17 @@ const KNOWN_FAILURES: &[(&str, bool, bool)] = &[ ("crates/ruff_python_parser/resources/inline/ok/type_param_type_var_tuple.py", true, true), ("crates/ruff_python_parser/resources/inline/ok/type_param_type_var.py", true, true), ("crates/ruff_python_parser/resources/valid/statement/type.py", true, true), - // Fails for unknown reasons: - ("crates/ruff_linter/resources/test/fixtures/flake8_future_annotations/no_future_import_uses_union_inner.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI011.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI015.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI016.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI019.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI020.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI030.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI034.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI035.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI036.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI051.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI052.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI055.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI062.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI063.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI064.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote2.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_type_checking/quote3.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH004_13.py", true, true), ("crates/ruff_linter/resources/test/fixtures/flake8_type_checking/TCH004_15.py", true, true), ("crates/ruff_linter/resources/test/fixtures/pyflakes/F401_19.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/pyflakes/F541.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/pyflakes/F632.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/pyflakes/F811_19.py", true, false), - ("crates/ruff_linter/resources/test/fixtures/pyflakes/F821_0.py", true, true), ("crates/ruff_linter/resources/test/fixtures/pyflakes/F821_14.py", false, true), ("crates/ruff_linter/resources/test/fixtures/pyflakes/F821_15.py", true, true), ("crates/ruff_linter/resources/test/fixtures/pyflakes/F821_17.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/pyflakes/F821_2.py", true, true), ("crates/ruff_linter/resources/test/fixtures/pyflakes/F821_20.py", true, true), ("crates/ruff_linter/resources/test/fixtures/pyflakes/F821_26.py", true, false), - ("crates/ruff_linter/resources/test/fixtures/pyflakes/project/foo/bar.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/pyflakes/project/foo/bop/baz.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/pylint/single_string_slots.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/pyupgrade/UP037_0.py", true, true), + // Fails for unknown reasons: + ("crates/ruff_linter/resources/test/fixtures/pyflakes/F541.py", true, true), + ("crates/ruff_linter/resources/test/fixtures/pyflakes/F632.py", true, true), + ("crates/ruff_linter/resources/test/fixtures/pyflakes/F811_19.py", true, false), ("crates/ruff_linter/resources/test/fixtures/pyupgrade/UP039.py", true, false), - ("crates/ruff_linter/resources/test/fixtures/pyupgrade/UP044.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/ruff/RUF013_0.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/ruff/RUF013_3.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/ruff/RUF022.py", true, true), - ("crates/ruff_linter/resources/test/fixtures/ruff/RUF038.py", true, true), ("crates/ruff_python_parser/resources/valid/expressions/f_string.py", true, true), ];