Skip to content

Commit

Permalink
Merge rules
Browse files Browse the repository at this point in the history
  • Loading branch information
tibor-reiss committed Feb 17, 2024
1 parent fdd51a4 commit 0137351
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 347 deletions.

This file was deleted.

Expand Up @@ -14,6 +14,15 @@
if value < value2: # [max-instead-of-if]
value = value2

if value > 10: # [min-instead-of-if]
value = 10

if value >= 10: # [min-instead-of-if]
value = 10

if value > value2: # [min-instead-of-if]
value = value2


class A:
def __init__(self):
Expand All @@ -24,6 +33,9 @@ def __init__(self):
if A1.value < 10: # [max-instead-of-if]
A1.value = 10

if A1.value > 10: # [min-instead-of-if]
A1.value = 10


class AA:
def __init__(self, value):
Expand Down Expand Up @@ -51,6 +63,12 @@ def __le__(self, b):
if A2 <= A1: # [max-instead-of-if]
A2 = A1

if A2 > A1: # [min-instead-of-if]
A2 = A1

if A2 >= A1: # [min-instead-of-if]
A2 = A1

# Negative
if value < 10:
value = 2
Expand Down Expand Up @@ -80,3 +98,32 @@ def __le__(self, b):
value = 5
elif value == 3:
value = 2

if value > 10:
value = 2

if value >= 3:
value = 5

if value > 10:
value = 2
value2 = 3

if value > value2:
value = value3

if value > 5:
value = value3

if 2 > value >= 3:
value = 1

if value > 10:
value = 10
else:
value = 3

