From bad4cdadbdb9043ea76a4a67b327a35b12d75fe8 Mon Sep 17 00:00:00 2001 From: Eddy810 Date: Fri, 20 Feb 2026 13:46:34 -0500 Subject: [PATCH 1/2] Add warning for list compre leak issue --- Python/symtable.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/Python/symtable.c b/Python/symtable.c index 23aeaaa76f613d..63145df6f9ff7a 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -179,6 +179,9 @@ static int symtable_visit_alias(struct symtable *st, alias_ty); static int symtable_visit_comprehension(struct symtable *st, comprehension_ty); static int symtable_visit_keyword(struct symtable *st, keyword_ty); static int symtable_visit_slice(struct symtable *st, slice_ty); +static int symtable_warn_listcomp_target(struct symtable *st, expr_ty target, + int lineno); +static int symtable_warn_listcomp_shadowing(struct symtable *st, expr_ty e); static int symtable_visit_params(struct symtable *st, asdl_seq *args, int top); static int symtable_visit_params_nested(struct symtable *st, asdl_seq *args); static int symtable_implicit_arg(struct symtable *st, int pos); @@ -1483,6 +1486,61 @@ symtable_visit_slice(struct symtable *st, slice_ty s) return 1; } +static int +symtable_warn_listcomp_target(struct symtable *st, expr_ty target, int lineno) +{ + switch (target->kind) { + case Name_kind: { + long flags = symtable_lookup(st, target->v.Name.id); + if (flags & (DEF_BOUND | DEF_GLOBAL)) { + char buf[256]; + PyOS_snprintf(buf, sizeof(buf), + "list comprehension rebinds name '%.100s'; " + "in 3.x the name is not rebound", + PyString_AS_STRING(target->v.Name.id)); + if (!symtable_warn(st, PyExc_Py3xWarning, buf, lineno)) + return 0; + } + break; + } + case Tuple_kind: { + int i; + for (i = 0; i < asdl_seq_LEN(target->v.Tuple.elts); i++) { + expr_ty elt = (expr_ty)asdl_seq_GET(target->v.Tuple.elts, i); + if (!symtable_warn_listcomp_target(st, elt, lineno)) + return 0; + } + break; + } + case List_kind: { + int i; + for (i = 0; i < asdl_seq_LEN(target->v.List.elts); i++) { + expr_ty elt = (expr_ty)asdl_seq_GET(target->v.List.elts, i); + if (!symtable_warn_listcomp_target(st, elt, lineno)) + return 0; + } + break; + } + default: + break; + } + return 1; +} + +static int +symtable_warn_listcomp_shadowing(struct symtable *st, expr_ty e) +{ + int i; + asdl_seq *generators = e->v.ListComp.generators; + + for (i = 0; i < asdl_seq_LEN(generators); i++) { + comprehension_ty lc = (comprehension_ty)asdl_seq_GET(generators, i); + if (!symtable_warn_listcomp_target(st, lc->target, e->lineno)) + return 0; + } + return 1; +} + static int symtable_new_tmpname(struct symtable *st) { @@ -1556,6 +1614,10 @@ symtable_visit_listcomp(struct symtable *st, expr_ty e) { asdl_seq *generators = e->v.ListComp.generators; int i, is_generator; + + if (Py_Py3kWarningFlag && !symtable_warn_listcomp_shadowing(st, e)) + return 0; + /* In order to check for yield expressions under '-3', we clear the generator flag, and restore it at the end */ is_generator = st->st_cur->ste_generator; From fe9a8685c3efa99fef2b0a7004c4297b692f1b8e Mon Sep 17 00:00:00 2001 From: Eddy810 Date: Fri, 20 Feb 2026 13:49:52 -0500 Subject: [PATCH 2/2] commit test in test_grammar.py --- Lib/test/test_grammar.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index 76778e63d47e9b..c18db1805da405 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -1070,6 +1070,9 @@ def foo(): print([x for x in [1, 2, 2]]) with warnings.catch_warnings(record=True) as w: warnings.filterwarnings('always', category=Py3xWarning) def foo(): x = 0; [x for x in [1, 2, 2]]; print(x) + with check_py3k_warnings( + ("list comprehension rebinds name 'x'", Py3xWarning)): + exec "x = 'outer'; ys = [x for x in range(3)]\n" in {} def foo(): x = 0; print(x); [x for x in [1, 2, 2]] def foo(): x = 0