Move flake8-comprehensions violations to rule files (#2477)

This commit is contained in:
Aarni Koskela 2023-02-02 16:26:50 +02:00 committed by GitHub
parent f5fd6f59ea
commit aa85c81280
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1211 additions and 1028 deletions

View File

@ -130,22 +130,22 @@ ruff_macros::define_rule_mapping!(
// flake8-blind-except
BLE001 => violations::BlindExcept,
// flake8-comprehensions
C400 => violations::UnnecessaryGeneratorList,
C401 => violations::UnnecessaryGeneratorSet,
C402 => violations::UnnecessaryGeneratorDict,
C403 => violations::UnnecessaryListComprehensionSet,
C404 => violations::UnnecessaryListComprehensionDict,
C405 => violations::UnnecessaryLiteralSet,
C406 => violations::UnnecessaryLiteralDict,
C408 => violations::UnnecessaryCollectionCall,
C409 => violations::UnnecessaryLiteralWithinTupleCall,
C410 => violations::UnnecessaryLiteralWithinListCall,
C411 => violations::UnnecessaryListCall,
C413 => violations::UnnecessaryCallAroundSorted,
C414 => violations::UnnecessaryDoubleCastOrProcess,
C415 => violations::UnnecessarySubscriptReversal,
C416 => violations::UnnecessaryComprehension,
C417 => violations::UnnecessaryMap,
C400 => rules::flake8_comprehensions::rules::UnnecessaryGeneratorList,
C401 => rules::flake8_comprehensions::rules::UnnecessaryGeneratorSet,
C402 => rules::flake8_comprehensions::rules::UnnecessaryGeneratorDict,
C403 => rules::flake8_comprehensions::rules::UnnecessaryListComprehensionSet,
C404 => rules::flake8_comprehensions::rules::UnnecessaryListComprehensionDict,
C405 => rules::flake8_comprehensions::rules::UnnecessaryLiteralSet,
C406 => rules::flake8_comprehensions::rules::UnnecessaryLiteralDict,
C408 => rules::flake8_comprehensions::rules::UnnecessaryCollectionCall,
C409 => rules::flake8_comprehensions::rules::UnnecessaryLiteralWithinTupleCall,
C410 => rules::flake8_comprehensions::rules::UnnecessaryLiteralWithinListCall,
C411 => rules::flake8_comprehensions::rules::UnnecessaryListCall,
C413 => rules::flake8_comprehensions::rules::UnnecessaryCallAroundSorted,
C414 => rules::flake8_comprehensions::rules::UnnecessaryDoubleCastOrProcess,
C415 => rules::flake8_comprehensions::rules::UnnecessarySubscriptReversal,
C416 => rules::flake8_comprehensions::rules::UnnecessaryComprehension,
C417 => rules::flake8_comprehensions::rules::UnnecessaryMap,
// flake8-debugger
T100 => violations::Debugger,
// mccabe

View File

@ -1,728 +0,0 @@
use log::error;
use num_bigint::BigInt;
use rustpython_ast::{Comprehension, Constant, Expr, ExprKind, Keyword, Unaryop};
use super::fixes;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::registry::Diagnostic;
use crate::violations;
fn function_name(func: &Expr) -> Option<&str> {
if let ExprKind::Name { id, .. } = &func.node {
Some(id)
} else {
None
}
}
fn exactly_one_argument_with_matching_function<'a>(
name: &str,
func: &Expr,
args: &'a [Expr],
keywords: &[Keyword],
) -> Option<&'a ExprKind> {
if !keywords.is_empty() {
return None;
}
if args.len() != 1 {
return None;
}
if function_name(func)? != name {
return None;
}
Some(&args[0].node)
}
fn first_argument_with_matching_function<'a>(
name: &str,
func: &Expr,
args: &'a [Expr],
) -> Option<&'a ExprKind> {
if function_name(func)? == name {
Some(&args.first()?.node)
} else {
None
}
}
/// C400 (`list(generator)`)
pub fn unnecessary_generator_list(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = exactly_one_argument_with_matching_function("list", func, args, keywords) else {
return;
};
if !checker.is_builtin("list") {
return;
}
if let ExprKind::GeneratorExp { .. } = argument {
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryGeneratorList,
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_generator_list(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
}
/// C401 (`set(generator)`)
pub fn unnecessary_generator_set(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.is_builtin("set") {
return;
}
if let ExprKind::GeneratorExp { .. } = argument {
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryGeneratorSet,
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_generator_set(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
}
/// C402 (`dict((x, y) for x, y in iterable)`)
pub fn unnecessary_generator_dict(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return;
};
if let ExprKind::GeneratorExp { elt, .. } = argument {
match &elt.node {
ExprKind::Tuple { elts, .. } if elts.len() == 2 => {
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryGeneratorDict,
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_generator_dict(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
_ => {}
}
}
}
/// C403 (`set([...])`)
pub fn unnecessary_list_comprehension_set(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.is_builtin("set") {
return;
}
if let ExprKind::ListComp { .. } = &argument {
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryListComprehensionSet,
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_list_comprehension_set(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
}
/// C404 (`dict([...])`)
pub fn unnecessary_list_comprehension_dict(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return;
};
if !checker.is_builtin("dict") {
return;
}
let ExprKind::ListComp { elt, .. } = &argument else {
return;
};
let ExprKind::Tuple { elts, .. } = &elt.node else {
return;
};
if elts.len() != 2 {
return;
}
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryListComprehensionDict,
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_list_comprehension_dict(checker.locator, checker.stylist, expr)
{
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
/// C405 (`set([1, 2])`)
pub fn unnecessary_literal_set(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.is_builtin("set") {
return;
}
let kind = match argument {
ExprKind::List { .. } => "list",
ExprKind::Tuple { .. } => "tuple",
_ => return,
};
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryLiteralSet {
obj_type: kind.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_set(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
/// C406 (`dict([(1, 2)])`)
pub fn unnecessary_literal_dict(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return;
};
if !checker.is_builtin("dict") {
return;
}
let (kind, elts) = match argument {
ExprKind::Tuple { elts, .. } => ("tuple", elts),
ExprKind::List { elts, .. } => ("list", elts),
_ => return,
};
// Accept `dict((1, 2), ...))` `dict([(1, 2), ...])`.
if !elts
.iter()
.all(|elt| matches!(&elt.node, ExprKind::Tuple { elts, .. } if elts.len() == 2))
{
return;
}
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryLiteralDict {
obj_type: kind.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_dict(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
/// C408
pub fn unnecessary_collection_call(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
if !args.is_empty() {
return;
}
let Some(id) = function_name(func) else {
return;
};
match id {
"dict" if keywords.is_empty() || keywords.iter().all(|kw| kw.node.arg.is_some()) => {
// `dict()` or `dict(a=1)` (as opposed to `dict(**a)`)
}
"list" | "tuple" => {
// `list()` or `tuple()`
}
_ => return,
};
if !checker.is_builtin(id) {
return;
}
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryCollectionCall {
obj_type: id.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_collection_call(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
/// C409
pub fn unnecessary_literal_within_tuple_call(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
) {
let Some(argument) = first_argument_with_matching_function("tuple", func, args) else {
return;
};
if !checker.is_builtin("tuple") {
return;
}
let argument_kind = match argument {
ExprKind::Tuple { .. } => "tuple",
ExprKind::List { .. } => "list",
_ => return,
};
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryLiteralWithinTupleCall {
literal: argument_kind.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_within_tuple_call(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
/// C410
pub fn unnecessary_literal_within_list_call(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
) {
let Some(argument) = first_argument_with_matching_function("list", func, args) else {
return;
};
if !checker.is_builtin("list") {
return;
}
let argument_kind = match argument {
ExprKind::Tuple { .. } => "tuple",
ExprKind::List { .. } => "list",
_ => return,
};
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryLiteralWithinListCall {
literal: argument_kind.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_within_list_call(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
/// C411
pub fn unnecessary_list_call(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
let Some(argument) = first_argument_with_matching_function("list", func, args) else {
return;
};
if !checker.is_builtin("list") {
return;
}
if !matches!(argument, ExprKind::ListComp { .. }) {
return;
}
let mut diagnostic =
Diagnostic::new(violations::UnnecessaryListCall, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_list_call(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
/// C413
pub fn unnecessary_call_around_sorted(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
) {
let Some(outer) = function_name(func) else {
return;
};
if !(outer == "list" || outer == "reversed") {
return;
}
let Some(arg) = args.first() else {
return;
};
let ExprKind::Call { func, .. } = &arg.node else {
return;
};
let Some(inner) = function_name(func) else {
return;
};
if inner != "sorted" {
return;
}
if !checker.is_builtin(inner) || !checker.is_builtin(outer) {
return;
}
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryCallAroundSorted {
func: outer.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_call_around_sorted(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
/// C414
pub fn unnecessary_double_cast_or_process(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
) {
fn diagnostic(inner: &str, outer: &str, location: Range) -> Diagnostic {
Diagnostic::new(
violations::UnnecessaryDoubleCastOrProcess {
inner: inner.to_string(),
outer: outer.to_string(),
},
location,
)
}
let Some(outer) = function_name(func) else {
return;
};
if !(outer == "list"
|| outer == "tuple"
|| outer == "set"
|| outer == "reversed"
|| outer == "sorted")
{
return;
}
let Some(arg) = args.first() else {
return;
};
let ExprKind::Call { func, .. } = &arg.node else {
return;
};
let Some(inner) = function_name(func) else {
return;
};
if !checker.is_builtin(inner) || !checker.is_builtin(outer) {
return;
}
// Ex) set(tuple(...))
if (outer == "set" || outer == "sorted")
&& (inner == "list" || inner == "tuple" || inner == "reversed" || inner == "sorted")
{
checker
.diagnostics
.push(diagnostic(inner, outer, Range::from_located(expr)));
return;
}
// Ex) list(tuple(...))
if (outer == "list" || outer == "tuple") && (inner == "list" || inner == "tuple") {
checker
.diagnostics
.push(diagnostic(inner, outer, Range::from_located(expr)));
return;
}
// Ex) set(set(...))
if outer == "set" && inner == "set" {
checker
.diagnostics
.push(diagnostic(inner, outer, Range::from_located(expr)));
}
}
/// C415
pub fn unnecessary_subscript_reversal(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
) {
let Some(first_arg) = args.first() else {
return;
};
let Some(id) = function_name(func) else {
return;
};
if !(id == "set" || id == "sorted" || id == "reversed") {
return;
}
if !checker.is_builtin(id) {
return;
}
let ExprKind::Subscript { slice, .. } = &first_arg.node else {
return;
};
let ExprKind::Slice { lower, upper, step } = &slice.node else {
return;
};
if lower.is_some() || upper.is_some() {
return;
}
let Some(step) = step.as_ref() else {
return;
};
let ExprKind::UnaryOp {
op: Unaryop::USub,
operand,
} = &step.node else {
return;
};
let ExprKind::Constant {
value: Constant::Int(val),
..
} = &operand.node else {
return;
};
if *val != BigInt::from(1) {
return;
};
checker.diagnostics.push(Diagnostic::new(
violations::UnnecessarySubscriptReversal {
func: id.to_string(),
},
Range::from_located(expr),
));
}
/// C416
pub fn unnecessary_comprehension(
checker: &mut Checker,
expr: &Expr,
elt: &Expr,
generators: &[Comprehension],
) {
if generators.len() != 1 {
return;
}
let generator = &generators[0];
if !(generator.ifs.is_empty() && generator.is_async == 0) {
return;
}
let Some(elt_id) = function_name(elt) else {
return;
};
let Some(target_id) = function_name(&generator.target) else {
return;
};
if elt_id != target_id {
return;
}
let id = match &expr.node {
ExprKind::ListComp { .. } => "list",
ExprKind::SetComp { .. } => "set",
_ => return,
};
if !checker.is_builtin(id) {
return;
}
let mut diagnostic = Diagnostic::new(
violations::UnnecessaryComprehension {
obj_type: id.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_comprehension(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
/// C417
pub fn unnecessary_map(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
fn diagnostic(kind: &str, location: Range) -> Diagnostic {
Diagnostic::new(
violations::UnnecessaryMap {
obj_type: kind.to_string(),
},
location,
)
}
let Some(id) = function_name(func) else {
return;
};
match id {
"map" => {
if !checker.is_builtin(id) {
return;
}
if args.len() == 2 && matches!(&args[0].node, ExprKind::Lambda { .. }) {
checker
.diagnostics
.push(diagnostic("generator", Range::from_located(expr)));
}
}
"list" | "set" => {
if !checker.is_builtin(id) {
return;
}
if let Some(arg) = args.first() {
if let ExprKind::Call { func, args, .. } = &arg.node {
let Some(argument) = first_argument_with_matching_function("map", func, args) else {
return;
};
if let ExprKind::Lambda { .. } = argument {
checker
.diagnostics
.push(diagnostic(id, Range::from_located(expr)));
}
}
}
}
"dict" => {
if !checker.is_builtin(id) {
return;
}
if args.len() == 1 {
if let ExprKind::Call { func, args, .. } = &args[0].node {
let Some(argument) = first_argument_with_matching_function("map", func, args) else {
return;
};
if let ExprKind::Lambda { body, .. } = &argument {
if matches!(&body.node, ExprKind::Tuple { elts, .. } | ExprKind::List { elts, .. } if elts.len() == 2)
{
checker
.diagnostics
.push(diagnostic(id, Range::from_located(expr)));
}
}
}
}
}
_ => (),
}
}

View File

@ -0,0 +1,39 @@
use rustpython_ast::{Expr, ExprKind, Keyword};
pub fn function_name(func: &Expr) -> Option<&str> {
if let ExprKind::Name { id, .. } = &func.node {
Some(id)
} else {
None
}
}
pub fn exactly_one_argument_with_matching_function<'a>(
name: &str,
func: &Expr,
args: &'a [Expr],
keywords: &[Keyword],
) -> Option<&'a ExprKind> {
if !keywords.is_empty() {
return None;
}
if args.len() != 1 {
return None;
}
if function_name(func)? != name {
return None;
}
Some(&args[0].node)
}
pub fn first_argument_with_matching_function<'a>(
name: &str,
func: &Expr,
args: &'a [Expr],
) -> Option<&'a ExprKind> {
if function_name(func)? == name {
Some(&args.first()?.node)
} else {
None
}
}

View File

@ -0,0 +1,48 @@
mod helpers;
mod unnecessary_call_around_sorted;
mod unnecessary_collection_call;
mod unnecessary_comprehension;
mod unnecessary_double_cast_or_process;
mod unnecessary_generator_dict;
mod unnecessary_generator_list;
mod unnecessary_generator_set;
mod unnecessary_list_call;
mod unnecessary_list_comprehension_dict;
mod unnecessary_list_comprehension_set;
mod unnecessary_literal_dict;
mod unnecessary_literal_set;
mod unnecessary_literal_within_list_call;
mod unnecessary_literal_within_tuple_call;
mod unnecessary_map;
mod unnecessary_subscript_reversal;
pub use unnecessary_call_around_sorted::{
unnecessary_call_around_sorted, UnnecessaryCallAroundSorted,
};
pub use unnecessary_collection_call::{unnecessary_collection_call, UnnecessaryCollectionCall};
pub use unnecessary_comprehension::{unnecessary_comprehension, UnnecessaryComprehension};
pub use unnecessary_double_cast_or_process::{
unnecessary_double_cast_or_process, UnnecessaryDoubleCastOrProcess,
};
pub use unnecessary_generator_dict::{unnecessary_generator_dict, UnnecessaryGeneratorDict};
pub use unnecessary_generator_list::{unnecessary_generator_list, UnnecessaryGeneratorList};
pub use unnecessary_generator_set::{unnecessary_generator_set, UnnecessaryGeneratorSet};
pub use unnecessary_list_call::{unnecessary_list_call, UnnecessaryListCall};
pub use unnecessary_list_comprehension_dict::{
unnecessary_list_comprehension_dict, UnnecessaryListComprehensionDict,
};
pub use unnecessary_list_comprehension_set::{
unnecessary_list_comprehension_set, UnnecessaryListComprehensionSet,
};
pub use unnecessary_literal_dict::{unnecessary_literal_dict, UnnecessaryLiteralDict};
pub use unnecessary_literal_set::{unnecessary_literal_set, UnnecessaryLiteralSet};
pub use unnecessary_literal_within_list_call::{
unnecessary_literal_within_list_call, UnnecessaryLiteralWithinListCall,
};
pub use unnecessary_literal_within_tuple_call::{
unnecessary_literal_within_tuple_call, UnnecessaryLiteralWithinTupleCall,
};
pub use unnecessary_map::{unnecessary_map, UnnecessaryMap};
pub use unnecessary_subscript_reversal::{
unnecessary_subscript_reversal, UnnecessarySubscriptReversal,
};

View File

@ -0,0 +1,73 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind};
define_violation!(
pub struct UnnecessaryCallAroundSorted {
pub func: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryCallAroundSorted {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryCallAroundSorted { func } = self;
format!("Unnecessary `{func}` call around `sorted()`")
}
fn autofix_title(&self) -> String {
let UnnecessaryCallAroundSorted { func } = self;
format!("Remove unnecessary `{func}` call")
}
}
/// C413
pub fn unnecessary_call_around_sorted(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
) {
let Some(outer) = helpers::function_name(func) else {
return;
};
if !(outer == "list" || outer == "reversed") {
return;
}
let Some(arg) = args.first() else {
return;
};
let ExprKind::Call { func, .. } = &arg.node else {
return;
};
let Some(inner) = helpers::function_name(func) else {
return;
};
if inner != "sorted" {
return;
}
if !checker.is_builtin(inner) || !checker.is_builtin(outer) {
return;
}
let mut diagnostic = Diagnostic::new(
UnnecessaryCallAroundSorted {
func: outer.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_call_around_sorted(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}

View File

@ -0,0 +1,70 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, Keyword};
define_violation!(
pub struct UnnecessaryCollectionCall {
pub obj_type: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryCollectionCall {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryCollectionCall { obj_type } = self;
format!("Unnecessary `{obj_type}` call (rewrite as a literal)")
}
fn autofix_title(&self) -> String {
"Rewrite as a literal".to_string()
}
}
/// C408
pub fn unnecessary_collection_call(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
if !args.is_empty() {
return;
}
let Some(id) = helpers::function_name(func) else {
return;
};
match id {
"dict" if keywords.is_empty() || keywords.iter().all(|kw| kw.node.arg.is_some()) => {
// `dict()` or `dict(a=1)` (as opposed to `dict(**a)`)
}
"list" | "tuple" => {
// `list()` or `tuple()`
}
_ => return,
};
if !checker.is_builtin(id) {
return;
}
let mut diagnostic = Diagnostic::new(
UnnecessaryCollectionCall {
obj_type: id.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_collection_call(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}

View File

@ -0,0 +1,77 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Comprehension, Expr, ExprKind};
define_violation!(
pub struct UnnecessaryComprehension {
pub obj_type: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryComprehension {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryComprehension { obj_type } = self;
format!("Unnecessary `{obj_type}` comprehension (rewrite using `{obj_type}()`)")
}
fn autofix_title(&self) -> String {
let UnnecessaryComprehension { obj_type } = self;
format!("Rewrite using `{obj_type}()`")
}
}
/// C416
pub fn unnecessary_comprehension(
checker: &mut Checker,
expr: &Expr,
elt: &Expr,
generators: &[Comprehension],
) {
if generators.len() != 1 {
return;
}
let generator = &generators[0];
if !(generator.ifs.is_empty() && generator.is_async == 0) {
return;
}
let Some(elt_id) = helpers::function_name(elt) else {
return;
};
let Some(target_id) = helpers::function_name(&generator.target) else {
return;
};
if elt_id != target_id {
return;
}
let id = match &expr.node {
ExprKind::ListComp { .. } => "list",
ExprKind::SetComp { .. } => "set",
_ => return,
};
if !checker.is_builtin(id) {
return;
}
let mut diagnostic = Diagnostic::new(
UnnecessaryComprehension {
obj_type: id.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_comprehension(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}

View File

@ -0,0 +1,90 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::violation::Violation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind};
define_violation!(
pub struct UnnecessaryDoubleCastOrProcess {
pub inner: String,
pub outer: String,
}
);
impl Violation for UnnecessaryDoubleCastOrProcess {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryDoubleCastOrProcess { inner, outer } = self;
format!("Unnecessary `{inner}` call within `{outer}()`")
}
}
/// C414
pub fn unnecessary_double_cast_or_process(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
) {
fn diagnostic(inner: &str, outer: &str, location: Range) -> Diagnostic {
Diagnostic::new(
UnnecessaryDoubleCastOrProcess {
inner: inner.to_string(),
outer: outer.to_string(),
},
location,
)
}
let Some(outer) = helpers::function_name(func) else {
return;
};
if !(outer == "list"
|| outer == "tuple"
|| outer == "set"
|| outer == "reversed"
|| outer == "sorted")
{
return;
}
let Some(arg) = args.first() else {
return;
};
let ExprKind::Call { func, .. } = &arg.node else {
return;
};
let Some(inner) = helpers::function_name(func) else {
return;
};
if !checker.is_builtin(inner) || !checker.is_builtin(outer) {
return;
}
// Ex) set(tuple(...))
if (outer == "set" || outer == "sorted")
&& (inner == "list" || inner == "tuple" || inner == "reversed" || inner == "sorted")
{
checker
.diagnostics
.push(diagnostic(inner, outer, Range::from_located(expr)));
return;
}
// Ex) list(tuple(...))
if (outer == "list" || outer == "tuple") && (inner == "list" || inner == "tuple") {
checker
.diagnostics
.push(diagnostic(inner, outer, Range::from_located(expr)));
return;
}
// Ex) set(set(...))
if outer == "set" && inner == "set" {
checker
.diagnostics
.push(diagnostic(inner, outer, Range::from_located(expr)));
}
}

View File

@ -0,0 +1,59 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind, Keyword};
define_violation!(
pub struct UnnecessaryGeneratorDict;
);
impl AlwaysAutofixableViolation for UnnecessaryGeneratorDict {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary generator (rewrite as a `dict` comprehension)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `dict` comprehension".to_string()
}
}
/// C402 (`dict((x, y) for x, y in iterable)`)
pub fn unnecessary_generator_dict(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return;
};
if let ExprKind::GeneratorExp { elt, .. } = argument {
match &elt.node {
ExprKind::Tuple { elts, .. } if elts.len() == 2 => {
let mut diagnostic =
Diagnostic::new(UnnecessaryGeneratorDict, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_generator_dict(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
_ => {}
}
}
}

View File

@ -0,0 +1,52 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind, Keyword};
define_violation!(
pub struct UnnecessaryGeneratorList;
);
impl AlwaysAutofixableViolation for UnnecessaryGeneratorList {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary generator (rewrite as a `list` comprehension)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `list` comprehension".to_string()
}
}
/// C400 (`list(generator)`)
pub fn unnecessary_generator_list(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = helpers::exactly_one_argument_with_matching_function("list", func, args, keywords) else {
return;
};
if !checker.is_builtin("list") {
return;
}
if let ExprKind::GeneratorExp { .. } = argument {
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorList, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_generator_list(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
}

View File

@ -0,0 +1,52 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind, Keyword};
define_violation!(
pub struct UnnecessaryGeneratorSet;
);
impl AlwaysAutofixableViolation for UnnecessaryGeneratorSet {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary generator (rewrite as a `set` comprehension)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `set` comprehension".to_string()
}
}
/// C401 (`set(generator)`)
pub fn unnecessary_generator_set(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.is_builtin("set") {
return;
}
if let ExprKind::GeneratorExp { .. } = argument {
let mut diagnostic = Diagnostic::new(UnnecessaryGeneratorSet, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_generator_set(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
}

View File

@ -0,0 +1,47 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind};
define_violation!(
pub struct UnnecessaryListCall;
);
impl AlwaysAutofixableViolation for UnnecessaryListCall {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary `list` call (remove the outer call to `list()`)")
}
fn autofix_title(&self) -> String {
"Remove outer `list` call".to_string()
}
}
/// C411
pub fn unnecessary_list_call(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
let Some(argument) = helpers::first_argument_with_matching_function("list", func, args) else {
return;
};
if !checker.is_builtin("list") {
return;
}
if !matches!(argument, ExprKind::ListComp { .. }) {
return;
}
let mut diagnostic = Diagnostic::new(UnnecessaryListCall, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_list_call(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}

View File

@ -0,0 +1,61 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind, Keyword};
define_violation!(
pub struct UnnecessaryListComprehensionDict;
);
impl AlwaysAutofixableViolation for UnnecessaryListComprehensionDict {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary `list` comprehension (rewrite as a `dict` comprehension)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `dict` comprehension".to_string()
}
}
/// C404 (`dict([...])`)
pub fn unnecessary_list_comprehension_dict(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return;
};
if !checker.is_builtin("dict") {
return;
}
let ExprKind::ListComp { elt, .. } = &argument else {
return;
};
let ExprKind::Tuple { elts, .. } = &elt.node else {
return;
};
if elts.len() != 2 {
return;
}
let mut diagnostic =
Diagnostic::new(UnnecessaryListComprehensionDict, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_list_comprehension_dict(checker.locator, checker.stylist, expr)
{
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}

View File

@ -0,0 +1,57 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind, Keyword};
define_violation!(
pub struct UnnecessaryListComprehensionSet;
);
impl AlwaysAutofixableViolation for UnnecessaryListComprehensionSet {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary `list` comprehension (rewrite as a `set` comprehension)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `set` comprehension".to_string()
}
}
/// C403 (`set([...])`)
pub fn unnecessary_list_comprehension_set(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.is_builtin("set") {
return;
}
if let ExprKind::ListComp { .. } = &argument {
let mut diagnostic =
Diagnostic::new(UnnecessaryListComprehensionSet, Range::from_located(expr));
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_list_comprehension_set(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}
}

View File

@ -0,0 +1,70 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind, Keyword};
define_violation!(
pub struct UnnecessaryLiteralDict {
pub obj_type: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryLiteralDict {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryLiteralDict { obj_type } = self;
format!("Unnecessary `{obj_type}` literal (rewrite as a `dict` literal)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `dict` literal".to_string()
}
}
/// C406 (`dict([(1, 2)])`)
pub fn unnecessary_literal_dict(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = helpers::exactly_one_argument_with_matching_function("dict", func, args, keywords) else {
return;
};
if !checker.is_builtin("dict") {
return;
}
let (kind, elts) = match argument {
ExprKind::Tuple { elts, .. } => ("tuple", elts),
ExprKind::List { elts, .. } => ("list", elts),
_ => return,
};
// Accept `dict((1, 2), ...))` `dict([(1, 2), ...])`.
if !elts
.iter()
.all(|elt| matches!(&elt.node, ExprKind::Tuple { elts, .. } if elts.len() == 2))
{
return;
}
let mut diagnostic = Diagnostic::new(
UnnecessaryLiteralDict {
obj_type: kind.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_dict(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}

View File

@ -0,0 +1,63 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind, Keyword};
define_violation!(
pub struct UnnecessaryLiteralSet {
pub obj_type: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryLiteralSet {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryLiteralSet { obj_type } = self;
format!("Unnecessary `{obj_type}` literal (rewrite as a `set` literal)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `set` literal".to_string()
}
}
/// C405 (`set([1, 2])`)
pub fn unnecessary_literal_set(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
keywords: &[Keyword],
) {
let Some(argument) = helpers::exactly_one_argument_with_matching_function("set", func, args, keywords) else {
return;
};
if !checker.is_builtin("set") {
return;
}
let kind = match argument {
ExprKind::List { .. } => "list",
ExprKind::Tuple { .. } => "tuple",
_ => return,
};
let mut diagnostic = Diagnostic::new(
UnnecessaryLiteralSet {
obj_type: kind.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_set(checker.locator, checker.stylist, expr) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}

View File

@ -0,0 +1,82 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind};
define_violation!(
pub struct UnnecessaryLiteralWithinListCall {
pub literal: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryLiteralWithinListCall {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryLiteralWithinListCall { literal } = self;
if literal == "list" {
format!(
"Unnecessary `{literal}` literal passed to `list()` (remove the outer call to \
`list()`)"
)
} else {
format!(
"Unnecessary `{literal}` literal passed to `list()` (rewrite as a `list` literal)"
)
}
}
fn autofix_title(&self) -> String {
let UnnecessaryLiteralWithinListCall { literal } = self;
{
if literal == "list" {
"Remove outer `list` call".to_string()
} else {
"Rewrite as a `list` literal".to_string()
}
}
}
}
/// C410
pub fn unnecessary_literal_within_list_call(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
) {
let Some(argument) = helpers::first_argument_with_matching_function("list", func, args) else {
return;
};
if !checker.is_builtin("list") {
return;
}
let argument_kind = match argument {
ExprKind::Tuple { .. } => "tuple",
ExprKind::List { .. } => "list",
_ => return,
};
let mut diagnostic = Diagnostic::new(
UnnecessaryLiteralWithinListCall {
literal: argument_kind.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_within_list_call(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}

View File

@ -0,0 +1,83 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::rules::flake8_comprehensions::fixes;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind};
define_violation!(
pub struct UnnecessaryLiteralWithinTupleCall {
pub literal: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryLiteralWithinTupleCall {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryLiteralWithinTupleCall { literal } = self;
if literal == "list" {
format!(
"Unnecessary `{literal}` literal passed to `tuple()` (rewrite as a `tuple` \
literal)"
)
} else {
format!(
"Unnecessary `{literal}` literal passed to `tuple()` (remove the outer call to \
`tuple()`)"
)
}
}
fn autofix_title(&self) -> String {
let UnnecessaryLiteralWithinTupleCall { literal } = self;
{
if literal == "list" {
"Rewrite as a `tuple` literal".to_string()
} else {
"Remove outer `tuple` call".to_string()
}
}
}
}
/// C409
pub fn unnecessary_literal_within_tuple_call(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
) {
let Some(argument) = helpers::first_argument_with_matching_function("tuple", func, args) else {
return;
};
if !checker.is_builtin("tuple") {
return;
}
let argument_kind = match argument {
ExprKind::Tuple { .. } => "tuple",
ExprKind::List { .. } => "list",
_ => return,
};
let mut diagnostic = Diagnostic::new(
UnnecessaryLiteralWithinTupleCall {
literal: argument_kind.to_string(),
},
Range::from_located(expr),
);
if checker.patch(diagnostic.kind.rule()) {
match fixes::fix_unnecessary_literal_within_tuple_call(
checker.locator,
checker.stylist,
expr,
) {
Ok(fix) => {
diagnostic.amend(fix);
}
Err(e) => error!("Failed to generate fix: {e}"),
}
}
checker.diagnostics.push(diagnostic);
}

View File

@ -0,0 +1,95 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::violation::Violation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind};
define_violation!(
pub struct UnnecessaryMap {
pub obj_type: String,
}
);
impl Violation for UnnecessaryMap {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryMap { obj_type } = self;
if obj_type == "generator" {
format!("Unnecessary `map` usage (rewrite using a generator expression)")
} else {
format!("Unnecessary `map` usage (rewrite using a `{obj_type}` comprehension)")
}
}
}
/// C417
pub fn unnecessary_map(checker: &mut Checker, expr: &Expr, func: &Expr, args: &[Expr]) {
fn diagnostic(kind: &str, location: Range) -> Diagnostic {
Diagnostic::new(
UnnecessaryMap {
obj_type: kind.to_string(),
},
location,
)
}
let Some(id) = helpers::function_name(func) else {
return;
};
match id {
"map" => {
if !checker.is_builtin(id) {
return;
}
if args.len() == 2 && matches!(&args[0].node, ExprKind::Lambda { .. }) {
checker
.diagnostics
.push(diagnostic("generator", Range::from_located(expr)));
}
}
"list" | "set" => {
if !checker.is_builtin(id) {
return;
}
if let Some(arg) = args.first() {
if let ExprKind::Call { func, args, .. } = &arg.node {
let Some(argument) = helpers::first_argument_with_matching_function("map", func, args) else {
return;
};
if let ExprKind::Lambda { .. } = argument {
checker
.diagnostics
.push(diagnostic(id, Range::from_located(expr)));
}
}
}
}
"dict" => {
if !checker.is_builtin(id) {
return;
}
if args.len() == 1 {
if let ExprKind::Call { func, args, .. } = &args[0].node {
let Some(argument) = helpers::first_argument_with_matching_function("map", func, args) else {
return;
};
if let ExprKind::Lambda { body, .. } = &argument {
if matches!(&body.node, ExprKind::Tuple { elts, .. } | ExprKind::List { elts, .. } if elts.len() == 2)
{
checker
.diagnostics
.push(diagnostic(id, Range::from_located(expr)));
}
}
}
}
}
_ => (),
}
}

View File

@ -0,0 +1,77 @@
use super::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::define_violation;
use crate::registry::Diagnostic;
use crate::violation::Violation;
use num_bigint::BigInt;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Constant, Expr, ExprKind, Unaryop};
define_violation!(
pub struct UnnecessarySubscriptReversal {
pub func: String,
}
);
impl Violation for UnnecessarySubscriptReversal {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessarySubscriptReversal { func } = self;
format!("Unnecessary subscript reversal of iterable within `{func}()`")
}
}
/// C415
pub fn unnecessary_subscript_reversal(
checker: &mut Checker,
expr: &Expr,
func: &Expr,
args: &[Expr],
) {
let Some(first_arg) = args.first() else {
return;
};
let Some(id) = helpers::function_name(func) else {
return;
};
if !(id == "set" || id == "sorted" || id == "reversed") {
return;
}
if !checker.is_builtin(id) {
return;
}
let ExprKind::Subscript { slice, .. } = &first_arg.node else {
return;
};
let ExprKind::Slice { lower, upper, step } = &slice.node else {
return;
};
if lower.is_some() || upper.is_some() {
return;
}
let Some(step) = step.as_ref() else {
return;
};
let ExprKind::UnaryOp {
op: Unaryop::USub,
operand,
} = &step.node else {
return;
};
let ExprKind::Constant {
value: Constant::Int(val),
..
} = &operand.node else {
return;
};
if *val != BigInt::from(1) {
return;
};
checker.diagnostics.push(Diagnostic::new(
UnnecessarySubscriptReversal {
func: id.to_string(),
},
Range::from_located(expr),
));
}

View File

@ -1396,290 +1396,6 @@ impl Violation for BlindExcept {
}
}
// flake8-comprehensions
define_violation!(
pub struct UnnecessaryGeneratorList;
);
impl AlwaysAutofixableViolation for UnnecessaryGeneratorList {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary generator (rewrite as a `list` comprehension)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `list` comprehension".to_string()
}
}
define_violation!(
pub struct UnnecessaryGeneratorSet;
);
impl AlwaysAutofixableViolation for UnnecessaryGeneratorSet {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary generator (rewrite as a `set` comprehension)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `set` comprehension".to_string()
}
}
define_violation!(
pub struct UnnecessaryGeneratorDict;
);
impl AlwaysAutofixableViolation for UnnecessaryGeneratorDict {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary generator (rewrite as a `dict` comprehension)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `dict` comprehension".to_string()
}
}
define_violation!(
pub struct UnnecessaryListComprehensionSet;
);
impl AlwaysAutofixableViolation for UnnecessaryListComprehensionSet {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary `list` comprehension (rewrite as a `set` comprehension)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `set` comprehension".to_string()
}
}
define_violation!(
pub struct UnnecessaryListComprehensionDict;
);
impl AlwaysAutofixableViolation for UnnecessaryListComprehensionDict {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary `list` comprehension (rewrite as a `dict` comprehension)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `dict` comprehension".to_string()
}
}
define_violation!(
pub struct UnnecessaryLiteralSet {
pub obj_type: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryLiteralSet {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryLiteralSet { obj_type } = self;
format!("Unnecessary `{obj_type}` literal (rewrite as a `set` literal)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `set` literal".to_string()
}
}
define_violation!(
pub struct UnnecessaryLiteralDict {
pub obj_type: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryLiteralDict {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryLiteralDict { obj_type } = self;
format!("Unnecessary `{obj_type}` literal (rewrite as a `dict` literal)")
}
fn autofix_title(&self) -> String {
"Rewrite as a `dict` literal".to_string()
}
}
define_violation!(
pub struct UnnecessaryCollectionCall {
pub obj_type: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryCollectionCall {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryCollectionCall { obj_type } = self;
format!("Unnecessary `{obj_type}` call (rewrite as a literal)")
}
fn autofix_title(&self) -> String {
"Rewrite as a literal".to_string()
}
}
define_violation!(
pub struct UnnecessaryLiteralWithinTupleCall {
pub literal: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryLiteralWithinTupleCall {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryLiteralWithinTupleCall { literal } = self;
if literal == "list" {
format!(
"Unnecessary `{literal}` literal passed to `tuple()` (rewrite as a `tuple` \
literal)"
)
} else {
format!(
"Unnecessary `{literal}` literal passed to `tuple()` (remove the outer call to \
`tuple()`)"
)
}
}
fn autofix_title(&self) -> String {
let UnnecessaryLiteralWithinTupleCall { literal } = self;
{
if literal == "list" {
"Rewrite as a `tuple` literal".to_string()
} else {
"Remove outer `tuple` call".to_string()
}
}
}
}
define_violation!(
pub struct UnnecessaryLiteralWithinListCall {
pub literal: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryLiteralWithinListCall {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryLiteralWithinListCall { literal } = self;
if literal == "list" {
format!(
"Unnecessary `{literal}` literal passed to `list()` (remove the outer call to \
`list()`)"
)
} else {
format!(
"Unnecessary `{literal}` literal passed to `list()` (rewrite as a `list` literal)"
)
}
}
fn autofix_title(&self) -> String {
let UnnecessaryLiteralWithinListCall { literal } = self;
{
if literal == "list" {
"Remove outer `list` call".to_string()
} else {
"Rewrite as a `list` literal".to_string()
}
}
}
}
define_violation!(
pub struct UnnecessaryListCall;
);
impl AlwaysAutofixableViolation for UnnecessaryListCall {
#[derive_message_formats]
fn message(&self) -> String {
format!("Unnecessary `list` call (remove the outer call to `list()`)")
}
fn autofix_title(&self) -> String {
"Remove outer `list` call".to_string()
}
}
define_violation!(
pub struct UnnecessaryCallAroundSorted {
pub func: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryCallAroundSorted {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryCallAroundSorted { func } = self;
format!("Unnecessary `{func}` call around `sorted()`")
}
fn autofix_title(&self) -> String {
let UnnecessaryCallAroundSorted { func } = self;
format!("Remove unnecessary `{func}` call")
}
}
define_violation!(
pub struct UnnecessaryDoubleCastOrProcess {
pub inner: String,
pub outer: String,
}
);
impl Violation for UnnecessaryDoubleCastOrProcess {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryDoubleCastOrProcess { inner, outer } = self;
format!("Unnecessary `{inner}` call within `{outer}()`")
}
}
define_violation!(
pub struct UnnecessarySubscriptReversal {
pub func: String,
}
);
impl Violation for UnnecessarySubscriptReversal {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessarySubscriptReversal { func } = self;
format!("Unnecessary subscript reversal of iterable within `{func}()`")
}
}
define_violation!(
pub struct UnnecessaryComprehension {
pub obj_type: String,
}
);
impl AlwaysAutofixableViolation for UnnecessaryComprehension {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryComprehension { obj_type } = self;
format!("Unnecessary `{obj_type}` comprehension (rewrite using `{obj_type}()`)")
}
fn autofix_title(&self) -> String {
let UnnecessaryComprehension { obj_type } = self;
format!("Rewrite using `{obj_type}()`")
}
}
define_violation!(
pub struct UnnecessaryMap {
pub obj_type: String,
}
);
impl Violation for UnnecessaryMap {
#[derive_message_formats]
fn message(&self) -> String {
let UnnecessaryMap { obj_type } = self;
if obj_type == "generator" {
format!("Unnecessary `map` usage (rewrite using a generator expression)")
} else {
format!("Unnecessary `map` usage (rewrite using a `{obj_type}` comprehension)")
}
}
}
// flake8-debugger
define_violation!(