mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
[pyupgrade
] Do not offer fix when at least one target is global
/nonlocal
(UP028
) (#16451)
## Summary Resolves #16445. `UP028` is now no longer always fixable: it will not offer a fix when at least one `ExprName` target is bound to either a `global` or a `nonlocal` declaration. ## Test Plan `cargo nextest run` and `cargo insta test`.
This commit is contained in:
parent
d93ed293eb
commit
a3ae76edc0
5 changed files with 115 additions and 27 deletions
|
@ -163,3 +163,26 @@ def f():
|
|||
pass
|
||||
except Exception as x:
|
||||
pass
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/15540
|
||||
def f():
|
||||
for a in 1,:
|
||||
yield a
|
||||
|
||||
|
||||
SOME_GLOBAL = None
|
||||
|
||||
def f(iterable):
|
||||
global SOME_GLOBAL
|
||||
|
||||
for SOME_GLOBAL in iterable:
|
||||
yield SOME_GLOBAL
|
||||
|
||||
some_non_local = None
|
||||
|
||||
def g():
|
||||
nonlocal some_non_local
|
||||
|
||||
for some_non_local in iterable:
|
||||
yield some_non_local
|
||||
|
|
|
@ -123,7 +123,20 @@ def f():
|
|||
yield x, y, x + y
|
||||
|
||||
|
||||
# https://github.com/astral-sh/ruff/issues/15540
|
||||
def f():
|
||||
for a in 1,:
|
||||
yield a
|
||||
global some_global
|
||||
|
||||
for element in iterable:
|
||||
some_global = element
|
||||
yield some_global
|
||||
|
||||
|
||||
def f():
|
||||
some_nonlocal = 1
|
||||
|
||||
def g():
|
||||
nonlocal some_nonlocal
|
||||
|
||||
for element in iterable:
|
||||
some_nonlocal = element
|
||||
yield some_nonlocal
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
|
||||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||
use ruff_python_ast::parenthesize::parenthesized_range;
|
||||
use ruff_python_ast::{self as ast, Expr, Stmt};
|
||||
|
@ -17,11 +17,19 @@ use crate::checkers::ast::Checker;
|
|||
/// ```python
|
||||
/// for x in foo:
|
||||
/// yield x
|
||||
///
|
||||
/// global y
|
||||
/// for y in foo:
|
||||
/// yield y
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```python
|
||||
/// yield from foo
|
||||
///
|
||||
/// for _element in foo:
|
||||
/// y = _element
|
||||
/// yield y
|
||||
/// ```
|
||||
///
|
||||
/// ## Fix safety
|
||||
|
@ -31,6 +39,9 @@ use crate::checkers::ast::Checker;
|
|||
/// to a `yield from` could lead to an attribute error if the underlying
|
||||
/// generator does not implement the `send` method.
|
||||
///
|
||||
/// Additionally, if at least one target is `global` or `nonlocal`,
|
||||
/// no fix will be offered.
|
||||
///
|
||||
/// In most cases, however, the fix is safe, and such a modification should have
|
||||
/// no effect on the behavior of the program.
|
||||
///
|
||||
|
@ -40,14 +51,16 @@ use crate::checkers::ast::Checker;
|
|||
#[derive(ViolationMetadata)]
|
||||
pub(crate) struct YieldInForLoop;
|
||||
|
||||
impl AlwaysFixableViolation for YieldInForLoop {
|
||||
impl Violation for YieldInForLoop {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[derive_message_formats]
|
||||
fn message(&self) -> String {
|
||||
"Replace `yield` over `for` loop with `yield from`".to_string()
|
||||
}
|
||||
|
||||
fn fix_title(&self) -> String {
|
||||
"Replace with `yield from`".to_string()
|
||||
fn fix_title(&self) -> Option<String> {
|
||||
Some("Replace with `yield from`".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,10 +143,22 @@ pub(crate) fn yield_in_for_loop(checker: &Checker, stmt_for: &ast::StmtFor) {
|
|||
format!("yield from {contents}")
|
||||
};
|
||||
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
contents,
|
||||
stmt_for.range(),
|
||||
)));
|
||||
if !collect_names(value).any(|name| {
|
||||
let semantic = checker.semantic();
|
||||
let mut bindings = semantic.current_scope().get_all(name.id.as_str());
|
||||
|
||||
bindings.any(|id| {
|
||||
let binding = semantic.binding(id);
|
||||
|
||||
binding.is_global() || binding.is_nonlocal()
|
||||
})
|
||||
}) {
|
||||
diagnostic.set_fix(Fix::unsafe_edit(Edit::range_replacement(
|
||||
contents,
|
||||
stmt_for.range(),
|
||||
)));
|
||||
}
|
||||
|
||||
checker.report_diagnostic(diagnostic);
|
||||
}
|
||||
|
||||
|
|
|
@ -380,3 +380,46 @@ UP028_0.py:134:5: UP028 [*] Replace `yield` over `for` loop with `yield from`
|
|||
136 135 | # Shadowing with multiple `except` blocks
|
||||
137 136 | try:
|
||||
138 137 | pass
|
||||
|
||||
UP028_0.py:170:5: UP028 [*] Replace `yield` over `for` loop with `yield from`
|
||||
|
|
||||
168 | # https://github.com/astral-sh/ruff/issues/15540
|
||||
169 | def f():
|
||||
170 | / for a in 1,:
|
||||
171 | | yield a
|
||||
| |_______________^ UP028
|
||||
|
|
||||
= help: Replace with `yield from`
|
||||
|
||||
ℹ Unsafe fix
|
||||
167 167 |
|
||||
168 168 | # https://github.com/astral-sh/ruff/issues/15540
|
||||
169 169 | def f():
|
||||
170 |- for a in 1,:
|
||||
171 |- yield a
|
||||
170 |+ yield from (1,)
|
||||
172 171 |
|
||||
173 172 |
|
||||
174 173 | SOME_GLOBAL = None
|
||||
|
||||
UP028_0.py:179:5: UP028 Replace `yield` over `for` loop with `yield from`
|
||||
|
|
||||
177 | global SOME_GLOBAL
|
||||
178 |
|
||||
179 | / for SOME_GLOBAL in iterable:
|
||||
180 | | yield SOME_GLOBAL
|
||||
| |_________________________^ UP028
|
||||
181 |
|
||||
182 | some_non_local = None
|
||||
|
|
||||
= help: Replace with `yield from`
|
||||
|
||||
UP028_0.py:187:9: UP028 Replace `yield` over `for` loop with `yield from`
|
||||
|
|
||||
185 | nonlocal some_non_local
|
||||
186 |
|
||||
187 | / for some_non_local in iterable:
|
||||
188 | | yield some_non_local
|
||||
| |________________________________^ UP028
|
||||
|
|
||||
= help: Replace with `yield from`
|
||||
|
|
|
@ -1,20 +1,4 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
UP028_1.py:128:5: UP028 [*] Replace `yield` over `for` loop with `yield from`
|
||||
|
|
||||
126 | # https://github.com/astral-sh/ruff/issues/15540
|
||||
127 | def f():
|
||||
128 | / for a in 1,:
|
||||
129 | | yield a
|
||||
| |_______________^ UP028
|
||||
|
|
||||
= help: Replace with `yield from`
|
||||
|
||||
ℹ Unsafe fix
|
||||
125 125 |
|
||||
126 126 | # https://github.com/astral-sh/ruff/issues/15540
|
||||
127 127 | def f():
|
||||
128 |- for a in 1,:
|
||||
129 |- yield a
|
||||
128 |+ yield from (1,)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue