Repository: astral-sh/ruff
File: crates/ruff_linter/src/rules/ruff/rules/logging_eager_conversion.rs (Rust)

Code before:
            } else {
                None
            }
        })
        .zip(call.arguments.args.iter().skip(msg_pos + 1))
    {
        // Check if the argument is a call to eagerly format a value
        if let Expr::Call(ast::ExprCall {
            func,
            arguments: str_call_args,
            ..
        }) = arg
        {
            let CFormatType::String(format_conversion) = spec.format_type else {
                continue;
            };

            // Check for various eager conversion patterns
            match format_conversion {
                // %s with str() - remove str() call
                // Only flag if str() has exactly one non-starred positional argument and no keyword arguments
                FormatConversion::Str
                    if checker.semantic().match_builtin_expr(func.as_ref(), "str")
                        && str_call_args.args.len() == 1
                        && !str_call_args.args[0].is_starred_expr()
                        && str_call_args.keywords.is_empty() =>
                {
                    checker.report_diagnostic(
                        LoggingEagerConversion {
                            format_conversion,
                            function_name: None,
                        },
                        arg.range(),
                    );
                }
                // %s with repr() - suggest using %r instead
                FormatConversion::Str
                    if checker.semantic().match_builtin_expr(func.as_ref(), "repr") =>
                {
                    checker.report_diagnostic(
                        LoggingEagerConversion {
                            format_conversion: FormatConversion::Repr,
                            function_name: None,
                        },
                        arg.range(),
                    );
                }
                // %s with ascii() - suggest using %a instead
                FormatConversion::Str
                    if checker
                        .semantic()

Review comment (suggestion):
`logging.warning("%s", str(object="!"))`, with one keyword argument, should still be flagged.

Code after:
            } else {
                None
            }
        })
        .zip(call.arguments.args.iter().skip(msg_pos + 1))
    {
        // Check if the argument is a call to eagerly format a value
        if let Expr::Call(ast::ExprCall {
            func,
            arguments: str_call_args,
            ..
        }) = arg
        {
            let CFormatType::String(format_conversion) = spec.format_type else {
                continue;
            };

            // Check for various eager conversion patterns
            match format_conversion {
                // %s with str() - remove str() call
                // Only flag if str() has exactly one argument (positional or keyword) that is not unpacked
                FormatConversion::Str
                    if checker.semantic().match_builtin_expr(func.as_ref(), "str")
                        && str_call_args.len() == 1
                        && str_call_args
                            .find_argument("object", 0)
                            .is_some_and(|arg| !arg.is_variadic()) =>
                {
                    checker.report_diagnostic(
                        LoggingEagerConversion {
                            format_conversion,
                            function_name: None,
                        },
                        arg.range(),
                    );
                }
                // %s with repr() - suggest using %r instead
                FormatConversion::Str
                    if checker.semantic().match_builtin_expr(func.as_ref(), "repr") =>
                {
                    checker.report_diagnostic(
                        LoggingEagerConversion {
                            format_conversion: FormatConversion::Repr,
                            function_name: None,
                        },
                        arg.range(),
                    );
                }
                // %s with ascii() - suggest using %a instead
                FormatConversion::Str
                    if checker