mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 04:19:43 +00:00
[pylint
] Correct ordering of arguments in fix for if-stmt-min-max
(PLR1730
) (#16080)
Some checks are pending
CI / cargo test (linux) (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
Some checks are pending
CI / cargo test (linux) (push) Blocked by required conditions
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions
The PR addresses the issue #16040 . --- The logic used into the rule is the following: Suppose to have an expression of the form ```python if a cmp b: c = d ``` where `a`,` b`, `c` and `d` are Python obj and `cmp` one of `<`, `>`, `<=`, `>=`. Then: - `if a=c and b=d` - if `<=` fix with `a = max(b, a)` - if `>=` fix with `a = min(b, a)` - if `>` fix with `a = min(a, b)` - if `<` fix with `a = max(a, b)` - `if a=d and b=c` - if `<=` fix with `b = min(a, b)` - if `>=` fix with `b = max(a, b)` - if `>` fix with `b = max(b, a)` - if `<` fix with `b = min(b, a)` - do nothing, i.e., we cannot fix this case. --- In total we have 8 different and possible cases. ``` | Case | Expression | Fix | |-------|------------------|---------------| | 1 | if a >= b: a = b | a = min(b, a) | | 2 | if a <= b: a = b | a = max(b, a) | | 3 | if a <= b: b = a | b = min(a, b) | | 4 | if a >= b: b = a | b = max(a, b) | | 5 | if a > b: a = b | a = min(a, b) | | 6 | if a < b: a = b | a = max(a, b) | | 7 | if a < b: b = a | b = min(b, a) | | 8 | if a > b: b = a | b = max(b, a) | ``` I added them in the tests. Please double-check that I didn't make any mistakes. It's quite easy to mix up > and <. --------- Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
366ae1feaa
commit
ae1b381c06
3 changed files with 733 additions and 403 deletions
|
@ -1,39 +1,98 @@
|
||||||
# pylint: disable=missing-docstring, invalid-name, too-few-public-methods, redefined-outer-name
|
# pylint: disable=missing-docstring, invalid-name, too-few-public-methods, redefined-outer-name
|
||||||
|
|
||||||
|
|
||||||
|
# the rule take care of the following cases:
|
||||||
|
#
|
||||||
|
# | Case | Expression | Fix |
|
||||||
|
# |-------|------------------|---------------|
|
||||||
|
# | 1 | if a >= b: a = b | a = min(b, a) |
|
||||||
|
# | 2 | if a <= b: a = b | a = max(b, a) |
|
||||||
|
# | 3 | if a <= b: b = a | b = min(a, b) |
|
||||||
|
# | 4 | if a >= b: b = a | b = max(a, b) |
|
||||||
|
# | 5 | if a > b: a = b | a = min(a, b) |
|
||||||
|
# | 6 | if a < b: a = b | a = max(a, b) |
|
||||||
|
# | 7 | if a < b: b = a | b = min(b, a) |
|
||||||
|
# | 8 | if a > b: b = a | b = max(b, a) |
|
||||||
|
|
||||||
|
# the 8 base cases
|
||||||
|
a, b = [], []
|
||||||
|
|
||||||
|
# case 1: a = min(b, a)
|
||||||
|
if a >= b:
|
||||||
|
a = b
|
||||||
|
|
||||||
|
# case 2: a = max(b, a)
|
||||||
|
if a <= b:
|
||||||
|
a = b
|
||||||
|
|
||||||
|
# case 3: b = min(a, b)
|
||||||
|
if a <= b:
|
||||||
|
b = a
|
||||||
|
|
||||||
|
# case 4: b = max(a, b)
|
||||||
|
if a >= b:
|
||||||
|
b = a
|
||||||
|
|
||||||
|
# case 5: a = min(a, b)
|
||||||
|
if a > b:
|
||||||
|
a = b
|
||||||
|
|
||||||
|
# case 6: a = max(a, b)
|
||||||
|
if a < b:
|
||||||
|
a = b
|
||||||
|
|
||||||
|
# case 7: b = min(b, a)
|
||||||
|
if a < b:
|
||||||
|
b = a
|
||||||
|
|
||||||
|
# case 8: b = max(b, a)
|
||||||
|
if a > b:
|
||||||
|
b = a
|
||||||
|
|
||||||
|
|
||||||
|
# test cases with assigned variables and primitives
|
||||||
value = 10
|
value = 10
|
||||||
value2 = 0
|
value2 = 0
|
||||||
value3 = 3
|
value3 = 3
|
||||||
|
|
||||||
# Positive
|
# base case 6: value = max(value, 10)
|
||||||
if value < 10: # [max-instead-of-if]
|
if value < 10:
|
||||||
value = 10
|
value = 10
|
||||||
|
|
||||||
if value <= 10: # [max-instead-of-if]
|
# base case 2: value = max(10, value)
|
||||||
|
if value <= 10:
|
||||||
value = 10
|
value = 10
|
||||||
|
|
||||||
if value < value2: # [max-instead-of-if]
|
# base case 6: value = max(value, value2)
|
||||||
|
if value < value2:
|
||||||
value = value2
|
value = value2
|
||||||
|
|
||||||
if value > 10: # [min-instead-of-if]
|
# base case 5: value = min(value, 10)
|
||||||
|
if value > 10:
|
||||||
value = 10
|
value = 10
|
||||||
|
|
||||||
if value >= 10: # [min-instead-of-if]
|
# base case 1: value = min(10, value)
|
||||||
|
if value >= 10:
|
||||||
value = 10
|
value = 10
|
||||||
|
|
||||||
if value > value2: # [min-instead-of-if]
|
# base case 5: value = min(value, value2)
|
||||||
|
if value > value2:
|
||||||
value = value2
|
value = value2
|
||||||
|
|
||||||
|
|
||||||
|
# cases with calls
|
||||||
class A:
|
class A:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.value = 13
|
self.value = 13
|
||||||
|
|
||||||
|
|
||||||
A1 = A()
|
A1 = A()
|
||||||
if A1.value < 10: # [max-instead-of-if]
|
|
||||||
|
|
||||||
|
if A1.value < 10:
|
||||||
A1.value = 10
|
A1.value = 10
|
||||||
|
|
||||||
if A1.value > 10: # [min-instead-of-if]
|
if A1.value > 10:
|
||||||
A1.value = 10
|
A1.value = 10
|
||||||
|
|
||||||
|
|
||||||
|
@ -159,3 +218,22 @@ class Foo:
|
||||||
self._min = value
|
self._min = value
|
||||||
if self._max >= value:
|
if self._max >= value:
|
||||||
self._max = value
|
self._max = value
|
||||||
|
|
||||||
|
|
||||||
|
counter = {"a": 0, "b": 0}
|
||||||
|
|
||||||
|
# base case 2: counter["a"] = max(counter["b"], counter["a"])
|
||||||
|
if counter["a"] <= counter["b"]:
|
||||||
|
counter["a"] = counter["b"]
|
||||||
|
|
||||||
|
# case 3: counter["b"] = min(counter["a"], counter["b"])
|
||||||
|
if counter["a"] <= counter["b"]:
|
||||||
|
counter["b"] = counter["a"]
|
||||||
|
|
||||||
|
# case 5: counter["a"] = min(counter["a"], counter["b"])
|
||||||
|
if counter["a"] > counter["b"]:
|
||||||
|
counter["b"] = counter["a"]
|
||||||
|
|
||||||
|
# case 8: counter["a"] = max(counter["b"], counter["a"])
|
||||||
|
if counter["a"] > counter["b"]:
|
||||||
|
counter["b"] = counter["a"]
|
||||||
|
|
|
@ -106,47 +106,44 @@ pub(crate) fn if_stmt_min_max(checker: &Checker, stmt_if: &ast::StmtIf) {
|
||||||
let [op] = &**ops else {
|
let [op] = &**ops else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let [right] = &**comparators else {
|
let [right] = &**comparators else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let left_cmp = ComparableExpr::from(left);
|
// extract helpful info from expression of the form
|
||||||
let body_target_cmp = ComparableExpr::from(body_target);
|
// `if cmp_left op cmp_right: target = assignment_value`
|
||||||
let right_cmp = ComparableExpr::from(right);
|
let cmp_left = ComparableExpr::from(left);
|
||||||
let body_value_cmp = ComparableExpr::from(body_value);
|
let cmp_right = ComparableExpr::from(right);
|
||||||
|
let target = ComparableExpr::from(body_target);
|
||||||
|
let assignment_value = ComparableExpr::from(body_value);
|
||||||
|
|
||||||
let left_is_target = left_cmp == body_target_cmp;
|
// Ex): if a < b: a = b
|
||||||
let right_is_target = right_cmp == body_target_cmp;
|
let (min_max, flip_args) = if cmp_left == target && cmp_right == assignment_value {
|
||||||
let left_is_value = left_cmp == body_value_cmp;
|
match op {
|
||||||
let right_is_value = right_cmp == body_value_cmp;
|
CmpOp::Lt => (MinMax::Max, false),
|
||||||
|
CmpOp::LtE => (MinMax::Max, true),
|
||||||
let min_max = match (
|
CmpOp::Gt => (MinMax::Min, false),
|
||||||
left_is_target,
|
CmpOp::GtE => (MinMax::Min, true),
|
||||||
right_is_target,
|
|
||||||
left_is_value,
|
|
||||||
right_is_value,
|
|
||||||
) {
|
|
||||||
(true, false, false, true) => match op {
|
|
||||||
CmpOp::Lt | CmpOp::LtE => MinMax::Max,
|
|
||||||
CmpOp::Gt | CmpOp::GtE => MinMax::Min,
|
|
||||||
_ => return,
|
_ => return,
|
||||||
},
|
}
|
||||||
(false, true, true, false) => match op {
|
}
|
||||||
CmpOp::Lt | CmpOp::LtE => MinMax::Min,
|
// Ex): `if a < b: b = a`
|
||||||
CmpOp::Gt | CmpOp::GtE => MinMax::Max,
|
else if cmp_left == assignment_value && cmp_right == target {
|
||||||
|
match op {
|
||||||
|
CmpOp::Lt => (MinMax::Min, true),
|
||||||
|
CmpOp::LtE => (MinMax::Min, false),
|
||||||
|
CmpOp::Gt => (MinMax::Max, true),
|
||||||
|
CmpOp::GtE => (MinMax::Max, false),
|
||||||
_ => return,
|
_ => return,
|
||||||
},
|
}
|
||||||
_ => return,
|
} else {
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine whether to use `min()` or `max()`, and make sure that the first
|
let (arg1, arg2) = if flip_args {
|
||||||
// arg of the `min()` or `max()` method is equal to the target of the comparison.
|
|
||||||
// This is to be consistent with the Python implementation of the methods `min()` and `max()`.
|
|
||||||
let (arg1, arg2) = if left_is_target {
|
|
||||||
(&**left, right)
|
|
||||||
} else {
|
|
||||||
(right, &**left)
|
(right, &**left)
|
||||||
|
} else {
|
||||||
|
(&**left, right)
|
||||||
};
|
};
|
||||||
|
|
||||||
let replacement = format!(
|
let replacement = format!(
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue