From 5ccd9073980d0b1576af930b33f979fb6d374edd Mon Sep 17 00:00:00 2001 From: Harutaka Kawamura Date: Sun, 9 Oct 2022 06:17:34 +0900 Subject: [PATCH] Implement C408 (#364) --- README.md | 2 + resources/test/fixtures/C408.py | 5 +++ src/ast/checks.rs | 30 +++++++++++++- src/check_ast.rs | 15 ++++++- src/checks.rs | 15 ++++++- src/linter.rs | 12 ++++++ src/snapshots/ruff__linter__tests__c408.snap | 41 ++++++++++++++++++++ 7 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 resources/test/fixtures/C408.py create mode 100644 src/snapshots/ruff__linter__tests__c408.snap diff --git a/README.md b/README.md index 5eaed1c320..2adbbee0db 100644 --- a/README.md +++ b/README.md @@ -284,6 +284,8 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com | C403 | UnnecessaryListComprehensionSet | Unnecessary list comprehension - rewrite as a set comprehension | | | | C404 | UnnecessaryListComprehensionDict | Unnecessary list comprehension - rewrite as a dict comprehension | | | | C405 | UnnecessaryLiteralSet | Unnecessary literal - rewrite as a set literal | | | +| C406 | UnnecessaryLiteralDict | Unnecessary literal - rewrite as a dict literal | | | +| C408 | UnnecessaryCollectionCall | Unnecessary call - rewrite as a literal | | | | SPR001 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` | | 🛠 | | T201 | PrintFound | `print` found | | 🛠 | | T203 | PPrintFound | `pprint` found | | 🛠 | diff --git a/resources/test/fixtures/C408.py b/resources/test/fixtures/C408.py new file mode 100644 index 0000000000..a9effa28c3 --- /dev/null +++ b/resources/test/fixtures/C408.py @@ -0,0 +1,5 @@ +t = tuple() +l = list() +d1 = dict() +d2 = dict(a=1) +d3 = dict(**d2) diff --git a/src/ast/checks.rs b/src/ast/checks.rs index 49fe18db10..c9fa9388e4 100644 --- a/src/ast/checks.rs +++ b/src/ast/checks.rs @@ -4,7 +4,7 @@ use itertools::izip; use regex::Regex; use rustpython_parser::ast::{ Arg, ArgData, Arguments, Cmpop, Constant, Excepthandler, ExcepthandlerKind, Expr, ExprKind, - Stmt, StmtKind, Unaryop, + KeywordData, Located, Stmt, StmtKind, Unaryop, }; use serde::{Deserialize, Serialize}; @@ -957,6 +957,34 @@ pub fn unnecessary_literal_dict(expr: &Expr, func: &Expr, args: &Vec) -> O None } +pub fn unnecessary_collection_call( + expr: &Expr, + func: &Expr, + args: &Vec, + keywords: &Vec>, +) -> Option { + if args.is_empty() { + if let ExprKind::Name { id, .. } = &func.node { + if id == "list" || id == "tuple" { + // list() or tuple() + return Some(Check::new( + CheckKind::UnnecessaryCollectionCall(id.to_string()), + Range::from_located(expr), + )); + } else if id == "dict" { + // dict() or dict(a=1) + if keywords.is_empty() || keywords.iter().all(|kw| kw.node.arg.is_some()) { + return Some(Check::new( + CheckKind::UnnecessaryCollectionCall(id.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 b8f3416008..4624cd803f 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -748,7 +748,12 @@ where } ExprContext::Del => self.handle_node_delete(expr), }, - ExprKind::Call { func, args, .. } => { + ExprKind::Call { + func, + args, + keywords, + .. + } => { if self.settings.enabled.contains(&CheckCode::U005) { plugins::assert_equals(self, func); } @@ -812,6 +817,14 @@ where }; } + if self.settings.enabled.contains(&CheckCode::C408) { + if let Some(check) = + checks::unnecessary_collection_call(expr, func, args, keywords) + { + 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 d304db0c38..be7feb4e56 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; 62] = [ +pub const ALL_CHECK_CODES: [CheckCode; 63] = [ // pycodestyle errors CheckCode::E402, CheckCode::E501, @@ -116,6 +116,7 @@ pub const ALL_CHECK_CODES: [CheckCode; 62] = [ CheckCode::C404, CheckCode::C405, CheckCode::C406, + CheckCode::C408, // flake8-super CheckCode::SPR001, // flake8-print @@ -191,6 +192,7 @@ pub enum CheckCode { C404, C405, C406, + C408, // flake8-super SPR001, // flake8-print @@ -269,6 +271,7 @@ impl FromStr for CheckCode { "C404" => Ok(CheckCode::C404), "C405" => Ok(CheckCode::C405), "C406" => Ok(CheckCode::C406), + "C408" => Ok(CheckCode::C408), // flake8-super "SPR001" => Ok(CheckCode::SPR001), // flake8-print @@ -348,6 +351,7 @@ impl CheckCode { CheckCode::C404 => "C404", CheckCode::C405 => "C405", CheckCode::C406 => "C406", + CheckCode::C408 => "C408", // flake8-super CheckCode::SPR001 => "SPR001", // flake8-print @@ -436,6 +440,9 @@ impl CheckCode { CheckCode::C404 => CheckKind::UnnecessaryListComprehensionDict, CheckCode::C405 => CheckKind::UnnecessaryLiteralSet("".to_string()), CheckCode::C406 => CheckKind::UnnecessaryLiteralDict("".to_string()), + CheckCode::C408 => { + CheckKind::UnnecessaryCollectionCall("".to_string()) + } // flake8-super CheckCode::SPR001 => CheckKind::SuperCallWithParameters, // flake8-print @@ -524,6 +531,7 @@ pub enum CheckKind { UnnecessaryListComprehensionDict, UnnecessaryLiteralSet(String), UnnecessaryLiteralDict(String), + UnnecessaryCollectionCall(String), // flake8-super SuperCallWithParameters, // flake8-print @@ -599,6 +607,7 @@ impl CheckKind { CheckKind::UnnecessaryListComprehensionDict => "UnnecessaryListComprehensionDict", CheckKind::UnnecessaryLiteralSet(_) => "UnnecessaryLiteralSet", CheckKind::UnnecessaryLiteralDict(_) => "UnnecessaryLiteralDict", + CheckKind::UnnecessaryCollectionCall(_) => "UnnecessaryCollectionCall", // flake8-super CheckKind::SuperCallWithParameters => "SuperCallWithParameters", // flake8-print @@ -674,6 +683,7 @@ impl CheckKind { CheckKind::UnnecessaryListComprehensionDict => &CheckCode::C404, CheckKind::UnnecessaryLiteralSet(_) => &CheckCode::C405, CheckKind::UnnecessaryLiteralDict(_) => &CheckCode::C406, + CheckKind::UnnecessaryCollectionCall(_) => &CheckCode::C408, // flake8-super CheckKind::SuperCallWithParameters => &CheckCode::SPR001, // flake8-print @@ -854,6 +864,9 @@ impl CheckKind { CheckKind::UnnecessaryLiteralDict(obj_type) => { format!("Unnecessary {obj_type} literal - rewrite as a dict literal") } + CheckKind::UnnecessaryCollectionCall(obj_type) => { + format!("Unnecessary {obj_type} call - rewrite as a 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 c58d56de1b..4ba35803d2 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -870,6 +870,18 @@ mod tests { Ok(()) } + #[test] + fn c408() -> Result<()> { + let mut checks = check_path( + Path::new("./resources/test/fixtures/C408.py"), + &settings::Settings::for_rule(CheckCode::C408), + &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__c408.snap b/src/snapshots/ruff__linter__tests__c408.snap new file mode 100644 index 0000000000..7c6d501387 --- /dev/null +++ b/src/snapshots/ruff__linter__tests__c408.snap @@ -0,0 +1,41 @@ +--- +source: src/linter.rs +expression: checks +--- +- kind: + UnnecessaryCollectionCall: tuple + location: + row: 1 + column: 5 + end_location: + row: 1 + column: 12 + fix: ~ +- kind: + UnnecessaryCollectionCall: list + location: + row: 2 + column: 5 + end_location: + row: 2 + column: 11 + fix: ~ +- kind: + UnnecessaryCollectionCall: dict + location: + row: 3 + column: 6 + end_location: + row: 3 + column: 12 + fix: ~ +- kind: + UnnecessaryCollectionCall: dict + location: + row: 4 + column: 6 + end_location: + row: 4 + column: 15 + fix: ~ +