Move flake8-simplify violations to rule modules (#2495)

This commit is contained in:
Aarni Koskela 2023-02-02 22:13:16 +02:00 committed by GitHub
parent 335395adec
commit 77716108af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 560 additions and 529 deletions

View file

@ -198,31 +198,31 @@ ruff_macros::define_rule_mapping!(
YTT302 => violations::SysVersionCmpStr10,
YTT303 => violations::SysVersionSlice1Referenced,
// flake8-simplify
SIM115 => violations::OpenFileWithContextHandler,
SIM101 => violations::DuplicateIsinstanceCall,
SIM102 => violations::NestedIfStatements,
SIM103 => violations::ReturnBoolConditionDirectly,
SIM105 => violations::UseContextlibSuppress,
SIM107 => violations::ReturnInTryExceptFinally,
SIM108 => violations::UseTernaryOperator,
SIM109 => violations::CompareWithTuple,
SIM110 => violations::ConvertLoopToAny,
SIM111 => violations::ConvertLoopToAll,
SIM112 => violations::UseCapitalEnvironmentVariables,
SIM117 => violations::MultipleWithStatements,
SIM118 => violations::KeyInDict,
SIM201 => violations::NegateEqualOp,
SIM202 => violations::NegateNotEqualOp,
SIM208 => violations::DoubleNegation,
SIM210 => violations::IfExprWithTrueFalse,
SIM211 => violations::IfExprWithFalseTrue,
SIM212 => violations::IfExprWithTwistedArms,
SIM220 => violations::AAndNotA,
SIM221 => violations::AOrNotA,
SIM222 => violations::OrTrue,
SIM223 => violations::AndFalse,
SIM300 => violations::YodaConditions,
SIM401 => violations::DictGetWithDefault,
SIM115 => rules::flake8_simplify::rules::OpenFileWithContextHandler,
SIM101 => rules::flake8_simplify::rules::DuplicateIsinstanceCall,
SIM102 => rules::flake8_simplify::rules::NestedIfStatements,
SIM103 => rules::flake8_simplify::rules::ReturnBoolConditionDirectly,
SIM105 => rules::flake8_simplify::rules::UseContextlibSuppress,
SIM107 => rules::flake8_simplify::rules::ReturnInTryExceptFinally,
SIM108 => rules::flake8_simplify::rules::UseTernaryOperator,
SIM109 => rules::flake8_simplify::rules::CompareWithTuple,
SIM110 => rules::flake8_simplify::rules::ConvertLoopToAny,
SIM111 => rules::flake8_simplify::rules::ConvertLoopToAll,
SIM112 => rules::flake8_simplify::rules::UseCapitalEnvironmentVariables,
SIM117 => rules::flake8_simplify::rules::MultipleWithStatements,
SIM118 => rules::flake8_simplify::rules::KeyInDict,
SIM201 => rules::flake8_simplify::rules::NegateEqualOp,
SIM202 => rules::flake8_simplify::rules::NegateNotEqualOp,
SIM208 => rules::flake8_simplify::rules::DoubleNegation,
SIM210 => rules::flake8_simplify::rules::IfExprWithTrueFalse,
SIM211 => rules::flake8_simplify::rules::IfExprWithFalseTrue,
SIM212 => rules::flake8_simplify::rules::IfExprWithTwistedArms,
SIM220 => rules::flake8_simplify::rules::AAndNotA,
SIM221 => rules::flake8_simplify::rules::AOrNotA,
SIM222 => rules::flake8_simplify::rules::OrTrue,
SIM223 => rules::flake8_simplify::rules::AndFalse,
SIM300 => rules::flake8_simplify::rules::YodaConditions,
SIM401 => rules::flake8_simplify::rules::DictGetWithDefault,
// pyupgrade
UP001 => rules::pyupgrade::rules::UselessMetaclassType,
UP003 => rules::pyupgrade::rules::TypeOfPrimitive,

View file

@ -1,3 +1,7 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use std::collections::BTreeMap;
use std::iter;
@ -9,7 +13,104 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct DuplicateIsinstanceCall {
pub name: String,
}
);
impl AlwaysAutofixableViolation for DuplicateIsinstanceCall {
#[derive_message_formats]
fn message(&self) -> String {
let DuplicateIsinstanceCall { name } = self;
format!("Multiple `isinstance` calls for `{name}`, merge into a single call")
}
fn autofix_title(&self) -> String {
let DuplicateIsinstanceCall { name } = self;
format!("Merge `isinstance` calls for `{name}`")
}
}
define_violation!(
pub struct CompareWithTuple {
pub replacement: String,
}
);
impl AlwaysAutofixableViolation for CompareWithTuple {
#[derive_message_formats]
fn message(&self) -> String {
let CompareWithTuple { replacement } = self;
format!("Use `{replacement}` instead of multiple equality comparisons")
}
fn autofix_title(&self) -> String {
let CompareWithTuple { replacement, .. } = self;
format!("Replace with `{replacement}`")
}
}
define_violation!(
pub struct AAndNotA {
pub name: String,
}
);
impl AlwaysAutofixableViolation for AAndNotA {
#[derive_message_formats]
fn message(&self) -> String {
let AAndNotA { name } = self;
format!("Use `False` instead of `{name} and not {name}`")
}
fn autofix_title(&self) -> String {
"Replace with `False`".to_string()
}
}
define_violation!(
pub struct AOrNotA {
pub name: String,
}
);
impl AlwaysAutofixableViolation for AOrNotA {
#[derive_message_formats]
fn message(&self) -> String {
let AOrNotA { name } = self;
format!("Use `True` instead of `{name} or not {name}`")
}
fn autofix_title(&self) -> String {
"Replace with `True`".to_string()
}
}
define_violation!(
pub struct OrTrue;
);
impl AlwaysAutofixableViolation for OrTrue {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use `True` instead of `... or True`")
}
fn autofix_title(&self) -> String {
"Replace with `True`".to_string()
}
}
define_violation!(
pub struct AndFalse;
);
impl AlwaysAutofixableViolation for AndFalse {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use `False` instead of `... and False`")
}
fn autofix_title(&self) -> String {
"Replace with `False`".to_string()
}
}
/// Return `true` if two `Expr` instances are equivalent names.
fn is_same_expr<'a>(a: &'a Expr, b: &'a Expr) -> Option<&'a str> {
@ -65,7 +166,7 @@ pub fn duplicate_isinstance_call(checker: &mut Checker, expr: &Expr) {
for (arg_name, indices) in duplicates {
if indices.len() > 1 {
let mut diagnostic = Diagnostic::new(
violations::DuplicateIsinstanceCall {
DuplicateIsinstanceCall {
name: arg_name.to_string(),
},
Range::from_located(expr),
@ -211,7 +312,7 @@ pub fn compare_with_tuple(checker: &mut Checker, expr: &Expr) {
})],
});
let mut diagnostic = Diagnostic::new(
violations::CompareWithTuple {
CompareWithTuple {
replacement: unparse_expr(&in_expr, checker.stylist),
},
Range::from_located(expr),
@ -278,7 +379,7 @@ pub fn a_and_not_a(checker: &mut Checker, expr: &Expr) {
for non_negate_expr in &non_negated_expr {
if let Some(id) = is_same_expr(negate_expr, non_negate_expr) {
let mut diagnostic = Diagnostic::new(
violations::AAndNotA {
AAndNotA {
name: id.to_string(),
},
Range::from_located(expr),
@ -332,7 +433,7 @@ pub fn a_or_not_a(checker: &mut Checker, expr: &Expr) {
for non_negate_expr in &non_negated_expr {
if let Some(id) = is_same_expr(negate_expr, non_negate_expr) {
let mut diagnostic = Diagnostic::new(
violations::AOrNotA {
AOrNotA {
name: id.to_string(),
},
Range::from_located(expr),
@ -364,7 +465,7 @@ pub fn or_true(checker: &mut Checker, expr: &Expr) {
..
} = &value.node
{
let mut diagnostic = Diagnostic::new(violations::OrTrue, Range::from_located(value));
let mut diagnostic = Diagnostic::new(OrTrue, Range::from_located(value));
if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement(
"True".to_string(),
@ -391,7 +492,7 @@ pub fn and_false(checker: &mut Checker, expr: &Expr) {
..
} = &value.node
{
let mut diagnostic = Diagnostic::new(violations::AndFalse, Range::from_located(value));
let mut diagnostic = Diagnostic::new(AndFalse, Range::from_located(value));
if checker.patch(diagnostic.kind.rule()) {
diagnostic.amend(Fix::replacement(
"False".to_string(),

View file

@ -1,3 +1,6 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Constant, Expr, ExprKind};
use crate::ast::helpers::{create_expr, unparse_expr};
@ -5,7 +8,25 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct UseCapitalEnvironmentVariables {
pub expected: String,
pub original: String,
}
);
impl AlwaysAutofixableViolation for UseCapitalEnvironmentVariables {
#[derive_message_formats]
fn message(&self) -> String {
let UseCapitalEnvironmentVariables { expected, original } = self;
format!("Use capitalized environment variable `{expected}` instead of `{original}`")
}
fn autofix_title(&self) -> String {
let UseCapitalEnvironmentVariables { expected, original } = self;
format!("Replace `{original}` with `{expected}`")
}
}
/// SIM112
pub fn use_capital_environment_variables(checker: &mut Checker, expr: &Expr) {
@ -37,7 +58,7 @@ pub fn use_capital_environment_variables(checker: &mut Checker, expr: &Expr) {
}
let mut diagnostic = Diagnostic::new(
violations::UseCapitalEnvironmentVariables {
UseCapitalEnvironmentVariables {
expected: capital_env_var.clone(),
original: env_var.clone(),
},
@ -79,7 +100,7 @@ fn check_os_environ_subscript(checker: &mut Checker, expr: &Expr) {
}
let mut diagnostic = Diagnostic::new(
violations::UseCapitalEnvironmentVariables {
UseCapitalEnvironmentVariables {
expected: capital_env_var.clone(),
original: env_var.clone(),
},

View file

@ -1,3 +1,6 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{
Comprehension, Constant, Expr, ExprContext, ExprKind, Location, Stmt, StmtKind, Unaryop,
};
@ -8,7 +11,42 @@ use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::{Diagnostic, Rule};
use crate::source_code::Stylist;
use crate::violations;
define_violation!(
pub struct ConvertLoopToAny {
pub any: String,
}
);
impl AlwaysAutofixableViolation for ConvertLoopToAny {
#[derive_message_formats]
fn message(&self) -> String {
let ConvertLoopToAny { any } = self;
format!("Use `{any}` instead of `for` loop")
}
fn autofix_title(&self) -> String {
let ConvertLoopToAny { any } = self;
format!("Replace with `{any}`")
}
}
define_violation!(
pub struct ConvertLoopToAll {
pub all: String,
}
);
impl AlwaysAutofixableViolation for ConvertLoopToAll {
#[derive_message_formats]
fn message(&self) -> String {
let ConvertLoopToAll { all } = self;
format!("Use `{all}` instead of `for` loop")
}
fn autofix_title(&self) -> String {
let ConvertLoopToAll { all } = self;
format!("Replace with `{all}`")
}
}
struct Loop<'a> {
return_value: bool,
@ -197,7 +235,7 @@ pub fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt, sibling:
}
let mut diagnostic = Diagnostic::new(
violations::ConvertLoopToAny {
ConvertLoopToAny {
any: contents.clone(),
},
Range::from_located(stmt),
@ -244,7 +282,7 @@ pub fn convert_for_loop_to_any_all(checker: &mut Checker, stmt: &Stmt, sibling:
}
let mut diagnostic = Diagnostic::new(
violations::ConvertLoopToAll {
ConvertLoopToAll {
all: contents.clone(),
},
Range::from_located(stmt),

View file

@ -1,4 +1,7 @@
use crate::violation::{AlwaysAutofixableViolation, Availability, Violation};
use crate::{define_violation, AutofixKind};
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Cmpop, Constant, Expr, ExprContext, ExprKind, Stmt, StmtKind};
use crate::ast::comparable::ComparableExpr;
@ -11,7 +14,75 @@ use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::rules::flake8_simplify::rules::fix_if;
use crate::violations;
define_violation!(
pub struct NestedIfStatements;
);
impl AlwaysAutofixableViolation for NestedIfStatements {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use a single `if` statement instead of nested `if` statements")
}
fn autofix_title(&self) -> String {
"Combine `if` statements using `and`".to_string()
}
}
define_violation!(
pub struct ReturnBoolConditionDirectly {
pub cond: String,
}
);
impl AlwaysAutofixableViolation for ReturnBoolConditionDirectly {
#[derive_message_formats]
fn message(&self) -> String {
let ReturnBoolConditionDirectly { cond } = self;
format!("Return the condition `{cond}` directly")
}
fn autofix_title(&self) -> String {
let ReturnBoolConditionDirectly { cond } = self;
format!("Replace with `return {cond}`")
}
}
define_violation!(
pub struct UseTernaryOperator {
pub contents: String,
}
);
impl Violation for UseTernaryOperator {
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Sometimes));
#[derive_message_formats]
fn message(&self) -> String {
let UseTernaryOperator { contents } = self;
format!("Use ternary operator `{contents}` instead of if-else-block")
}
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
Some(|UseTernaryOperator { contents }| format!("Replace if-else-block with `{contents}`"))
}
}
define_violation!(
pub struct DictGetWithDefault {
pub contents: String,
}
);
impl AlwaysAutofixableViolation for DictGetWithDefault {
#[derive_message_formats]
fn message(&self) -> String {
let DictGetWithDefault { contents } = self;
format!("Use `{contents}` instead of an `if` block")
}
fn autofix_title(&self) -> String {
let DictGetWithDefault { contents } = self;
format!("Replace with `{contents}`")
}
}
fn is_main_check(expr: &Expr) -> bool {
if let ExprKind::Compare {
@ -97,7 +168,7 @@ pub fn nested_if_statements(
checker.locator,
);
let mut diagnostic = Diagnostic::new(
violations::NestedIfStatements,
NestedIfStatements,
colon.map_or_else(
|| Range::from_located(stmt),
|colon| Range::new(stmt.location, colon.end_location),
@ -176,7 +247,7 @@ pub fn return_bool_condition_directly(checker: &mut Checker, stmt: &Stmt) {
let condition = unparse_expr(test, checker.stylist);
let mut diagnostic = Diagnostic::new(
violations::ReturnBoolConditionDirectly { cond: condition },
ReturnBoolConditionDirectly { cond: condition },
Range::from_located(stmt),
);
if checker.patch(diagnostic.kind.rule())
@ -308,7 +379,7 @@ pub fn use_ternary_operator(checker: &mut Checker, stmt: &Stmt, parent: Option<&
}
let mut diagnostic = Diagnostic::new(
violations::UseTernaryOperator {
UseTernaryOperator {
contents: contents.clone(),
},
Range::from_located(stmt),
@ -439,7 +510,7 @@ pub fn use_dict_get_with_default(
}
let mut diagnostic = Diagnostic::new(
violations::DictGetWithDefault {
DictGetWithDefault {
contents: contents.clone(),
},
Range::from_located(stmt),

View file

@ -1,3 +1,6 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Constant, Expr, ExprContext, ExprKind, Unaryop};
use crate::ast::helpers::{create_expr, unparse_expr};
@ -5,7 +8,70 @@ use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct IfExprWithTrueFalse {
pub expr: String,
}
);
impl AlwaysAutofixableViolation for IfExprWithTrueFalse {
#[derive_message_formats]
fn message(&self) -> String {
let IfExprWithTrueFalse { expr } = self;
format!("Use `bool({expr})` instead of `True if {expr} else False`")
}
fn autofix_title(&self) -> String {
let IfExprWithTrueFalse { expr } = self;
format!("Replace with `not {expr}")
}
}
define_violation!(
pub struct IfExprWithFalseTrue {
pub expr: String,
}
);
impl AlwaysAutofixableViolation for IfExprWithFalseTrue {
#[derive_message_formats]
fn message(&self) -> String {
let IfExprWithFalseTrue { expr } = self;
format!("Use `not {expr}` instead of `False if {expr} else True`")
}
fn autofix_title(&self) -> String {
let IfExprWithFalseTrue { expr } = self;
format!("Replace with `bool({expr})")
}
}
define_violation!(
pub struct IfExprWithTwistedArms {
pub expr_body: String,
pub expr_else: String,
}
);
impl AlwaysAutofixableViolation for IfExprWithTwistedArms {
#[derive_message_formats]
fn message(&self) -> String {
let IfExprWithTwistedArms {
expr_body,
expr_else,
} = self;
format!(
"Use `{expr_else} if {expr_else} else {expr_body}` instead of `{expr_body} if not \
{expr_else} else {expr_else}`"
)
}
fn autofix_title(&self) -> String {
let IfExprWithTwistedArms {
expr_body,
expr_else,
} = self;
format!("Replace with `{expr_else} if {expr_else} else {expr_body}`")
}
}
/// SIM210
pub fn explicit_true_false_in_ifexpr(
@ -29,7 +95,7 @@ pub fn explicit_true_false_in_ifexpr(
}
let mut diagnostic = Diagnostic::new(
violations::IfExprWithTrueFalse {
IfExprWithTrueFalse {
expr: unparse_expr(test, checker.stylist),
},
Range::from_located(expr),
@ -84,7 +150,7 @@ pub fn explicit_false_true_in_ifexpr(
}
let mut diagnostic = Diagnostic::new(
violations::IfExprWithFalseTrue {
IfExprWithFalseTrue {
expr: unparse_expr(test, checker.stylist),
},
Range::from_located(expr),
@ -132,7 +198,7 @@ pub fn twisted_arms_in_ifexpr(
}
let mut diagnostic = Diagnostic::new(
violations::IfExprWithTwistedArms {
IfExprWithTwistedArms {
expr_body: unparse_expr(body, checker.stylist),
expr_else: unparse_expr(orelse, checker.stylist),
},

View file

@ -1,3 +1,6 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Cmpop, Expr, ExprKind, Stmt, StmtKind, Unaryop};
use crate::ast::helpers::{create_expr, unparse_expr};
@ -5,7 +8,60 @@ use crate::ast::types::{Range, ScopeKind};
use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct NegateEqualOp {
pub left: String,
pub right: String,
}
);
impl AlwaysAutofixableViolation for NegateEqualOp {
#[derive_message_formats]
fn message(&self) -> String {
let NegateEqualOp { left, right } = self;
format!("Use `{left} != {right}` instead of `not {left} == {right}`")
}
fn autofix_title(&self) -> String {
"Replace with `!=` operator".to_string()
}
}
define_violation!(
pub struct NegateNotEqualOp {
pub left: String,
pub right: String,
}
);
impl AlwaysAutofixableViolation for NegateNotEqualOp {
#[derive_message_formats]
fn message(&self) -> String {
let NegateNotEqualOp { left, right } = self;
format!("Use `{left} == {right}` instead of `not {left} != {right}`")
}
fn autofix_title(&self) -> String {
"Replace with `==` operator".to_string()
}
}
define_violation!(
pub struct DoubleNegation {
pub expr: String,
}
);
impl AlwaysAutofixableViolation for DoubleNegation {
#[derive_message_formats]
fn message(&self) -> String {
let DoubleNegation { expr } = self;
format!("Use `{expr}` instead of `not (not {expr})`")
}
fn autofix_title(&self) -> String {
let DoubleNegation { expr } = self;
format!("Replace with `{expr}`")
}
}
const DUNDER_METHODS: &[&str] = &["__eq__", "__ne__", "__lt__", "__le__", "__gt__", "__ge__"];
@ -45,7 +101,7 @@ pub fn negation_with_equal_op(checker: &mut Checker, expr: &Expr, op: &Unaryop,
}
let mut diagnostic = Diagnostic::new(
violations::NegateEqualOp {
NegateEqualOp {
left: unparse_expr(left, checker.stylist),
right: unparse_expr(&comparators[0], checker.stylist),
},
@ -96,7 +152,7 @@ pub fn negation_with_not_equal_op(
}
let mut diagnostic = Diagnostic::new(
violations::NegateNotEqualOp {
NegateNotEqualOp {
left: unparse_expr(left, checker.stylist),
right: unparse_expr(&comparators[0], checker.stylist),
},
@ -132,7 +188,7 @@ pub fn double_negation(checker: &mut Checker, expr: &Expr, op: &Unaryop, operand
}
let mut diagnostic = Diagnostic::new(
violations::DoubleNegation {
DoubleNegation {
expr: operand.to_string(),
},
Range::from_located(expr),

View file

@ -1,4 +1,7 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use log::error;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Located, Stmt, StmtKind, Withitem};
use super::fix_with;
@ -6,7 +9,23 @@ use crate::ast::helpers::{first_colon_range, has_comments_in};
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct MultipleWithStatements;
);
impl AlwaysAutofixableViolation for MultipleWithStatements {
#[derive_message_formats]
fn message(&self) -> String {
format!(
"Use a single `with` statement with multiple contexts instead of nested `with` \
statements"
)
}
fn autofix_title(&self) -> String {
"Combine `with` statements".to_string()
}
}
fn find_last_with(body: &[Stmt]) -> Option<(&Vec<Withitem>, &Vec<Stmt>)> {
let [Located { node: StmtKind::With { items, body, .. }, ..}] = body else { return None };
@ -43,7 +62,7 @@ pub fn multiple_with_statements(
checker.locator,
);
let mut diagnostic = Diagnostic::new(
violations::MultipleWithStatements,
MultipleWithStatements,
colon.map_or_else(
|| Range::from_located(with_stmt),
|colon| Range::new(with_stmt.location, colon.end_location),

View file

@ -1,10 +1,31 @@
use crate::define_violation;
use crate::violation::AlwaysAutofixableViolation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Cmpop, Expr, ExprKind};
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::fix::Fix;
use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct KeyInDict {
pub key: String,
pub dict: String,
}
);
impl AlwaysAutofixableViolation for KeyInDict {
#[derive_message_formats]
fn message(&self) -> String {
let KeyInDict { key, dict } = self;
format!("Use `{key} in {dict}` instead of `{key} in {dict}.keys()`")
}
fn autofix_title(&self) -> String {
let KeyInDict { key, dict } = self;
format!("Convert to `{key} in {dict}`")
}
}
/// SIM118
fn key_in_dict(checker: &mut Checker, left: &Expr, right: &Expr, range: Range) {
@ -35,7 +56,7 @@ fn key_in_dict(checker: &mut Checker, left: &Expr, right: &Expr, range: Range) {
.slice_source_code_range(&Range::from_located(value));
let mut diagnostic = Diagnostic::new(
violations::KeyInDict {
KeyInDict {
key: left_content.to_string(),
dict: value_content.to_string(),
},

View file

@ -1,22 +1,30 @@
pub use ast_bool_op::{
a_and_not_a, a_or_not_a, and_false, compare_with_tuple, duplicate_isinstance_call, or_true,
AAndNotA, AOrNotA, AndFalse, CompareWithTuple, DuplicateIsinstanceCall, OrTrue,
};
pub use ast_expr::use_capital_environment_variables;
pub use ast_for::convert_for_loop_to_any_all;
pub use ast_expr::{use_capital_environment_variables, UseCapitalEnvironmentVariables};
pub use ast_for::{convert_for_loop_to_any_all, ConvertLoopToAll, ConvertLoopToAny};
pub use ast_if::{
nested_if_statements, return_bool_condition_directly, use_dict_get_with_default,
use_ternary_operator,
use_ternary_operator, DictGetWithDefault, NestedIfStatements, ReturnBoolConditionDirectly,
UseTernaryOperator,
};
pub use ast_ifexp::{
explicit_false_true_in_ifexpr, explicit_true_false_in_ifexpr, twisted_arms_in_ifexpr,
IfExprWithFalseTrue, IfExprWithTrueFalse, IfExprWithTwistedArms,
};
pub use ast_unary_op::{double_negation, negation_with_equal_op, negation_with_not_equal_op};
pub use ast_with::multiple_with_statements;
pub use key_in_dict::{key_in_dict_compare, key_in_dict_for};
pub use open_file_with_context_handler::open_file_with_context_handler;
pub use return_in_try_except_finally::return_in_try_except_finally;
pub use use_contextlib_suppress::use_contextlib_suppress;
pub use yoda_conditions::yoda_conditions;
pub use ast_unary_op::{
double_negation, negation_with_equal_op, negation_with_not_equal_op, DoubleNegation,
NegateEqualOp, NegateNotEqualOp,
};
pub use ast_with::{multiple_with_statements, MultipleWithStatements};
pub use key_in_dict::{key_in_dict_compare, key_in_dict_for, KeyInDict};
pub use open_file_with_context_handler::{
open_file_with_context_handler, OpenFileWithContextHandler,
};
pub use return_in_try_except_finally::{return_in_try_except_finally, ReturnInTryExceptFinally};
pub use use_contextlib_suppress::{use_contextlib_suppress, UseContextlibSuppress};
pub use yoda_conditions::{yoda_conditions, YodaConditions};
mod ast_bool_op;
mod ast_expr;

View file

@ -1,10 +1,22 @@
use crate::define_violation;
use crate::violation::Violation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Expr, ExprKind};
use rustpython_parser::ast::StmtKind;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct OpenFileWithContextHandler;
);
impl Violation for OpenFileWithContextHandler {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use context handler for opening files")
}
}
/// Return `true` if the current expression is nested in an `await
/// exit_stack.enter_async_context` call.
@ -94,7 +106,7 @@ pub fn open_file_with_context_handler(checker: &mut Checker, func: &Expr) {
}
checker.diagnostics.push(Diagnostic::new(
violations::OpenFileWithContextHandler,
OpenFileWithContextHandler,
Range::from_located(func),
));
}

View file

@ -1,9 +1,21 @@
use crate::define_violation;
use crate::violation::Violation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Stmt, StmtKind};
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct ReturnInTryExceptFinally;
);
impl Violation for ReturnInTryExceptFinally {
#[derive_message_formats]
fn message(&self) -> String {
format!("Don't use `return` in `try`/`except` and `finally`")
}
}
fn find_return(stmts: &[Stmt]) -> Option<&Stmt> {
stmts
@ -27,7 +39,7 @@ pub fn return_in_try_except_finally(
if let Some(finally_return) = find_return(finalbody) {
if try_has_return || except_has_return {
checker.diagnostics.push(Diagnostic::new(
violations::ReturnInTryExceptFinally,
ReturnInTryExceptFinally,
Range::from_located(finally_return),
));
}

View file

@ -1,11 +1,25 @@
use crate::define_violation;
use crate::violation::Violation;
use ruff_macros::derive_message_formats;
use rustpython_ast::{Excepthandler, ExcepthandlerKind, Located, Stmt, StmtKind};
use crate::ast::helpers;
use crate::ast::types::Range;
use crate::checkers::ast::Checker;
use crate::registry::Diagnostic;
use crate::violations;
define_violation!(
pub struct UseContextlibSuppress {
pub exception: String,
}
);
impl Violation for UseContextlibSuppress {
#[derive_message_formats]
fn message(&self) -> String {
let UseContextlibSuppress { exception } = self;
format!("Use `contextlib.suppress({exception})` instead of try-except-pass")
}
}
/// SIM105
pub fn use_contextlib_suppress(
checker: &mut Checker,
@ -49,7 +63,7 @@ pub fn use_contextlib_suppress(
handler_names.join(", ")
};
checker.diagnostics.push(Diagnostic::new(
violations::UseContextlibSuppress { exception },
UseContextlibSuppress { exception },
Range::from_located(stmt),
));
}

View file

@ -5,11 +5,45 @@ use crate::fix::Fix;
use crate::python::string::{self};
use crate::registry::Diagnostic;
use crate::source_code::{Locator, Stylist};
use crate::violations;
use crate::violation::{Availability, Violation};
use crate::{define_violation, AutofixKind};
use anyhow::Result;
use libcst_native::{Codegen, CodegenState, CompOp};
use ruff_macros::derive_message_formats;
use rustpython_ast::{Cmpop, Expr, ExprKind};
define_violation!(
pub struct YodaConditions {
pub suggestion: Option<String>,
}
);
impl Violation for YodaConditions {
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
#[derive_message_formats]
fn message(&self) -> String {
let YodaConditions { suggestion } = self;
if let Some(suggestion) = suggestion {
format!("Yoda conditions are discouraged, use `{suggestion}` instead")
} else {
format!("Yoda conditions are discouraged")
}
}
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
let YodaConditions { suggestion, .. } = self;
if suggestion.is_some() {
Some(|YodaConditions { suggestion }| {
let suggestion = suggestion.as_ref().unwrap();
format!("Replace Yoda condition with `{suggestion}`")
})
} else {
None
}
}
}
/// Return `true` if an [`Expr`] is a constant or a constant-like name.
fn is_constant_like(expr: &Expr) -> bool {
match &expr.node {
@ -119,7 +153,7 @@ pub fn yoda_conditions(
if let Ok(suggestion) = reverse_comparison(expr, checker.locator, checker.stylist) {
let mut diagnostic = Diagnostic::new(
violations::YodaConditions {
YodaConditions {
suggestion: Some(suggestion.to_string()),
},
Range::from_located(expr),
@ -134,7 +168,7 @@ pub fn yoda_conditions(
checker.diagnostics.push(diagnostic);
} else {
checker.diagnostics.push(Diagnostic::new(
violations::YodaConditions { suggestion: None },
YodaConditions { suggestion: None },
Range::from_located(expr),
));
}

View file

@ -487,461 +487,6 @@ impl Violation for SysVersionSlice1Referenced {
}
}
// flake8-simplify
define_violation!(
pub struct OpenFileWithContextHandler;
);
impl Violation for OpenFileWithContextHandler {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use context handler for opening files")
}
}
define_violation!(
pub struct UseCapitalEnvironmentVariables {
pub expected: String,
pub original: String,
}
);
impl AlwaysAutofixableViolation for UseCapitalEnvironmentVariables {
#[derive_message_formats]
fn message(&self) -> String {
let UseCapitalEnvironmentVariables { expected, original } = self;
format!("Use capitalized environment variable `{expected}` instead of `{original}`")
}
fn autofix_title(&self) -> String {
let UseCapitalEnvironmentVariables { expected, original } = self;
format!("Replace `{original}` with `{expected}`")
}
}
define_violation!(
pub struct DuplicateIsinstanceCall {
pub name: String,
}
);
impl AlwaysAutofixableViolation for DuplicateIsinstanceCall {
#[derive_message_formats]
fn message(&self) -> String {
let DuplicateIsinstanceCall { name } = self;
format!("Multiple `isinstance` calls for `{name}`, merge into a single call")
}
fn autofix_title(&self) -> String {
let DuplicateIsinstanceCall { name } = self;
format!("Merge `isinstance` calls for `{name}`")
}
}
define_violation!(
pub struct NestedIfStatements;
);
impl AlwaysAutofixableViolation for NestedIfStatements {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use a single `if` statement instead of nested `if` statements")
}
fn autofix_title(&self) -> String {
"Combine `if` statements using `and`".to_string()
}
}
define_violation!(
pub struct ReturnBoolConditionDirectly {
pub cond: String,
}
);
impl AlwaysAutofixableViolation for ReturnBoolConditionDirectly {
#[derive_message_formats]
fn message(&self) -> String {
let ReturnBoolConditionDirectly { cond } = self;
format!("Return the condition `{cond}` directly")
}
fn autofix_title(&self) -> String {
let ReturnBoolConditionDirectly { cond } = self;
format!("Replace with `return {cond}`")
}
}
define_violation!(
pub struct UseContextlibSuppress {
pub exception: String,
}
);
impl Violation for UseContextlibSuppress {
#[derive_message_formats]
fn message(&self) -> String {
let UseContextlibSuppress { exception } = self;
format!("Use `contextlib.suppress({exception})` instead of try-except-pass")
}
}
define_violation!(
pub struct ReturnInTryExceptFinally;
);
impl Violation for ReturnInTryExceptFinally {
#[derive_message_formats]
fn message(&self) -> String {
format!("Don't use `return` in `try`/`except` and `finally`")
}
}
define_violation!(
pub struct UseTernaryOperator {
pub contents: String,
}
);
impl Violation for UseTernaryOperator {
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Sometimes));
#[derive_message_formats]
fn message(&self) -> String {
let UseTernaryOperator { contents } = self;
format!("Use ternary operator `{contents}` instead of if-else-block")
}
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
Some(|UseTernaryOperator { contents }| format!("Replace if-else-block with `{contents}`"))
}
}
define_violation!(
pub struct CompareWithTuple {
pub replacement: String,
}
);
impl AlwaysAutofixableViolation for CompareWithTuple {
#[derive_message_formats]
fn message(&self) -> String {
let CompareWithTuple { replacement } = self;
format!("Use `{replacement}` instead of multiple equality comparisons")
}
fn autofix_title(&self) -> String {
let CompareWithTuple { replacement, .. } = self;
format!("Replace with `{replacement}`")
}
}
define_violation!(
pub struct ConvertLoopToAny {
pub any: String,
}
);
impl AlwaysAutofixableViolation for ConvertLoopToAny {
#[derive_message_formats]
fn message(&self) -> String {
let ConvertLoopToAny { any } = self;
format!("Use `{any}` instead of `for` loop")
}
fn autofix_title(&self) -> String {
let ConvertLoopToAny { any } = self;
format!("Replace with `{any}`")
}
}
define_violation!(
pub struct ConvertLoopToAll {
pub all: String,
}
);
impl AlwaysAutofixableViolation for ConvertLoopToAll {
#[derive_message_formats]
fn message(&self) -> String {
let ConvertLoopToAll { all } = self;
format!("Use `{all}` instead of `for` loop")
}
fn autofix_title(&self) -> String {
let ConvertLoopToAll { all } = self;
format!("Replace with `{all}`")
}
}
define_violation!(
pub struct MultipleWithStatements;
);
impl AlwaysAutofixableViolation for MultipleWithStatements {
#[derive_message_formats]
fn message(&self) -> String {
format!(
"Use a single `with` statement with multiple contexts instead of nested `with` \
statements"
)
}
fn autofix_title(&self) -> String {
"Combine `with` statements".to_string()
}
}
define_violation!(
pub struct KeyInDict {
pub key: String,
pub dict: String,
}
);
impl AlwaysAutofixableViolation for KeyInDict {
#[derive_message_formats]
fn message(&self) -> String {
let KeyInDict { key, dict } = self;
format!("Use `{key} in {dict}` instead of `{key} in {dict}.keys()`")
}
fn autofix_title(&self) -> String {
let KeyInDict { key, dict } = self;
format!("Convert to `{key} in {dict}`")
}
}
define_violation!(
pub struct NegateEqualOp {
pub left: String,
pub right: String,
}
);
impl AlwaysAutofixableViolation for NegateEqualOp {
#[derive_message_formats]
fn message(&self) -> String {
let NegateEqualOp { left, right } = self;
format!("Use `{left} != {right}` instead of `not {left} == {right}`")
}
fn autofix_title(&self) -> String {
"Replace with `!=` operator".to_string()
}
}
define_violation!(
pub struct NegateNotEqualOp {
pub left: String,
pub right: String,
}
);
impl AlwaysAutofixableViolation for NegateNotEqualOp {
#[derive_message_formats]
fn message(&self) -> String {
let NegateNotEqualOp { left, right } = self;
format!("Use `{left} == {right}` instead of `not {left} != {right}`")
}
fn autofix_title(&self) -> String {
"Replace with `==` operator".to_string()
}
}
define_violation!(
pub struct DoubleNegation {
pub expr: String,
}
);
impl AlwaysAutofixableViolation for DoubleNegation {
#[derive_message_formats]
fn message(&self) -> String {
let DoubleNegation { expr } = self;
format!("Use `{expr}` instead of `not (not {expr})`")
}
fn autofix_title(&self) -> String {
let DoubleNegation { expr } = self;
format!("Replace with `{expr}`")
}
}
define_violation!(
pub struct AAndNotA {
pub name: String,
}
);
impl AlwaysAutofixableViolation for AAndNotA {
#[derive_message_formats]
fn message(&self) -> String {
let AAndNotA { name } = self;
format!("Use `False` instead of `{name} and not {name}`")
}
fn autofix_title(&self) -> String {
"Replace with `False`".to_string()
}
}
define_violation!(
pub struct AOrNotA {
pub name: String,
}
);
impl AlwaysAutofixableViolation for AOrNotA {
#[derive_message_formats]
fn message(&self) -> String {
let AOrNotA { name } = self;
format!("Use `True` instead of `{name} or not {name}`")
}
fn autofix_title(&self) -> String {
"Replace with `True`".to_string()
}
}
define_violation!(
pub struct OrTrue;
);
impl AlwaysAutofixableViolation for OrTrue {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use `True` instead of `... or True`")
}
fn autofix_title(&self) -> String {
"Replace with `True`".to_string()
}
}
define_violation!(
pub struct AndFalse;
);
impl AlwaysAutofixableViolation for AndFalse {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use `False` instead of `... and False`")
}
fn autofix_title(&self) -> String {
"Replace with `False`".to_string()
}
}
define_violation!(
pub struct YodaConditions {
pub suggestion: Option<String>,
}
);
impl Violation for YodaConditions {
const AUTOFIX: Option<AutofixKind> = Some(AutofixKind::new(Availability::Always));
#[derive_message_formats]
fn message(&self) -> String {
let YodaConditions { suggestion } = self;
if let Some(suggestion) = suggestion {
format!("Yoda conditions are discouraged, use `{suggestion}` instead")
} else {
format!("Yoda conditions are discouraged")
}
}
fn autofix_title_formatter(&self) -> Option<fn(&Self) -> String> {
let YodaConditions { suggestion, .. } = self;
if suggestion.is_some() {
Some(|YodaConditions { suggestion }| {
let suggestion = suggestion.as_ref().unwrap();
format!("Replace Yoda condition with `{suggestion}`")
})
} else {
None
}
}
}
define_violation!(
pub struct IfExprWithTrueFalse {
pub expr: String,
}
);
impl AlwaysAutofixableViolation for IfExprWithTrueFalse {
#[derive_message_formats]
fn message(&self) -> String {
let IfExprWithTrueFalse { expr } = self;
format!("Use `bool({expr})` instead of `True if {expr} else False`")
}
fn autofix_title(&self) -> String {
let IfExprWithTrueFalse { expr } = self;
format!("Replace with `not {expr}")
}
}
define_violation!(
pub struct IfExprWithFalseTrue {
pub expr: String,
}
);
impl AlwaysAutofixableViolation for IfExprWithFalseTrue {
#[derive_message_formats]
fn message(&self) -> String {
let IfExprWithFalseTrue { expr } = self;
format!("Use `not {expr}` instead of `False if {expr} else True`")
}
fn autofix_title(&self) -> String {
let IfExprWithFalseTrue { expr } = self;
format!("Replace with `bool({expr})")
}
}
define_violation!(
pub struct IfExprWithTwistedArms {
pub expr_body: String,
pub expr_else: String,
}
);
impl AlwaysAutofixableViolation for IfExprWithTwistedArms {
#[derive_message_formats]
fn message(&self) -> String {
let IfExprWithTwistedArms {
expr_body,
expr_else,
} = self;
format!(
"Use `{expr_else} if {expr_else} else {expr_body}` instead of `{expr_body} if not \
{expr_else} else {expr_else}`"
)
}
fn autofix_title(&self) -> String {
let IfExprWithTwistedArms {
expr_body,
expr_else,
} = self;
format!("Replace with `{expr_else} if {expr_else} else {expr_body}`")
}
}
define_violation!(
pub struct DictGetWithDefault {
pub contents: String,
}
);
impl AlwaysAutofixableViolation for DictGetWithDefault {
#[derive_message_formats]
fn message(&self) -> String {
let DictGetWithDefault { contents } = self;
format!("Use `{contents}` instead of an `if` block")
}
fn autofix_title(&self) -> String {
let DictGetWithDefault { contents } = self;
format!("Replace with `{contents}`")
}
}
define_violation!(
pub struct UnpackInsteadOfConcatenatingToCollectionLiteral {
pub expr: String,
}
);
impl Violation for UnpackInsteadOfConcatenatingToCollectionLiteral {
#[derive_message_formats]
fn message(&self) -> String {
let UnpackInsteadOfConcatenatingToCollectionLiteral { expr } = self;
format!("Consider `{expr}` instead of concatenation")
}
}
// pydocstyle
define_violation!(
@ -2355,6 +1900,19 @@ impl Violation for KeywordArgumentBeforeStarArgument {
}
}
define_violation!(
pub struct UnpackInsteadOfConcatenatingToCollectionLiteral {
pub expr: String,
}
);
impl Violation for UnpackInsteadOfConcatenatingToCollectionLiteral {
#[derive_message_formats]
fn message(&self) -> String {
let UnpackInsteadOfConcatenatingToCollectionLiteral { expr } = self;
format!("Consider `{expr}` instead of concatenation")
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct UnusedCodes {
pub unknown: Vec<String>,