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

Code before:
                                        'x' => None,
                                        // 's' (char[]) and 'p' (pascal string) are bytes
                                        's' | 'p' => Some(KnownClass::Bytes.to_instance(db)),
                                        // 'c' is char (bytes of length 1)
                                        'c' => Some(KnownClass::Bytes.to_instance(db)),
                                        // Integers
                                        'b' | 'B' | 'h' | 'H' | 'i' | 'I' | 'l' | 'L' | 'q'
                                        | 'Q' | 'n' | 'N' | 'P' => {
                                            Some(KnownClass::Int.to_instance(db))
                                        }
                                        // Booleans
                                        '?' => Some(KnownClass::Bool.to_instance(db)),
                                        // Floats
                                        'e' | 'f' | 'd' => Some(KnownClass::Float.to_instance(db)),
                                        _ => None,
                                    };

                                    if let Some(ty) = ty {
                                        if matches!(tag, 's' | 'p') {
                                            // For 's' and 'p', the count represents size in bytes,
                                            // yielding a single bytes object.
                                            elements.push(ty);
                                        } else {
                                            // For other types, count represents repetition.
                                            for _ in 0..count {
                                                elements.push(ty);
                                            }
                                        }
                                    }
                                }
                            }

                            overload.set_return_type(Type::heterogeneous_tuple(
                                db,
                                elements.into_iter(),
                            ));
                        }

                        _ => {
                            // Ideally, either the implementation, or exactly one of the overloads
                            // of the function can have the dataclass_transform decorator applied.
                            // However, we do not yet enforce this, and in the case of multiple
                            // applications of the decorator, we will only consider the last one.
                            let transformer_params = function_type
                                .iter_overloads_and_implementation(db)
                                .rev()
                                .find_map(|function_overload| {
                                    function_overload.dataclass_transformer_params(db)
                                });

                            if let Some(params) = transformer_params {

Review comment (suggestion):
This loop should probably be bounded to some reasonable limit, and beyond that we should fall back to some other type? Currently I can exhaust all the memory on my machine and get `ty` OOM killed by checking this :)

```py
def _(buf: bytes):
    reveal_type(struct.unpack("18446744073709551616c", buf))
```

Whatever we choose to do about this, it would make a good extra test case.

Code after:
                            };

                            let return_type = parse_struct_format(db, format_literal.value(db))
                                .map(|elements| Type::heterogeneous_tuple(db, elements.into_iter()))
                                .unwrap_or_else(|| Type::homogeneous_tuple(db, Type::unknown()));

                            overload.set_return_type(return_type);
                        }

                        _ => {
                            // Ideally, either the implementation, or exactly one of the overloads
                            // of the function can have the dataclass_transform decorator applied.
                            // However, we do not yet enforce this, and in the case of multiple
                            // applications of the decorator, we will only consider the last one.
                            let transformer_params = function_type
                                .iter_overloads_and_implementation(db)
                                .rev()
                                .find_map(|function_overload| {
                                    function_overload.dataclass_transformer_params(db)
                                });

                            if let Some(params) = transformer_params {
                                // If this function was called with a keyword argument like
                                // `order=False`, we extract the argument type and overwrite
                                // the corresponding flag in `dataclass_params`.
                                let dataclass_params =
                                    DataclassParams::from_transformer_params(db, params);
                                let mut flags = dataclass_params.flags(db);

                                for (param, flag) in DATACLASS_FLAGS {
                                    if let Ok(Some(Type::BooleanLiteral(value))) =
                                        overload.parameter_type_by_name(param, false)
                                    {
                                        flags.set(*flag, value);
                                    }
                                }

                                let dataclass_params = DataclassParams::new(
                                    db,
                                    flags,
                                    dataclass_params.field_specifiers(db),
                                );

                                // The dataclass_transform spec doesn't clarify how to tell whether
                                // a decorated function is a decorator or a decorator factory. We
                                // use heuristics based on the number and type of positional arguments:
                                //
                                // - Zero positional arguments: assume it's a decorator factory.
                                // - More than one positional argument: assume it's a decorator factory.
                                // - Exactly one positional argument that's a class: ambiguous, so check
                                //   the return type to disambiguate (class-like means decorate directly).