mirror of https://github.com/astral-sh/ruff
[`refurb`] Minor nits regarding `for-loop-writes` and `for-loop-set-mutations` (#15958)
This commit is contained in:
parent
827a076a2f
commit
7ca778f492
|
|
@ -4,7 +4,8 @@ use ruff_python_ast::{Expr, Stmt, StmtFor};
|
||||||
use ruff_python_semantic::analyze::typing;
|
use ruff_python_semantic::analyze::typing;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::rules::refurb::rules::for_loop_writes::parenthesize_loop_iter_if_necessary;
|
|
||||||
|
use super::helpers::parenthesize_loop_iter_if_necessary;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for code that updates a set with the contents of an iterable by
|
/// Checks for code that updates a set with the contents of an iterable by
|
||||||
|
|
@ -35,6 +36,10 @@ use crate::rules::refurb::rules::for_loop_writes::parenthesize_loop_iter_if_nece
|
||||||
/// s.difference_update((1, 2, 3))
|
/// s.difference_update((1, 2, 3))
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// ## Fix safety
|
||||||
|
/// The fix will be marked as unsafe if applying the fix would delete any comments.
|
||||||
|
/// Otherwise, it is marked as safe.
|
||||||
|
///
|
||||||
/// ## References
|
/// ## References
|
||||||
/// - [Python documentation: `set`](https://docs.python.org/3/library/stdtypes.html#set)
|
/// - [Python documentation: `set`](https://docs.python.org/3/library/stdtypes.html#set)
|
||||||
#[derive(ViolationMetadata)]
|
#[derive(ViolationMetadata)]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix};
|
use ruff_diagnostics::{AlwaysFixableViolation, Applicability, Diagnostic, Edit, Fix};
|
||||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
|
||||||
use ruff_python_ast::{Expr, ExprList, ExprName, ExprTuple, Stmt, StmtFor};
|
use ruff_python_ast::{Expr, ExprList, ExprName, ExprTuple, Stmt, StmtFor};
|
||||||
use ruff_python_semantic::analyze::typing;
|
use ruff_python_semantic::analyze::typing;
|
||||||
use ruff_python_semantic::{Binding, ScopeId, SemanticModel, TypingOnlyBindingsStatus};
|
use ruff_python_semantic::{Binding, ScopeId, SemanticModel, TypingOnlyBindingsStatus};
|
||||||
|
|
@ -8,6 +7,8 @@ use ruff_text_size::{Ranged, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
use super::helpers::parenthesize_loop_iter_if_necessary;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for the use of `IOBase.write` in a for loop.
|
/// Checks for the use of `IOBase.write` in a for loop.
|
||||||
///
|
///
|
||||||
|
|
@ -245,26 +246,3 @@ fn loop_variables_are_used_outside_loop(
|
||||||
.iter()
|
.iter()
|
||||||
.any(|name| name_overwrites_outer(name) || name_is_used_later(name))
|
.any(|name| name_overwrites_outer(name) || name_is_used_later(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn parenthesize_loop_iter_if_necessary(for_stmt: &StmtFor, checker: &Checker) -> String {
|
|
||||||
let locator = checker.locator();
|
|
||||||
let iter = for_stmt.iter.as_ref();
|
|
||||||
|
|
||||||
let original_parenthesized_range = parenthesized_range(
|
|
||||||
iter.into(),
|
|
||||||
for_stmt.into(),
|
|
||||||
checker.comment_ranges(),
|
|
||||||
checker.source(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(range) = original_parenthesized_range {
|
|
||||||
return locator.slice(range).to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
let iter_in_source = locator.slice(iter);
|
|
||||||
|
|
||||||
match iter {
|
|
||||||
Expr::Tuple(tuple) if !tuple.parenthesized => format!("({iter_in_source})"),
|
|
||||||
_ => iter_in_source.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use ruff_python_ast::{self as ast, parenthesize::parenthesized_range};
|
||||||
|
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
/// A helper function that extracts the `iter` from a [`ast::StmtFor`] node and,
|
||||||
|
/// if the `iter` is an unparenthesized tuple, adds parentheses:
|
||||||
|
///
|
||||||
|
/// - `for x in z: ...` -> `"x"`
|
||||||
|
/// - `for (x, y) in z: ...` -> `"(x, y)"`
|
||||||
|
/// - `for [x, y] in z: ...` -> `"[x, y]"`
|
||||||
|
/// - `for x, y in z: ...` -> `"(x, y)"` # <-- Parentheses added only for this example
|
||||||
|
pub(super) fn parenthesize_loop_iter_if_necessary<'a>(
|
||||||
|
for_stmt: &'a ast::StmtFor,
|
||||||
|
checker: &'a Checker,
|
||||||
|
) -> Cow<'a, str> {
|
||||||
|
let locator = checker.locator();
|
||||||
|
let iter = for_stmt.iter.as_ref();
|
||||||
|
|
||||||
|
let original_parenthesized_range = parenthesized_range(
|
||||||
|
iter.into(),
|
||||||
|
for_stmt.into(),
|
||||||
|
checker.comment_ranges(),
|
||||||
|
checker.source(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(range) = original_parenthesized_range {
|
||||||
|
return Cow::Borrowed(locator.slice(range));
|
||||||
|
}
|
||||||
|
|
||||||
|
let iter_in_source = locator.slice(iter);
|
||||||
|
|
||||||
|
match iter {
|
||||||
|
ast::Expr::Tuple(tuple) if !tuple.parenthesized => {
|
||||||
|
Cow::Owned(format!("({iter_in_source})"))
|
||||||
|
}
|
||||||
|
_ => Cow::Borrowed(iter_in_source),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -42,6 +42,7 @@ mod for_loop_writes;
|
||||||
mod fstring_number_format;
|
mod fstring_number_format;
|
||||||
mod hardcoded_string_charset;
|
mod hardcoded_string_charset;
|
||||||
mod hashlib_digest_hex;
|
mod hashlib_digest_hex;
|
||||||
|
mod helpers;
|
||||||
mod if_exp_instead_of_or_operator;
|
mod if_exp_instead_of_or_operator;
|
||||||
mod if_expr_min_max;
|
mod if_expr_min_max;
|
||||||
mod implicit_cwd;
|
mod implicit_cwd;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue