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

Code before:
                                        // '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)),
                                        _ => {
                                            // Unknown/unsupported format character
                                            escaped = true;
                                            break;
                                        }
                                    };

                                    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 {
                                            // To prevent OOM errors, we cap the number of repetitions by escaping to Unknown.
                                            if count >= 2 ^ 16 {
                                                escaped = true;
                                                break;
                                            }
                                            for _ in 0..count {
                                                elements.push(ty);
                                            }
                                        }
                                    }
                                }
                            }

                            if escaped {
                                // set return type to tuple[Unknown, ...]
                                overload
                                    .set_return_type(Type::homogeneous_tuple(db, Type::unknown()));
                            } else {
                                overload.set_return_type(Type::heterogeneous_tuple(
                                    db,
                                    elements.into_iter(),
                                ));
                            }
                        }

                        _ => {
                            // Ideally, either the implementation, or exactly one of the overloads

Review comment (bug):
2 ^ 16 means 2 XOR 16 in Rust, so this is equivalent to `count >= 18` (https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=a290d2ec64af9b8124fe1a55c41052ca), probably not what you intended.

2¹⁶ would actually be way too high of a limit, I think. That would mean we would construct tuple types with 65536 entries. I'll change the limit to 32 for now and add some tests that this thresholding kicks in at the right position.

Code after:
                            let Some(format_literal) = format.as_string_literal() else {
                                continue;
                            };

                            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.