diff --git a/resources/test/fixtures/C406.py b/resources/test/fixtures/C406.py new file mode 100644 index 0000000000..3975adcbf2 --- /dev/null +++ b/resources/test/fixtures/C406.py @@ -0,0 +1,5 @@ +d1 = dict([(1, 2)]) +d2 = dict(((1, 2),)) +d3 = dict([]) +d4 = dict(()) +d5 = dict() diff --git a/src/ast/checks.rs b/src/ast/checks.rs index 25c1ccfbb1..49fe18db10 100644 --- a/src/ast/checks.rs +++ b/src/ast/checks.rs @@ -903,6 +903,60 @@ pub fn unnecessary_literal_set(expr: &Expr, func: &Expr, args: &Vec) -> Op None } +/// Check `dict([(1, 2)])` compliance. +pub fn unnecessary_literal_dict(expr: &Expr, func: &Expr, args: &Vec) -> Option { + if args.len() == 1 { + if let ExprKind::Name { id, .. } = &func.node { + if id == "dict" { + match &args[0].node { + ExprKind::Tuple { elts, .. } => { + if let Some(elt) = elts.first() { + match &elt.node { + // dict((1, 2), ...)) + ExprKind::Tuple { elts, .. } if elts.len() == 2 => { + return Some(Check::new( + CheckKind::UnnecessaryLiteralDict("tuple".to_string()), + Range::from_located(expr), + )); + } + _ => {} + } + } else { + // dict(()) + return Some(Check::new( + CheckKind::UnnecessaryLiteralDict("tuple".to_string()), + Range::from_located(expr), + )); + } + } + ExprKind::List { elts, .. } => { + if let Some(elt) = elts.first() { + match &elt.node { + // dict([(1, 2), ...]) + ExprKind::Tuple { elts, .. } if elts.len() == 2 => { + return Some(Check::new( + CheckKind::UnnecessaryLiteralDict("list".to_string()), + Range::from_located(expr), + )); + } + _ => {} + } + } else { + // dict([]) + return Some(Check::new( + CheckKind::UnnecessaryLiteralDict("list".to_string()), + Range::from_located(expr), + )); + } + } + _ => {} + } + } + } + } + None +} + // flake8-super /// Check that `super()` has no args pub fn check_super_args( diff --git a/src/check_ast.rs b/src/check_ast.rs index 1a4b45beb4..b8f3416008 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -806,6 +806,12 @@ where }; } + if self.settings.enabled.contains(&CheckCode::C406) { + if let Some(check) = checks::unnecessary_literal_dict(expr, func, args) { + self.checks.push(check); + }; + } + // pyupgrade if self.settings.enabled.contains(&CheckCode::U002) && self.settings.target_version >= PythonVersion::Py310 diff --git a/src/checks.rs b/src/checks.rs index cb48ea8d50..d304db0c38 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; 61] = [ +pub const ALL_CHECK_CODES: [CheckCode; 62] = [ // pycodestyle errors CheckCode::E402, CheckCode::E501, @@ -115,6 +115,7 @@ pub const ALL_CHECK_CODES: [CheckCode; 61] = [ CheckCode::C403, CheckCode::C404, CheckCode::C405, + CheckCode::C406, // flake8-super CheckCode::SPR001, // flake8-print @@ -189,6 +190,7 @@ pub enum CheckCode { C403, C404, C405, + C406, // flake8-super SPR001, // flake8-print @@ -265,6 +267,8 @@ impl FromStr for CheckCode { "C402" => Ok(CheckCode::C402), "C403" => Ok(CheckCode::C403), "C404" => Ok(CheckCode::C404), + "C405" => Ok(CheckCode::C405), + "C406" => Ok(CheckCode::C406), // flake8-super "SPR001" => Ok(CheckCode::SPR001), // flake8-print @@ -343,6 +347,7 @@ impl CheckCode { CheckCode::C403 => "C403", CheckCode::C404 => "C404", CheckCode::C405 => "C405", + CheckCode::C406 => "C406", // flake8-super CheckCode::SPR001 => "SPR001", // flake8-print @@ -430,6 +435,7 @@ impl CheckCode { CheckCode::C403 => CheckKind::UnnecessaryListComprehensionSet, CheckCode::C404 => CheckKind::UnnecessaryListComprehensionDict, CheckCode::C405 => CheckKind::UnnecessaryLiteralSet("".to_string()), + CheckCode::C406 => CheckKind::UnnecessaryLiteralDict("".to_string()), // flake8-super CheckCode::SPR001 => CheckKind::SuperCallWithParameters, // flake8-print @@ -517,6 +523,7 @@ pub enum CheckKind { UnnecessaryListComprehensionSet, UnnecessaryListComprehensionDict, UnnecessaryLiteralSet(String), + UnnecessaryLiteralDict(String), // flake8-super SuperCallWithParameters, // flake8-print @@ -591,6 +598,7 @@ impl CheckKind { CheckKind::UnnecessaryListComprehensionSet => "UnnecessaryListComprehensionSet", CheckKind::UnnecessaryListComprehensionDict => "UnnecessaryListComprehensionDict", CheckKind::UnnecessaryLiteralSet(_) => "UnnecessaryLiteralSet", + CheckKind::UnnecessaryLiteralDict(_) => "UnnecessaryLiteralDict", // flake8-super CheckKind::SuperCallWithParameters => "SuperCallWithParameters", // flake8-print @@ -665,6 +673,7 @@ impl CheckKind { CheckKind::UnnecessaryListComprehensionSet => &CheckCode::C403, CheckKind::UnnecessaryListComprehensionDict => &CheckCode::C404, CheckKind::UnnecessaryLiteralSet(_) => &CheckCode::C405, + CheckKind::UnnecessaryLiteralDict(_) => &CheckCode::C406, // flake8-super CheckKind::SuperCallWithParameters => &CheckCode::SPR001, // flake8-print @@ -842,6 +851,9 @@ impl CheckKind { CheckKind::UnnecessaryLiteralSet(obj_type) => { format!("Unnecessary {obj_type} literal - rewrite as a set literal") } + CheckKind::UnnecessaryLiteralDict(obj_type) => { + format!("Unnecessary {obj_type} literal - rewrite as a dict literal") + } // flake8-super CheckKind::SuperCallWithParameters => { "Use `super()` instead of `super(__class__, self)`".to_string() diff --git a/src/linter.rs b/src/linter.rs index d0549e48af..c58d56de1b 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -858,6 +858,18 @@ mod tests { Ok(()) } + #[test] + fn c406() -> Result<()> { + let mut checks = check_path( + Path::new("./resources/test/fixtures/C406.py"), + &settings::Settings::for_rule(CheckCode::C406), + &fixer::Mode::Generate, + )?; + checks.sort_by_key(|check| check.location); + insta::assert_yaml_snapshot!(checks); + Ok(()) + } + #[test] fn spr001() -> Result<()> { let mut checks = check_path( diff --git a/src/snapshots/ruff__linter__tests__c406.snap b/src/snapshots/ruff__linter__tests__c406.snap new file mode 100644 index 0000000000..4f569baea1 --- /dev/null +++ b/src/snapshots/ruff__linter__tests__c406.snap @@ -0,0 +1,41 @@ +--- +source: src/linter.rs +expression: checks +--- +- kind: + UnnecessaryLiteralDict: list + location: + row: 1 + column: 6 + end_location: + row: 1 + column: 20 + fix: ~ +- kind: + UnnecessaryLiteralDict: tuple + location: + row: 2 + column: 6 + end_location: + row: 2 + column: 21 + fix: ~ +- kind: + UnnecessaryLiteralDict: list + location: + row: 3 + column: 6 + end_location: + row: 3 + column: 14 + fix: ~ +- kind: + UnnecessaryLiteralDict: tuple + location: + row: 4 + column: 6 + end_location: + row: 4 + column: 14 + fix: ~ +