diff --git a/README.md b/README.md index 969a73529f..4a17f8a127 100644 --- a/README.md +++ b/README.md @@ -215,7 +215,7 @@ ruff also implements some of the most popular Flake8 plugins natively, including - [`flake8-builtins`](https://pypi.org/project/flake8-builtins/) - [`flake8-super`](https://pypi.org/project/flake8-super/) - [`flake8-print`](https://pypi.org/project/flake8-print/) -- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/) (11/16) +- [`flake8-comprehensions`](https://pypi.org/project/flake8-comprehensions/) (12/16) - [`flake8-bugbear`](https://pypi.org/project/flake8-bugbear/) (3/32) - [`flake8-docstrings`](https://pypi.org/project/flake8-docstrings/) (25/48) - [`pyupgrade`](https://pypi.org/project/pyupgrade/) (8/34) @@ -293,6 +293,7 @@ The 🛠 emoji indicates that a rule is automatically fixable by the `--fix` com | C408 | UnnecessaryCollectionCall | Unnecessary call - rewrite as a literal | | | | C409 | UnnecessaryLiteralWithinTupleCall | Unnecessary literal passed to tuple() - remove the outer call to tuple() | | | | C410 | UnnecessaryLiteralWithinListCall | Unnecessary literal passed to list() - rewrite as a list literal | | | +| C414 | UnnecessaryDoubleCastOrProcess | Unnecessary call within (). | | | | C415 | UnnecessarySubscriptReversal | Unnecessary subscript reversal of iterable within () | | | | T201 | PrintFound | `print` found | | 🛠 | | T203 | PPrintFound | `pprint` found | | 🛠 | diff --git a/resources/test/fixtures/C414.py b/resources/test/fixtures/C414.py new file mode 100644 index 0000000000..bd6491cd46 --- /dev/null +++ b/resources/test/fixtures/C414.py @@ -0,0 +1,14 @@ +x = [1, 2, 3] +list(list(x)) +list(tuple(x)) +tuple(list(x)) +tuple(tuple(x)) +set(set(x)) +set(list(x)) +set(tuple(x)) +set(sorted(x)) +set(reversed(x)) +sorted(list(x)) +sorted(tuple(x)) +sorted(sorted(x)) +sorted(reversed(x)) diff --git a/src/ast/checkers.rs b/src/ast/checkers.rs index b8114f51cb..03d0176669 100644 --- a/src/ast/checkers.rs +++ b/src/ast/checkers.rs @@ -1004,6 +1004,68 @@ pub fn unnecessary_literal_within_list_call( None } +pub fn unnecessary_double_cast_or_process( + expr: &Expr, + func: &Expr, + args: &[Expr], +) -> Option { + if let ExprKind::Name { id: outer, .. } = &func.node { + if outer == "list" + || outer == "tuple" + || outer == "set" + || outer == "reversed" + || outer == "sorted" + { + if let Some(arg) = args.first() { + if let ExprKind::Call { func, .. } = &arg.node { + if let ExprKind::Name { id: inner, .. } = &func.node { + // Ex) set(tuple(...)) + if (outer == "set" || outer == "sorted") + && (inner == "list" + || inner == "tuple" + || inner == "reversed" + || inner == "sorted") + { + return Some(Check::new( + CheckKind::UnnecessaryDoubleCastOrProcess( + inner.to_string(), + outer.to_string(), + ), + Range::from_located(expr), + )); + } + + // Ex) list(tuple(...)) + if (outer == "list" || outer == "tuple") + && (inner == "list" || inner == "tuple") + { + return Some(Check::new( + CheckKind::UnnecessaryDoubleCastOrProcess( + inner.to_string(), + outer.to_string(), + ), + Range::from_located(expr), + )); + } + + // Ex) set(set(...)) + if outer == "set" && inner == "set" { + return Some(Check::new( + CheckKind::UnnecessaryDoubleCastOrProcess( + inner.to_string(), + outer.to_string(), + ), + Range::from_located(expr), + )); + } + } + } + } + } + } + None +} + pub fn unnecessary_subscript_reversal(expr: &Expr, func: &Expr, args: &[Expr]) -> Option { if let Some(first_arg) = args.first() { if let ExprKind::Name { id, .. } = &func.node { diff --git a/src/check_ast.rs b/src/check_ast.rs index 96b5598f65..76f2b1f42c 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -827,6 +827,15 @@ where self.checks.push(check); }; } + + if self.settings.enabled.contains(&CheckCode::C414) { + if let Some(check) = + checkers::unnecessary_double_cast_or_process(expr, func, args) + { + self.checks.push(check); + }; + } + if self.settings.enabled.contains(&CheckCode::C415) { if let Some(check) = checkers::unnecessary_subscript_reversal(expr, func, args) { diff --git a/src/checks.rs b/src/checks.rs index cdf4344647..086fc719f1 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -137,6 +137,7 @@ pub enum CheckCode { C408, C409, C410, + C414, C415, // flake8-print T201, @@ -259,6 +260,7 @@ pub enum CheckKind { UnnecessaryCollectionCall(String), UnnecessaryLiteralWithinTupleCall(String), UnnecessaryLiteralWithinListCall(String), + UnnecessaryDoubleCastOrProcess(String, String), UnnecessarySubscriptReversal(String), // flake8-print PrintFound, @@ -388,6 +390,10 @@ impl CheckCode { CheckCode::C410 => { CheckKind::UnnecessaryLiteralWithinListCall("".to_string()) } + CheckCode::C414 => CheckKind::UnnecessaryDoubleCastOrProcess( + "".to_string(), + "".to_string(), + ), CheckCode::C415 => { CheckKind::UnnecessarySubscriptReversal("".to_string()) } @@ -506,6 +512,7 @@ impl CheckKind { CheckKind::UnnecessaryCollectionCall(_) => &CheckCode::C408, CheckKind::UnnecessaryLiteralWithinTupleCall(..) => &CheckCode::C409, CheckKind::UnnecessaryLiteralWithinListCall(..) => &CheckCode::C410, + CheckKind::UnnecessaryDoubleCastOrProcess(..) => &CheckCode::C414, CheckKind::UnnecessarySubscriptReversal(_) => &CheckCode::C415, // flake8-print CheckKind::PrintFound => &CheckCode::T201, @@ -757,6 +764,9 @@ impl CheckKind { ) } } + CheckKind::UnnecessaryDoubleCastOrProcess(inner, outer) => { + format!("Unnecessary {inner} call within {outer}().") + } CheckKind::UnnecessarySubscriptReversal(func) => { format!("Unnecessary subscript reversal of iterable within {func}()") } diff --git a/src/linter.rs b/src/linter.rs index e37346ca07..95b45e2d7a 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -996,6 +996,18 @@ mod tests { Ok(()) } + #[test] + fn c414() -> Result<()> { + let mut checks = check_path( + Path::new("./resources/test/fixtures/C414.py"), + &settings::Settings::for_rule(CheckCode::C414), + &fixer::Mode::Generate, + )?; + checks.sort_by_key(|check| check.location); + insta::assert_yaml_snapshot!(checks); + Ok(()) + } + #[test] fn c415() -> Result<()> { let mut checks = check_path( diff --git a/src/snapshots/ruff__linter__tests__c414.snap b/src/snapshots/ruff__linter__tests__c414.snap new file mode 100644 index 0000000000..65b1fbeb76 --- /dev/null +++ b/src/snapshots/ruff__linter__tests__c414.snap @@ -0,0 +1,148 @@ +--- +source: src/linter.rs +expression: checks +--- +- kind: + UnnecessaryDoubleCastOrProcess: + - list + - list + location: + row: 2 + column: 1 + end_location: + row: 2 + column: 14 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - tuple + - list + location: + row: 3 + column: 1 + end_location: + row: 3 + column: 15 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - list + - tuple + location: + row: 4 + column: 1 + end_location: + row: 4 + column: 15 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - tuple + - tuple + location: + row: 5 + column: 1 + end_location: + row: 5 + column: 16 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - set + - set + location: + row: 6 + column: 1 + end_location: + row: 6 + column: 12 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - list + - set + location: + row: 7 + column: 1 + end_location: + row: 7 + column: 13 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - tuple + - set + location: + row: 8 + column: 1 + end_location: + row: 8 + column: 14 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - sorted + - set + location: + row: 9 + column: 1 + end_location: + row: 9 + column: 15 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - reversed + - set + location: + row: 10 + column: 1 + end_location: + row: 10 + column: 17 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - list + - sorted + location: + row: 11 + column: 1 + end_location: + row: 11 + column: 16 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - tuple + - sorted + location: + row: 12 + column: 1 + end_location: + row: 12 + column: 17 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - sorted + - sorted + location: + row: 13 + column: 1 + end_location: + row: 13 + column: 18 + fix: ~ +- kind: + UnnecessaryDoubleCastOrProcess: + - reversed + - sorted + location: + row: 14 + column: 1 + end_location: + row: 14 + column: 20 + fix: ~ +