@@ -2217,6 +2217,52 @@ fn is_var_used_as_dict_key_in_stmt(var_name: &str, stmt: &HirStmt) -> bool {
22172217 }
22182218}
22192219
2220+ /// Check if a variable is reassigned in a statement
2221+ /// DEPYLER-0756: Loop variables that are reassigned need `mut` in for pattern
2222+ fn is_var_reassigned_in_stmt ( var_name : & str , stmt : & HirStmt ) -> bool {
2223+ match stmt {
2224+ HirStmt :: Assign { target, .. } => {
2225+ // Only count as reassignment if the target is the exact variable
2226+ // Note: Augmented assignment (+=, -=) is also represented as Assign in HIR
2227+ matches ! ( target, AssignTarget :: Symbol ( name) if name == var_name)
2228+ }
2229+ HirStmt :: If {
2230+ then_body,
2231+ else_body,
2232+ ..
2233+ } => {
2234+ then_body
2235+ . iter ( )
2236+ . any ( |s| is_var_reassigned_in_stmt ( var_name, s) )
2237+ || else_body. as_ref ( ) . is_some_and ( |body| {
2238+ body. iter ( ) . any ( |s| is_var_reassigned_in_stmt ( var_name, s) )
2239+ } )
2240+ }
2241+ HirStmt :: While { body, .. } => body. iter ( ) . any ( |s| is_var_reassigned_in_stmt ( var_name, s) ) ,
2242+ HirStmt :: For { body, .. } => body. iter ( ) . any ( |s| is_var_reassigned_in_stmt ( var_name, s) ) ,
2243+ HirStmt :: Try {
2244+ body,
2245+ handlers,
2246+ orelse,
2247+ finalbody,
2248+ ..
2249+ } => {
2250+ body. iter ( ) . any ( |s| is_var_reassigned_in_stmt ( var_name, s) )
2251+ || handlers
2252+ . iter ( )
2253+ . any ( |h| h. body . iter ( ) . any ( |s| is_var_reassigned_in_stmt ( var_name, s) ) )
2254+ || orelse. as_ref ( ) . is_some_and ( |stmts| {
2255+ stmts. iter ( ) . any ( |s| is_var_reassigned_in_stmt ( var_name, s) )
2256+ } )
2257+ || finalbody. as_ref ( ) . is_some_and ( |stmts| {
2258+ stmts. iter ( ) . any ( |s| is_var_reassigned_in_stmt ( var_name, s) )
2259+ } )
2260+ }
2261+ HirStmt :: With { body, .. } => body. iter ( ) . any ( |s| is_var_reassigned_in_stmt ( var_name, s) ) ,
2262+ _ => false ,
2263+ }
2264+ }
2265+
22202266/// Check if a variable is used in a statement
22212267/// DEPYLER-0303 Phase 2: Fixed to check assignment targets too (for `d[k] = v`)
22222268fn is_var_used_in_stmt ( var_name : & str , stmt : & HirStmt ) -> bool {
@@ -2360,6 +2406,13 @@ pub(crate) fn codegen_for_stmt(
23602406 body. iter ( ) . any ( |stmt| is_var_used_in_stmt ( var_name, stmt) )
23612407 } ;
23622408
2409+ // DEPYLER-0756: Helper to check if a variable is reassigned in the loop body
2410+ // If a loop variable is reassigned (e.g., `line = line.strip()`), we need `mut`
2411+ let is_reassigned_in_body = |var_name : & str | -> bool {
2412+ body. iter ( )
2413+ . any ( |stmt| is_var_reassigned_in_stmt ( var_name, stmt) )
2414+ } ;
2415+
23632416 // Generate target pattern based on AssignTarget type
23642417 let target_pattern: syn:: Pat = match target {
23652418 AssignTarget :: Symbol ( name) => {
@@ -2370,11 +2423,17 @@ pub(crate) fn codegen_for_stmt(
23702423 format ! ( "_{}" , name)
23712424 } ;
23722425 let ident = safe_ident ( & var_name) ; // DEPYLER-0023
2373- parse_quote ! { #ident }
2426+ // DEPYLER-0756: Add `mut` if variable is reassigned inside the loop
2427+ if is_reassigned_in_body ( name) {
2428+ parse_quote ! { mut #ident }
2429+ } else {
2430+ parse_quote ! { #ident }
2431+ }
23742432 }
23752433 AssignTarget :: Tuple ( targets) => {
23762434 // For tuple unpacking, check each variable individually
2377- let idents: Vec < syn:: Ident > = targets
2435+ // DEPYLER-0756: Check if any tuple element is reassigned
2436+ let patterns: Vec < syn:: Pat > = targets
23782437 . iter ( )
23792438 . map ( |t| match t {
23802439 AssignTarget :: Symbol ( s) => {
@@ -2384,12 +2443,18 @@ pub(crate) fn codegen_for_stmt(
23842443 } else {
23852444 format ! ( "_{}" , s)
23862445 } ;
2387- safe_ident ( & var_name) // DEPYLER-0023
2446+ let ident = safe_ident ( & var_name) ; // DEPYLER-0023
2447+ // DEPYLER-0756: Add `mut` if this tuple element is reassigned
2448+ if is_reassigned_in_body ( s) {
2449+ parse_quote ! { mut #ident }
2450+ } else {
2451+ parse_quote ! { #ident }
2452+ }
23882453 }
2389- _ => safe_ident ( " _nested" ) , // Nested tuple unpacking - use placeholder
2454+ _ => parse_quote ! { _nested } , // Nested tuple unpacking - use placeholder
23902455 } )
23912456 . collect ( ) ;
2392- parse_quote ! { ( #( #idents ) , * ) }
2457+ parse_quote ! { ( #( #patterns ) , * ) }
23932458 }
23942459 _ => bail ! ( "Unsupported for loop target type" ) ,
23952460 } ;
0 commit comments