From ac9a13bc9af67dfa177e3587481435e8fa3aabd2 Mon Sep 17 00:00:00 2001 From: Tester Date: Fri, 3 Nov 2023 19:34:35 +0330 Subject: [PATCH 1/5] `PIE804`: Prevent keyword arguments duplication --- .../resources/test/fixtures/flake8_pie/PIE804.py | 3 +++ .../flake8_pie/rules/unnecessary_dict_kwargs.rs | 8 +++++++- ...les__flake8_pie__tests__PIE804_PIE804.py.snap | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py b/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py index 84274c853a8aa..4f2fc743a7c84 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py @@ -20,3 +20,6 @@ foo(**{"": True}) foo(**{f"buzz__{bar}": True}) abc(**{"for": 3}) + +# Duplicated key names wont be fixed to avoid syntax error. +abc(**{'a': b}, **{'a': c}) # PIE804 diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs index 8a518c88906f4..0303415310d21 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use itertools::Itertools; use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_python_ast::{self as ast, Expr, Keyword}; @@ -53,9 +55,12 @@ impl AlwaysFixableViolation for UnnecessaryDictKwargs { /// PIE804 pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs: &[Keyword]) { + let mut all_kwargs = HashSet::new(); + for kw in kwargs { // keyword is a spread operator (indicated by None) - if kw.arg.is_some() { + if let Some(name) = &kw.arg { + all_kwargs.insert(name.as_str()); continue; } @@ -81,6 +86,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, expr: &Expr, kwargs let kwargs = keys .iter() .filter_map(|key| key.as_ref().and_then(as_kwarg)) + .filter(|name| all_kwargs.insert(name)) .collect::>(); if kwargs.len() != keys.len() { continue; diff --git a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap index fee4c78e95c2a..7dc0ba180a713 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap @@ -1,5 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_pie/mod.rs +assertion_line: 29 --- PIE804.py:1:1: PIE804 [*] Unnecessary `dict` kwargs | @@ -119,4 +120,19 @@ PIE804.py:11:1: PIE804 [*] Unnecessary `dict` kwargs 13 13 | 14 14 | foo(**{**data, "foo": "buzz"}) +PIE804.py:25:1: PIE804 [*] Unnecessary `dict` kwargs + | +24 | # Duplicated key names wont be fixed to avoid syntax error. +25 | abc(**{'a': b}, **{'a': c}) # PIE804 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PIE804 + | + = help: Remove unnecessary kwargs + +ℹ Fix +22 22 | abc(**{"for": 3}) +23 23 | +24 24 | # Duplicated key names wont be fixed to avoid syntax error. +25 |-abc(**{'a': b}, **{'a': c}) # PIE804 + 25 |+abc(a=b, **{'a': c}) # PIE804 + From 620bb527d1b1da21e6d2a9f5732eba405b9ca53b Mon Sep 17 00:00:00 2001 From: T-256 <132141463+T-256@users.noreply.github.com> Date: Mon, 6 Nov 2023 20:14:02 +0330 Subject: [PATCH 2/5] Fix safety note in docs --- .../flake8_pie/rules/unnecessary_dict_kwargs.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs index 0303415310d21..ca13827c13217 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs @@ -36,6 +36,19 @@ use crate::checkers::ast::Checker; /// print(foo(bar=2)) # prints 3 /// ``` /// +/// ## Fix safety +/// This rule's fix is marked as safe, but there are some scenarios +/// that fix causes the TypeError at runtime: +/// +/// ```python +/// def foo(bar): +/// return bar + 1 +/// +/// +/// # TypeError: foo() got multiple values for keyword argument 'bar' +/// foo(bar=2, **{"bar": 3}) +/// ``` +/// /// ## References /// - [Python documentation: Dictionary displays](https://docs.python.org/3/reference/expressions.html#dictionary-displays) /// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls) From efe1a8d9bd572de67efe528015c43f5f9b65c264 Mon Sep 17 00:00:00 2001 From: T-256 <132141463+T-256@users.noreply.github.com> Date: Fri, 10 Nov 2023 16:39:10 +0330 Subject: [PATCH 3/5] Safe fix --- ...ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap index 7b5c23504a2a8..647981934e239 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap @@ -128,7 +128,7 @@ PIE804.py:25:1: PIE804 [*] Unnecessary `dict` kwargs | = help: Remove unnecessary kwargs -ℹ Fix +ℹ Safe fix 22 22 | abc(**{"for": 3}) 23 23 | 24 24 | # Duplicated key names wont be fixed to avoid syntax error. From 829b687cc213e9b4dd0ec123d0b26ad7043247f1 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 12 Dec 2023 18:05:02 -0500 Subject: [PATCH 4/5] Pre-init hash map --- .../test/fixtures/flake8_pie/PIE804.py | 2 +- .../rules/unnecessary_dict_kwargs.rs | 31 ++++++++++--------- ...__flake8_pie__tests__PIE804_PIE804.py.snap | 8 ++--- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py b/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py index 366f9b67e57a8..2b2f61934024b 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py @@ -21,5 +21,5 @@ abc(**{"for": 3}) foo(**{},) -# Duplicated key names wont be fixed to avoid syntax error. +# Duplicated key names won't be fixed, to avoid syntax errors. abc(**{'a': b}, **{'a': c}) # PIE804 diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs index bb8fc5f56145b..976f7c41e494e 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs @@ -1,13 +1,13 @@ -use std::collections::HashSet; +use std::hash::BuildHasherDefault; use itertools::Itertools; -use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; -use ruff_python_ast::{self as ast, Expr}; +use rustc_hash::FxHashSet; +use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, violation}; -use ruff_text_size::Ranged; - +use ruff_python_ast::{self as ast, Expr}; use ruff_python_stdlib::identifiers::is_identifier; +use ruff_text_size::Ranged; use crate::checkers::ast::Checker; use crate::fix::edits::{remove_argument, Parentheses}; @@ -69,16 +69,19 @@ impl AlwaysFixableViolation for UnnecessaryDictKwargs { /// PIE804 pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCall) { - let mut all_kwargs = HashSet::new(); + let mut seen = FxHashSet::with_capacity_and_hasher( + call.arguments.keywords.len(), + BuildHasherDefault::default(), + ); - for kw in &call.arguments.keywords { + for keyword in &call.arguments.keywords { // keyword is a spread operator (indicated by None) - if let Some(name) = &kw.arg { - all_kwargs.insert(name.as_str()); + if let Some(name) = &keyword.arg { + seen.insert(name.as_str()); continue; } - let Expr::Dict(ast::ExprDict { keys, values, .. }) = &kw.value else { + let Expr::Dict(ast::ExprDict { keys, values, .. }) = &keyword.value else { continue; }; @@ -88,7 +91,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCal diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( format!("**{}", checker.locator().slice(values[0].range())), - kw.range(), + keyword.range(), ))); checker.diagnostics.push(diagnostic); @@ -100,7 +103,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCal let kwargs = keys .iter() .filter_map(|key| key.as_ref().and_then(as_kwarg)) - .filter(|name| all_kwargs.insert(name)) + .filter(|name| seen.insert(name)) .collect::>(); if kwargs.len() != keys.len() { continue; @@ -111,7 +114,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCal if values.is_empty() { diagnostic.try_set_fix(|| { remove_argument( - kw, + keyword, &call.arguments, Parentheses::Preserve, checker.locator().contents(), @@ -127,7 +130,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCal format!("{}={}", kwarg, checker.locator().slice(value.range())) }) .join(", "), - kw.range(), + keyword.range(), ))); } diff --git a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap index f783b02370670..4505b048af907 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap @@ -128,7 +128,7 @@ PIE804.py:22:1: PIE804 [*] Unnecessary `dict` kwargs 22 | foo(**{},) | ^^^^^^^^^^ PIE804 23 | -24 | # Duplicated key names wont be fixed to avoid syntax error. +24 | # Duplicated key names won't be fixed, to avoid syntax errors. | = help: Remove unnecessary kwargs @@ -139,12 +139,12 @@ PIE804.py:22:1: PIE804 [*] Unnecessary `dict` kwargs 22 |-foo(**{},) 22 |+foo() 23 23 | -24 24 | # Duplicated key names wont be fixed to avoid syntax error. +24 24 | # Duplicated key names won't be fixed, to avoid syntax errors. 25 25 | abc(**{'a': b}, **{'a': c}) # PIE804 PIE804.py:25:1: PIE804 [*] Unnecessary `dict` kwargs | -24 | # Duplicated key names wont be fixed to avoid syntax error. +24 | # Duplicated key names won't be fixed, to avoid syntax errors. 25 | abc(**{'a': b}, **{'a': c}) # PIE804 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PIE804 | @@ -153,7 +153,7 @@ PIE804.py:25:1: PIE804 [*] Unnecessary `dict` kwargs ℹ Safe fix 22 22 | foo(**{},) 23 23 | -24 24 | # Duplicated key names wont be fixed to avoid syntax error. +24 24 | # Duplicated key names won't be fixed, to avoid syntax errors. 25 |-abc(**{'a': b}, **{'a': c}) # PIE804 25 |+abc(a=b, **{'a': c}) # PIE804 From 3f2fd98511811a042a4a1497da01b08f7a5f00e4 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 12 Dec 2023 18:12:20 -0500 Subject: [PATCH 5/5] Avoid fix for any duplicates --- .../test/fixtures/flake8_pie/PIE804.py | 1 + .../rules/unnecessary_dict_kwargs.rs | 99 ++++++++++++------- ...__flake8_pie__tests__PIE804_PIE804.py.snap | 66 +++++++++---- 3 files changed, 110 insertions(+), 56 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py b/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py index 2b2f61934024b..a5b3674422e1a 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pie/PIE804.py @@ -23,3 +23,4 @@ # Duplicated key names won't be fixed, to avoid syntax errors. abc(**{'a': b}, **{'a': c}) # PIE804 +abc(a=1, **{'a': c}, **{'b': c}) # PIE804 diff --git a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs index 976f7c41e494e..5f0bf0abb48d0 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs +++ b/crates/ruff_linter/src/rules/flake8_pie/rules/unnecessary_dict_kwargs.rs @@ -3,7 +3,7 @@ use std::hash::BuildHasherDefault; use itertools::Itertools; use rustc_hash::FxHashSet; -use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; +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_stdlib::identifiers::is_identifier; @@ -37,47 +37,31 @@ use crate::fix::edits::{remove_argument, Parentheses}; /// print(foo(bar=2)) # prints 3 /// ``` /// -/// ## Fix safety -/// This rule's fix is marked as safe, but there are some scenarios -/// that fix causes the TypeError at runtime: -/// -/// ```python -/// def foo(bar): -/// return bar + 1 -/// -/// -/// # TypeError: foo() got multiple values for keyword argument 'bar' -/// foo(bar=2, **{"bar": 3}) -/// ``` -/// /// ## References /// - [Python documentation: Dictionary displays](https://docs.python.org/3/reference/expressions.html#dictionary-displays) /// - [Python documentation: Calls](https://docs.python.org/3/reference/expressions.html#calls) #[violation] pub struct UnnecessaryDictKwargs; -impl AlwaysFixableViolation for UnnecessaryDictKwargs { +impl Violation for UnnecessaryDictKwargs { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] fn message(&self) -> String { format!("Unnecessary `dict` kwargs") } - fn fix_title(&self) -> String { - format!("Remove unnecessary kwargs") + fn fix_title(&self) -> Option { + Some(format!("Remove unnecessary kwargs")) } } /// PIE804 pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCall) { - let mut seen = FxHashSet::with_capacity_and_hasher( - call.arguments.keywords.len(), - BuildHasherDefault::default(), - ); - + let mut duplicate_keywords = None; for keyword in &call.arguments.keywords { - // keyword is a spread operator (indicated by None) - if let Some(name) = &keyword.arg { - seen.insert(name.as_str()); + // keyword is a spread operator (indicated by None). + if keyword.arg.is_some() { continue; } @@ -87,7 +71,7 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCal // Ex) `foo(**{**bar})` if matches!(keys.as_slice(), [None]) { - let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, call.range()); + let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, keyword.range()); diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( format!("**{}", checker.locator().slice(values[0].range())), @@ -103,13 +87,12 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCal let kwargs = keys .iter() .filter_map(|key| key.as_ref().and_then(as_kwarg)) - .filter(|name| seen.insert(name)) .collect::>(); if kwargs.len() != keys.len() { continue; } - let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, call.range()); + let mut diagnostic = Diagnostic::new(UnnecessaryDictKwargs, keyword.range()); if values.is_empty() { diagnostic.try_set_fix(|| { @@ -122,22 +105,64 @@ pub(crate) fn unnecessary_dict_kwargs(checker: &mut Checker, call: &ast::ExprCal .map(Fix::safe_edit) }); } else { - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - kwargs + // Compute the set of duplicate keywords (lazily). + if duplicate_keywords.is_none() { + duplicate_keywords = Some(duplicates(call)); + } + + // Avoid fixing if doing so could introduce a duplicate keyword argument. + if let Some(duplicate_keywords) = duplicate_keywords.as_ref() { + if kwargs .iter() - .zip(values.iter()) - .map(|(kwarg, value)| { - format!("{}={}", kwarg, checker.locator().slice(value.range())) - }) - .join(", "), - keyword.range(), - ))); + .all(|kwarg| !duplicate_keywords.contains(kwarg)) + { + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + kwargs + .iter() + .zip(values.iter()) + .map(|(kwarg, value)| { + format!("{}={}", kwarg, checker.locator().slice(value.range())) + }) + .join(", "), + keyword.range(), + ))); + } + } } checker.diagnostics.push(diagnostic); } } +/// Determine the set of keywords that appear in multiple positions (either directly, as in +/// `func(x=1)`, or indirectly, as in `func(**{"x": 1})`). +fn duplicates(call: &ast::ExprCall) -> FxHashSet<&str> { + let mut seen = FxHashSet::with_capacity_and_hasher( + call.arguments.keywords.len(), + BuildHasherDefault::default(), + ); + let mut duplicates = FxHashSet::with_capacity_and_hasher( + call.arguments.keywords.len(), + BuildHasherDefault::default(), + ); + for keyword in &call.arguments.keywords { + if let Some(name) = &keyword.arg { + if !seen.insert(name.as_str()) { + duplicates.insert(name.as_str()); + } + } else if let Expr::Dict(ast::ExprDict { keys, .. }) = &keyword.value { + for key in keys { + if let Some(name) = key.as_ref().and_then(as_kwarg) { + if !seen.insert(name) { + duplicates.insert(name); + } + } + } + } + } + duplicates +} + /// Return `Some` if a key is a valid keyword argument name, or `None` otherwise. fn as_kwarg(key: &Expr) -> Option<&str> { if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = key { diff --git a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap index 4505b048af907..15d993a0befee 100644 --- a/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pie/snapshots/ruff_linter__rules__flake8_pie__tests__PIE804_PIE804.py.snap @@ -1,10 +1,10 @@ --- source: crates/ruff_linter/src/rules/flake8_pie/mod.rs --- -PIE804.py:1:1: PIE804 [*] Unnecessary `dict` kwargs +PIE804.py:1:5: PIE804 [*] Unnecessary `dict` kwargs | 1 | foo(**{"bar": True}) # PIE804 - | ^^^^^^^^^^^^^^^^^^^^ PIE804 + | ^^^^^^^^^^^^^^^ PIE804 2 | 3 | foo(**{"r2d2": True}) # PIE804 | @@ -17,12 +17,12 @@ PIE804.py:1:1: PIE804 [*] Unnecessary `dict` kwargs 3 3 | foo(**{"r2d2": True}) # PIE804 4 4 | -PIE804.py:3:1: PIE804 [*] Unnecessary `dict` kwargs +PIE804.py:3:5: PIE804 [*] Unnecessary `dict` kwargs | 1 | foo(**{"bar": True}) # PIE804 2 | 3 | foo(**{"r2d2": True}) # PIE804 - | ^^^^^^^^^^^^^^^^^^^^^ PIE804 + | ^^^^^^^^^^^^^^^^ PIE804 4 | 5 | Foo.objects.create(**{"bar": True}) # PIE804 | @@ -37,12 +37,12 @@ PIE804.py:3:1: PIE804 [*] Unnecessary `dict` kwargs 5 5 | Foo.objects.create(**{"bar": True}) # PIE804 6 6 | -PIE804.py:5:1: PIE804 [*] Unnecessary `dict` kwargs +PIE804.py:5:20: PIE804 [*] Unnecessary `dict` kwargs | 3 | foo(**{"r2d2": True}) # PIE804 4 | 5 | Foo.objects.create(**{"bar": True}) # PIE804 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PIE804 + | ^^^^^^^^^^^^^^^ PIE804 6 | 7 | Foo.objects.create(**{"_id": some_id}) # PIE804 | @@ -58,12 +58,12 @@ PIE804.py:5:1: PIE804 [*] Unnecessary `dict` kwargs 7 7 | Foo.objects.create(**{"_id": some_id}) # PIE804 8 8 | -PIE804.py:7:1: PIE804 [*] Unnecessary `dict` kwargs +PIE804.py:7:20: PIE804 [*] Unnecessary `dict` kwargs | 5 | Foo.objects.create(**{"bar": True}) # PIE804 6 | 7 | Foo.objects.create(**{"_id": some_id}) # PIE804 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PIE804 + | ^^^^^^^^^^^^^^^^^^ PIE804 8 | 9 | Foo.objects.create(**{**bar}) # PIE804 | @@ -79,12 +79,12 @@ PIE804.py:7:1: PIE804 [*] Unnecessary `dict` kwargs 9 9 | Foo.objects.create(**{**bar}) # PIE804 10 10 | -PIE804.py:9:1: PIE804 [*] Unnecessary `dict` kwargs +PIE804.py:9:20: PIE804 [*] Unnecessary `dict` kwargs | 7 | Foo.objects.create(**{"_id": some_id}) # PIE804 8 | 9 | Foo.objects.create(**{**bar}) # PIE804 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PIE804 + | ^^^^^^^^^ PIE804 10 | 11 | foo(**{}) | @@ -100,12 +100,12 @@ PIE804.py:9:1: PIE804 [*] Unnecessary `dict` kwargs 11 11 | foo(**{}) 12 12 | -PIE804.py:11:1: PIE804 [*] Unnecessary `dict` kwargs +PIE804.py:11:5: PIE804 [*] Unnecessary `dict` kwargs | 9 | Foo.objects.create(**{**bar}) # PIE804 10 | 11 | foo(**{}) - | ^^^^^^^^^ PIE804 + | ^^^^ PIE804 12 | 13 | foo(**{**data, "foo": "buzz"}) | @@ -121,12 +121,12 @@ PIE804.py:11:1: PIE804 [*] Unnecessary `dict` kwargs 13 13 | foo(**{**data, "foo": "buzz"}) 14 14 | foo(**buzz) -PIE804.py:22:1: PIE804 [*] Unnecessary `dict` kwargs +PIE804.py:22:5: PIE804 [*] Unnecessary `dict` kwargs | 20 | foo(**{f"buzz__{bar}": True}) 21 | abc(**{"for": 3}) 22 | foo(**{},) - | ^^^^^^^^^^ PIE804 + | ^^^^ PIE804 23 | 24 | # Duplicated key names won't be fixed, to avoid syntax errors. | @@ -142,19 +142,47 @@ PIE804.py:22:1: PIE804 [*] Unnecessary `dict` kwargs 24 24 | # Duplicated key names won't be fixed, to avoid syntax errors. 25 25 | abc(**{'a': b}, **{'a': c}) # PIE804 -PIE804.py:25:1: PIE804 [*] Unnecessary `dict` kwargs +PIE804.py:25:5: PIE804 Unnecessary `dict` kwargs | 24 | # Duplicated key names won't be fixed, to avoid syntax errors. 25 | abc(**{'a': b}, **{'a': c}) # PIE804 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PIE804 + | ^^^^^^^^^^ PIE804 +26 | abc(a=1, **{'a': c}, **{'b': c}) # PIE804 + | + = help: Remove unnecessary kwargs + +PIE804.py:25:17: PIE804 Unnecessary `dict` kwargs + | +24 | # Duplicated key names won't be fixed, to avoid syntax errors. +25 | abc(**{'a': b}, **{'a': c}) # PIE804 + | ^^^^^^^^^^ PIE804 +26 | abc(a=1, **{'a': c}, **{'b': c}) # PIE804 + | + = help: Remove unnecessary kwargs + +PIE804.py:26:10: PIE804 Unnecessary `dict` kwargs + | +24 | # Duplicated key names won't be fixed, to avoid syntax errors. +25 | abc(**{'a': b}, **{'a': c}) # PIE804 +26 | abc(a=1, **{'a': c}, **{'b': c}) # PIE804 + | ^^^^^^^^^^ PIE804 + | + = help: Remove unnecessary kwargs + +PIE804.py:26:22: PIE804 [*] Unnecessary `dict` kwargs + | +24 | # Duplicated key names won't be fixed, to avoid syntax errors. +25 | abc(**{'a': b}, **{'a': c}) # PIE804 +26 | abc(a=1, **{'a': c}, **{'b': c}) # PIE804 + | ^^^^^^^^^^ PIE804 | = help: Remove unnecessary kwargs ℹ Safe fix -22 22 | foo(**{},) 23 23 | 24 24 | # Duplicated key names won't be fixed, to avoid syntax errors. -25 |-abc(**{'a': b}, **{'a': c}) # PIE804 - 25 |+abc(a=b, **{'a': c}) # PIE804 +25 25 | abc(**{'a': b}, **{'a': c}) # PIE804 +26 |-abc(a=1, **{'a': c}, **{'b': c}) # PIE804 + 26 |+abc(a=1, **{'a': c}, b=c) # PIE804