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

Code before:
    ) -> bool {
        let db = self.db();

        let mut first_tcx = None;

        // A wrapper over `infer_value_ty` that allows inferring the value type multiple times
        // during attribute resolution.
        let pure_infer_value_ty = infer_value_ty;
        let mut infer_value_ty = |builder: &mut Self, tcx: TypeContext<'db>| -> Type<'db> {
            // Overwrite the previously inferred value, preferring later inferences, which are
            // likely more precise. Note that we still ensure each inference is assignable to
            // its declared type, so this mainly affects the IDE hover type.
            let prev_multi_inference_state =
                builder.set_multi_inference_state(MultiInferenceState::Overwrite);

            // If we are inferring the argument multiple times, silence diagnostics to avoid duplicated warnings.
            let was_in_multi_inference = if let Some(first_tcx) = first_tcx {
                // The first time we infer an argument during multi-inference must be without type context,
                // to avoid leaking diagnostics for bidirectional inference attempts.
                debug_assert_eq!(first_tcx, TypeContext::default());

                builder.context.set_multi_inference(true)
            } else {
                builder.context.is_in_multi_inference()
            };

            let value_ty = pure_infer_value_ty(builder, tcx);

            // Reset the multi-inference state.
            first_tcx.get_or_insert(tcx);
            builder.multi_inference_state = prev_multi_inference_state;
            builder.context.set_multi_inference(was_in_multi_inference);

            value_ty
        };

        // This closure should only be called if `value_ty` was inferred with `attr_ty` as type context.
        let ensure_assignable_to =
            |builder: &Self, value_ty: Type<'db>, attr_ty: Type<'db>| -> bool {
                let assignable = value_ty.is_assignable_to(db, attr_ty);
                if !assignable && emit_diagnostics {
                    report_invalid_attribute_assignment(
                        &builder.context,
                        target.into(),
                        attr_ty,
                        value_ty,
                        attribute,
                    );
                }
                assignable
            };

Review comment (suggestion):
A more exact way of checking whether a class is user-defined might be:

```suggestion
                                // If it's a user-defined class, suggest adding a `__setitem__` method.
                                if object_ty
                                    .as_nominal_instance()
                                    .and_then(|instance| {
                                        file_to_module(
                                            db,
                                            instance.class(db).class_literal(db).0.file(db),
                                        )
                                    })
                                    .and_then(|module| module.search_path(db))
                                    .is_some_and(crate::SearchPath::is_first_party)
```

Code after:
                                            "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(&mut diagnostic);
                                    }
                                }
                            }
                            false
                        }
                        CallDunderError::MethodNotAvailable => {
                            if emit_diagnostic
                                && let Some(builder) =
                                    self.context.report_lint(&INVALID_ASSIGNMENT, target)
                            {
                                let mut diagnostic = builder.into_diagnostic(format_args!(
                                    "Cannot assign to a subscript on an object of type `{}`",
                                    object_ty.display(db),
                                ));
                                attach_original_type_info(&mut diagnostic);

                                // Use `KnownClass` as a crude proxy for checking if this is not a user-defined class. Otherwise,
                                // we end up suggesting things like "Consider adding a `__setitem__` method to `None`".
                                if object_ty
                                    .as_nominal_instance()
                                    .is_some_and(|instance| instance.class(db).known(db).is_some())
                                {
                                    diagnostic.info(format_args!(
                                        "`{}` does not have a `__setitem__` method.",
                                        object_ty.display(db),
                                    ));
                                } else {
                                    diagnostic.help(format_args!(
                                        "Consider adding a `__setitem__` method to `{}`.",
                                        object_ty.display(db),
                                    ));
                                }
                            }
                            false
                        }
                    },
                }
            }
        }
    }

    /// Make sure that the attribute assignment `obj.attribute = value` is valid.
    ///
    /// `target` is the node for the left-hand side, `object_ty` is the type of `obj`, `attribute` is
    /// the name of the attribute being assigned, and `value_ty` is the type of the right-hand side of
    /// the assignment. If the assignment is invalid, emit diagnostics.