mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 02:12:22 +00:00
[flake8-simplify
] Implement dict-get-with-none-default
(SIM910
) (#3874)
This commit is contained in:
parent
2b21effa77
commit
46bcb1f725
9 changed files with 231 additions and 1 deletions
27
crates/ruff/resources/test/fixtures/flake8_simplify/SIM910.py
vendored
Normal file
27
crates/ruff/resources/test/fixtures/flake8_simplify/SIM910.py
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
# SIM910
|
||||
{}.get(key, None)
|
||||
|
||||
# SIM910
|
||||
{}.get("key", None)
|
||||
|
||||
# OK
|
||||
{}.get(key)
|
||||
|
||||
# OK
|
||||
{}.get("key")
|
||||
|
||||
# OK
|
||||
{}.get(key, False)
|
||||
|
||||
# OK
|
||||
{}.get("key", False)
|
||||
|
||||
# SIM910
|
||||
if a := {}.get(key, None):
|
||||
pass
|
||||
|
||||
# SIM910
|
||||
a = {}.get(key, None)
|
||||
|
||||
# SIM910
|
||||
({}).get(key, None)
|
|
@ -2918,6 +2918,10 @@ where
|
|||
flake8_simplify::rules::open_file_with_context_handler(self, func);
|
||||
}
|
||||
|
||||
if self.settings.rules.enabled(Rule::DictGetWithNoneDefault) {
|
||||
flake8_simplify::rules::dict_get_with_none_default(self, expr);
|
||||
}
|
||||
|
||||
// flake8-use-pathlib
|
||||
if self.settings.rules.any_enabled(&[
|
||||
Rule::OsPathAbspath,
|
||||
|
|
|
@ -356,6 +356,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
|
|||
(Flake8Simplify, "223") => Rule::ExprAndFalse,
|
||||
(Flake8Simplify, "300") => Rule::YodaConditions,
|
||||
(Flake8Simplify, "401") => Rule::IfElseBlockInsteadOfDictGet,
|
||||
(Flake8Simplify, "910") => Rule::DictGetWithNoneDefault,
|
||||
|
||||
// pyupgrade
|
||||
(Pyupgrade, "001") => Rule::UselessMetaclassType,
|
||||
|
|
|
@ -322,6 +322,7 @@ ruff_macros::register_rules!(
|
|||
rules::flake8_simplify::rules::ExprAndFalse,
|
||||
rules::flake8_simplify::rules::YodaConditions,
|
||||
rules::flake8_simplify::rules::IfElseBlockInsteadOfDictGet,
|
||||
rules::flake8_simplify::rules::DictGetWithNoneDefault,
|
||||
// pyupgrade
|
||||
rules::pyupgrade::rules::UselessMetaclassType,
|
||||
rules::pyupgrade::rules::TypeOfPrimitive,
|
||||
|
|
|
@ -38,6 +38,7 @@ mod tests {
|
|||
#[test_case(Rule::ExprAndFalse, Path::new("SIM223.py"); "SIM223")]
|
||||
#[test_case(Rule::YodaConditions, Path::new("SIM300.py"); "SIM300")]
|
||||
#[test_case(Rule::IfElseBlockInsteadOfDictGet, Path::new("SIM401.py"); "SIM401")]
|
||||
#[test_case(Rule::DictGetWithNoneDefault, Path::new("SIM910.py"); "SIM910")]
|
||||
#[test_case(Rule::IfElseBlockInsteadOfDictLookup, Path::new("SIM116.py"); "SIM116")]
|
||||
#[test_case(Rule::IfWithSameArms, Path::new("SIM114.py"); "SIM114")]
|
||||
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
|
|
|
@ -27,6 +27,25 @@ impl AlwaysAutofixableViolation for UncapitalizedEnvironmentVariables {
|
|||
}
|
||||
}
|
||||
|
||||
#[violation]
|
||||
pub struct DictGetWithNoneDefault {
|
||||
pub expected: String,
|
||||
pub original: String,
|
||||
}
|
||||
|
||||
impl AlwaysAutofixableViolation for DictGetWithNoneDefault {
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
let DictGetWithNoneDefault { expected, original } = self;
|
||||
format!("Use `{expected}` instead of `{original}`")
|
||||
}
|
||||
|
||||
fn autofix_title(&self) -> String {
|
||||
let DictGetWithNoneDefault { expected, original } = self;
|
||||
format!("Replace `{original}` with `{expected}`")
|
||||
}
|
||||
}
|
||||
|
||||
/// SIM112
|
||||
pub fn use_capital_environment_variables(checker: &mut Checker, expr: &Expr) {
|
||||
// check `os.environ['foo']`
|
||||
|
@ -123,3 +142,64 @@ fn check_os_environ_subscript(checker: &mut Checker, expr: &Expr) {
|
|||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
||||
/// SIM910
|
||||
pub fn dict_get_with_none_default(checker: &mut Checker, expr: &Expr) {
|
||||
let ExprKind::Call { func, args, keywords } = &expr.node else {
|
||||
return;
|
||||
};
|
||||
if !keywords.is_empty() {
|
||||
return;
|
||||
}
|
||||
let ExprKind::Attribute { value, attr, .. } = &func.node else {
|
||||
return;
|
||||
};
|
||||
if !matches!(value.node, ExprKind::Dict { .. }) {
|
||||
return;
|
||||
}
|
||||
if attr != "get" {
|
||||
return;
|
||||
}
|
||||
let Some(key) = args.get(0) else {
|
||||
return;
|
||||
};
|
||||
if !matches!(key.node, ExprKind::Constant { .. } | ExprKind::Name { .. }) {
|
||||
return;
|
||||
}
|
||||
let Some(default) = args.get(1) else {
|
||||
return;
|
||||
};
|
||||
if !matches!(
|
||||
default.node,
|
||||
ExprKind::Constant {
|
||||
value: Constant::None,
|
||||
..
|
||||
}
|
||||
) {
|
||||
return;
|
||||
};
|
||||
|
||||
let expected = format!(
|
||||
"{}({})",
|
||||
checker.locator.slice(func),
|
||||
checker.locator.slice(key)
|
||||
);
|
||||
let original = checker.locator.slice(expr).to_string();
|
||||
|
||||
let mut diagnostic = Diagnostic::new(
|
||||
DictGetWithNoneDefault {
|
||||
expected: expected.clone(),
|
||||
original,
|
||||
},
|
||||
Range::from(expr),
|
||||
);
|
||||
|
||||
if checker.patch(diagnostic.kind.rule()) {
|
||||
diagnostic.set_fix(Edit::replacement(
|
||||
expected,
|
||||
expr.location,
|
||||
expr.end_location.unwrap(),
|
||||
));
|
||||
}
|
||||
checker.diagnostics.push(diagnostic);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,10 @@ pub use ast_bool_op::{
|
|||
expr_or_not_expr, expr_or_true, CompareWithTuple, DuplicateIsinstanceCall, ExprAndFalse,
|
||||
ExprAndNotExpr, ExprOrNotExpr, ExprOrTrue,
|
||||
};
|
||||
pub use ast_expr::{use_capital_environment_variables, UncapitalizedEnvironmentVariables};
|
||||
pub use ast_expr::{
|
||||
dict_get_with_none_default, use_capital_environment_variables, DictGetWithNoneDefault,
|
||||
UncapitalizedEnvironmentVariables,
|
||||
};
|
||||
pub use ast_if::{
|
||||
if_with_same_arms, manual_dict_lookup, needless_bool, nested_if_statements,
|
||||
use_dict_get_with_default, use_ternary_operator, CollapsibleIf, IfElseBlockInsteadOfDictGet,
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
source: crates/ruff/src/rules/flake8_simplify/mod.rs
|
||||
expression: diagnostics
|
||||
---
|
||||
- kind:
|
||||
name: DictGetWithNoneDefault
|
||||
body: "Use `{}.get(key)` instead of `{}.get(key, None)`"
|
||||
suggestion: "Replace `{}.get(key, None)` with `{}.get(key)`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 17
|
||||
fix:
|
||||
edits:
|
||||
- content: "{}.get(key)"
|
||||
location:
|
||||
row: 2
|
||||
column: 0
|
||||
end_location:
|
||||
row: 2
|
||||
column: 17
|
||||
parent: ~
|
||||
- kind:
|
||||
name: DictGetWithNoneDefault
|
||||
body: "Use `{}.get(\"key\")` instead of `{}.get(\"key\", None)`"
|
||||
suggestion: "Replace `{}.get(\"key\", None)` with `{}.get(\"key\")`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 19
|
||||
fix:
|
||||
edits:
|
||||
- content: "{}.get(\"key\")"
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
end_location:
|
||||
row: 5
|
||||
column: 19
|
||||
parent: ~
|
||||
- kind:
|
||||
name: DictGetWithNoneDefault
|
||||
body: "Use `{}.get(key)` instead of `{}.get(key, None)`"
|
||||
suggestion: "Replace `{}.get(key, None)` with `{}.get(key)`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 20
|
||||
column: 8
|
||||
end_location:
|
||||
row: 20
|
||||
column: 25
|
||||
fix:
|
||||
edits:
|
||||
- content: "{}.get(key)"
|
||||
location:
|
||||
row: 20
|
||||
column: 8
|
||||
end_location:
|
||||
row: 20
|
||||
column: 25
|
||||
parent: ~
|
||||
- kind:
|
||||
name: DictGetWithNoneDefault
|
||||
body: "Use `{}.get(key)` instead of `{}.get(key, None)`"
|
||||
suggestion: "Replace `{}.get(key, None)` with `{}.get(key)`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 24
|
||||
column: 4
|
||||
end_location:
|
||||
row: 24
|
||||
column: 21
|
||||
fix:
|
||||
edits:
|
||||
- content: "{}.get(key)"
|
||||
location:
|
||||
row: 24
|
||||
column: 4
|
||||
end_location:
|
||||
row: 24
|
||||
column: 21
|
||||
parent: ~
|
||||
- kind:
|
||||
name: DictGetWithNoneDefault
|
||||
body: "Use `({}).get(key)` instead of `({}).get(key, None)`"
|
||||
suggestion: "Replace `({}).get(key, None)` with `({}).get(key)`"
|
||||
fixable: true
|
||||
location:
|
||||
row: 27
|
||||
column: 0
|
||||
end_location:
|
||||
row: 27
|
||||
column: 19
|
||||
fix:
|
||||
edits:
|
||||
- content: "({}).get(key)"
|
||||
location:
|
||||
row: 27
|
||||
column: 0
|
||||
end_location:
|
||||
row: 27
|
||||
column: 19
|
||||
parent: ~
|
||||
|
3
ruff.schema.json
generated
3
ruff.schema.json
generated
|
@ -2168,6 +2168,9 @@
|
|||
"SIM4",
|
||||
"SIM40",
|
||||
"SIM401",
|
||||
"SIM9",
|
||||
"SIM91",
|
||||
"SIM910",
|
||||
"SLF",
|
||||
"SLF0",
|
||||
"SLF00",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue