diff --git a/crates/ty_python_semantic/resources/mdtest/assignment/annotations.md b/crates/ty_python_semantic/resources/mdtest/assignment/annotations.md index 3baf76dce3..36f53afe4d 100644 --- a/crates/ty_python_semantic/resources/mdtest/assignment/annotations.md +++ b/crates/ty_python_semantic/resources/mdtest/assignment/annotations.md @@ -222,10 +222,10 @@ reveal_type(r) # revealed: dict[int | str, int | str] ## Incorrect collection literal assignments are complained about ```py -# error: [invalid-assignment] "Object of type `list[Unknown | int]` is not assignable to `list[str]`" +# error: [invalid-assignment] "Object of type `list[str | int]` is not assignable to `list[str]`" a: list[str] = [1, 2, 3] -# error: [invalid-assignment] "Object of type `set[Unknown | int | str]` is not assignable to `set[int]`" +# error: [invalid-assignment] "Object of type `set[int | str]` is not assignable to `set[int]`" b: set[int] = {1, 2, "3"} ``` @@ -422,7 +422,7 @@ reveal_type(d) # revealed: list[int | tuple[int, int]] e: list[int] = f(True) reveal_type(e) # revealed: list[int] -# error: [invalid-assignment] "Object of type `list[str]` is not assignable to `list[int]`" +# error: [invalid-assignment] "Object of type `list[int | str]` is not assignable to `list[int]`" g: list[int] = f("a") # error: [invalid-assignment] "Object of type `list[str]` is not assignable to `tuple[int]`" @@ -459,12 +459,12 @@ reveal_type(b) # revealed: TD # error: [missing-typed-dict-key] "Missing required key 'x' in TypedDict `TD` constructor" # error: [invalid-key] "Unknown key "y" for TypedDict `TD`" -# error: [invalid-assignment] "Object of type `Unknown | dict[Unknown | str, Unknown | int]` is not assignable to `TD`" +# error: [invalid-assignment] "Object of type `TD | dict[Unknown | str, Unknown | int]` is not assignable to `TD`" c: TD = f([{"y": 0}, {"x": 1}]) # error: [missing-typed-dict-key] "Missing required key 'x' in TypedDict `TD` constructor" # error: [invalid-key] "Unknown key "y" for TypedDict `TD`" -# error: [invalid-assignment] "Object of type `Unknown | dict[Unknown | str, Unknown | int]` is not assignable to `TD | None`" +# error: [invalid-assignment] "Object of type `TD | None | dict[Unknown | str, Unknown | int]` is not assignable to `TD | None`" c: TD | None = f([{"y": 0}, {"x": 1}]) ``` diff --git a/crates/ty_python_semantic/resources/mdtest/bidirectional.md b/crates/ty_python_semantic/resources/mdtest/bidirectional.md index 300d44a8ba..20f28ef72a 100644 --- a/crates/ty_python_semantic/resources/mdtest/bidirectional.md +++ b/crates/ty_python_semantic/resources/mdtest/bidirectional.md @@ -268,8 +268,8 @@ class A: A(f(1)) -# error: [invalid-argument-type] "Argument to function `__new__` is incorrect: Expected `list[int | str]`, found `list[list[Unknown]]`" -# error: [invalid-argument-type] "Argument to bound method `__init__` is incorrect: Expected `list[int | None]`, found `list[list[Unknown]]`" +# error: [invalid-argument-type] "Argument to function `__new__` is incorrect: Expected `list[int | str]`, found `list[int | None | list[Unknown]] & list[int | str | list[Unknown]] & list[list[Unknown]]`" +# error: [invalid-argument-type] "Argument to bound method `__init__` is incorrect: Expected `list[int | None]`, found `list[int | None | list[Unknown]] & list[int | str | list[Unknown]] & list[list[Unknown]]`" A(f([])) ``` diff --git a/crates/ty_python_semantic/resources/mdtest/call/builtins.md b/crates/ty_python_semantic/resources/mdtest/call/builtins.md index bac85f49d6..0d58d86b3f 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/builtins.md +++ b/crates/ty_python_semantic/resources/mdtest/call/builtins.md @@ -57,7 +57,7 @@ type("Foo", Base, {}) # error: [invalid-argument-type] "Argument to class `type` is incorrect: Expected `tuple[type, ...]`, found `tuple[Literal[1], Literal[2]]`" type("Foo", (1, 2), {}) -# error: [invalid-argument-type] "Argument to class `type` is incorrect: Expected `dict[str, Any]`, found `dict[Unknown | bytes, Unknown | int]`" +# error: [invalid-argument-type] "Argument to class `type` is incorrect: Expected `dict[str, Any]`, found `dict[str | bytes, Any]`" type("Foo", (Base,), {b"attr": 1}) ``` diff --git a/crates/ty_python_semantic/resources/mdtest/call/union.md b/crates/ty_python_semantic/resources/mdtest/call/union.md index 7bb4e02044..4f374ac754 100644 --- a/crates/ty_python_semantic/resources/mdtest/call/union.md +++ b/crates/ty_python_semantic/resources/mdtest/call/union.md @@ -277,6 +277,6 @@ def _(flag: bool): x = f({"x": 1}) reveal_type(x) # revealed: int - # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `T`, found `dict[Unknown | str, Unknown | int]`" + # error: [invalid-argument-type] "Argument to function `f` is incorrect: Expected `T`, found `dict[str, int] & dict[Unknown | str, Unknown | int]`" f({"y": 1}) ``` diff --git a/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md b/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md index fcfa28953e..09df84b42e 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/legacy/classes.md @@ -273,7 +273,7 @@ class C(Generic[T]): reveal_type(C(1)) # revealed: C[int] -# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`" +# error: [invalid-assignment] "Object of type `C[int | str]` is not assignable to `C[int]`" wrong_innards: C[int] = C("five") ``` @@ -289,7 +289,7 @@ class C(Generic[T]): reveal_type(C(1)) # revealed: C[int] -# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`" +# error: [invalid-assignment] "Object of type `C[int | str]` is not assignable to `C[int]`" wrong_innards: C[int] = C("five") ``` @@ -308,7 +308,7 @@ class C(Generic[T]): reveal_type(C(1)) # revealed: C[int] -# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`" +# error: [invalid-assignment] "Object of type `C[int | str]` is not assignable to `C[int]`" wrong_innards: C[int] = C("five") ``` @@ -327,7 +327,7 @@ class C(Generic[T]): reveal_type(C(1)) # revealed: C[int] -# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`" +# error: [invalid-assignment] "Object of type `C[int | str]` is not assignable to `C[int]`" wrong_innards: C[int] = C("five") class D(Generic[T]): @@ -338,7 +338,7 @@ class D(Generic[T]): reveal_type(D(1)) # revealed: D[int] -# error: [invalid-assignment] "Object of type `D[str]` is not assignable to `D[int]`" +# error: [invalid-assignment] "Object of type `D[int | str]` is not assignable to `D[int]`" wrong_innards: D[int] = D("five") ``` @@ -454,7 +454,7 @@ reveal_type(C(1, 1)) # revealed: C[int] reveal_type(C(1, "string")) # revealed: C[int] reveal_type(C(1, True)) # revealed: C[int] -# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`" +# error: [invalid-assignment] "Object of type `C[int | str]` is not assignable to `C[int]`" wrong_innards: C[int] = C("five", 1) ``` diff --git a/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md b/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md index a701c0fcac..1f3d69e01b 100644 --- a/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md +++ b/crates/ty_python_semantic/resources/mdtest/generics/pep695/classes.md @@ -249,7 +249,7 @@ class C[T]: reveal_type(C(1)) # revealed: C[int] -# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`" +# error: [invalid-assignment] "Object of type `C[int | str]` is not assignable to `C[int]`" wrong_innards: C[int] = C("five") ``` @@ -263,7 +263,7 @@ class C[T]: reveal_type(C(1)) # revealed: C[int] -# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`" +# error: [invalid-assignment] "Object of type `C[int | str]` is not assignable to `C[int]`" wrong_innards: C[int] = C("five") ``` @@ -280,7 +280,7 @@ class C[T]: reveal_type(C(1)) # revealed: C[int] -# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`" +# error: [invalid-assignment] "Object of type `C[int | str]` is not assignable to `C[int]`" wrong_innards: C[int] = C("five") ``` @@ -297,7 +297,7 @@ class C[T]: reveal_type(C(1)) # revealed: C[int] -# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`" +# error: [invalid-assignment] "Object of type `C[int | str]` is not assignable to `C[int]`" wrong_innards: C[int] = C("five") class D[T]: @@ -310,7 +310,7 @@ class D[T]: reveal_type(D(1)) # revealed: D[int] -# error: [invalid-assignment] "Object of type `D[str]` is not assignable to `D[int]`" +# error: [invalid-assignment] "Object of type `D[int | str]` is not assignable to `D[int]`" wrong_innards: D[int] = D("five") ``` @@ -395,7 +395,7 @@ reveal_type(C(1, 1)) # revealed: C[int] reveal_type(C(1, "string")) # revealed: C[int] reveal_type(C(1, True)) # revealed: C[int] -# error: [invalid-assignment] "Object of type `C[str]` is not assignable to `C[int]`" +# error: [invalid-assignment] "Object of type `C[int | str]` is not assignable to `C[int]`" wrong_innards: C[int] = C("five", 1) ``` diff --git a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m…_-_Invalid_assignment_d…_-_Multiple_targets_(e20ddfd7a91affb0).snap b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m…_-_Invalid_assignment_d…_-_Multiple_targets_(e20ddfd7a91affb0).snap index 8ac6d47663..593bef3b2b 100644 --- a/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m…_-_Invalid_assignment_d…_-_Multiple_targets_(e20ddfd7a91affb0).snap +++ b/crates/ty_python_semantic/resources/mdtest/snapshots/invalid_assignment.m…_-_Invalid_assignment_d…_-_Multiple_targets_(e20ddfd7a91affb0).snap @@ -23,13 +23,13 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/invalid_assi # Diagnostics ``` -error[invalid-assignment]: Object of type `tuple[Literal["a"], Literal["b"]]` is not assignable to `int` +error[invalid-assignment]: Object of type `Literal["a"]` is not assignable to `int` --> src/mdtest_snippet.py:4:1 | 2 | y: str 3 | 4 | x, y = ("a", "b") # error: [invalid-assignment] - | - ^^^^^^^^^^ Incompatible value of type `tuple[Literal["a"], Literal["b"]]` + | - ^^^^^^^^^^ Incompatible value of type `Literal["a"]` | | | Declared type `int` 5 | @@ -40,13 +40,13 @@ info: rule `invalid-assignment` is enabled by default ``` ``` -error[invalid-assignment]: Object of type `tuple[Literal[0], Literal[0]]` is not assignable to `str` +error[invalid-assignment]: Object of type `Literal[0]` is not assignable to `str` --> src/mdtest_snippet.py:6:4 | 4 | x, y = ("a", "b") # error: [invalid-assignment] 5 | 6 | x, y = (0, 0) # error: [invalid-assignment] - | - ^^^^^^ Incompatible value of type `tuple[Literal[0], Literal[0]]` + | - ^^^^^^ Incompatible value of type `Literal[0]` | | | Declared type `str` | diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index f6c556f639..6194965659 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -27,8 +27,7 @@ pub(crate) use self::diagnostic::register_lints; pub use self::diagnostic::{TypeCheckDiagnostics, UNDEFINED_REVEAL}; pub(crate) use self::infer::{ TypeContext, infer_deferred_types, infer_definition_types, infer_expression_type, - infer_expression_types, infer_isolated_expression, infer_scope_types, - static_expression_truthiness, + infer_expression_types, infer_scope_types, static_expression_truthiness, }; pub(crate) use self::signatures::{CallableSignature, Parameter, Parameters, Signature}; pub(crate) use self::subclass_of::{SubclassOfInner, SubclassOfType}; @@ -1369,14 +1368,13 @@ impl<'db> Type<'db> { }; // Avoid literal promotion if it leads to an unassignable type. - if tcx - .annotation - .is_none_or(|annotation| promoted.is_assignable_to(db, annotation)) - { - return promoted; + if tcx.annotation.is_some_and(|annotation| { + self.is_assignable_to(db, annotation) && !promoted.is_assignable_to(db, annotation) + }) { + return self; } - self + promoted } /// Return a "normalized" version of `self` that ensures that equivalent types have the same Salsa ID. diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index c4aac0bb96..280aa7984d 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -39,7 +39,7 @@ use crate::types::{ DataclassParams, FieldInstance, KnownBoundMethodType, KnownClass, KnownInstanceType, MemberLookupPolicy, NominalInstanceType, PropertyInstanceType, SpecialFormType, TrackedConstraintSet, TypeAliasType, TypeContext, TypeVarVariance, UnionBuilder, UnionType, - WrapperDescriptorKind, enums, ide_support, infer_isolated_expression, todo_type, + WrapperDescriptorKind, enums, ide_support, todo_type, }; use ruff_db::diagnostic::{Annotation, Diagnostic, SubDiagnostic, SubDiagnosticSeverity}; use ruff_python_ast::{self as ast, ArgOrKeyword, PythonVersion}; @@ -3800,23 +3800,6 @@ impl<'db> BindingError<'db> { return; }; - // Re-infer the argument type of call expressions, ignoring the type context for more - // precise error messages. - let provided_ty = match Self::get_argument_node(node, *argument_index) { - None => *provided_ty, - - // Ignore starred arguments, as those are difficult to re-infer. - Some( - ast::ArgOrKeyword::Arg(ast::Expr::Starred(_)) - | ast::ArgOrKeyword::Keyword(ast::Keyword { arg: None, .. }), - ) => *provided_ty, - - Some( - ast::ArgOrKeyword::Arg(value) - | ast::ArgOrKeyword::Keyword(ast::Keyword { value, .. }), - ) => infer_isolated_expression(context.db(), context.scope(), value), - }; - let provided_ty_display = provided_ty.display(context.db()); let expected_ty_display = expected_ty.display(context.db()); diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index 050a55fb93..0ab5928e2c 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -25,7 +25,7 @@ use crate::types::string_annotation::{ use crate::types::{ BoundTypeVarInstance, ClassType, DynamicType, LintDiagnosticGuard, Protocol, ProtocolInstanceType, SpecialFormType, SubclassOfInner, Type, TypeContext, binding_type, - infer_isolated_expression, protocol_class::ProtocolClass, + protocol_class::ProtocolClass, }; use crate::types::{KnownInstanceType, MemberLookupPolicy}; use crate::{Db, DisplaySettings, FxIndexMap, Module, ModuleName, Program, declare_lint}; @@ -2190,7 +2190,7 @@ pub(super) fn report_invalid_assignment<'db>( target_node: AnyNodeRef, definition: Definition<'db>, target_ty: Type, - mut value_ty: Type<'db>, + value_ty: Type<'db>, ) { let definition_kind = definition.kind(context.db()); let value_node = match definition_kind { @@ -2209,13 +2209,6 @@ pub(super) fn report_invalid_assignment<'db>( let settings = DisplaySettings::from_possibly_ambiguous_type_pair(context.db(), target_ty, value_ty); - if let Some(value_node) = value_node { - // Re-infer the RHS of the annotated assignment, ignoring the type context for more precise - // error messages. - value_ty = - infer_isolated_expression(context.db(), definition.scope(context.db()), value_node); - } - let diagnostic_range = if let Some(value_node) = value_node { // Expand the range to include parentheses around the value, if any. This allows // invalid-assignment diagnostics to be suppressed on the opening or closing parenthesis: diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 8adeab79eb..1d77a76e78 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -37,7 +37,6 @@ //! be considered a bug.) use ruff_db::parsed::{ParsedModuleRef, parsed_module}; -use ruff_python_ast as ast; use ruff_text_size::Ranged; use rustc_hash::{FxHashMap, FxHashSet}; use salsa; @@ -206,24 +205,6 @@ fn infer_expression_types_impl<'db>( .finish_expression() } -/// Infer the type of an expression in isolation. -/// -/// The type returned by this function may be different than the type of the expression -/// if it was inferred within its region, as it does not account for surrounding type context. -/// This can be useful to re-infer the type of an expression for diagnostics. -pub(crate) fn infer_isolated_expression<'db>( - db: &'db dyn Db, - scope: ScopeId<'db>, - expr: &ast::Expr, -) -> Type<'db> { - let file = scope.file(db); - let module = parsed_module(db, file).load(db); - let index = semantic_index(db, file); - - TypeInferenceBuilder::new(db, InferenceRegion::Scope(scope), index, &module) - .infer_isolated_expression(expr) -} - fn expression_cycle_recover<'db>( db: &'db dyn Db, cycle: &salsa::Cycle, diff --git a/crates/ty_python_semantic/src/types/infer/builder.rs b/crates/ty_python_semantic/src/types/infer/builder.rs index 4b14806bf6..dc4dc2a7e9 100644 --- a/crates/ty_python_semantic/src/types/infer/builder.rs +++ b/crates/ty_python_semantic/src/types/infer/builder.rs @@ -11703,13 +11703,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { } } - /// Infer the type of the given expression in isolation, ignoring the surrounding region. - pub(super) fn infer_isolated_expression(mut self, expr: &ast::Expr) -> Type<'db> { - let expr_ty = self.infer_expression_impl(expr, TypeContext::default()); - let _ = self.context.finish(); - expr_ty - } - pub(super) fn finish_expression(mut self) -> ExpressionInference<'db> { self.infer_region();