Repository: astral-sh/ruff
File: crates/ty_python_semantic/src/types/infer/builder.rs (Rust)

Code before:
                                                slice_node,
                                                rhs_value_node,
                                                TypedDictAssignmentKind::Subscript,
                                                true,
                                            );
                                        }
                                    } else {
                                        if emit_diagnostic
                                            && let Some(builder) = self
                                                .context
                                                .report_lint(&INVALID_ASSIGNMENT, object_node)
                                        {
                                            let assigned_d = rhs_value_ty.display(db);
                                            let value_d = object_ty.display(db);

                                            let diagnostic = builder.into_diagnostic(format_args!(
                                                "Method `__setitem__` of type `{}` cannot be called with \
                                                a key of type `{}` and a value of type `{assigned_d}` on object of type `{value_d}`",
                                                bindings.callable_type().display(db),
                                                slice_ty.display(db),
                                            ));
                                            attach_original_type_info(diagnostic);
                                        }
                                    }
                                }
                                CallErrorKind::PossiblyNotCallable => {
                                    if emit_diagnostic
                                        && let Some(builder) = self
                                            .context
                                            .report_lint(&CALL_NON_CALLABLE, object_node)
                                    {
                                        let diagnostic = builder.into_diagnostic(format_args!(
                                            "Method `__setitem__` of type `{}` may not be callable on object of type `{}`",
                                            bindings.callable_type().display(db),
                                            object_ty.display(db),
                                        ));
                                        attach_original_type_info(diagnostic);
                                    }
                                }
                            }
                            false
                        }
                        CallDunderError::MethodNotAvailable => {
                            if emit_diagnostic
                                && let Some(builder) =
                                    self.context.report_lint(&INVALID_ASSIGNMENT, object_node)
                            {
                                let diagnostic = builder.into_diagnostic(format_args!(
                                    "Cannot assign to a subscript on an object of type `{}` with no `__setitem__` method",
                                    object_ty.display(db),
                                ));

Review comment (suggestion):
I see that we can only give the nice subdiagnostic if it's a dict, but the top-level diagnostic summary here seems like it would be an improvement for non-dictionaries as well as dictionaries? The terms "key", "value", and "subscript assignment" aren't specific to dictionaries; they apply to any object with a `__setitem__` method.

But I suppose that can be done as a separate change

Code after:
                                                rhs_value_node,
                                                TypedDictAssignmentKind::Subscript,
                                                true,
                                            );
                                        }
                                    } else {
                                        if emit_diagnostic
                                            && let Some(builder) = self.context.report_lint(
                                                &INVALID_ASSIGNMENT,
                                                target.range.cover(rhs_value_node.range()),
                                            )
                                        {
                                            let assigned_d = rhs_value_ty.display(db);
                                            let object_d = object_ty.display(db);

                                            // Special diagnostic for dictionaries
                                            if let Some([expected_key_ty, expected_value_ty]) =
                                                object_ty
                                                    .known_specialization(db, KnownClass::Dict)
                                                    .map(|s| s.types(db))
                                            {
                                                let mut diagnostic = builder.into_diagnostic(format_args!(
                                                    "Invalid subscript assignment with key of type `{}` and value of \
                                                     type `{assigned_d}` on object of type `{object_d}`",
                                                    slice_ty.display(db),
                                                ));

                                                if !slice_ty.is_assignable_to(db, *expected_key_ty)
                                                {
                                                    diagnostic.annotate(
                                                        self.context
                                                            .secondary(target.slice.as_ref())
                                                            .message(format_args!(
                                                                "Expected key of type `{}`, got `{}`",
                                                                expected_key_ty.display(db),
                                                                slice_ty.display(db),
                                                            )),
                                                    );
                                                }

                                                if !rhs_value_ty
                                                    .is_assignable_to(db, *expected_value_ty)
                                                {
                                                    diagnostic.annotate(
                                                        self.context
                                                            .secondary(rhs_value_node)
                                                            .message(format_args!(
                                                                "Expected value of type `{}`, got `{}`",
                                                                expected_value_ty.display(db),
                                                                rhs_value_ty.display(db),
                                                            )),