Fix SIM118 auto-fix (#3695)

This commit is contained in:
Jonathan Plasse 2023-03-23 22:14:56 +01:00 committed by GitHub
parent f58345dee3
commit aea925a898
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 5 deletions

View file

@ -20,3 +20,5 @@ for key in list(obj.keys()):
{k: k for k in obj.keys()} # SIM118
(k for k in obj.keys()) # SIM118
key in (obj or {}).keys() # SIM118

View file

@ -1,10 +1,16 @@
use anyhow::Result;
use libcst_native::{Codegen, CodegenState};
use log::error;
use rustpython_parser::ast::{Cmpop, Expr, ExprKind};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
use ruff_diagnostics::Fix;
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::source_code::{Locator, Stylist};
use ruff_python_ast::types::Range;
use crate::checkers::ast::Checker;
use crate::cst::matchers::{match_attribute, match_call, match_expression};
use crate::registry::AsRule;
#[violation]
@ -26,6 +32,26 @@ impl AlwaysAutofixableViolation for InDictKeys {
}
}
fn get_value_content_for_key_in_dict(
locator: &Locator,
stylist: &Stylist,
expr: &rustpython_parser::ast::Expr,
) -> Result<String> {
let content = locator.slice(expr);
let mut expression = match_expression(content)?;
let call = match_call(&mut expression)?;
let attribute = match_attribute(&mut call.func)?;
let mut state = CodegenState {
default_newline: stylist.line_ending(),
default_indent: stylist.indentation(),
..CodegenState::default()
};
attribute.value.codegen(&mut state);
Ok(state.to_string())
}
/// SIM118
fn key_in_dict(checker: &mut Checker, left: &Expr, right: &Expr, range: Range) {
let ExprKind::Call {
@ -39,7 +65,7 @@ fn key_in_dict(checker: &mut Checker, left: &Expr, right: &Expr, range: Range) {
return;
}
let ExprKind::Attribute { attr, value, .. } = &func.node else {
let ExprKind::Attribute { attr, .. } = &func.node else {
return;
};
if attr != "keys" {
@ -48,18 +74,25 @@ fn key_in_dict(checker: &mut Checker, left: &Expr, right: &Expr, range: Range) {
// Slice exact content to preserve formatting.
let left_content = checker.locator.slice(left);
let value_content = checker.locator.slice(value);
let value_content =
match get_value_content_for_key_in_dict(checker.locator, checker.stylist, right) {
Ok(value_content) => value_content,
Err(err) => {
error!("Failed to get value content for key in dict: {}", err);
return;
}
};
let mut diagnostic = Diagnostic::new(
InDictKeys {
key: left_content.to_string(),
dict: value_content.to_string(),
dict: value_content.clone(),
},
range,
);
if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement(
value_content.to_string(),
value_content,
right.location,
right.end_location.unwrap(),
));

View file

@ -182,4 +182,24 @@ expression: diagnostics
row: 22
column: 22
parent: ~
- kind:
name: InDictKeys
body: "Use `key in (obj or {})` instead of `key in (obj or {}).keys()`"
suggestion: "Convert to `key in (obj or {})`"
fixable: true
location:
row: 24
column: 0
end_location:
row: 24
column: 25
fix:
content: "(obj or {})"
location:
row: 24
column: 7
end_location:
row: 24
column: 25
parent: ~