diff --git a/crates/ruff/src/rules/flake8_logging/snapshots/ruff__rules__flake8_logging__tests__LOG007_LOG007.py.snap b/crates/ruff/src/rules/flake8_logging/snapshots/ruff__rules__flake8_logging__tests__LOG007_LOG007.py.snap index 598d9fd8b1e57d..32d3ec421dbe67 100644 --- a/crates/ruff/src/rules/flake8_logging/snapshots/ruff__rules__flake8_logging__tests__LOG007_LOG007.py.snap +++ b/crates/ruff/src/rules/flake8_logging/snapshots/ruff__rules__flake8_logging__tests__LOG007_LOG007.py.snap @@ -37,4 +37,13 @@ LOG007.py:10:1: LOG007 Use of `logging.exception` with falsy `exc_info` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LOG007 | +LOG007.py:15:1: LOG007 Use of `logging.exception` with falsy `exc_info` + | +13 | from logging import exception +14 | +15 | exception("foo", exc_info=False) # LOG007 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LOG007 +16 | exception("foo", exc_info=True) # OK + | + diff --git a/crates/ruff_python_semantic/src/analyze/logging.rs b/crates/ruff_python_semantic/src/analyze/logging.rs index 205e3bb8e4c3b9..290bd7b92df351 100644 --- a/crates/ruff_python_semantic/src/analyze/logging.rs +++ b/crates/ruff_python_semantic/src/analyze/logging.rs @@ -21,41 +21,55 @@ pub fn is_logger_candidate( semantic: &SemanticModel, logger_objects: &[String], ) -> bool { - let Expr::Attribute(ast::ExprAttribute { value, .. }) = func else { - return false; - }; + // logging.exception(), logger.error(), ... + if let Expr::Attribute(ast::ExprAttribute { value, .. }) = func { + // If the symbol was imported from another module, ensure that it's either a user-specified + // logger object, the `logging` module itself, or `flask.current_app.logger`. + if let Some(call_path) = semantic.resolve_call_path(value) { + if matches!( + call_path.as_slice(), + ["logging"] | ["flask", "current_app", "logger"] + ) { + return true; + } - // If the symbol was imported from another module, ensure that it's either a user-specified - // logger object, the `logging` module itself, or `flask.current_app.logger`. - if let Some(call_path) = semantic.resolve_call_path(value) { - if matches!( - call_path.as_slice(), - ["logging"] | ["flask", "current_app", "logger"] - ) { - return true; - } + if logger_objects + .iter() + .any(|logger| from_qualified_name(logger) == call_path) + { + return true; + } - if logger_objects - .iter() - .any(|logger| from_qualified_name(logger) == call_path) - { - return true; + return false; } - return false; + // Otherwise, if the symbol was defined in the current module, match against some common + // logger names. + if let Some(call_path) = collect_call_path(value) { + if let Some(tail) = call_path.last() { + if tail.starts_with("log") + || tail.ends_with("logger") + || tail.ends_with("logging") + || tail.starts_with("LOG") + || tail.ends_with("LOGGER") + || tail.ends_with("LOGGING") + { + return true; + } + } + } } - // Otherwise, if the symbol was defined in the current module, match against some common - // logger names. - if let Some(call_path) = collect_call_path(value) { - if let Some(tail) = call_path.last() { - if tail.starts_with("log") - || tail.ends_with("logger") - || tail.ends_with("logging") - || tail.starts_with("LOG") - || tail.ends_with("LOGGER") - || tail.ends_with("LOGGING") - { + // exception(), error(), ... + if let Expr::Name(_) = func { + if let Some(call_path) = semantic.resolve_call_path(func) { + if matches!( + call_path.as_slice(), + [ + "logging", + "critical" | "debug" | "error" | "exception" | "info" | "log" | "warning" + ] + ) { return true; } }