diff --git a/README.md b/README.md index 978a241654..4e3ed9dfc7 100644 --- a/README.md +++ b/README.md @@ -280,6 +280,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com | A003 | BuiltinAttributeShadowing | Class attribute `...` is shadowing a python builtin | | | | C400 | UnnecessaryGeneratorList | Unnecessary generator - rewrite as a list comprehension | | | | C401 | UnnecessaryGeneratorSet | Unnecessary generator - rewrite as a set comprehension | | | +| C402 | UnnecessaryGeneratorDict | Unnecessary generator - rewrite as a dict comprehension | | | | C403 | UnnecessaryListComprehensionSet | Unnecessary list comprehension - rewrite as a set comprehension | | | | C404 | UnnecessaryListComprehensionDict | Unnecessary list comprehension - rewrite as a dict comprehension | | | | SPR001 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` | | 🛠 | diff --git a/resources/test/fixtures/C402.py b/resources/test/fixtures/C402.py new file mode 100644 index 0000000000..4f9e9aa34e --- /dev/null +++ b/resources/test/fixtures/C402.py @@ -0,0 +1 @@ +d = dict((x, x) for x in range(3)) diff --git a/src/ast/checks.rs b/src/ast/checks.rs index c82c167bed..6d643e1bb1 100644 --- a/src/ast/checks.rs +++ b/src/ast/checks.rs @@ -808,6 +808,28 @@ pub fn unnecessary_generator_set(expr: &Expr, func: &Expr, args: &Vec) -> None } +/// Check `dict((x, y) for x, y in iterable)` compliance. +pub fn unnecessary_generator_dict(expr: &Expr, func: &Expr, args: &Vec) -> Option { + if args.len() == 1 { + if let ExprKind::Name { id, .. } = &func.node { + if id == "dict" { + if let ExprKind::GeneratorExp { elt, .. } = &args[0].node { + match &elt.node { + ExprKind::Tuple { elts, .. } if elts.len() == 2 => { + return Some(Check::new( + CheckKind::UnnecessaryListComprehensionDict, + Range::from_located(expr), + )); + } + _ => {} + } + } + } + } + } + None +} + /// Check `set([...])` compliance. pub fn unnecessary_list_comprehension_set( expr: &Expr, diff --git a/src/check_ast.rs b/src/check_ast.rs index 5294633eb7..9f65ab8599 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -778,6 +778,12 @@ where }; } + if self.settings.enabled.contains(&CheckCode::C402) { + if let Some(check) = checks::unnecessary_generator_dict(expr, func, args) { + self.checks.push(check); + }; + } + if self.settings.enabled.contains(&CheckCode::C403) { if let Some(check) = checks::unnecessary_list_comprehension_set(expr, func, args) diff --git a/src/checks.rs b/src/checks.rs index d5fc518571..36138d018f 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -57,7 +57,7 @@ pub const DEFAULT_CHECK_CODES: [CheckCode; 43] = [ CheckCode::F901, ]; -pub const ALL_CHECK_CODES: [CheckCode; 59] = [ +pub const ALL_CHECK_CODES: [CheckCode; 60] = [ // pycodestyle errors CheckCode::E402, CheckCode::E501, @@ -111,6 +111,7 @@ pub const ALL_CHECK_CODES: [CheckCode; 59] = [ // flake8-comprehensions CheckCode::C400, CheckCode::C401, + CheckCode::C402, CheckCode::C403, CheckCode::C404, // flake8-super @@ -183,6 +184,7 @@ pub enum CheckCode { // flake8-comprehensions C400, C401, + C402, C403, C404, // flake8-super @@ -332,6 +334,7 @@ impl CheckCode { // flake8-comprehensions CheckCode::C400 => "C400", CheckCode::C401 => "C401", + CheckCode::C402 => "C402", CheckCode::C403 => "C403", CheckCode::C404 => "C404", // flake8-super @@ -417,6 +420,7 @@ impl CheckCode { // flake8-comprehensions CheckCode::C400 => CheckKind::UnnecessaryGeneratorList, CheckCode::C401 => CheckKind::UnnecessaryGeneratorSet, + CheckCode::C402 => CheckKind::UnnecessaryGeneratorDict, CheckCode::C403 => CheckKind::UnnecessaryListComprehensionSet, CheckCode::C404 => CheckKind::UnnecessaryListComprehensionDict, // flake8-super @@ -502,6 +506,7 @@ pub enum CheckKind { // flakes8-comprehensions UnnecessaryGeneratorList, UnnecessaryGeneratorSet, + UnnecessaryGeneratorDict, UnnecessaryListComprehensionSet, UnnecessaryListComprehensionDict, // flake8-super @@ -574,6 +579,7 @@ impl CheckKind { // flake8-comprehensions CheckKind::UnnecessaryGeneratorList => "UnnecessaryGeneratorList", CheckKind::UnnecessaryGeneratorSet => "UnnecessaryGeneratorSet", + CheckKind::UnnecessaryGeneratorDict => "UnnecessaryGeneratorDict", CheckKind::UnnecessaryListComprehensionSet => "UnnecessaryListComprehensionSet", CheckKind::UnnecessaryListComprehensionDict => "UnnecessaryListComprehensionDict", // flake8-super @@ -646,6 +652,7 @@ impl CheckKind { // flake8-comprehensions CheckKind::UnnecessaryGeneratorList => &CheckCode::C400, CheckKind::UnnecessaryGeneratorSet => &CheckCode::C401, + CheckKind::UnnecessaryGeneratorDict => &CheckCode::C402, CheckKind::UnnecessaryListComprehensionSet => &CheckCode::C403, CheckKind::UnnecessaryListComprehensionDict => &CheckCode::C404, // flake8-super @@ -813,6 +820,9 @@ impl CheckKind { CheckKind::UnnecessaryGeneratorSet => { "Unnecessary generator - rewrite as a set comprehension".to_string() } + CheckKind::UnnecessaryGeneratorDict => { + "Unnecessary generator - rewrite as a dict comprehension".to_string() + } CheckKind::UnnecessaryListComprehensionSet => { "Unnecessary list comprehension - rewrite as a set comprehension".to_string() } diff --git a/src/linter.rs b/src/linter.rs index 5b43b60813..de7850a4ca 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -810,6 +810,18 @@ mod tests { Ok(()) } + #[test] + fn c402() -> Result<()> { + let mut checks = check_path( + Path::new("./resources/test/fixtures/C402.py"), + &settings::Settings::for_rule(CheckCode::C402), + &fixer::Mode::Generate, + )?; + checks.sort_by_key(|check| check.location); + insta::assert_yaml_snapshot!(checks); + Ok(()) + } + #[test] fn c403() -> Result<()> { let mut checks = check_path( diff --git a/src/snapshots/ruff__linter__tests__c402.snap b/src/snapshots/ruff__linter__tests__c402.snap new file mode 100644 index 0000000000..56c17d5e99 --- /dev/null +++ b/src/snapshots/ruff__linter__tests__c402.snap @@ -0,0 +1,13 @@ +--- +source: src/linter.rs +expression: checks +--- +- kind: UnnecessaryListComprehensionDict + location: + row: 1 + column: 5 + end_location: + row: 1 + column: 35 + fix: ~ +