[pygrep_hooks] Add fix for deprecated-log-warn (PGH002) (#9519)

## Summary

add autofix for `deprecated_log_warn` (`PGH002`)

## Test Plan

`cargo test`

---------

Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>
This commit is contained in:
Steve C 2024-01-15 21:54:40 -05:00 committed by GitHub
parent 9a2f3e2cef
commit 2bddde2627
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 117 additions and 8 deletions

View file

@ -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(())
}
}

View file

@ -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<String> {
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);
}

View file

@ -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`

View file

@ -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")