diff --git a/crates/ruff/resources/test/fixtures/pylint/logging_too_few_args.py b/crates/ruff/resources/test/fixtures/pylint/logging_too_few_args.py index 2fc6f49ab5756..65f021ea428e1 100644 --- a/crates/ruff/resources/test/fixtures/pylint/logging_too_few_args.py +++ b/crates/ruff/resources/test/fixtures/pylint/logging_too_few_args.py @@ -19,6 +19,10 @@ # do not handle keyword arguments logging.error("%(objects)d modifications: %(modifications)d errors: %(errors)d") +logging.info(msg="Hello %s") + +logging.info(msg="Hello %s %s") + import warning warning.warning("Hello %s %s", "World!") diff --git a/crates/ruff/resources/test/fixtures/pylint/logging_too_many_args.py b/crates/ruff/resources/test/fixtures/pylint/logging_too_many_args.py index c087b05c4e900..c64641eaed224 100644 --- a/crates/ruff/resources/test/fixtures/pylint/logging_too_many_args.py +++ b/crates/ruff/resources/test/fixtures/pylint/logging_too_many_args.py @@ -15,6 +15,10 @@ # do not handle keyword arguments logging.error("%(objects)d modifications: %(modifications)d errors: %(errors)d", {"objects": 1, "modifications": 1, "errors": 1}) +logging.info(msg="Hello") + +logging.info(msg="Hello", something="else") + import warning warning.warning("Hello %s", "World!", "again") diff --git a/crates/ruff/src/rules/pylint/rules/logging.rs b/crates/ruff/src/rules/pylint/rules/logging.rs index 5d2f0604acfc7..5f778ea5d6ab2 100644 --- a/crates/ruff/src/rules/pylint/rules/logging.rs +++ b/crates/ruff/src/rules/pylint/rules/logging.rs @@ -111,8 +111,8 @@ pub(crate) fn logging_call(checker: &mut Checker, call: &ast::ExprCall) { let Some(Expr::Constant(ast::ExprConstant { value: Constant::Str(value), .. - })) = call.arguments.find_argument("msg", 0) - else { + })) = call.arguments + .find_positional( 0) else { return; }; diff --git a/crates/ruff_python_ast/src/nodes.rs b/crates/ruff_python_ast/src/nodes.rs index 46efa5b09807f..5011ba76d90fb 100644 --- a/crates/ruff_python_ast/src/nodes.rs +++ b/crates/ruff_python_ast/src/nodes.rs @@ -2126,23 +2126,21 @@ impl Arguments { }) } + /// Return the positional argument at the given index, or `None` if no such argument exists. + pub fn find_positional(&self, position: usize) -> Option<&Expr> { + self.args + .iter() + .take_while(|expr| !expr.is_starred_expr()) + .nth(position) + } + /// Return the argument with the given name or at the given position, or `None` if no such /// argument exists. Used to retrieve arguments that can be provided _either_ as keyword or /// positional arguments. pub fn find_argument(&self, name: &str, position: usize) -> Option<&Expr> { - self.keywords - .iter() - .find(|keyword| { - let Keyword { arg, .. } = keyword; - arg.as_ref().is_some_and(|arg| arg == name) - }) + self.find_keyword(name) .map(|keyword| &keyword.value) - .or_else(|| { - self.args - .iter() - .take_while(|expr| !expr.is_starred_expr()) - .nth(position) - }) + .or_else(|| self.find_positional(position)) } }