if value >= 3:
value = 5
elif value == 3:
value = 2
7 changes: 2 additions & 5 deletions crates/ruff_linter/src/checkers/ast/analyze/statement.rs
Expand Up @@ -1130,11 +1130,8 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::TooManyBooleanExpressions) {
pylint::rules::too_many_boolean_expressions(checker, if_);
}
if checker.enabled(Rule::MaxInsteadOfIf) {
pylint::rules::max_instead_of_if(checker, if_);
}
if checker.enabled(Rule::MinInsteadOfIf) {
pylint::rules::min_instead_of_if(checker, if_);
if checker.enabled(Rule::MinMaxInsteadOfIf) {
pylint::rules::min_max_instead_of_if(checker, if_);
}
if checker.source_type.is_stub() {
if checker.any_enabled(&[
Expand Down
3 changes: 1 addition & 2 deletions crates/ruff_linter/src/codes.rs
Expand Up @@ -278,8 +278,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "R1714") => (RuleGroup::Stable, rules::pylint::rules::RepeatedEqualityComparison),
(Pylint, "R1706") => (RuleGroup::Removed, rules::pylint::rules::AndOrTernary),
(Pylint, "R1722") => (RuleGroup::Stable, rules::pylint::rules::SysExitAlias),
(Pylint, "R1730") => (RuleGroup::Preview, rules::pylint::rules::MinInsteadOfIf),
(Pylint, "R1731") => (RuleGroup::Preview, rules::pylint::rules::MaxInsteadOfIf),
(Pylint, "R1730") => (RuleGroup::Preview, rules::pylint::rules::MinMaxInsteadOfIf),
(Pylint, "R1733") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryDictIndexLookup),
(Pylint, "R1736") => (RuleGroup::Preview, rules::pylint::rules::UnnecessaryListIndexLookup),
(Pylint, "R2004") => (RuleGroup::Stable, rules::pylint::rules::MagicValueComparison),
Expand Down
3 changes: 1 addition & 2 deletions crates/ruff_linter/src/rules/pylint/mod.rs
Expand Up @@ -42,8 +42,7 @@ mod tests {
#[test_case(Rule::EqWithoutHash, Path::new("eq_without_hash.py"))]
#[test_case(Rule::EmptyComment, Path::new("empty_comment.py"))]
#[test_case(Rule::ManualFromImport, Path::new("import_aliasing.py"))]
#[test_case(Rule::MaxInsteadOfIf, Path::new("max_instead_of_if.py"))]
#[test_case(Rule::MinInsteadOfIf, Path::new("min_instead_of_if.py"))]
#[test_case(Rule::MinMaxInsteadOfIf, Path::new("min_max_instead_of_if.py"))]
#[test_case(Rule::SingleStringSlots, Path::new("single_string_slots.py"))]
#[test_case(Rule::SysExitAlias, Path::new("sys_exit_alias_0.py"))]
#[test_case(Rule::SysExitAlias, Path::new("sys_exit_alias_1.py"))]
Expand Down
Expand Up @@ -9,11 +9,11 @@ use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker;

/// ## What it does
/// Check for an if node that can be refactored as a max python builtin.
/// Check for an if node that can be refactored as a min/max python builtin.
///
/// ## Why is this bad?
/// An if block where the test and assignment have the same structure can
/// be expressed more concisely by using the python builtin max function.
/// be expressed more concisely by using the python builtin min/max function.
///
/// ## Example
/// ```python
Expand All @@ -28,54 +28,22 @@ use crate::checkers::ast::Checker;
///
/// ## References
/// - [Python documentation: max function](https://docs.python.org/3/library/functions.html#max)
#[violation]
pub struct MaxInsteadOfIf {
contents: String,
}

impl Violation for MaxInsteadOfIf {
#[derive_message_formats]
fn message(&self) -> String {
let MaxInsteadOfIf { contents } = self;
format!("Consider using `{contents}` instead of unnecessary if block")
}
}

/// ## What it does
/// Check for an if node that can be refactored as a min python builtin.
///
/// ## Why is this bad?
/// An if block where the test and assignment have the same structure can
/// be expressed more concisely by using the python builtin min function.
///
/// ## Example
/// ```python
/// if value > 10:
/// value = 10
/// ```
///
/// Use instead:
/// ```python
/// value = min(value, 10)
/// ```
///
/// ## References
/// - [Python documentation: min function](https://docs.python.org/3/library/functions.html#min)
#[violation]
pub struct MinInsteadOfIf {
pub struct MinMaxInsteadOfIf {
contents: String,
}

impl Violation for MinInsteadOfIf {
impl Violation for MinMaxInsteadOfIf {
#[derive_message_formats]
fn message(&self) -> String {
let MinInsteadOfIf { contents } = self;
let MinMaxInsteadOfIf { contents } = self;
format!("Consider using `{contents}` instead of unnecessary if block")
}
}

/// R1730
pub(crate) fn min_instead_of_if(checker: &mut Checker, stmt_if: &ast::StmtIf) {
/// R1730 (and also R1731)
pub(crate) fn min_max_instead_of_if(checker: &mut Checker, stmt_if: &ast::StmtIf) {
let ast::StmtIf {
test,
body,
Expand Down Expand Up @@ -117,99 +85,20 @@ pub(crate) fn min_instead_of_if(checker: &mut Checker, stmt_if: &ast::StmtIf) {
return;
};

if !matches!(op, CmpOp::Gt | CmpOp::GtE) {
return;
}
if !match_left(left, body_target) {
return;
}
if !match_right(right_statement, body_value) {
return;
}

let func_node = ast::ExprName {
id: "min".into(),
ctx: ExprContext::Load,
range: TextRange::default(),
let min_or_max = match op {
CmpOp::Gt | CmpOp::GtE => MinMax::Min,
CmpOp::Lt | CmpOp::LtE => MinMax::Max,
_ => return,
};
let value_node = ast::ExprCall {
func: Box::new(func_node.into()),
arguments: Arguments {
args: Box::from([body_target.clone(), body_value.deref().clone()]),
keywords: Box::from([]),
range: TextRange::default(),
},
range: TextRange::default(),
};
let assign_node = ast::StmtAssign {
targets: vec![body_target.clone()],
value: Box::new(value_node.into()),
range: TextRange::default(),
};
let diagnostic = Diagnostic::new(
MinInsteadOfIf {
contents: checker.generator().stmt(&assign_node.into()),
},
stmt_if.range(),
);
checker.diagnostics.push(diagnostic);
}

/// R1731
pub(crate) fn max_instead_of_if(checker: &mut Checker, stmt_if: &ast::StmtIf) {
let ast::StmtIf {
test,
body,
elif_else_clauses,
range: _,
} = stmt_if;

if !elif_else_clauses.is_empty() {
return;
}

let [Stmt::Assign(ast::StmtAssign {
targets: body_targets,
value: body_value,
..
})] = body.as_slice()
else {
return;
};
let [body_target] = body_targets.as_slice() else {
return;
};

let Some(ast::ExprCompare {
ops,
left,
comparators,
..
}) = test.as_compare_expr()
else {
return;
};

if !(!body_target.is_subscript_expr() && !left.is_subscript_expr()) {
return;
}

let ([op], [right_statement]) = (&**ops, &**comparators) else {
return;
};

if !matches!(op, CmpOp::Lt | CmpOp::LtE) {
return;
}
if !match_left(left, body_target) {
return;
}
if !match_right(right_statement, body_value) {
return;
}

let func_node = ast::ExprName {
id: "max".into(),
id: min_or_max.as_str().into(),
ctx: ExprContext::Load,
range: TextRange::default(),
};
Expand All @@ -228,7 +117,7 @@ pub(crate) fn max_instead_of_if(checker: &mut Checker, stmt_if: &ast::StmtIf) {
range: TextRange::default(),
};
let diagnostic = Diagnostic::new(
MaxInsteadOfIf {
MinMaxInsteadOfIf {
contents: checker.generator().stmt(&assign_node.into()),
},
stmt_if.range(),
Expand Down Expand Up @@ -338,3 +227,17 @@ fn match_attributes(expr1: &ExprAttribute, expr2: &ExprAttribute) -> bool {

false
}

enum MinMax {
Min,
Max,
}

impl MinMax {
fn as_str(&self) -> &'static str {
match self {
Self::Min => "min",
Self::Max => "max",
}
}
}

0 comments on commit 0137351

Please sign in to comment.