diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/mod.rs b/crates/ruff_linter/src/rules/pygrep_hooks/mod.rs index 5a64e2c53513f..ef1e12b2b8f56 100644 --- a/crates/ruff_linter/src/rules/pygrep_hooks/mod.rs +++ b/crates/ruff_linter/src/rules/pygrep_hooks/mod.rs @@ -9,6 +9,8 @@ mod tests { use test_case::test_case; use crate::registry::Rule; + use crate::settings::types::PreviewMode; + use crate::settings::LinterSettings; use crate::test::test_path; use crate::{assert_messages, settings}; @@ -29,4 +31,22 @@ mod tests { assert_messages!(snapshot, diagnostics); Ok(()) } + + #[test_case(Rule::DeprecatedLogWarn, Path::new("PGH002_1.py"))] + fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { + let snapshot = format!( + "preview__{}_{}", + rule_code.noqa_code(), + path.to_string_lossy() + ); + let diagnostics = test_path( + Path::new("pygrep_hooks").join(path).as_path(), + &LinterSettings { + preview: PreviewMode::Enabled, + ..LinterSettings::for_rule(rule_code) + }, + )?; + assert_messages!(snapshot, diagnostics); + Ok(()) + } } diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs b/crates/ruff_linter/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs index 547f1aa9f79c5..19f6df52f9986 100644 --- a/crates/ruff_linter/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs +++ b/crates/ruff_linter/src/rules/pygrep_hooks/rules/deprecated_log_warn.rs @@ -1,12 +1,12 @@ -use ruff_python_ast::{self as ast, Expr, ExprCall}; -use ruff_python_semantic::analyze::logging; - -use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::{self as ast, Expr}; +use ruff_python_semantic::analyze::logging; use ruff_python_stdlib::logging::LoggingLevel; use ruff_text_size::Ranged; use crate::checkers::ast::Checker; +use crate::importer::ImportRequest; /// ## What it does /// Check for usages of the deprecated `warn` method from the `logging` module. @@ -38,14 +38,20 @@ use crate::checkers::ast::Checker; pub struct DeprecatedLogWarn; impl Violation for DeprecatedLogWarn { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] fn message(&self) -> String { format!("`warn` is deprecated in favor of `warning`") } + + fn fix_title(&self) -> Option { + Some(format!("Replace with `warning`")) + } } /// PGH002 -pub(crate) fn deprecated_log_warn(checker: &mut Checker, call: &ExprCall) { +pub(crate) fn deprecated_log_warn(checker: &mut Checker, call: &ast::ExprCall) { match call.func.as_ref() { Expr::Attribute(ast::ExprAttribute { attr, .. }) => { if !logging::is_logger_candidate( @@ -74,7 +80,28 @@ pub(crate) fn deprecated_log_warn(checker: &mut Checker, call: &ExprCall) { _ => return, } - checker - .diagnostics - .push(Diagnostic::new(DeprecatedLogWarn, call.func.range())); + let mut diagnostic = Diagnostic::new(DeprecatedLogWarn, call.func.range()); + if checker.settings.preview.is_enabled() { + match call.func.as_ref() { + Expr::Attribute(ast::ExprAttribute { attr, .. }) => { + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + "warning".to_string(), + attr.range(), + ))); + } + Expr::Name(_) => { + diagnostic.try_set_fix(|| { + let (import_edit, binding) = checker.importer().get_or_import_symbol( + &ImportRequest::import("logging", "warning"), + call.start(), + checker.semantic(), + )?; + let name_edit = Edit::range_replacement(binding, call.func.range()); + Ok(Fix::safe_edits(import_edit, [name_edit])) + }); + } + _ => {} + } + } + checker.diagnostics.push(diagnostic); } diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH002_PGH002_1.py.snap b/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH002_PGH002_1.py.snap index df73d2858d097..203a3fa5ee715 100644 --- a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH002_PGH002_1.py.snap +++ b/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__PGH002_PGH002_1.py.snap @@ -9,6 +9,7 @@ PGH002_1.py:4:1: PGH002 `warn` is deprecated in favor of `warning` | ^^^^^^^^^^^^ PGH002 5 | warn("not ok") | + = help: Replace with `warning` PGH002_1.py:5:1: PGH002 `warn` is deprecated in favor of `warning` | @@ -18,6 +19,7 @@ PGH002_1.py:5:1: PGH002 `warn` is deprecated in favor of `warning` 6 | 7 | logger = logging.getLogger(__name__) | + = help: Replace with `warning` PGH002_1.py:8:1: PGH002 `warn` is deprecated in favor of `warning` | @@ -25,5 +27,6 @@ PGH002_1.py:8:1: PGH002 `warn` is deprecated in favor of `warning` 8 | logger.warn("this is not ok") | ^^^^^^^^^^^ PGH002 | + = help: Replace with `warning` diff --git a/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__preview__PGH002_PGH002_1.py.snap b/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__preview__PGH002_PGH002_1.py.snap new file mode 100644 index 0000000000000..6c1c5f1f712be --- /dev/null +++ b/crates/ruff_linter/src/rules/pygrep_hooks/snapshots/ruff_linter__rules__pygrep_hooks__tests__preview__PGH002_PGH002_1.py.snap @@ -0,0 +1,59 @@ +--- +source: crates/ruff_linter/src/rules/pygrep_hooks/mod.rs +--- +PGH002_1.py:4:1: PGH002 [*] `warn` is deprecated in favor of `warning` + | +2 | from logging import warn +3 | +4 | logging.warn("this is not ok") + | ^^^^^^^^^^^^ PGH002 +5 | warn("not ok") + | + = help: Replace with `warning` + +ℹ Safe fix +1 1 | import logging +2 2 | from logging import warn +3 3 | +4 |-logging.warn("this is not ok") + 4 |+logging.warning("this is not ok") +5 5 | warn("not ok") +6 6 | +7 7 | logger = logging.getLogger(__name__) + +PGH002_1.py:5:1: PGH002 [*] `warn` is deprecated in favor of `warning` + | +4 | logging.warn("this is not ok") +5 | warn("not ok") + | ^^^^ PGH002 +6 | +7 | logger = logging.getLogger(__name__) + | + = help: Replace with `warning` + +ℹ Safe fix +2 2 | from logging import warn +3 3 | +4 4 | logging.warn("this is not ok") +5 |-warn("not ok") + 5 |+logging.warning("not ok") +6 6 | +7 7 | logger = logging.getLogger(__name__) +8 8 | logger.warn("this is not ok") + +PGH002_1.py:8:1: PGH002 [*] `warn` is deprecated in favor of `warning` + | +7 | logger = logging.getLogger(__name__) +8 | logger.warn("this is not ok") + | ^^^^^^^^^^^ PGH002 + | + = help: Replace with `warning` + +ℹ Safe fix +5 5 | warn("not ok") +6 6 | +7 7 | logger = logging.getLogger(__name__) +8 |-logger.warn("this is not ok") + 8 |+logger.warning("this is not ok") + +