[flake8-simplify] Add autofix for contextlib.suppress (SIM105) (#3915)

This commit is contained in:
Leiser Fernández Gallo 2023-04-10 00:45:19 +02:00 committed by GitHub
parent 311ba29d0f
commit 002caadf9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 143 additions and 25 deletions

View file

@ -1,36 +1,44 @@
use rustpython_parser::ast::{Excepthandler, ExcepthandlerKind, Located, Stmt, StmtKind}; use rustpython_parser::ast::{Excepthandler, ExcepthandlerKind, Located, Location, Stmt, StmtKind};
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::call_path::compose_call_path; use ruff_python_ast::call_path::compose_call_path;
use ruff_python_ast::helpers; use ruff_python_ast::helpers;
use ruff_python_ast::types::Range; use ruff_python_ast::types::Range;
use crate::autofix::actions::get_or_import_symbol;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::registry::AsRule;
#[violation] #[violation]
pub struct SuppressibleException { pub struct SuppressibleException {
pub exception: String, pub exception: String,
} }
impl Violation for SuppressibleException { impl AlwaysAutofixableViolation for SuppressibleException {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
let SuppressibleException { exception } = self; let SuppressibleException { exception } = self;
format!("Use `contextlib.suppress({exception})` instead of try-except-pass") format!("Use `contextlib.suppress({exception})` instead of `try`-`except`-`pass`")
}
fn autofix_title(&self) -> String {
let SuppressibleException { exception } = self;
format!("Replace with `contextlib.suppress({exception})`")
} }
} }
/// SIM105 /// SIM105
pub fn suppressible_exception( pub fn suppressible_exception(
checker: &mut Checker, checker: &mut Checker,
stmt: &Stmt, stmt: &Stmt,
body: &[Stmt], try_body: &[Stmt],
handlers: &[Excepthandler], handlers: &[Excepthandler],
orelse: &[Stmt], orelse: &[Stmt],
finalbody: &[Stmt], finalbody: &[Stmt],
) { ) {
if !matches!( if !matches!(
body, try_body,
[Located { [Located {
node: StmtKind::Delete { .. } node: StmtKind::Delete { .. }
| StmtKind::Assign { .. } | StmtKind::Assign { .. }
@ -62,10 +70,36 @@ pub fn suppressible_exception(
} else { } else {
handler_names.join(", ") handler_names.join(", ")
}; };
checker.diagnostics.push(Diagnostic::new( let mut diagnostic = Diagnostic::new(
SuppressibleException { exception }, SuppressibleException {
exception: exception.clone(),
},
Range::from(stmt), Range::from(stmt),
)); );
if checker.patch(diagnostic.kind.rule()) {
diagnostic.try_set_fix(|| {
let (import_edit, binding) = get_or_import_symbol(
"contextlib",
"suppress",
&checker.ctx,
&checker.importer,
checker.locator,
)?;
let try_ending = stmt.location.with_col_offset(3); // size of "try"
let replace_try = Edit::replacement(
format!("with {binding}({exception})"),
stmt.location,
try_ending,
);
let handler_line_begin = Location::new(handler.location.row(), 0);
let remove_handler =
Edit::deletion(handler_line_begin, handler.end_location.unwrap());
Ok(Fix::from_iter([import_edit, replace_try, remove_handler]))
});
}
checker.diagnostics.push(diagnostic);
} }
} }
} }

View file

@ -4,9 +4,9 @@ expression: diagnostics
--- ---
- kind: - kind:
name: SuppressibleException name: SuppressibleException
body: "Use `contextlib.suppress(ValueError)` instead of try-except-pass" body: "Use `contextlib.suppress(ValueError)` instead of `try`-`except`-`pass`"
suggestion: ~ suggestion: "Replace with `contextlib.suppress(ValueError)`"
fixable: false fixable: true
location: location:
row: 4 row: 4
column: 0 column: 0
@ -14,13 +14,34 @@ expression: diagnostics
row: 7 row: 7
column: 8 column: 8
fix: fix:
edits: [] edits:
- content: "import contextlib\n"
location:
row: 1
column: 0
end_location:
row: 1
column: 0
- content: with contextlib.suppress(ValueError)
location:
row: 4
column: 0
end_location:
row: 4
column: 3
- content: ""
location:
row: 6
column: 0
end_location:
row: 7
column: 8
parent: ~ parent: ~
- kind: - kind:
name: SuppressibleException name: SuppressibleException
body: "Use `contextlib.suppress(ValueError, OSError)` instead of try-except-pass" body: "Use `contextlib.suppress(ValueError, OSError)` instead of `try`-`except`-`pass`"
suggestion: ~ suggestion: "Replace with `contextlib.suppress(ValueError, OSError)`"
fixable: false fixable: true
location: location:
row: 9 row: 9
column: 0 column: 0
@ -28,13 +49,34 @@ expression: diagnostics
row: 12 row: 12
column: 8 column: 8
fix: fix:
edits: [] edits:
- content: "import contextlib\n"
location:
row: 1
column: 0
end_location:
row: 1
column: 0
- content: "with contextlib.suppress(ValueError, OSError)"
location:
row: 9
column: 0
end_location:
row: 9
column: 3
- content: ""
location:
row: 11
column: 0
end_location:
row: 12
column: 8
parent: ~ parent: ~
- kind: - kind:
name: SuppressibleException name: SuppressibleException
body: "Use `contextlib.suppress(Exception)` instead of try-except-pass" body: "Use `contextlib.suppress(Exception)` instead of `try`-`except`-`pass`"
suggestion: ~ suggestion: "Replace with `contextlib.suppress(Exception)`"
fixable: false fixable: true
location: location:
row: 14 row: 14
column: 0 column: 0
@ -42,13 +84,34 @@ expression: diagnostics
row: 17 row: 17
column: 8 column: 8
fix: fix:
edits: [] edits:
- content: "import contextlib\n"
location:
row: 1
column: 0
end_location:
row: 1
column: 0
- content: with contextlib.suppress(Exception)
location:
row: 14
column: 0
end_location:
row: 14
column: 3
- content: ""
location:
row: 16
column: 0
end_location:
row: 17
column: 8
parent: ~ parent: ~
- kind: - kind:
name: SuppressibleException name: SuppressibleException
body: "Use `contextlib.suppress(a.Error, b.Error)` instead of try-except-pass" body: "Use `contextlib.suppress(a.Error, b.Error)` instead of `try`-`except`-`pass`"
suggestion: ~ suggestion: "Replace with `contextlib.suppress(a.Error, b.Error)`"
fixable: false fixable: true
location: location:
row: 19 row: 19
column: 0 column: 0
@ -56,6 +119,27 @@ expression: diagnostics
row: 22 row: 22
column: 8 column: 8
fix: fix:
edits: [] edits:
- content: "import contextlib\n"
location:
row: 1
column: 0
end_location:
row: 1
column: 0
- content: "with contextlib.suppress(a.Error, b.Error)"
location:
row: 19
column: 0
end_location:
row: 19
column: 3
- content: ""
location:
row: 21
column: 0
end_location:
row: 22
column: 8
parent: ~ parent: ~