mirror of https://github.com/astral-sh/ruff
Invert reverse argument regardless of whether it's a boolean (#7372)
## Summary When fixing `reversed(sorted(x, reverse=False))`, we rewrite as `sorted(x, reverse=True)`. However, if the `reverse` argument isn't `True` or `False`, we leave it as-is, which is incorrect. Now, given `reversed(sorted(x, reverse=y))`, we rewrite as `sorted(x, reverse=not y)`.
This commit is contained in:
parent
ebd1b296fd
commit
6e625bd93d
|
|
@ -7,6 +7,8 @@ reversed(sorted(x, reverse=True))
|
||||||
reversed(sorted(x, key=lambda e: e, reverse=True))
|
reversed(sorted(x, key=lambda e: e, reverse=True))
|
||||||
reversed(sorted(x, reverse=True, key=lambda e: e))
|
reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||||
reversed(sorted(x, reverse=False))
|
reversed(sorted(x, reverse=False))
|
||||||
|
reversed(sorted(x, reverse=x))
|
||||||
|
reversed(sorted(x, reverse=not x))
|
||||||
|
|
||||||
# Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
# Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||||
reversed(sorted(i for i in range(42)))
|
reversed(sorted(i for i in range(42)))
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use libcst_native::{Expression, NameOrAttribute, ParenthesizableWhitespace, SimpleWhitespace};
|
use libcst_native::{
|
||||||
|
Expression, Name, NameOrAttribute, ParenthesizableWhitespace, SimpleWhitespace, UnaryOperation,
|
||||||
|
};
|
||||||
|
|
||||||
fn compose_call_path_inner<'a>(expr: &'a Expression, parts: &mut Vec<&'a str>) {
|
fn compose_call_path_inner<'a>(expr: &'a Expression, parts: &mut Vec<&'a str>) {
|
||||||
match expr {
|
match expr {
|
||||||
|
|
@ -50,3 +52,41 @@ pub(crate) fn or_space(whitespace: ParenthesizableWhitespace) -> Parenthesizable
|
||||||
whitespace
|
whitespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Negate a condition, i.e., `a` => `not a` and `not a` => `a`.
|
||||||
|
pub(crate) fn negate<'a>(expression: &Expression<'a>) -> Expression<'a> {
|
||||||
|
if let Expression::UnaryOperation(ref expression) = expression {
|
||||||
|
if matches!(expression.operator, libcst_native::UnaryOp::Not { .. }) {
|
||||||
|
return *expression.expression.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Expression::Name(ref expression) = expression {
|
||||||
|
match expression.value {
|
||||||
|
"True" => {
|
||||||
|
return Expression::Name(Box::new(Name {
|
||||||
|
value: "False",
|
||||||
|
lpar: vec![],
|
||||||
|
rpar: vec![],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
"False" => {
|
||||||
|
return Expression::Name(Box::new(Name {
|
||||||
|
value: "True",
|
||||||
|
lpar: vec![],
|
||||||
|
rpar: vec![],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression::UnaryOperation(Box::new(UnaryOperation {
|
||||||
|
operator: libcst_native::UnaryOp::Not {
|
||||||
|
whitespace_after: space(),
|
||||||
|
},
|
||||||
|
expression: Box::new(expression.clone()),
|
||||||
|
lpar: vec![],
|
||||||
|
rpar: vec![],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,17 @@ use libcst_native::{
|
||||||
RightCurlyBrace, RightParen, RightSquareBracket, Set, SetComp, SimpleString, SimpleWhitespace,
|
RightCurlyBrace, RightParen, RightSquareBracket, Set, SetComp, SimpleString, SimpleWhitespace,
|
||||||
TrailingWhitespace, Tuple,
|
TrailingWhitespace, Tuple,
|
||||||
};
|
};
|
||||||
use ruff_python_ast::Expr;
|
|
||||||
use ruff_text_size::{Ranged, TextRange};
|
|
||||||
|
|
||||||
use ruff_diagnostics::{Edit, Fix};
|
use ruff_diagnostics::{Edit, Fix};
|
||||||
|
use ruff_python_ast::Expr;
|
||||||
use ruff_python_codegen::Stylist;
|
use ruff_python_codegen::Stylist;
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
use ruff_source_file::Locator;
|
use ruff_source_file::Locator;
|
||||||
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::autofix::codemods::CodegenStylist;
|
use crate::autofix::codemods::CodegenStylist;
|
||||||
use crate::autofix::edits::pad;
|
use crate::autofix::edits::pad;
|
||||||
use crate::cst::helpers::space;
|
use crate::cst::helpers::{negate, space};
|
||||||
use crate::rules::flake8_comprehensions::rules::ObjectType;
|
use crate::rules::flake8_comprehensions::rules::ObjectType;
|
||||||
use crate::{
|
use crate::{
|
||||||
checkers::ast::Checker,
|
checkers::ast::Checker,
|
||||||
|
|
@ -728,7 +728,7 @@ pub(crate) fn fix_unnecessary_call_around_sorted(
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}) {
|
}) {
|
||||||
// Negate the `reverse` argument
|
// Negate the `reverse` argument.
|
||||||
inner_call
|
inner_call
|
||||||
.args
|
.args
|
||||||
.clone()
|
.clone()
|
||||||
|
|
@ -741,31 +741,9 @@ pub(crate) fn fix_unnecessary_call_around_sorted(
|
||||||
..
|
..
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
if let Expression::Name(ref val) = arg.value {
|
arg.value = negate(&arg.value);
|
||||||
if val.value == "True" {
|
|
||||||
// TODO: even better would be to drop the argument, as False is the default
|
|
||||||
arg.value = Expression::Name(Box::new(Name {
|
|
||||||
value: "False",
|
|
||||||
lpar: vec![],
|
|
||||||
rpar: vec![],
|
|
||||||
}));
|
|
||||||
arg
|
|
||||||
} else if val.value == "False" {
|
|
||||||
arg.value = Expression::Name(Box::new(Name {
|
|
||||||
value: "True",
|
|
||||||
lpar: vec![],
|
|
||||||
rpar: vec![],
|
|
||||||
}));
|
|
||||||
arg
|
|
||||||
} else {
|
|
||||||
arg
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
arg
|
arg
|
||||||
}
|
|
||||||
} else {
|
|
||||||
arg
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect_vec()
|
.collect_vec()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -103,15 +103,16 @@ C413.py:7:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||||
7 |+sorted(x, key=lambda e: e, reverse=False)
|
7 |+sorted(x, key=lambda e: e, reverse=False)
|
||||||
8 8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
8 8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||||
9 9 | reversed(sorted(x, reverse=False))
|
9 9 | reversed(sorted(x, reverse=False))
|
||||||
10 10 |
|
10 10 | reversed(sorted(x, reverse=x))
|
||||||
|
|
||||||
C413.py:8:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
C413.py:8:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||||
|
|
|
|
||||||
6 | reversed(sorted(x, reverse=True))
|
6 | reversed(sorted(x, reverse=True))
|
||||||
7 | reversed(sorted(x, key=lambda e: e, reverse=True))
|
7 | reversed(sorted(x, key=lambda e: e, reverse=True))
|
||||||
8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||||
9 | reversed(sorted(x, reverse=False))
|
9 | reversed(sorted(x, reverse=False))
|
||||||
|
10 | reversed(sorted(x, reverse=x))
|
||||||
|
|
|
|
||||||
= help: Remove unnecessary `reversed` call
|
= help: Remove unnecessary `reversed` call
|
||||||
|
|
||||||
|
|
@ -122,8 +123,8 @@ C413.py:8:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||||
8 |-reversed(sorted(x, reverse=True, key=lambda e: e))
|
8 |-reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||||
8 |+sorted(x, reverse=False, key=lambda e: e)
|
8 |+sorted(x, reverse=False, key=lambda e: e)
|
||||||
9 9 | reversed(sorted(x, reverse=False))
|
9 9 | reversed(sorted(x, reverse=False))
|
||||||
10 10 |
|
10 10 | reversed(sorted(x, reverse=x))
|
||||||
11 11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
11 11 | reversed(sorted(x, reverse=not x))
|
||||||
|
|
||||||
C413.py:9:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
C413.py:9:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||||
|
|
|
|
||||||
|
|
@ -131,8 +132,8 @@ C413.py:9:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||||
8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||||
9 | reversed(sorted(x, reverse=False))
|
9 | reversed(sorted(x, reverse=False))
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||||
10 |
|
10 | reversed(sorted(x, reverse=x))
|
||||||
11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
11 | reversed(sorted(x, reverse=not x))
|
||||||
|
|
|
|
||||||
= help: Remove unnecessary `reversed` call
|
= help: Remove unnecessary `reversed` call
|
||||||
|
|
||||||
|
|
@ -142,46 +143,87 @@ C413.py:9:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||||
8 8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
8 8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||||
9 |-reversed(sorted(x, reverse=False))
|
9 |-reversed(sorted(x, reverse=False))
|
||||||
9 |+sorted(x, reverse=True)
|
9 |+sorted(x, reverse=True)
|
||||||
10 10 |
|
10 10 | reversed(sorted(x, reverse=x))
|
||||||
11 11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
11 11 | reversed(sorted(x, reverse=not x))
|
||||||
12 12 | reversed(sorted(i for i in range(42)))
|
12 12 |
|
||||||
|
|
||||||
C413.py:12:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
C413.py:10:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||||
|
|
|
|
||||||
11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||||
12 | reversed(sorted(i for i in range(42)))
|
9 | reversed(sorted(x, reverse=False))
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
10 | reversed(sorted(x, reverse=x))
|
||||||
13 | reversed(sorted((i for i in range(42)), reverse=True))
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||||
|
11 | reversed(sorted(x, reverse=not x))
|
||||||
|
|
|
|
||||||
= help: Remove unnecessary `reversed` call
|
= help: Remove unnecessary `reversed` call
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Suggested fix
|
||||||
|
7 7 | reversed(sorted(x, key=lambda e: e, reverse=True))
|
||||||
|
8 8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||||
9 9 | reversed(sorted(x, reverse=False))
|
9 9 | reversed(sorted(x, reverse=False))
|
||||||
10 10 |
|
10 |-reversed(sorted(x, reverse=x))
|
||||||
11 11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
10 |+sorted(x, reverse=not x)
|
||||||
12 |-reversed(sorted(i for i in range(42)))
|
11 11 | reversed(sorted(x, reverse=not x))
|
||||||
12 |+sorted((i for i in range(42)), reverse=True)
|
12 12 |
|
||||||
13 13 | reversed(sorted((i for i in range(42)), reverse=True))
|
13 13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||||
14 14 |
|
|
||||||
15 15 |
|
|
||||||
|
|
||||||
C413.py:13:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
C413.py:11:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||||
|
|
|
|
||||||
11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
9 | reversed(sorted(x, reverse=False))
|
||||||
12 | reversed(sorted(i for i in range(42)))
|
10 | reversed(sorted(x, reverse=x))
|
||||||
13 | reversed(sorted((i for i in range(42)), reverse=True))
|
11 | reversed(sorted(x, reverse=not x))
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||||
|
12 |
|
||||||
|
13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||||
|
|
|
||||||
|
= help: Remove unnecessary `reversed` call
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
8 8 | reversed(sorted(x, reverse=True, key=lambda e: e))
|
||||||
|
9 9 | reversed(sorted(x, reverse=False))
|
||||||
|
10 10 | reversed(sorted(x, reverse=x))
|
||||||
|
11 |-reversed(sorted(x, reverse=not x))
|
||||||
|
11 |+sorted(x, reverse=x)
|
||||||
|
12 12 |
|
||||||
|
13 13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||||
|
14 14 | reversed(sorted(i for i in range(42)))
|
||||||
|
|
||||||
|
C413.py:14:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||||
|
|
|
||||||
|
13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||||
|
14 | reversed(sorted(i for i in range(42)))
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||||
|
15 | reversed(sorted((i for i in range(42)), reverse=True))
|
||||||
|
|
|
||||||
|
= help: Remove unnecessary `reversed` call
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
11 11 | reversed(sorted(x, reverse=not x))
|
||||||
|
12 12 |
|
||||||
|
13 13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||||
|
14 |-reversed(sorted(i for i in range(42)))
|
||||||
|
14 |+sorted((i for i in range(42)), reverse=True)
|
||||||
|
15 15 | reversed(sorted((i for i in range(42)), reverse=True))
|
||||||
|
16 16 |
|
||||||
|
17 17 |
|
||||||
|
|
||||||
|
C413.py:15:1: C413 [*] Unnecessary `reversed` call around `sorted()`
|
||||||
|
|
|
||||||
|
13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||||
|
14 | reversed(sorted(i for i in range(42)))
|
||||||
|
15 | reversed(sorted((i for i in range(42)), reverse=True))
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C413
|
||||||
|
|
|
|
||||||
= help: Remove unnecessary `reversed` call
|
= help: Remove unnecessary `reversed` call
|
||||||
|
|
||||||
ℹ Suggested fix
|
ℹ Suggested fix
|
||||||
10 10 |
|
12 12 |
|
||||||
11 11 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
13 13 | # Regression test for: https://github.com/astral-sh/ruff/issues/7289
|
||||||
12 12 | reversed(sorted(i for i in range(42)))
|
14 14 | reversed(sorted(i for i in range(42)))
|
||||||
13 |-reversed(sorted((i for i in range(42)), reverse=True))
|
15 |-reversed(sorted((i for i in range(42)), reverse=True))
|
||||||
13 |+sorted((i for i in range(42)), reverse=False)
|
15 |+sorted((i for i in range(42)), reverse=False)
|
||||||
14 14 |
|
16 16 |
|
||||||
15 15 |
|
17 17 |
|
||||||
16 16 | def reversed(*args, **kwargs):
|
18 18 | def reversed(*args, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use anyhow::Result;
|
||||||
use anyhow::{bail, Context};
|
use anyhow::{bail, Context};
|
||||||
use libcst_native::{
|
use libcst_native::{
|
||||||
self, Assert, BooleanOp, CompoundStatement, Expression, ParenthesizedNode, SimpleStatementLine,
|
self, Assert, BooleanOp, CompoundStatement, Expression, ParenthesizedNode, SimpleStatementLine,
|
||||||
SimpleWhitespace, SmallStatement, Statement, TrailingWhitespace, UnaryOperation,
|
SimpleWhitespace, SmallStatement, Statement, TrailingWhitespace,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation};
|
||||||
|
|
@ -21,7 +21,7 @@ use ruff_text_size::Ranged;
|
||||||
|
|
||||||
use crate::autofix::codemods::CodegenStylist;
|
use crate::autofix::codemods::CodegenStylist;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::cst::helpers::space;
|
use crate::cst::helpers::negate;
|
||||||
use crate::cst::matchers::match_indented_block;
|
use crate::cst::matchers::match_indented_block;
|
||||||
use crate::cst::matchers::match_module;
|
use crate::cst::matchers::match_module;
|
||||||
use crate::importer::ImportRequest;
|
use crate::importer::ImportRequest;
|
||||||
|
|
@ -567,23 +567,6 @@ fn is_composite_condition(test: &Expr) -> CompositionKind {
|
||||||
CompositionKind::None
|
CompositionKind::None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Negate a condition, i.e., `a` => `not a` and `not a` => `a`.
|
|
||||||
fn negate<'a>(expression: &Expression<'a>) -> Expression<'a> {
|
|
||||||
if let Expression::UnaryOperation(ref expression) = expression {
|
|
||||||
if matches!(expression.operator, libcst_native::UnaryOp::Not { .. }) {
|
|
||||||
return *expression.expression.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Expression::UnaryOperation(Box::new(UnaryOperation {
|
|
||||||
operator: libcst_native::UnaryOp::Not {
|
|
||||||
whitespace_after: space(),
|
|
||||||
},
|
|
||||||
expression: Box::new(expression.clone()),
|
|
||||||
lpar: vec![],
|
|
||||||
rpar: vec![],
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Propagate parentheses from a parent to a child expression, if necessary.
|
/// Propagate parentheses from a parent to a child expression, if necessary.
|
||||||
///
|
///
|
||||||
/// For example, when splitting:
|
/// For example, when splitting:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue