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

Code before:
            Other(Cow<'db, TupleSpec<'db>>),
            None,
        }

        let variadic_type = match argument_type {
            // When accessing an instance attribute that is a `P.args`, the type we infer is
            // `Unknown | P.args`. This needs to be special cased here to avoid calling
            // `iterate` on it which will lose the `ParamSpec` information as it will return
            // `object` that comes from the upper bound of `P.args`. What we want is to always
            // use the `P.args` type to perform type checking against the parameter type. This
            // will allow us to error when `*args: P.args` is matched against, for example,
            // `n: int` and correctly type check when `*args: P.args` is matched against
            // `*args: P.args` (another ParamSpec).
            Some(argument_type) => match argument_type.as_paramspec_typevar(db) {
                Some(paramspec) => VariadicArgumentType::ParamSpec(paramspec),
                None => match argument_type {
                    // `Type::iterate` unions tuple specs in a way that can invent additional
                    // arities. Iterate each union element individually and compute per-position
                    // union types, length bounds, and variable element so that the rest of the
                    // matching logic handles unions correctly.
                    //
                    // We restrict this to cases where all remaining positional parameters are
                    // defaulted and there is no variadic parameter, because the per-position
                    // union loses the correlation between element lengths and per-position types.
                    // In overloaded contexts, this loss would prevent the expansion step from
                    // correctly splitting the union into separate argument lists.
                    //
                    // TODO: This is overly conservative. We could also apply this when all
                    // non-defaulted parameters are covered by the shortest union element,
                    // e.g. `f(a: int, b: int = 0)` with `*x` where `x: tuple[int] | tuple[int, int]`.
                    Type::Union(union)
                        if self.parameters.variadic().is_none()
                            && self
                                .parameters
                                .positional()
                                .skip(self.next_positional)
                                .all(|parameter| parameter.default_type().is_some()) =>
                    {
                        let tuple_specs: Vec<_> =
                            union.elements(db).iter().map(|ty| ty.iterate(db)).collect();

                        let min_len = tuple_specs
                            .iter()
                            .map(|s| s.len().minimum())
                            .min()
                            .unwrap_or(0);
                        let any_variable = tuple_specs.iter().any(|s| s.len().is_variable());
                        let max_elements = tuple_specs
                            .iter()
                            .map(|s| s.all_elements().len())
                            .max()

Review comment (suggestion):
I assume you tried lifting this restriction, and observed this overload problem.

Did you observe this in the ecosystem, or in an existing test?

If the former, we should add a test.

Code after:
    ) -> Result<(), ()> {
        let Some((parameter_index, parameter)) = self
            .parameters
            .keyword_by_name(name)
            .or_else(|| self.parameters.keyword_variadic())
        else {
            if let Some((parameter_index, parameter)) =
                self.parameters.positional_only_by_name(name)
            {
                self.errors
                    .push(BindingError::PositionalOnlyParameterAsKwarg {
                        argument_index: self.get_argument_index(argument_index),
                        parameter: ParameterContext::new(parameter, parameter_index, true),
                    });
                self.parameter_info[parameter_index].suppress_missing_error = true;
            } else {
                self.errors.push(BindingError::UnknownArgument {
                    argument_name: ast::name::Name::new(name),
                    argument_index: self.get_argument_index(argument_index),
                });
            }
            return Err(());
        };
        self.assign_argument(
            argument_index,
            argument,
            argument_type,
            parameter_index,
            parameter,
            false,
            false,
        );
        Ok(())
    }

    /// Match a variadic argument to the remaining positional, standard or variadic parameters.
    fn match_variadic(
        &mut self,
        db: &'db dyn Db,
        argument_index: usize,
        argument: Argument<'a>,
        argument_type: Option<Type<'db>>,
    ) -> Result<(), ()> {
        enum VariadicArgumentType<'db> {
            ParamSpec(Type<'db>),
            /// A union type where each element has been individually iterated into a tuple spec.
            /// We pre-compute the per-position union types, length bounds, and variable element
            /// so the rest of the matching logic can handle unions without special-casing.
            Union {
                argument_types: Vec<Type<'db>>,
                length: TupleLength,