From 0c624af036f31443a051b8a7eba51dfd6e0941c0 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sun, 22 Jan 2023 21:43:34 -0500 Subject: [PATCH] Add flake8-pie PIE800: no-unnecessary-spread (#1881) Checks for unnecessary spreads, like `{**foo, **{"bar": True}}` rel: https://github.com/charliermarsh/ruff/issues/1879 rel: https://github.com/charliermarsh/ruff/issues/1543 --- README.md | 1 + resources/test/fixtures/flake8_pie/PIE800.py | 17 +++++++ ruff.schema.json | 1 + src/checkers/ast.rs | 4 ++ src/registry.rs | 1 + src/rules/flake8_pie/mod.rs | 5 ++- src/rules/flake8_pie/rules.rs | 15 +++++++ ...__flake8_pie__tests__PIE800_PIE800.py.snap | 45 +++++++++++++++++++ src/violations.rs | 10 +++++ 9 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 resources/test/fixtures/flake8_pie/PIE800.py create mode 100644 src/rules/flake8_pie/snapshots/ruff__rules__flake8_pie__tests__PIE800_PIE800.py.snap diff --git a/README.md b/README.md index cddc0de526..e48a7b5f3d 100644 --- a/README.md +++ b/README.md @@ -1151,6 +1151,7 @@ For more, see [flake8-pie](https://pypi.org/project/flake8-pie/) on PyPI. | PIE790 | no-unnecessary-pass | Unnecessary `pass` statement | 🛠 | | PIE794 | dupe-class-field-definitions | Class field `{name}` is defined multiple times | 🛠 | | PIE796 | prefer-unique-enums | Enum contains duplicate value: `{value}` | | +| PIE800 | no-unnecessary-spread | Unnecessary spread `**` | | | PIE804 | no-unnecessary-dict-kwargs | Unnecessary `dict` kwargs | | | PIE807 | prefer-list-builtin | Prefer `list` over useless lambda | 🛠 | diff --git a/resources/test/fixtures/flake8_pie/PIE800.py b/resources/test/fixtures/flake8_pie/PIE800.py new file mode 100644 index 0000000000..3584d2c03f --- /dev/null +++ b/resources/test/fixtures/flake8_pie/PIE800.py @@ -0,0 +1,17 @@ +{"foo": 1, **{"bar": 1}} # PIE800 + +foo({**foo, **{"bar": True}}) # PIE800 + +{**foo, **{"bar": 10}} # PIE800 + +{**foo, **buzz, **{bar: 10}} # PIE800 + +{**foo, "bar": True } # OK + +{"foo": 1, "buzz": {"bar": 1}} # OK + +{**foo, "bar": True } # OK + +Table.objects.filter(inst=inst, **{f"foo__{bar}__exists": True}) # OK + +buzz = {**foo, "bar": { 1: 2 }} # OK diff --git a/ruff.schema.json b/ruff.schema.json index 6c59adf5ad..2a310b20c6 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -1553,6 +1553,7 @@ "PIE796", "PIE8", "PIE80", + "PIE800", "PIE804", "PIE807", "PL", diff --git a/src/checkers/ast.rs b/src/checkers/ast.rs index 33b73bc9b0..b342d30e25 100644 --- a/src/checkers/ast.rs +++ b/src/checkers/ast.rs @@ -2596,6 +2596,10 @@ where { pyflakes::rules::repeated_keys(self, keys, values); } + + if self.settings.rules.enabled(&Rule::NoUnnecessarySpread) { + flake8_pie::rules::no_unnecessary_spread(self, keys, values); + } } ExprKind::Yield { .. } => { if self.settings.rules.enabled(&Rule::YieldOutsideFunction) { diff --git a/src/registry.rs b/src/registry.rs index a60584c99d..4593cd10af 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -413,6 +413,7 @@ ruff_macros::define_rule_mapping!( PIE790 => violations::NoUnnecessaryPass, PIE794 => violations::DupeClassFieldDefinitions, PIE796 => violations::PreferUniqueEnums, + PIE800 => violations::NoUnnecessarySpread, PIE804 => violations::NoUnnecessaryDictKwargs, PIE807 => violations::PreferListBuiltin, // flake8-commas diff --git a/src/rules/flake8_pie/mod.rs b/src/rules/flake8_pie/mod.rs index 0281097c3c..3c827973be 100644 --- a/src/rules/flake8_pie/mod.rs +++ b/src/rules/flake8_pie/mod.rs @@ -12,11 +12,12 @@ mod tests { use crate::registry::Rule; use crate::settings; - #[test_case(Rule::NoUnnecessaryPass, Path::new("PIE790.py"); "PIE790")] #[test_case(Rule::DupeClassFieldDefinitions, Path::new("PIE794.py"); "PIE794")] - #[test_case(Rule::PreferUniqueEnums, Path::new("PIE796.py"); "PIE796")] #[test_case(Rule::NoUnnecessaryDictKwargs, Path::new("PIE804.py"); "PIE804")] + #[test_case(Rule::NoUnnecessaryPass, Path::new("PIE790.py"); "PIE790")] + #[test_case(Rule::NoUnnecessarySpread, Path::new("PIE800.py"); "PIE800")] #[test_case(Rule::PreferListBuiltin, Path::new("PIE807.py"); "PIE807")] + #[test_case(Rule::PreferUniqueEnums, Path::new("PIE796.py"); "PIE796")] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.code(), path.to_string_lossy()); let diagnostics = test_path( diff --git a/src/rules/flake8_pie/rules.rs b/src/rules/flake8_pie/rules.rs index e18cbe85b8..a429fbe339 100644 --- a/src/rules/flake8_pie/rules.rs +++ b/src/rules/flake8_pie/rules.rs @@ -153,6 +153,21 @@ where } } +/// PIE800 +pub fn no_unnecessary_spread(checker: &mut Checker, keys: &[Option], values: &[Expr]) { + for item in keys.iter().zip(values.iter()) { + if let (None, value) = item { + // We only care about when the key is None which indicates a spread `**` + // inside a dict. + if let ExprKind::Dict { .. } = value.node { + let diagnostic = + Diagnostic::new(violations::NoUnnecessarySpread, Range::from_located(value)); + checker.diagnostics.push(diagnostic); + } + } + } +} + /// Return `true` if a key is a valid keyword argument name. fn is_valid_kwarg_name(key: &Expr) -> bool { if let ExprKind::Constant { diff --git a/src/rules/flake8_pie/snapshots/ruff__rules__flake8_pie__tests__PIE800_PIE800.py.snap b/src/rules/flake8_pie/snapshots/ruff__rules__flake8_pie__tests__PIE800_PIE800.py.snap new file mode 100644 index 0000000000..ed8d3d4c96 --- /dev/null +++ b/src/rules/flake8_pie/snapshots/ruff__rules__flake8_pie__tests__PIE800_PIE800.py.snap @@ -0,0 +1,45 @@ +--- +source: src/rules/flake8_pie/mod.rs +expression: diagnostics +--- +- kind: + NoUnnecessarySpread: ~ + location: + row: 1 + column: 13 + end_location: + row: 1 + column: 23 + fix: ~ + parent: ~ +- kind: + NoUnnecessarySpread: ~ + location: + row: 3 + column: 14 + end_location: + row: 3 + column: 27 + fix: ~ + parent: ~ +- kind: + NoUnnecessarySpread: ~ + location: + row: 5 + column: 10 + end_location: + row: 5 + column: 21 + fix: ~ + parent: ~ +- kind: + NoUnnecessarySpread: ~ + location: + row: 7 + column: 18 + end_location: + row: 7 + column: 27 + fix: ~ + parent: ~ + diff --git a/src/violations.rs b/src/violations.rs index e82585b244..318e1f7d3a 100644 --- a/src/violations.rs +++ b/src/violations.rs @@ -4984,6 +4984,16 @@ impl Violation for PreferUniqueEnums { } } +define_violation!( + pub struct NoUnnecessarySpread; +); +impl Violation for NoUnnecessarySpread { + #[derive_message_formats] + fn message(&self) -> String { + format!("Unnecessary spread `**`") + } +} + define_violation!( pub struct NoUnnecessaryDictKwargs; );