From 376ebd63e4619ce701d071b0ecf0e177e47a2427 Mon Sep 17 00:00:00 2001 From: Soham Kute <144021393+sohamukute@users.noreply.github.com> Date: Sun, 2 Nov 2025 01:34:41 +0530 Subject: [PATCH] FURB142: Parenthesize generator arg in fixer to preserve scoping - Fixes #21098. - When rewriting set.add(...) calls inside for-loops into set.update(...), wrap an unparenthesized generator-expression argument in parentheses to avoid altering scope and causing NameError. - Adds targeted logic in for_loop_set_mutations to detect Expr::Generator with parenthesized == false and parenthesize its source text. --- .../refurb/rules/for_loop_set_mutations.rs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/crates/ruff_linter/src/rules/refurb/rules/for_loop_set_mutations.rs b/crates/ruff_linter/src/rules/refurb/rules/for_loop_set_mutations.rs index 202f509557..df224cff73 100644 --- a/crates/ruff_linter/src/rules/refurb/rules/for_loop_set_mutations.rs +++ b/crates/ruff_linter/src/rules/refurb/rules/for_loop_set_mutations.rs @@ -111,13 +111,24 @@ pub(crate) fn for_loop_set_mutations(checker: &Checker, for_stmt: &StmtFor) { parenthesize_loop_iter_if_necessary(for_stmt, checker, IterLocation::Call), ) } - (for_target, arg) => format!( - "{}.{batch_method_name}({} for {} in {})", - set.id, - locator.slice(arg), - locator.slice(for_target), - parenthesize_loop_iter_if_necessary(for_stmt, checker, IterLocation::Comprehension), - ), + (for_target, arg) => { + // Parenthesize an unparenthesized generator expression argument to preserve semantics, + // e.g. `s.add(c for c in x)` -> `s.update((c for c in x) for x in ...)`. + let arg_text = match arg { + Expr::Generator(generator) if !generator.parenthesized => { + format!("({})", locator.slice(arg)) + } + _ => locator.slice(arg).to_string(), + }; + + format!( + "{}.{batch_method_name}({} for {} in {})", + set.id, + arg_text, + locator.slice(for_target), + parenthesize_loop_iter_if_necessary(for_stmt, checker, IterLocation::Comprehension), + ) + } }; let applicability = if checker.comment_ranges().intersects(for_stmt.range) {