diff --git a/crates/red_knot_python_semantic/resources/mdtest/assignment/augmented.md b/crates/red_knot_python_semantic/resources/mdtest/assignment/augmented.md index 316d5bdfda..fb202c0ab9 100644 --- a/crates/red_knot_python_semantic/resources/mdtest/assignment/augmented.md +++ b/crates/red_knot_python_semantic/resources/mdtest/assignment/augmented.md @@ -6,6 +6,10 @@ x = 3 x -= 1 reveal_type(x) # revealed: Literal[2] + +x = 1.0 +x /= 2 +reveal_type(x) # revealed: float ``` ## Dunder methods diff --git a/crates/red_knot_python_semantic/src/types/infer.rs b/crates/red_knot_python_semantic/src/types/infer.rs index c987028c90..a8d5e500c3 100644 --- a/crates/red_knot_python_semantic/src/types/infer.rs +++ b/crates/red_knot_python_semantic/src/types/infer.rs @@ -1425,26 +1425,32 @@ impl<'db> TypeInferenceBuilder<'db> { }; let value_type = self.infer_expression(value); + // If the target defines, e.g., `__iadd__`, infer the augmented assignment as a call to that + // dunder. if let Type::Instance(class) = target_type { let class_member = class.class_member(self.db, op.in_place_dunder()); - let call = class_member.call(self.db, &[target_type, value_type]); - - return match call.return_ty_result(self.db, AnyNodeRef::StmtAugAssign(assignment), self) - { - Ok(t) => t, - Err(e) => { - self.add_diagnostic( - assignment.into(), - "unsupported-operator", - format_args!( - "Operator `{op}=` is unsupported between objects of type `{}` and `{}`", - target_type.display(self.db), - value_type.display(self.db) - ), - ); - e.return_ty() - } - }; + if !class_member.is_unbound() { + let call = class_member.call(self.db, &[target_type, value_type]); + return match call.return_ty_result( + self.db, + AnyNodeRef::StmtAugAssign(assignment), + self, + ) { + Ok(t) => t, + Err(e) => { + self.add_diagnostic( + assignment.into(), + "unsupported-operator", + format_args!( + "Operator `{op}=` is unsupported between objects of type `{}` and `{}`", + target_type.display(self.db), + value_type.display(self.db) + ), + ); + e.return_ty() + } + }; + } } let left_ty = target_type; diff --git a/crates/ruff_benchmark/benches/red_knot.rs b/crates/ruff_benchmark/benches/red_knot.rs index b059df6af7..6e52ccc409 100644 --- a/crates/ruff_benchmark/benches/red_knot.rs +++ b/crates/ruff_benchmark/benches/red_knot.rs @@ -28,30 +28,22 @@ static EXPECTED_DIAGNOSTICS: &[&str] = &[ // We don't support terminal statements in control flow yet: "/src/tomllib/_parser.py:66:18: Name `s` used when possibly not defined", "/src/tomllib/_parser.py:98:12: Name `char` used when possibly not defined", - "/src/tomllib/_parser.py:99:13: Operator `+=` is unsupported for type `int` with type `Literal[1]`", "/src/tomllib/_parser.py:101:12: Name `char` used when possibly not defined", "/src/tomllib/_parser.py:104:14: Name `char` used when possibly not defined", "/src/tomllib/_parser.py:108:17: Conflicting declared types for `second_char`: Unknown, str | None", "/src/tomllib/_parser.py:115:14: Name `char` used when possibly not defined", "/src/tomllib/_parser.py:126:12: Name `char` used when possibly not defined", - "/src/tomllib/_parser.py:130:9: Operator `+=` is unsupported for type `int` with type `Literal[1]`", "/src/tomllib/_parser.py:267:9: Conflicting declared types for `char`: Unknown, str | None", "/src/tomllib/_parser.py:348:20: Name `nest` used when possibly not defined", "/src/tomllib/_parser.py:353:5: Name `nest` used when possibly not defined", "/src/tomllib/_parser.py:353:5: Method `__getitem__` of type `Unbound | @Todo` is not callable on object of type `Unbound | @Todo`", "/src/tomllib/_parser.py:364:9: Conflicting declared types for `char`: Unknown, str | None", - "/src/tomllib/_parser.py:367:5: Operator `+=` is unsupported for type `int` with type `Literal[1]`", "/src/tomllib/_parser.py:381:13: Conflicting declared types for `char`: Unknown, str | None", - "/src/tomllib/_parser.py:384:9: Operator `+=` is unsupported for type `int` with type `Literal[1]`", "/src/tomllib/_parser.py:395:9: Conflicting declared types for `char`: Unknown, str | None", - "/src/tomllib/_parser.py:429:9: Operator `+=` is unsupported for type `int` with type `Literal[1]`", "/src/tomllib/_parser.py:453:24: Name `nest` used when possibly not defined", "/src/tomllib/_parser.py:455:9: Name `nest` used when possibly not defined", "/src/tomllib/_parser.py:455:9: Method `__getitem__` of type `Unbound | @Todo` is not callable on object of type `Unbound | @Todo`", - "/src/tomllib/_parser.py:464:9: Operator `+=` is unsupported for type `int` with type `Literal[1]`", "/src/tomllib/_parser.py:482:16: Name `char` used when possibly not defined", - "/src/tomllib/_parser.py:484:13: Operator `+=` is unsupported for type `int` with type `Literal[1]`", - "/src/tomllib/_parser.py:545:5: Operator `+=` is unsupported for type `int` with type `Literal[1]`", "/src/tomllib/_parser.py:566:12: Name `char` used when possibly not defined", "/src/tomllib/_parser.py:573:12: Name `char` used when possibly not defined", "/src/tomllib/_parser.py:579:12: Name `char` used when possibly not defined",