mirror of https://github.com/astral-sh/ruff
Expand `RUF015` to include all expression types (#5767)
## Summary We now allow RUF015 to fix cases like: ```python list(range(10))[0] list(x.y)[0] list(x["y"])[0] ``` Further, we fix generators like: ```python [i + 1 for i in x][0] ``` By rewriting to `next(iter(i + 1 for i in x))`. I've retained the special-case that rewrites `[i for i in x][0]` to `next(iter(x))`. Closes https://github.com/astral-sh/ruff/issues/5764.
This commit is contained in:
parent
4e681070dc
commit
5f2014b0b8
|
|
@ -34,11 +34,23 @@ list(x)[::]
|
||||||
[i for i in x][::2]
|
[i for i in x][::2]
|
||||||
[i for i in x][::]
|
[i for i in x][::]
|
||||||
|
|
||||||
# OK (doesn't mirror the underlying list)
|
# RUF015 (doesn't mirror the underlying list)
|
||||||
[i + 1 for i in x][0]
|
[i + 1 for i in x][0]
|
||||||
[i for i in x if i > 5][0]
|
[i for i in x if i > 5][0]
|
||||||
[(i, i + 1) for i in x][0]
|
[(i, i + 1) for i in x][0]
|
||||||
|
|
||||||
# OK (multiple generators)
|
# RUF015 (multiple generators)
|
||||||
y = range(10)
|
y = range(10)
|
||||||
[i + j for i in x for j in y][0]
|
[i + j for i in x for j in y][0]
|
||||||
|
|
||||||
|
# RUF015
|
||||||
|
list(range(10))[0]
|
||||||
|
list(x.y)[0]
|
||||||
|
list(x["y"])[0]
|
||||||
|
|
||||||
|
# RUF015 (multi-line)
|
||||||
|
revision_heads_map_ast = [
|
||||||
|
a
|
||||||
|
for a in revision_heads_map_ast_obj.body
|
||||||
|
if isinstance(a, ast.Assign) and a.targets[0].id == "REVISION_HEADS_MAP"
|
||||||
|
][0]
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ impl Violation for InvalidIndexType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RUF015
|
/// RUF016
|
||||||
pub(crate) fn invalid_index_type(checker: &mut Checker, expr: &ExprSubscript) {
|
pub(crate) fn invalid_index_type(checker: &mut Checker, expr: &ExprSubscript) {
|
||||||
let ExprSubscript {
|
let ExprSubscript {
|
||||||
value,
|
value,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
use num_traits::{One, Zero};
|
use num_traits::{One, Zero};
|
||||||
use rustpython_parser::ast::{self, Comprehension, Constant, Expr, ExprSubscript};
|
use ruff_text_size::{TextRange, TextSize};
|
||||||
|
use rustpython_parser::ast::{self, Comprehension, Constant, Expr, Ranged};
|
||||||
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
|
@ -16,8 +20,8 @@ use crate::registry::AsRule;
|
||||||
/// ## Why is this bad?
|
/// ## Why is this bad?
|
||||||
/// Calling `list(...)` will create a new list of the entire collection, which
|
/// Calling `list(...)` will create a new list of the entire collection, which
|
||||||
/// can be very expensive for large collections. If you only need the first
|
/// can be very expensive for large collections. If you only need the first
|
||||||
/// element of the collection, you can use `next(iter(...))` to lazily fetch
|
/// element of the collection, you can use `next(...)` or `next(iter(...)` to
|
||||||
/// the first element without creating a new list.
|
/// lazily fetch the first element.
|
||||||
///
|
///
|
||||||
/// Note that migrating from `list(...)[0]` to `next(iter(...))` can change
|
/// Note that migrating from `list(...)[0]` to `next(iter(...))` can change
|
||||||
/// the behavior of your program in two ways:
|
/// the behavior of your program in two ways:
|
||||||
|
|
@ -26,16 +30,18 @@ use crate::registry::AsRule;
|
||||||
/// `next(iter(...))` will only evaluate the first element. As such, any
|
/// `next(iter(...))` will only evaluate the first element. As such, any
|
||||||
/// side effects that occur during iteration will be delayed.
|
/// side effects that occur during iteration will be delayed.
|
||||||
/// 2. Second, `list(...)[0]` will raise `IndexError` if the collection is
|
/// 2. Second, `list(...)[0]` will raise `IndexError` if the collection is
|
||||||
/// empty, while `next(iter(...))` will raise `StopIteration`.
|
/// empty, while `next(iter(...))` will raise `StopIteration`.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```python
|
/// ```python
|
||||||
/// head = list(range(1000000000000))[0]
|
/// head = list(x)[0]
|
||||||
|
/// head = [x * x for x in range(10)][0]
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Use instead:
|
/// Use instead:
|
||||||
/// ```python
|
/// ```python
|
||||||
/// head = next(iter(range(1000000000000)))
|
/// head = next(iter(x))
|
||||||
|
/// head = next(x * x for x in range(10))
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// ## References
|
/// ## References
|
||||||
|
|
@ -53,12 +59,13 @@ impl AlwaysAutofixableViolation for UnnecessaryIterableAllocationForFirstElement
|
||||||
iterable,
|
iterable,
|
||||||
subscript_kind,
|
subscript_kind,
|
||||||
} = self;
|
} = self;
|
||||||
|
let iterable = Self::truncate(iterable);
|
||||||
match subscript_kind {
|
match subscript_kind {
|
||||||
HeadSubscriptKind::Index => {
|
HeadSubscriptKind::Index => {
|
||||||
format!("Prefer `next(iter({iterable}))` over `list({iterable})[0]`")
|
format!("Prefer `next({iterable})` over single element slice")
|
||||||
}
|
}
|
||||||
HeadSubscriptKind::Slice => {
|
HeadSubscriptKind::Slice => {
|
||||||
format!("Prefer `[next(iter({iterable}))]` over `list({iterable})[:1]`")
|
format!("Prefer `[next({iterable})]` over single element slice")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -68,9 +75,21 @@ impl AlwaysAutofixableViolation for UnnecessaryIterableAllocationForFirstElement
|
||||||
iterable,
|
iterable,
|
||||||
subscript_kind,
|
subscript_kind,
|
||||||
} = self;
|
} = self;
|
||||||
|
let iterable = Self::truncate(iterable);
|
||||||
match subscript_kind {
|
match subscript_kind {
|
||||||
HeadSubscriptKind::Index => format!("Replace with `next(iter({iterable}))`"),
|
HeadSubscriptKind::Index => format!("Replace with `next({iterable})`"),
|
||||||
HeadSubscriptKind::Slice => format!("Replace with `[next(iter({iterable}))]"),
|
HeadSubscriptKind::Slice => format!("Replace with `[next({iterable})]"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnnecessaryIterableAllocationForFirstElement {
|
||||||
|
/// If the iterable is too long, or spans multiple lines, truncate it.
|
||||||
|
fn truncate(iterable: &str) -> &str {
|
||||||
|
if iterable.width() > 40 || iterable.contains(['\r', '\n']) {
|
||||||
|
"..."
|
||||||
|
} else {
|
||||||
|
iterable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -78,7 +97,7 @@ impl AlwaysAutofixableViolation for UnnecessaryIterableAllocationForFirstElement
|
||||||
/// RUF015
|
/// RUF015
|
||||||
pub(crate) fn unnecessary_iterable_allocation_for_first_element(
|
pub(crate) fn unnecessary_iterable_allocation_for_first_element(
|
||||||
checker: &mut Checker,
|
checker: &mut Checker,
|
||||||
subscript: &ExprSubscript,
|
subscript: &ast::ExprSubscript,
|
||||||
) {
|
) {
|
||||||
let ast::ExprSubscript {
|
let ast::ExprSubscript {
|
||||||
value,
|
value,
|
||||||
|
|
@ -91,9 +110,15 @@ pub(crate) fn unnecessary_iterable_allocation_for_first_element(
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(iterable) = iterable_name(value, checker.semantic()) else {
|
let Some(target) = match_iteration_target(value, checker.semantic()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
let iterable = checker.locator.slice(target.range);
|
||||||
|
let iterable = if target.iterable {
|
||||||
|
Cow::Borrowed(iterable)
|
||||||
|
} else {
|
||||||
|
Cow::Owned(format!("iter({iterable})"))
|
||||||
|
};
|
||||||
|
|
||||||
let mut diagnostic = Diagnostic::new(
|
let mut diagnostic = Diagnostic::new(
|
||||||
UnnecessaryIterableAllocationForFirstElement {
|
UnnecessaryIterableAllocationForFirstElement {
|
||||||
|
|
@ -105,8 +130,8 @@ pub(crate) fn unnecessary_iterable_allocation_for_first_element(
|
||||||
|
|
||||||
if checker.patch(diagnostic.kind.rule()) {
|
if checker.patch(diagnostic.kind.rule()) {
|
||||||
let replacement = match subscript_kind {
|
let replacement = match subscript_kind {
|
||||||
HeadSubscriptKind::Index => format!("next(iter({iterable}))"),
|
HeadSubscriptKind::Index => format!("next({iterable})"),
|
||||||
HeadSubscriptKind::Slice => format!("[next(iter({iterable}))]"),
|
HeadSubscriptKind::Slice => format!("[next({iterable})]"),
|
||||||
};
|
};
|
||||||
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(replacement, *range)));
|
diagnostic.set_fix(Fix::suggested(Edit::range_replacement(replacement, *range)));
|
||||||
}
|
}
|
||||||
|
|
@ -127,11 +152,11 @@ enum HeadSubscriptKind {
|
||||||
/// first `bool` checks that the element is in fact first, the second checks if it's a slice or an
|
/// first `bool` checks that the element is in fact first, the second checks if it's a slice or an
|
||||||
/// index.
|
/// index.
|
||||||
fn classify_subscript(expr: &Expr) -> Option<HeadSubscriptKind> {
|
fn classify_subscript(expr: &Expr) -> Option<HeadSubscriptKind> {
|
||||||
match expr {
|
let result = match expr {
|
||||||
Expr::Constant(ast::ExprConstant {
|
Expr::Constant(ast::ExprConstant {
|
||||||
value: Constant::Int(value),
|
value: Constant::Int(value),
|
||||||
..
|
..
|
||||||
}) if value.is_zero() => Some(HeadSubscriptKind::Index),
|
}) if value.is_zero() => HeadSubscriptKind::Index,
|
||||||
Expr::Slice(ast::ExprSlice {
|
Expr::Slice(ast::ExprSlice {
|
||||||
step, lower, upper, ..
|
step, lower, upper, ..
|
||||||
}) => {
|
}) => {
|
||||||
|
|
@ -158,16 +183,33 @@ fn classify_subscript(expr: &Expr) -> Option<HeadSubscriptKind> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(HeadSubscriptKind::Slice)
|
HeadSubscriptKind::Slice
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => return None,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Some(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch the name of the iterable from an expression if the expression returns an unmodified list
|
#[derive(Debug)]
|
||||||
/// which can be sliced into.
|
struct IterationTarget {
|
||||||
fn iterable_name<'a>(expr: &'a Expr, model: &SemanticModel) -> Option<&'a str> {
|
/// The [`TextRange`] of the target.
|
||||||
match expr {
|
range: TextRange,
|
||||||
|
/// Whether the target is an iterable (e.g., a generator). If not, the target must be wrapped
|
||||||
|
/// in `iter(...)` prior to calling `next(...)`.
|
||||||
|
iterable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the [`IterationTarget`] of an expression, if the expression can be sliced into (i.e.,
|
||||||
|
/// is a list comprehension, or call to `list` or `tuple`).
|
||||||
|
///
|
||||||
|
/// For example, given `list(x)`, returns the range of `x`. Given `[x * x for x in y]`, returns the
|
||||||
|
/// range of `x * x for x in y`.
|
||||||
|
///
|
||||||
|
/// As a special-case, given `[x for x in y]`, returns the range of `y` (rather than the
|
||||||
|
/// redundant comprehension).
|
||||||
|
fn match_iteration_target(expr: &Expr, model: &SemanticModel) -> Option<IterationTarget> {
|
||||||
|
let result = match expr {
|
||||||
Expr::Call(ast::ExprCall { func, args, .. }) => {
|
Expr::Call(ast::ExprCall { func, args, .. }) => {
|
||||||
let ast::ExprName { id, .. } = func.as_name_expr()?;
|
let ast::ExprName { id, .. } = func.as_name_expr()?;
|
||||||
|
|
||||||
|
|
@ -175,37 +217,82 @@ fn iterable_name<'a>(expr: &'a Expr, model: &SemanticModel) -> Option<&'a str> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let [arg] = args.as_slice() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
if !model.is_builtin(id.as_str()) {
|
if !model.is_builtin(id.as_str()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match args.first() {
|
match arg {
|
||||||
Some(Expr::Name(ast::ExprName { id: arg_name, .. })) => Some(arg_name.as_str()),
|
Expr::GeneratorExp(ast::ExprGeneratorExp {
|
||||||
Some(Expr::GeneratorExp(ast::ExprGeneratorExp {
|
|
||||||
elt, generators, ..
|
elt, generators, ..
|
||||||
})) => generator_iterable(elt, generators),
|
}) => match match_simple_comprehension(elt, generators) {
|
||||||
_ => None,
|
Some(range) => IterationTarget {
|
||||||
|
range,
|
||||||
|
iterable: false,
|
||||||
|
},
|
||||||
|
None => IterationTarget {
|
||||||
|
range: arg.range(),
|
||||||
|
iterable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Expr::ListComp(ast::ExprListComp {
|
||||||
|
elt, generators, ..
|
||||||
|
}) => match match_simple_comprehension(elt, generators) {
|
||||||
|
Some(range) => IterationTarget {
|
||||||
|
range,
|
||||||
|
iterable: false,
|
||||||
|
},
|
||||||
|
None => IterationTarget {
|
||||||
|
range: arg
|
||||||
|
.range()
|
||||||
|
// Remove the `[`
|
||||||
|
.add_start(TextSize::from(1))
|
||||||
|
// Remove the `]`
|
||||||
|
.sub_end(TextSize::from(1)),
|
||||||
|
iterable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_ => IterationTarget {
|
||||||
|
range: arg.range(),
|
||||||
|
iterable: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::ListComp(ast::ExprListComp {
|
Expr::ListComp(ast::ExprListComp {
|
||||||
elt, generators, ..
|
elt, generators, ..
|
||||||
}) => generator_iterable(elt, generators),
|
}) => match match_simple_comprehension(elt, generators) {
|
||||||
_ => None,
|
Some(range) => IterationTarget {
|
||||||
}
|
range,
|
||||||
|
iterable: false,
|
||||||
|
},
|
||||||
|
None => IterationTarget {
|
||||||
|
range: expr
|
||||||
|
.range()
|
||||||
|
// Remove the `[`
|
||||||
|
.add_start(TextSize::from(1))
|
||||||
|
// Remove the `]`
|
||||||
|
.sub_end(TextSize::from(1)),
|
||||||
|
iterable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a comprehension, returns the name of the iterable over which it iterates, if it's
|
/// Returns the [`Expr`] target for a comprehension, if the comprehension is "simple"
|
||||||
/// a simple comprehension (e.g., `x` for `[i for i in x]`).
|
/// (e.g., `x` for `[i for i in x]`).
|
||||||
fn generator_iterable<'a>(elt: &'a Expr, generators: &'a Vec<Comprehension>) -> Option<&'a str> {
|
fn match_simple_comprehension(elt: &Expr, generators: &[Comprehension]) -> Option<TextRange> {
|
||||||
// If the `elt` field is anything other than a [`Expr::Name`], we can't be sure that it
|
let [generator @ Comprehension {
|
||||||
// doesn't modify the elements of the underlying iterator (e.g., `[i + 1 for i in x][0]`).
|
is_async: false, ..
|
||||||
if !elt.is_name_expr() {
|
}] = generators
|
||||||
return None;
|
else {
|
||||||
}
|
|
||||||
|
|
||||||
// If there's more than 1 generator, we can't safely say that it fits the diagnostic conditions
|
|
||||||
// (e.g., `[(i, j) for i in x for j in y][0]`).
|
|
||||||
let [generator] = generators.as_slice() else {
|
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -214,8 +301,14 @@ fn generator_iterable<'a>(elt: &'a Expr, generators: &'a Vec<Comprehension>) ->
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ast::ExprName { id, .. } = generator.iter.as_name_expr()?;
|
// Verify that the generator is, e.g. `i for i in x`, as opposed to `i for j in x`.
|
||||||
Some(id.as_str())
|
let elt = elt.as_name_expr()?;
|
||||||
|
let target = generator.target.as_name_expr()?;
|
||||||
|
if elt.id != target.id {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(generator.iter.range())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If an expression is a constant integer, returns the value of that integer; otherwise,
|
/// If an expression is a constant integer, returns the value of that integer; otherwise,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
source: crates/ruff/src/rules/ruff/mod.rs
|
source: crates/ruff/src/rules/ruff/mod.rs
|
||||||
---
|
---
|
||||||
RUF015.py:4:1: RUF015 [*] Prefer `next(iter(x))` over `list(x)[0]`
|
RUF015.py:4:1: RUF015 [*] Prefer `next(iter(x))` over single element slice
|
||||||
|
|
|
|
||||||
3 | # RUF015
|
3 | # RUF015
|
||||||
4 | list(x)[0]
|
4 | list(x)[0]
|
||||||
|
|
@ -21,7 +21,7 @@ RUF015.py:4:1: RUF015 [*] Prefer `next(iter(x))` over `list(x)[0]`
|
||||||
6 6 | list(x)[:1:1]
|
6 6 | list(x)[:1:1]
|
||||||
7 7 | list(x)[:1:2]
|
7 7 | list(x)[:1:2]
|
||||||
|
|
||||||
RUF015.py:5:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:5:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
3 | # RUF015
|
3 | # RUF015
|
||||||
4 | list(x)[0]
|
4 | list(x)[0]
|
||||||
|
|
@ -42,7 +42,7 @@ RUF015.py:5:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
7 7 | list(x)[:1:2]
|
7 7 | list(x)[:1:2]
|
||||||
8 8 | tuple(x)[0]
|
8 8 | tuple(x)[0]
|
||||||
|
|
||||||
RUF015.py:6:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:6:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
4 | list(x)[0]
|
4 | list(x)[0]
|
||||||
5 | list(x)[:1]
|
5 | list(x)[:1]
|
||||||
|
|
@ -63,7 +63,7 @@ RUF015.py:6:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
8 8 | tuple(x)[0]
|
8 8 | tuple(x)[0]
|
||||||
9 9 | tuple(x)[:1]
|
9 9 | tuple(x)[:1]
|
||||||
|
|
||||||
RUF015.py:7:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:7:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
5 | list(x)[:1]
|
5 | list(x)[:1]
|
||||||
6 | list(x)[:1:1]
|
6 | list(x)[:1:1]
|
||||||
|
|
@ -84,7 +84,7 @@ RUF015.py:7:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
9 9 | tuple(x)[:1]
|
9 9 | tuple(x)[:1]
|
||||||
10 10 | tuple(x)[:1:1]
|
10 10 | tuple(x)[:1:1]
|
||||||
|
|
||||||
RUF015.py:8:1: RUF015 [*] Prefer `next(iter(x))` over `list(x)[0]`
|
RUF015.py:8:1: RUF015 [*] Prefer `next(iter(x))` over single element slice
|
||||||
|
|
|
|
||||||
6 | list(x)[:1:1]
|
6 | list(x)[:1:1]
|
||||||
7 | list(x)[:1:2]
|
7 | list(x)[:1:2]
|
||||||
|
|
@ -105,7 +105,7 @@ RUF015.py:8:1: RUF015 [*] Prefer `next(iter(x))` over `list(x)[0]`
|
||||||
10 10 | tuple(x)[:1:1]
|
10 10 | tuple(x)[:1:1]
|
||||||
11 11 | tuple(x)[:1:2]
|
11 11 | tuple(x)[:1:2]
|
||||||
|
|
||||||
RUF015.py:9:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:9:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
7 | list(x)[:1:2]
|
7 | list(x)[:1:2]
|
||||||
8 | tuple(x)[0]
|
8 | tuple(x)[0]
|
||||||
|
|
@ -126,7 +126,7 @@ RUF015.py:9:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
11 11 | tuple(x)[:1:2]
|
11 11 | tuple(x)[:1:2]
|
||||||
12 12 | list(i for i in x)[0]
|
12 12 | list(i for i in x)[0]
|
||||||
|
|
||||||
RUF015.py:10:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:10:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
8 | tuple(x)[0]
|
8 | tuple(x)[0]
|
||||||
9 | tuple(x)[:1]
|
9 | tuple(x)[:1]
|
||||||
|
|
@ -147,7 +147,7 @@ RUF015.py:10:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
12 12 | list(i for i in x)[0]
|
12 12 | list(i for i in x)[0]
|
||||||
13 13 | list(i for i in x)[:1]
|
13 13 | list(i for i in x)[:1]
|
||||||
|
|
||||||
RUF015.py:11:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:11:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
9 | tuple(x)[:1]
|
9 | tuple(x)[:1]
|
||||||
10 | tuple(x)[:1:1]
|
10 | tuple(x)[:1:1]
|
||||||
|
|
@ -168,7 +168,7 @@ RUF015.py:11:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
13 13 | list(i for i in x)[:1]
|
13 13 | list(i for i in x)[:1]
|
||||||
14 14 | list(i for i in x)[:1:1]
|
14 14 | list(i for i in x)[:1:1]
|
||||||
|
|
||||||
RUF015.py:12:1: RUF015 [*] Prefer `next(iter(x))` over `list(x)[0]`
|
RUF015.py:12:1: RUF015 [*] Prefer `next(iter(x))` over single element slice
|
||||||
|
|
|
|
||||||
10 | tuple(x)[:1:1]
|
10 | tuple(x)[:1:1]
|
||||||
11 | tuple(x)[:1:2]
|
11 | tuple(x)[:1:2]
|
||||||
|
|
@ -189,7 +189,7 @@ RUF015.py:12:1: RUF015 [*] Prefer `next(iter(x))` over `list(x)[0]`
|
||||||
14 14 | list(i for i in x)[:1:1]
|
14 14 | list(i for i in x)[:1:1]
|
||||||
15 15 | list(i for i in x)[:1:2]
|
15 15 | list(i for i in x)[:1:2]
|
||||||
|
|
||||||
RUF015.py:13:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:13:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
11 | tuple(x)[:1:2]
|
11 | tuple(x)[:1:2]
|
||||||
12 | list(i for i in x)[0]
|
12 | list(i for i in x)[0]
|
||||||
|
|
@ -210,7 +210,7 @@ RUF015.py:13:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
15 15 | list(i for i in x)[:1:2]
|
15 15 | list(i for i in x)[:1:2]
|
||||||
16 16 | [i for i in x][0]
|
16 16 | [i for i in x][0]
|
||||||
|
|
||||||
RUF015.py:14:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:14:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
12 | list(i for i in x)[0]
|
12 | list(i for i in x)[0]
|
||||||
13 | list(i for i in x)[:1]
|
13 | list(i for i in x)[:1]
|
||||||
|
|
@ -231,7 +231,7 @@ RUF015.py:14:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
16 16 | [i for i in x][0]
|
16 16 | [i for i in x][0]
|
||||||
17 17 | [i for i in x][:1]
|
17 17 | [i for i in x][:1]
|
||||||
|
|
||||||
RUF015.py:15:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:15:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
13 | list(i for i in x)[:1]
|
13 | list(i for i in x)[:1]
|
||||||
14 | list(i for i in x)[:1:1]
|
14 | list(i for i in x)[:1:1]
|
||||||
|
|
@ -252,7 +252,7 @@ RUF015.py:15:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
17 17 | [i for i in x][:1]
|
17 17 | [i for i in x][:1]
|
||||||
18 18 | [i for i in x][:1:1]
|
18 18 | [i for i in x][:1:1]
|
||||||
|
|
||||||
RUF015.py:16:1: RUF015 [*] Prefer `next(iter(x))` over `list(x)[0]`
|
RUF015.py:16:1: RUF015 [*] Prefer `next(iter(x))` over single element slice
|
||||||
|
|
|
|
||||||
14 | list(i for i in x)[:1:1]
|
14 | list(i for i in x)[:1:1]
|
||||||
15 | list(i for i in x)[:1:2]
|
15 | list(i for i in x)[:1:2]
|
||||||
|
|
@ -273,7 +273,7 @@ RUF015.py:16:1: RUF015 [*] Prefer `next(iter(x))` over `list(x)[0]`
|
||||||
18 18 | [i for i in x][:1:1]
|
18 18 | [i for i in x][:1:1]
|
||||||
19 19 | [i for i in x][:1:2]
|
19 19 | [i for i in x][:1:2]
|
||||||
|
|
||||||
RUF015.py:17:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:17:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
15 | list(i for i in x)[:1:2]
|
15 | list(i for i in x)[:1:2]
|
||||||
16 | [i for i in x][0]
|
16 | [i for i in x][0]
|
||||||
|
|
@ -294,7 +294,7 @@ RUF015.py:17:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
19 19 | [i for i in x][:1:2]
|
19 19 | [i for i in x][:1:2]
|
||||||
20 20 |
|
20 20 |
|
||||||
|
|
||||||
RUF015.py:18:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:18:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
16 | [i for i in x][0]
|
16 | [i for i in x][0]
|
||||||
17 | [i for i in x][:1]
|
17 | [i for i in x][:1]
|
||||||
|
|
@ -314,7 +314,7 @@ RUF015.py:18:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
20 20 |
|
20 20 |
|
||||||
21 21 | # OK (not indexing (solely) the first element)
|
21 21 | # OK (not indexing (solely) the first element)
|
||||||
|
|
||||||
RUF015.py:19:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
RUF015.py:19:1: RUF015 [*] Prefer `[next(iter(x))]` over single element slice
|
||||||
|
|
|
|
||||||
17 | [i for i in x][:1]
|
17 | [i for i in x][:1]
|
||||||
18 | [i for i in x][:1:1]
|
18 | [i for i in x][:1:1]
|
||||||
|
|
@ -335,4 +335,172 @@ RUF015.py:19:1: RUF015 [*] Prefer `[next(iter(x))]` over `list(x)[:1]`
|
||||||
21 21 | # OK (not indexing (solely) the first element)
|
21 21 | # OK (not indexing (solely) the first element)
|
||||||
22 22 | list(x)
|
22 22 | list(x)
|
||||||
|
|
||||||
|
RUF015.py:38:1: RUF015 [*] Prefer `next(i + 1 for i in x)` over single element slice
|
||||||
|
|
|
||||||
|
37 | # RUF015 (doesn't mirror the underlying list)
|
||||||
|
38 | [i + 1 for i in x][0]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ RUF015
|
||||||
|
39 | [i for i in x if i > 5][0]
|
||||||
|
40 | [(i, i + 1) for i in x][0]
|
||||||
|
|
|
||||||
|
= help: Replace with `next(i + 1 for i in x)`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
35 35 | [i for i in x][::]
|
||||||
|
36 36 |
|
||||||
|
37 37 | # RUF015 (doesn't mirror the underlying list)
|
||||||
|
38 |-[i + 1 for i in x][0]
|
||||||
|
38 |+next(i + 1 for i in x)
|
||||||
|
39 39 | [i for i in x if i > 5][0]
|
||||||
|
40 40 | [(i, i + 1) for i in x][0]
|
||||||
|
41 41 |
|
||||||
|
|
||||||
|
RUF015.py:39:1: RUF015 [*] Prefer `next(i for i in x if i > 5)` over single element slice
|
||||||
|
|
|
||||||
|
37 | # RUF015 (doesn't mirror the underlying list)
|
||||||
|
38 | [i + 1 for i in x][0]
|
||||||
|
39 | [i for i in x if i > 5][0]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF015
|
||||||
|
40 | [(i, i + 1) for i in x][0]
|
||||||
|
|
|
||||||
|
= help: Replace with `next(i for i in x if i > 5)`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
36 36 |
|
||||||
|
37 37 | # RUF015 (doesn't mirror the underlying list)
|
||||||
|
38 38 | [i + 1 for i in x][0]
|
||||||
|
39 |-[i for i in x if i > 5][0]
|
||||||
|
39 |+next(i for i in x if i > 5)
|
||||||
|
40 40 | [(i, i + 1) for i in x][0]
|
||||||
|
41 41 |
|
||||||
|
42 42 | # RUF015 (multiple generators)
|
||||||
|
|
||||||
|
RUF015.py:40:1: RUF015 [*] Prefer `next((i, i + 1) for i in x)` over single element slice
|
||||||
|
|
|
||||||
|
38 | [i + 1 for i in x][0]
|
||||||
|
39 | [i for i in x if i > 5][0]
|
||||||
|
40 | [(i, i + 1) for i in x][0]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF015
|
||||||
|
41 |
|
||||||
|
42 | # RUF015 (multiple generators)
|
||||||
|
|
|
||||||
|
= help: Replace with `next((i, i + 1) for i in x)`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
37 37 | # RUF015 (doesn't mirror the underlying list)
|
||||||
|
38 38 | [i + 1 for i in x][0]
|
||||||
|
39 39 | [i for i in x if i > 5][0]
|
||||||
|
40 |-[(i, i + 1) for i in x][0]
|
||||||
|
40 |+next((i, i + 1) for i in x)
|
||||||
|
41 41 |
|
||||||
|
42 42 | # RUF015 (multiple generators)
|
||||||
|
43 43 | y = range(10)
|
||||||
|
|
||||||
|
RUF015.py:44:1: RUF015 [*] Prefer `next(i + j for i in x for j in y)` over single element slice
|
||||||
|
|
|
||||||
|
42 | # RUF015 (multiple generators)
|
||||||
|
43 | y = range(10)
|
||||||
|
44 | [i + j for i in x for j in y][0]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF015
|
||||||
|
45 |
|
||||||
|
46 | # RUF015
|
||||||
|
|
|
||||||
|
= help: Replace with `next(i + j for i in x for j in y)`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
41 41 |
|
||||||
|
42 42 | # RUF015 (multiple generators)
|
||||||
|
43 43 | y = range(10)
|
||||||
|
44 |-[i + j for i in x for j in y][0]
|
||||||
|
44 |+next(i + j for i in x for j in y)
|
||||||
|
45 45 |
|
||||||
|
46 46 | # RUF015
|
||||||
|
47 47 | list(range(10))[0]
|
||||||
|
|
||||||
|
RUF015.py:47:1: RUF015 [*] Prefer `next(iter(range(10)))` over single element slice
|
||||||
|
|
|
||||||
|
46 | # RUF015
|
||||||
|
47 | list(range(10))[0]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ RUF015
|
||||||
|
48 | list(x.y)[0]
|
||||||
|
49 | list(x["y"])[0]
|
||||||
|
|
|
||||||
|
= help: Replace with `next(iter(range(10)))`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
44 44 | [i + j for i in x for j in y][0]
|
||||||
|
45 45 |
|
||||||
|
46 46 | # RUF015
|
||||||
|
47 |-list(range(10))[0]
|
||||||
|
47 |+next(iter(range(10)))
|
||||||
|
48 48 | list(x.y)[0]
|
||||||
|
49 49 | list(x["y"])[0]
|
||||||
|
50 50 |
|
||||||
|
|
||||||
|
RUF015.py:48:1: RUF015 [*] Prefer `next(iter(x.y))` over single element slice
|
||||||
|
|
|
||||||
|
46 | # RUF015
|
||||||
|
47 | list(range(10))[0]
|
||||||
|
48 | list(x.y)[0]
|
||||||
|
| ^^^^^^^^^^^^ RUF015
|
||||||
|
49 | list(x["y"])[0]
|
||||||
|
|
|
||||||
|
= help: Replace with `next(iter(x.y))`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
45 45 |
|
||||||
|
46 46 | # RUF015
|
||||||
|
47 47 | list(range(10))[0]
|
||||||
|
48 |-list(x.y)[0]
|
||||||
|
48 |+next(iter(x.y))
|
||||||
|
49 49 | list(x["y"])[0]
|
||||||
|
50 50 |
|
||||||
|
51 51 | # RUF015 (multi-line)
|
||||||
|
|
||||||
|
RUF015.py:49:1: RUF015 [*] Prefer `next(iter(x["y"]))` over single element slice
|
||||||
|
|
|
||||||
|
47 | list(range(10))[0]
|
||||||
|
48 | list(x.y)[0]
|
||||||
|
49 | list(x["y"])[0]
|
||||||
|
| ^^^^^^^^^^^^^^^ RUF015
|
||||||
|
50 |
|
||||||
|
51 | # RUF015 (multi-line)
|
||||||
|
|
|
||||||
|
= help: Replace with `next(iter(x["y"]))`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
46 46 | # RUF015
|
||||||
|
47 47 | list(range(10))[0]
|
||||||
|
48 48 | list(x.y)[0]
|
||||||
|
49 |-list(x["y"])[0]
|
||||||
|
49 |+next(iter(x["y"]))
|
||||||
|
50 50 |
|
||||||
|
51 51 | # RUF015 (multi-line)
|
||||||
|
52 52 | revision_heads_map_ast = [
|
||||||
|
|
||||||
|
RUF015.py:52:26: RUF015 [*] Prefer `next(...)` over single element slice
|
||||||
|
|
|
||||||
|
51 | # RUF015 (multi-line)
|
||||||
|
52 | revision_heads_map_ast = [
|
||||||
|
| __________________________^
|
||||||
|
53 | | a
|
||||||
|
54 | | for a in revision_heads_map_ast_obj.body
|
||||||
|
55 | | if isinstance(a, ast.Assign) and a.targets[0].id == "REVISION_HEADS_MAP"
|
||||||
|
56 | | ][0]
|
||||||
|
| |____^ RUF015
|
||||||
|
|
|
||||||
|
= help: Replace with `next(...)`
|
||||||
|
|
||||||
|
ℹ Suggested fix
|
||||||
|
49 49 | list(x["y"])[0]
|
||||||
|
50 50 |
|
||||||
|
51 51 | # RUF015 (multi-line)
|
||||||
|
52 |-revision_heads_map_ast = [
|
||||||
|
52 |+revision_heads_map_ast = next(
|
||||||
|
53 53 | a
|
||||||
|
54 54 | for a in revision_heads_map_ast_obj.body
|
||||||
|
55 55 | if isinstance(a, ast.Assign) and a.targets[0].id == "REVISION_HEADS_MAP"
|
||||||
|
56 |-][0]
|
||||||
|
56 |+)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue