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

Code before:
            let qualified_name = class.qualified_name(context.db());
            let components = qualified_name.components_excluding_self();

            if !components.is_empty() && components[0] == "numbers" {
                let is_numeric = match value_ty {
                    Type::IntLiteral(_) | Type::BooleanLiteral(_) => true,
                    Type::NominalInstance(value_instance) => {
                        let value_class = value_instance.class(context.db());
                        value_class.known(context.db()).map_or(false, |known| {
                            matches!(
                                known,
                                KnownClass::Int
                                    | KnownClass::Float
                                    | KnownClass::Complex
                                    | KnownClass::Bool
                            )
                        })
                    }
                    _ => false,
                };

                if is_numeric {
                    diag.info(
                        "Types from the `numbers` module aren't supported for static type checking. \
                         Consider using a protocol instead, such as `typing.SupportsFloat`",
                    );
                }
            }
        }
        _ => {}
    }
}

pub(super) fn report_invalid_attribute_assignment(
    context: &InferContext,
    node: AnyNodeRef,
    target_ty: Type,
    source_ty: Type,
    attribute_name: &'_ str,
) {
    // TODO: Ideally we would not emit diagnostics for `TypedDict` literal arguments
    // here (see `diagnostic::is_invalid_typed_dict_literal`). However, we may have
    // silenced diagnostics during attribute resolution, and rely on the assignability
    // diagnostic being emitted here.

    report_invalid_assignment_with_message(
        context,
        node,
        target_ty,
        format_args!(
            "Object of type `{}` is not assignable to attribute `{attribute_name}` of type `{}`",

Review comment (suggestion):
This is quite long for a single diagnostic. What about splitting it into two, e.g.

```suggestion
                    diag.info(
                        "Types from the `numbers` module aren't supported for static type checking."
                    );
                    diag.help(
                         "Consider using a protocol instead, such as `typing.SupportsFloat`",
                    );
```

Code after:
        if let Some(module) = file_to_module(db, file)
            && module.is_known(db, KnownModule::Numbers)
        {
            let is_numeric = [KnownClass::Int, KnownClass::Float, KnownClass::Complex]
                .iter()
                .any(|numeric| value_ty.is_subtype_of(db, numeric.to_instance(db)));

            if is_numeric {
                diag.info(
                    "Types from the `numbers` module aren't supported for static type checking",
                );
                diag.help("Consider using a protocol instead, such as `typing.SupportsFloat`");
            }
        }
    }
}

pub(super) fn report_invalid_attribute_assignment(
    context: &InferContext,
    node: AnyNodeRef,
    target_ty: Type,
    source_ty: Type,
    attribute_name: &'_ str,
) {
    // TODO: Ideally we would not emit diagnostics for `TypedDict` literal arguments
    // here (see `diagnostic::is_invalid_typed_dict_literal`). However, we may have
    // silenced diagnostics during attribute resolution, and rely on the assignability
    // diagnostic being emitted here.

    report_invalid_assignment_with_message(
        context,
        node,
        target_ty,
        format_args!(
            "Object of type `{}` is not assignable to attribute `{attribute_name}` of type `{}`",
            source_ty.display(context.db()),
            target_ty.display(context.db()),
        ),
    );
}

pub(super) fn report_bad_dunder_set_call<'db>(
    context: &InferContext<'db, '_>,
    dunder_set_failure: &CallError<'db>,
    attribute: &str,
    object_type: Type<'db>,
    target: &ast::ExprAttribute,
) {
    let Some(builder) = context.report_lint(&INVALID_ASSIGNMENT, target) else {
        return;
    };