Ignore list-copy recommendations for async `for` loops (#11250)

## Summary

Removes these from `PERF402`, but adds them to `PERF401`, with a custom
message to use an `async` comprehension.

Closes https://github.com/astral-sh/ruff/issues/10787.
This commit is contained in:
Charlie Marsh 2024-05-02 11:48:52 -07:00 committed by GitHub
parent 64700d296f
commit 3a7c01b365
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 69 additions and 17 deletions

View File

@ -72,3 +72,18 @@ def f():
result = Foo()
for i in items:
result.append(i) # Ok
def f():
items = [1, 2, 3, 4]
result = []
async for i in items:
if i % 2:
result.append(i) # PERF401
def f():
items = [1, 2, 3, 4]
result = []
async for i in items:
result.append(i) # PERF401

View File

@ -43,3 +43,10 @@ def f():
for path in ("foo", "bar"):
sys.path.append(path) # OK
def f():
items = [1, 2, 3, 4]
result = []
async for i in items:
result.append(i) # PERF402

View File

@ -1323,10 +1323,10 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
pylint::rules::dict_iter_missing_items(checker, target, iter);
}
if checker.enabled(Rule::ManualListComprehension) {
perflint::rules::manual_list_comprehension(checker, target, body);
perflint::rules::manual_list_comprehension(checker, for_stmt);
}
if checker.enabled(Rule::ManualListCopy) {
perflint::rules::manual_list_copy(checker, target, body);
perflint::rules::manual_list_copy(checker, for_stmt);
}
if checker.enabled(Rule::ManualDictComprehension) {
perflint::rules::manual_dict_comprehension(checker, target, body);

View File

@ -44,22 +44,28 @@ use crate::checkers::ast::Checker;
/// filtered.extend(x for x in original if x % 2)
/// ```
#[violation]
pub struct ManualListComprehension;
pub struct ManualListComprehension {
is_async: bool,
}
impl Violation for ManualListComprehension {
#[derive_message_formats]
fn message(&self) -> String {
format!("Use a list comprehension to create a transformed list")
let ManualListComprehension { is_async } = self;
match is_async {
false => format!("Use a list comprehension to create a transformed list"),
true => format!("Use an async list comprehension to create a transformed list"),
}
}
}
/// PERF401
pub(crate) fn manual_list_comprehension(checker: &mut Checker, target: &Expr, body: &[Stmt]) {
let Expr::Name(ast::ExprName { id, .. }) = target else {
pub(crate) fn manual_list_comprehension(checker: &mut Checker, for_stmt: &ast::StmtFor) {
let Expr::Name(ast::ExprName { id, .. }) = &*for_stmt.target else {
return;
};
let (stmt, if_test) = match body {
let (stmt, if_test) = match &*for_stmt.body {
// ```python
// for x in y:
// if z:
@ -121,10 +127,13 @@ pub(crate) fn manual_list_comprehension(checker: &mut Checker, target: &Expr, bo
return;
}
// Ignore direct list copies (e.g., `for x in y: filtered.append(x)`).
if if_test.is_none() {
if arg.as_name_expr().is_some_and(|arg| arg.id == *id) {
return;
// Ignore direct list copies (e.g., `for x in y: filtered.append(x)`), unless it's async, which
// `manual-list-copy` doesn't cover.
if !for_stmt.is_async {
if if_test.is_none() {
if arg.as_name_expr().is_some_and(|arg| arg.id == *id) {
return;
}
}
}
@ -179,7 +188,10 @@ pub(crate) fn manual_list_comprehension(checker: &mut Checker, target: &Expr, bo
return;
}
checker
.diagnostics
.push(Diagnostic::new(ManualListComprehension, *range));
checker.diagnostics.push(Diagnostic::new(
ManualListComprehension {
is_async: for_stmt.is_async,
},
*range,
));
}

View File

@ -45,12 +45,16 @@ impl Violation for ManualListCopy {
}
/// PERF402
pub(crate) fn manual_list_copy(checker: &mut Checker, target: &Expr, body: &[Stmt]) {
let Expr::Name(ast::ExprName { id, .. }) = target else {
pub(crate) fn manual_list_copy(checker: &mut Checker, for_stmt: &ast::StmtFor) {
if for_stmt.is_async {
return;
}
let Expr::Name(ast::ExprName { id, .. }) = &*for_stmt.target else {
return;
};
let [stmt] = body else {
let [stmt] = &*for_stmt.body else {
return;
};

View File

@ -17,4 +17,18 @@ PERF401.py:13:9: PERF401 Use a list comprehension to create a transformed list
| ^^^^^^^^^^^^^^^^^^^^ PERF401
|
PERF401.py:82:13: PERF401 Use an async list comprehension to create a transformed list
|
80 | async for i in items:
81 | if i % 2:
82 | result.append(i) # PERF401
| ^^^^^^^^^^^^^^^^ PERF401
|
PERF401.py:89:9: PERF401 Use an async list comprehension to create a transformed list
|
87 | result = []
88 | async for i in items:
89 | result.append(i) # PERF401
| ^^^^^^^^^^^^^^^^ PERF401
|