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

Code before:
                // Otherwise, annotate the target with its declared type.
                diag.annotate(context.secondary(target_node).message(format_args!(
                    "Declared type `{}`",
                    target_ty.display(context.db()),
                )));
            }
        }

        diag.set_primary_message(format_args!(
            "Incompatible value of type `{}`",
            value_ty.display(context.db()),
        ));

        // Overwrite the concise message to avoid showing the value type twice
        let message = diag.primary_message().to_string();
        diag.set_concise_message(message);
    }

    // special case message
    match target_ty {
        Type::NominalInstance(target_instance) => {
            let class = target_instance.class(context.db());
            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`",
                    );
                }
            }
        }

Review comment (suggestion):
I think it would be more robust to add a `KnownModule::Numbers` variant and do something like `target_instance.class(db).file(db).module(db).is_known(db, KnownModule::Numbers)` to check whether the class comes from the `numbers` module

Code after:
                // Otherwise, annotate the target with its declared type.
                diag.annotate(context.secondary(target_node).message(format_args!(
                    "Declared type `{}`",
                    target_ty.display(context.db()),
                )));
            }
        }

        diag.set_primary_message(format_args!(
            "Incompatible value of type `{}`",
            value_ty.display(context.db()),
        ));

        // Overwrite the concise message to avoid showing the value type twice
        let message = diag.primary_message().to_string();
        diag.set_concise_message(message);
    }

    // special case message
    if let Type::NominalInstance(target_instance) = target_ty {
        let db = context.db();
        let file = target_instance.class(db).class_literal(db).file(db);
        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.