Skip to main content

depyler_core/rust_gen/
context.rs

1//! Code generation context and core traits
2//!
3//! This module provides the central CodeGenContext struct that maintains
4//! state during Rust code generation, along with core traits used across
5//! the code generation pipeline.
6
7#[cfg(test)]
8#[path = "context_tests.rs"]
9mod tests;
10
11use crate::annotation_aware_type_mapper::AnnotationAwareTypeMapper;
12use crate::hir::{ExceptionScope, Type};
13use crate::string_optimization::StringOptimizer;
14use anyhow::Result;
15use std::collections::{HashMap, HashSet};
16
17/// Error type classification for Result<T, E> return types
18///
19/// DEPYLER-0310: Tracks whether function uses Box<dyn Error> (mixed types)
20/// or a concrete error type (single type). This determines if raise statements
21/// need Box::new() wrapper.
22///
23/// # Complexity
24/// N/A (enum definition)
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub enum ErrorType {
27    /// Concrete error type (e.g., ValueError, ZeroDivisionError)
28    /// No wrapping needed: `return Err(ValueError::new(...))`
29    Concrete(String),
30    /// Box<dyn Error> - mixed or generic error types
31    /// Needs wrapping: `return Err(Box::new(ValueError::new(...)))`
32    DynBox,
33}
34
35/// Code generation context
36///
37/// Maintains all state needed during Rust code generation including:
38/// - Type mapping and string optimization
39/// - Import tracking (needs_hashmap, needs_cow, etc.)
40/// - Variable scoping and mutability analysis
41/// - Generator state management
42///
43/// # Complexity
44/// N/A (data structure)
45pub struct CodeGenContext<'a> {
46    pub type_mapper: &'a crate::type_mapper::TypeMapper,
47    pub annotation_aware_mapper: AnnotationAwareTypeMapper,
48    pub string_optimizer: StringOptimizer,
49    pub union_enum_generator: crate::union_enum_gen::UnionEnumGenerator,
50    pub generated_enums: Vec<proc_macro2::TokenStream>,
51    pub needs_hashmap: bool,
52    pub needs_hashset: bool,
53    pub needs_vecdeque: bool,
54    pub needs_fnv_hashmap: bool,
55    pub needs_ahash_hashmap: bool,
56    pub needs_arc: bool,
57    pub needs_rc: bool,
58    pub needs_cow: bool,
59    pub needs_rand: bool,
60    pub needs_slice_random: bool, // GH-207: Track rand::seq::SliceRandom trait for choose/shuffle
61    pub needs_rand_distr: bool, // GH-207: Track rand_distr crate for distributions (gauss, triangular)
62    pub needs_serde_json: bool,
63    pub needs_regex: bool,
64    pub needs_chrono: bool,
65    pub needs_tempfile: bool, // DEPYLER-0490: Track tempfile crate for temporary file operations
66    pub needs_itertools: bool, // DEPYLER-0490: Track itertools crate for advanced iteration
67    pub needs_clap: bool,     // DEPYLER-0384: Track clap dependency for ArgumentParser
68    pub needs_csv: bool,
69    pub needs_rust_decimal: bool,
70    pub needs_num_rational: bool,
71    pub needs_base64: bool,
72    pub needs_md5: bool,
73    pub needs_sha1: bool, // DEPYLER-1001: sha1 crate for hashlib.sha1()
74    pub needs_sha2: bool,
75    pub needs_sha3: bool,
76    pub needs_digest: bool, // DEPYLER-0558: For Box<dyn DynDigest> type-erased hashers
77    pub needs_blake2: bool,
78    pub needs_hex: bool,
79    pub needs_uuid: bool,
80    pub needs_hmac: bool,
81    pub needs_crc32: bool,
82    pub needs_url_encoding: bool,
83    pub needs_io_read: bool, // DEPYLER-0458: Track std::io::Read trait for file I/O
84    pub needs_io_write: bool, // DEPYLER-0458: Track std::io::Write trait for file I/O
85    pub needs_bufread: bool, // DEPYLER-0522: Track std::io::BufRead trait for .lines() method
86    pub needs_once_cell: bool, // DEPYLER-REARCH-001: Track once_cell for lazy static initialization
87    pub needs_lazy_lock: bool, // DEPYLER-1016: Track std::sync::LazyLock for NASA mode (std-only)
88    pub needs_trueno: bool,  // Phase 3: NumPy→Trueno codegen (SIMD-accelerated tensor lib)
89    pub numpy_vars: HashSet<String>, // DEPYLER-0932: Track variables assigned from numpy operations
90    pub needs_tokio: bool,   // DEPYLER-0747: asyncio→tokio async runtime mapping
91    pub needs_glob: bool,    // DEPYLER-0829: glob crate for Path.glob()/rglob()
92    pub needs_statrs: bool,  // DEPYLER-1001: statrs crate for statistics module
93    pub needs_url: bool,     // DEPYLER-1001: url crate for urllib.parse module
94    pub declared_vars: Vec<HashSet<String>>,
95    pub current_function_can_fail: bool,
96    pub current_return_type: Option<Type>,
97    pub module_mapper: crate::module_mapper::ModuleMapper,
98    pub imported_modules: std::collections::HashMap<String, crate::module_mapper::ModuleMapping>,
99    pub imported_items: std::collections::HashMap<String, String>,
100    /// DEPYLER-1115: Track all imported module names (including external unmapped ones)
101    /// Used to generate `module::function()` syntax for phantom binding compatibility
102    pub all_imported_modules: HashSet<String>,
103    /// DEPYLER-1136: Map module aliases to their original module paths
104    /// For `import xml.etree.ElementTree as ET`: maps "ET" -> "xml.etree.ElementTree"
105    pub module_aliases: HashMap<String, String>,
106    pub mutable_vars: HashSet<String>,
107    pub needs_zerodivisionerror: bool,
108    pub needs_indexerror: bool,
109    pub needs_valueerror: bool,
110    pub needs_argumenttypeerror: bool,
111    pub needs_runtimeerror: bool, // DEPYLER-0551: Python RuntimeError
112    pub needs_filenotfounderror: bool, // DEPYLER-0551: Python FileNotFoundError
113    pub needs_syntaxerror: bool,  // GH-204: Python SyntaxError
114    pub needs_typeerror: bool,    // GH-204: Python TypeError
115    pub needs_keyerror: bool,     // GH-204: Python KeyError
116    pub needs_ioerror: bool,      // GH-204: Python IOError
117    pub needs_attributeerror: bool, // GH-204: Python AttributeError
118    pub needs_stopiteration: bool, // GH-204: Python StopIteration
119    pub is_classmethod: bool,
120    pub in_generator: bool,
121    pub generator_state_vars: HashSet<String>,
122    /// DEPYLER-1082: Track generator state vars with Iterator/Generator type
123    /// These need `while let Some(x) = self.g.next()` instead of `for x in self.g`
124    /// because Box<dyn Iterator> doesn't implement IntoIterator
125    pub generator_iterator_state_vars: HashSet<String>,
126    /// DEPYLER-1076: Track when function returns impl Iterator/IntoIterator
127    /// When true, closures in iterator chains that capture local variables need `move`
128    pub returns_impl_iterator: bool,
129    pub var_types: HashMap<String, Type>,
130    pub class_names: HashSet<String>,
131    pub mutating_methods: HashMap<String, HashSet<String>>,
132    /// DEPYLER-0737: Track property method names across all classes
133    /// In Python, @property allows method access without (), but Rust requires ()
134    /// Used in convert_attribute to emit method call syntax for property access
135    pub property_methods: HashSet<String>,
136    /// DEPYLER-0269: Track function return types for Display trait selection
137    /// Maps function name -> return type, populated during function generation
138    /// Used to track types of variables assigned from function calls
139    pub function_return_types: HashMap<String, Type>,
140    /// DEPYLER-0270: Track function parameter borrowing for auto-borrow decisions
141    /// Maps function name -> Vec of booleans (true if param is borrowed, false if owned)
142    /// Used to determine whether to add & when passing List/Dict/Set arguments
143    pub function_param_borrows: HashMap<String, Vec<bool>>,
144    /// DEPYLER-0574: Track function parameters that need &mut (mutable borrow)
145    /// Maps function name -> Vec of booleans (true if param is &mut, false if &)
146    /// Used to determine whether to add &mut when passing arguments
147    pub function_param_muts: HashMap<String, Vec<bool>>,
148    /// DEPYLER-0621: Track function parameter default values
149    /// Maps function name -> Vec of Option<HirExpr> (None if no default, Some if has default)
150    /// Used to supply default arguments at call sites when fewer args are passed
151    pub function_param_defaults: HashMap<String, Vec<Option<crate::hir::HirExpr>>>,
152    /// DEPYLER-0932: Track dataclass field default values
153    /// Maps class name -> Vec of Option<HirExpr> (None if no default, Some if has default)
154    /// Used to supply default arguments at constructor call sites when fewer args are passed
155    pub class_field_defaults: HashMap<String, Vec<Option<crate::hir::HirExpr>>>,
156    /// DEPYLER-0720: Track class field types for self.field attribute access
157    /// Maps field name -> Type, used to determine if self.X is float for int-to-float coercion
158    pub class_field_types: HashMap<String, Type>,
159    /// DEPYLER-1007: Track class method return types for return type inference
160    /// Maps (ClassName, MethodName) -> Type, used to infer types from method calls
161    /// Example: ("Point", "distance_squared") -> Type::Int
162    pub class_method_return_types: HashMap<(String, String), Type>,
163    /// DEPYLER-0307 Fix #9: Track variables that iterate over tuples (from zip())
164    /// Used to generate tuple field access syntax (tuple.0, tuple.1) instead of vector indexing
165    pub tuple_iter_vars: HashSet<String>,
166    /// DEPYLER-0520: Track variables assigned from iterator-producing expressions
167    /// Used to avoid adding .iter().cloned() to variables that are already iterators
168    pub iterator_vars: HashSet<String>,
169    /// DEPYLER-0758: Track parameters passed by reference in current function
170    /// Used to dereference reference params in arithmetic operations (e.g., date subtraction)
171    pub ref_params: HashSet<String>,
172    /// DEPYLER-1217: Track parameters passed by mutable reference (&mut T) in current function
173    /// Used to avoid double &mut when passing these params to other functions expecting &mut
174    pub mut_ref_params: HashSet<String>,
175    /// DEPYLER-0271: Tracks if current statement is the final statement in its block
176    /// Used to generate idiomatic expression-based returns (no `return` keyword)
177    pub is_final_statement: bool,
178    /// DEPYLER-0308: Track functions that return Result<bool, E>
179    /// Used to auto-unwrap in boolean contexts (if/while conditions)
180    pub result_bool_functions: HashSet<String>,
181    /// DEPYLER-0270: Track ALL functions that return Result<T, E>
182    /// Used to auto-unwrap at call sites in non-Result functions
183    pub result_returning_functions: HashSet<String>,
184    /// DEPYLER-0497: Track functions that return Option<T>
185    /// Used to unwrap Options in format! macro and other contexts where Display is required
186    pub option_returning_functions: HashSet<String>,
187    /// DEPYLER-0310: Current function's error type (for raise statement wrapping)
188    /// None if function doesn't return Result, Some(ErrorType) if it does
189    pub current_error_type: Option<ErrorType>,
190    /// DEPYLER-0333: Stack of exception scopes for try/except tracking
191    /// Tracks whether code is inside try/except blocks to determine error handling strategy
192    /// Empty stack = Unhandled scope (exceptions propagate to caller)
193    pub exception_scopes: Vec<ExceptionScope>,
194    /// DEPYLER-0363: Track ArgumentParser patterns for clap transformation
195    /// Accumulates ArgumentParser instances and add_argument calls
196    /// to generate #[derive(Parser)] struct definitions
197    pub argparser_tracker: crate::rust_gen::argparse_transform::ArgParserTracker,
198    /// DEPYLER-0424: Generated Args struct for ArgumentParser (emitted at module level)
199    /// Stored here so it can be hoisted outside main() function
200    pub generated_args_struct: Option<proc_macro2::TokenStream>,
201    /// DEPYLER-0424: Generated Commands enum for subcommands (emitted at module level)
202    /// Stored here so it can be hoisted outside main() function
203    pub generated_commands_enum: Option<proc_macro2::TokenStream>,
204    /// DEPYLER-0425: Current function's subcommand fields (for expression rewriting)
205    /// If current function accesses subcommand fields, this maps field names to variant name
206    /// Used by expr_gen to rewrite args.field → field (extracted via pattern matching)
207    pub current_subcommand_fields: Option<std::collections::HashSet<String>>,
208
209    /// DEPYLER-0447: Track argparse validator functions (type= parameter in add_argument)
210    /// These functions should have &str parameter type regardless of type inference
211    /// Populated when processing add_argument(type=validator_func) calls
212    pub validator_functions: std::collections::HashSet<String>,
213
214    /// DEPYLER-0461: Track whether we're currently generating code inside a serde_json::json!() macro
215    /// When true, nested dicts must also use json!() instead of HashMap (json!() doesn't accept code blocks)
216    pub in_json_context: bool,
217
218    /// DEPYLER-0452: Stdlib API mapping system for Python→Rust API translations
219    /// Maps Python stdlib patterns (module, class, attribute) to Rust code patterns
220    pub stdlib_mappings: crate::stdlib_mappings::StdlibMappings,
221
222    /// DEPYLER-0455 Bug 2: Track hoisted variables without type annotations
223    /// These variables need String literal normalization (.to_string()) to ensure
224    /// consistent type inference across if/else branches
225    /// Example: let mut format; if x { format = "json"; } else { format = s.to_lowercase(); }
226    /// Without normalization: &str vs String type mismatch
227    /// With normalization: String vs String (consistent)
228    pub hoisted_inference_vars: HashSet<String>,
229
230    /// DEPYLER-0823: Track variables assigned None placeholder that need hoisting
231    /// When `var = None; if cond: var = value; use(var)`, the None assignment is skipped
232    /// but the variable must be hoisted before the if block to be accessible after it.
233    /// Without hoisting: let var = value declared INSIDE if → E0425 outside
234    /// With hoisting: let mut var; if cond { var = value; } → var accessible outside
235    pub none_placeholder_vars: HashSet<String>,
236
237    /// DEPYLER-0108: Track precomputed Option field checks for argparse
238    /// Contains field names (e.g., "hash") where we've precomputed `let has_<field> = args.<field>.is_some();`
239    /// Used by convert_method_call to substitute `args.<field>.is_some()` with `has_<field>`
240    pub precomputed_option_fields: HashSet<String>,
241
242    /// DEPYLER-0456 Bug #2: Track CSE temp variables for subcommand checks
243    /// Maps CSE temp variable names (e.g., "_cse_temp_0") to command names (e.g., "init")
244    /// This allows is_subcommand_check() to recognize CSE-optimized subcommand comparisons
245    pub cse_subcommand_temps: std::collections::HashMap<String, String>,
246
247    /// GH-70: Track inferred parameter types for nested functions/closures
248    /// Maps nested function name → Vec<HirParam> with inferred types from usage patterns
249    /// Used by stmt_gen.rs when generating closures to apply inferred parameter types
250    /// instead of defaulting all parameters to () for Unknown types
251    pub nested_function_params: std::collections::HashMap<String, Vec<crate::hir::HirParam>>,
252
253    /// DEPYLER-0543: Track function parameters with `str` type annotation
254    /// These become `&str` in Rust and should NOT have `&` added when used as dict keys
255    /// Populated when generating function signatures
256    pub fn_str_params: HashSet<String>,
257
258    /// DEPYLER-0608: Track if currently generating a cmd_* handler function
259    /// When true, expr_gen should transform args.X → X (field is now a direct parameter)
260    pub in_cmd_handler: bool,
261
262    /// DEPYLER-0608: Track which fields were extracted from args.X accesses
263    /// Used in cmd_* handlers to know which field names are now parameters
264    pub cmd_handler_args_fields: Vec<String>,
265
266    /// DEPYLER-0608: Track if in a subcommand match arm that calls a handler
267    /// When true and calling cmd_*/handle_* with args, pass extracted fields instead
268    pub in_subcommand_match_arm: bool,
269
270    /// DEPYLER-0625: Track variables that need Box<dyn Write> type
271    /// When a variable is assigned File in one if-branch and Stdout in another,
272    /// we need to use `Box<dyn std::io::Write>` as the type and wrap values with Box::new()
273    pub boxed_dyn_write_vars: HashSet<String>,
274
275    /// DEPYLER-0608: Track extracted subcommand fields for match arm context
276    /// These fields are bound via `ref field` patterns in the match arm
277    pub subcommand_match_fields: Vec<String>,
278
279    /// DEPYLER-0613: Track names of functions that have been hoisted to top of body
280    /// These should be skipped when processing nested blocks (if/while/for/try)
281    /// to avoid duplicate emission
282    pub hoisted_function_names: Vec<String>,
283
284    /// DEPYLER-0617: Track if currently generating code inside main() function
285    /// When true, integer returns should become process::exit() or Ok(())
286    /// because Rust main can only return () or Result<(), E>
287    pub is_main_function: bool,
288
289    /// DEPYLER-0626: Track if current function returns Box<dyn Write>
290    /// When a function returns both File and Stdout, we need trait object return
291    /// Return statements should wrap values with Box::new()
292    pub function_returns_boxed_write: bool,
293
294    /// DEPYLER-0627: Track Option variables unwrapped via if-let pattern
295    /// Maps original variable name to unwrapped name (e.g., "override" -> "override_val")
296    /// Used inside if-let blocks to substitute variable references
297    pub option_unwrap_map: HashMap<String, String>,
298
299    /// DEPYLER-1151: Track Option variables narrowed after None check with early return
300    /// Pattern: `if x.is_none() { return }` narrows x to the inner type
301    /// When x is used after this guard, we automatically unwrap it
302    pub narrowed_option_vars: HashSet<String>,
303
304    /// DEPYLER-0627: Track if subprocess.run is used (needs CompletedProcess struct)
305    /// When True, generate a CompletedProcess struct in the output
306    pub needs_completed_process: bool,
307
308    /// DEPYLER-0648: Track functions that have vararg parameters (*args in Python)
309    /// These functions expect &[T] slice arguments, so call sites need to wrap args in &[...]
310    pub vararg_functions: HashSet<String>,
311
312    /// DEPYLER-1150: Track parameters in current function that are slices (from varargs)
313    /// When returning a slice param in a function that returns Vec<T>, add .to_vec()
314    pub slice_params: HashSet<String>,
315
316    /// DEPYLER-0716: Type substitutions for current function's generic inference
317    /// When a type parameter T can be inferred to a concrete type (e.g., String),
318    /// this maps "T" -> String. Used to substitute in return types.
319    pub type_substitutions: HashMap<String, Type>,
320
321    /// DEPYLER-0727: Track assignment target type for dict literal Value wrapping
322    /// When assigning to a variable with type annotation (e.g., `d: Dict[str, Any] = {...}`),
323    /// this stores the annotation so dict codegen can wrap values in serde_json::json!()
324    pub current_assign_type: Option<Type>,
325
326    /// DEPYLER-0741: Force dict values to be wrapped in Some() when in list context
327    /// When generating a list of dicts where ANY dict has None values, all dicts
328    /// must use Option<V> for consistency. This flag is set by convert_list.
329    pub force_dict_value_option_wrap: bool,
330
331    /// DEPYLER-0737: Track which function parameters are Optional (from =None default)
332    /// Maps function name → Vec<bool> where true means param at that index is Option<T>
333    /// Used by call sites to wrap non-None values in Some()
334    pub function_param_optionals: HashMap<String, Vec<bool>>,
335
336    /// DEPYLER-0795: Track loop variables that iterate over string.chars()
337    /// These variables are `char` type in Rust (not `str`), so ord(var) should be `var as u32`
338    /// not `var.chars().next().unwrap() as i32`
339    pub char_iter_vars: HashSet<String>,
340
341    /// DEPYLER-0821: Track Counter variables created from strings (HashMap<char, i32>)
342    /// Used to mark key variables as char when iterating with .items()
343    pub char_counter_vars: HashSet<String>,
344
345    /// DEPYLER-0936: Track ADT child→parent mappings for type rewriting
346    /// When a Python ABC hierarchy is converted to a Rust enum (e.g., Iter with children
347    /// ListIter, RangeIter), return types mentioning children must be rewritten to parent.
348    /// Maps child name (e.g., "ListIter") → parent name (e.g., "Iter")
349    pub adt_child_to_parent: HashMap<String, String>,
350
351    /// DEPYLER-0950: Track function parameter types for literal coercion at call sites
352    /// Maps function name → Vec<Type> for each parameter position
353    /// Used to coerce integer literals to float literals when callee expects f64
354    pub function_param_types: HashMap<String, Vec<Type>>,
355
356    /// DEPYLER-0964: Track function parameters that are `&mut Option<Dict>` type
357    /// When a parameter has type Dict[K,V] with default None, it becomes &mut Option<HashMap>
358    /// Inside the function body, assignments and method calls need special handling:
359    /// - Assignment: `memo = {}` → `*memo = Some(HashMap::new())`
360    /// - Method calls: `memo.get(k)` → `memo.as_ref().unwrap().get(&k)`
361    /// - Subscript: `memo[k] = v` → `memo.as_mut().unwrap().insert(k, v)`
362    pub mut_option_dict_params: HashSet<String>, // DEPYLER-0964: Track &mut Option<Dict> params
363    /// DEPYLER-1126: Track ALL parameters that are `&mut Option<T>` (any T, not just Dict)
364    /// When a parameter has Optional<T> type and is mutated in the function body,
365    /// it becomes &mut Option<T>. Assignments need dereferencing: `*param = value`
366    pub mut_option_params: HashSet<String>,
367    pub needs_depyler_value_enum: bool, // DEPYLER-FIX-RC2: Track need for DepylerValue enum
368    pub needs_python_string_ops: bool,  // DEPYLER-1202: Track need for PythonStringOps trait
369    pub needs_python_int_ops: bool,     // DEPYLER-1202: Track need for PythonIntOps trait
370    pub needs_depyler_date: bool,       // DEPYLER-1066: Track need for DepylerDate struct
371    pub needs_depyler_datetime: bool,   // DEPYLER-1067: Track need for DepylerDateTime struct
372    pub needs_depyler_timedelta: bool,  // DEPYLER-1068: Track need for DepylerTimeDelta struct
373    pub needs_depyler_regex_match: bool, // DEPYLER-1070: Track need for DepylerRegexMatch struct
374    /// DEPYLER-1060: Track module-level constant types (dict, list, set)
375    /// These persist across function boundaries and aren't cleared when processing functions.
376    /// Used by is_dict_expr() to recognize module-level statics accessed from within functions.
377    pub module_constant_types: HashMap<String, Type>,
378
379    /// DEPYLER-1112: Sovereign Type Database for external library type resolution
380    /// When enabled, provides O(1) lookup of external library function return types
381    /// to reduce E0308 type mismatch errors.
382    #[cfg(feature = "sovereign-types")]
383    pub type_query: Option<std::sync::Arc<std::sync::Mutex<depyler_knowledge::TypeQuery>>>,
384
385    /// DEPYLER-1113: Last external call return type for assignment propagation
386    /// When a MethodCall on an external module is encountered (e.g., requests.get),
387    /// stores the return type string for use when processing the assignment.
388    /// Reset to None after the assignment is processed.
389    pub last_external_call_return_type: Option<String>,
390
391    /// DEPYLER-1101: Oracle-learned type overrides from E0308 error feedback
392    /// When the compiler reports "expected `X`, found `Y`", we learn the correct type.
393    /// Maps variable name -> correct Type. Overrides inferred types during codegen.
394    /// Populated by repair_file_types() in utol.rs via transpile_with_constraints().
395    pub type_overrides: HashMap<String, Type>,
396
397    /// DEPYLER-1168: Track variables that are used after the current statement
398    /// When a function takes ownership of a parameter and the argument variable is used
399    /// later in the same scope, we need to insert .clone() at the call site.
400    /// Populated during statement iteration in stmt_gen.rs.
401    pub vars_used_later: HashSet<String>,
402}
403
404impl<'a> CodeGenContext<'a> {
405    /// Enter a new lexical scope
406    ///
407    /// # Complexity
408    /// 1 (simple push)
409    pub fn enter_scope(&mut self) {
410        self.declared_vars.push(HashSet::new());
411    }
412
413    /// Exit the current lexical scope
414    ///
415    /// # Complexity
416    /// 1 (simple pop)
417    pub fn exit_scope(&mut self) {
418        self.declared_vars.pop();
419    }
420
421    /// Check if a variable is declared in any scope
422    ///
423    /// # Complexity
424    /// 2 (iterator + any)
425    pub fn is_declared(&self, var_name: &str) -> bool {
426        self.declared_vars
427            .iter()
428            .any(|scope| scope.contains(var_name))
429    }
430
431    /// Declare a variable in the current scope
432    ///
433    /// # Complexity
434    /// 2 (if let + insert)
435    pub fn declare_var(&mut self, var_name: &str) {
436        if let Some(current_scope) = self.declared_vars.last_mut() {
437            current_scope.insert(var_name.to_string());
438        }
439    }
440
441    /// Process a Union type and generate an enum if needed
442    ///
443    /// Returns the enum name and optionally generates an enum definition
444    /// that is added to generated_enums.
445    ///
446    /// # Complexity
447    /// 2 (if + push)
448    pub fn process_union_type(&mut self, types: &[crate::hir::Type]) -> String {
449        let (enum_name, enum_def) = self.union_enum_generator.generate_union_enum(types);
450        if !enum_def.is_empty() {
451            self.generated_enums.push(enum_def);
452        }
453        enum_name
454    }
455
456    // ========================================================================
457    // DEPYLER-0333: Exception Scope Tracking
458    // ========================================================================
459
460    /// Get the current exception scope
461    ///
462    /// Returns the most recent scope from the stack, or Unhandled if stack is empty.
463    ///
464    /// # Complexity
465    /// 2 (last + unwrap_or)
466    pub fn current_exception_scope(&self) -> &ExceptionScope {
467        self.exception_scopes
468            .last()
469            .unwrap_or(&ExceptionScope::Unhandled)
470    }
471
472    /// Check if currently inside a try block
473    ///
474    /// # Complexity
475    /// 2 (current_exception_scope + matches)
476    pub fn is_in_try_block(&self) -> bool {
477        matches!(
478            self.current_exception_scope(),
479            ExceptionScope::TryCaught { .. }
480        )
481    }
482
483    /// Check if a specific exception type is handled by current try block
484    ///
485    /// Returns true if:
486    /// - Inside a try block with bare except (empty handled_types)
487    /// - Inside a try block that explicitly handles this exception type
488    ///
489    /// # Complexity
490    /// 4 (match + is_empty + contains + comparison)
491    pub fn is_exception_handled(&self, exception_type: &str) -> bool {
492        if let ExceptionScope::TryCaught { handled_types } = self.current_exception_scope() {
493            // Empty list = bare except (catches all)
494            handled_types.is_empty() || handled_types.contains(&exception_type.to_string())
495        } else {
496            false
497        }
498    }
499
500    /// Enter a try block scope with specified exception handlers
501    ///
502    /// # Complexity
503    /// 1 (simple push)
504    pub fn enter_try_scope(&mut self, handled_types: Vec<String>) {
505        self.exception_scopes
506            .push(ExceptionScope::TryCaught { handled_types });
507    }
508
509    /// Enter an exception handler scope
510    ///
511    /// # Complexity
512    /// 1 (simple push)
513    pub fn enter_handler_scope(&mut self) {
514        self.exception_scopes.push(ExceptionScope::Handler);
515    }
516
517    /// Exit the current exception scope
518    ///
519    /// # Complexity
520    /// 1 (simple pop)
521    pub fn exit_exception_scope(&mut self) {
522        self.exception_scopes.pop();
523    }
524
525    /// Returns the current exception scope nesting depth
526    ///
527    /// DEPYLER-0931: Used to detect nested try/except blocks for proper return wrapping.
528    /// When inside nested try/except, returns from inner match arms must be wrapped
529    /// in Ok() to match the outer closure's Result<T, E> return type.
530    ///
531    /// # Complexity
532    /// 1 (len() call)
533    pub fn exception_nesting_depth(&self) -> usize {
534        self.exception_scopes.len()
535    }
536
537    /// DEPYLER-1022: Return the fallback type annotation based on NASA mode
538    ///
539    /// In NASA mode (default), returns `: String` to avoid external crate dependencies.
540    /// In non-NASA mode, returns `: serde_json::Value` and sets needs_serde_json flag.
541    ///
542    /// # Complexity
543    /// 2 (if + quote)
544    pub fn fallback_type_annotation(&mut self) -> proc_macro2::TokenStream {
545        if self.type_mapper.nasa_mode {
546            quote::quote! { : String }
547        } else {
548            self.needs_serde_json = true;
549            quote::quote! { : serde_json::Value }
550        }
551    }
552
553    /// DEPYLER-1022: Return the fallback type (without colon) based on NASA mode
554    ///
555    /// In NASA mode, returns `String`. In non-NASA mode, returns `serde_json::Value`.
556    ///
557    /// # Complexity
558    /// 2 (if + quote)
559    pub fn fallback_type(&mut self) -> proc_macro2::TokenStream {
560        if self.type_mapper.nasa_mode {
561            quote::quote! { String }
562        } else {
563            self.needs_serde_json = true;
564            quote::quote! { serde_json::Value }
565        }
566    }
567
568    /// DEPYLER-1112: Look up the return type for an external library function
569    ///
570    /// Uses the Sovereign Type Database (when available) to resolve return types
571    /// for external library calls like `requests.get()` -> `Response`.
572    ///
573    /// Returns None if:
574    /// - The `sovereign-types` feature is not enabled
575    /// - No TypeQuery is configured
576    /// - The symbol is not found in the database
577    ///
578    /// # Example
579    ///
580    /// ```ignore
581    /// if let Some(ret_type) = ctx.lookup_external_return_type("requests", "get") {
582    ///     // ret_type = "Response"
583    /// }
584    /// ```
585    ///
586    /// # Complexity
587    /// 3 (feature check + option check + mutex lock + query)
588    #[cfg(feature = "sovereign-types")]
589    pub fn lookup_external_return_type(&self, module: &str, symbol: &str) -> Option<String> {
590        self.type_query.as_ref().and_then(|tq| {
591            tq.lock()
592                .ok()
593                .and_then(|mut query| query.find_return_type(module, symbol).ok())
594        })
595    }
596
597    /// DEPYLER-1112: Stub for when sovereign-types feature is disabled
598    ///
599    /// Always returns None since the type database is not available.
600    #[cfg(not(feature = "sovereign-types"))]
601    pub fn lookup_external_return_type(&self, _module: &str, _symbol: &str) -> Option<String> {
602        None
603    }
604
605    /// DEPYLER-1112: Check if a symbol exists in the external type database
606    ///
607    /// Returns true if the symbol is known (feature enabled and found in DB).
608    ///
609    /// # Complexity
610    /// 3 (feature check + option check + mutex lock + query)
611    #[cfg(feature = "sovereign-types")]
612    pub fn has_external_symbol(&self, module: &str, symbol: &str) -> bool {
613        self.type_query.as_ref().is_some_and(|tq| {
614            tq.lock()
615                .ok()
616                .is_some_and(|mut query| query.has_symbol(module, symbol))
617        })
618    }
619
620    /// DEPYLER-1112: Stub for when sovereign-types feature is disabled
621    #[cfg(not(feature = "sovereign-types"))]
622    pub fn has_external_symbol(&self, _module: &str, _symbol: &str) -> bool {
623        false
624    }
625}
626
627/// Trait for converting HIR elements to Rust tokens
628///
629/// This is the main trait for code generation. All HIR types that can
630/// be converted to Rust code implement this trait.
631pub trait RustCodeGen {
632    fn to_rust_tokens(&self, ctx: &mut CodeGenContext) -> Result<proc_macro2::TokenStream>;
633}
634
635/// Extension trait for converting expressions to Rust syn::Expr
636///
637/// Used internally for expression-to-expression conversions where
638/// we need syn::Expr specifically rather than TokenStream.
639pub trait ToRustExpr {
640    fn to_rust_expr(&self, ctx: &mut CodeGenContext) -> Result<syn::Expr>;
641}
642
643/// Default implementation for CodeGenContext (test-only)
644///
645/// Uses a static TypeMapper to allow Default trait implementation.
646/// This is primarily useful for unit testing.
647#[cfg(test)]
648impl Default for CodeGenContext<'static> {
649    fn default() -> Self {
650        test_helpers::test_context()
651    }
652}
653
654#[cfg(test)]
655pub mod test_helpers {
656    use super::*;
657    use once_cell::sync::Lazy;
658
659    /// Static TypeMapper for test contexts
660    static TEST_TYPE_MAPPER: Lazy<crate::type_mapper::TypeMapper> =
661        Lazy::new(crate::type_mapper::TypeMapper::new);
662
663    /// Create a test CodeGenContext with default values
664    ///
665    /// This is used by unit tests that need a CodeGenContext but don't
666    /// need custom type mapper configuration.
667    pub fn test_context() -> CodeGenContext<'static> {
668        CodeGenContext {
669            type_mapper: &TEST_TYPE_MAPPER,
670            annotation_aware_mapper: AnnotationAwareTypeMapper::with_base_mapper(
671                TEST_TYPE_MAPPER.clone(),
672            ),
673            string_optimizer: StringOptimizer::new(),
674            union_enum_generator: crate::union_enum_gen::UnionEnumGenerator::new(),
675            generated_enums: Vec::new(),
676            needs_hashmap: false,
677            needs_hashset: false,
678            needs_vecdeque: false,
679            needs_fnv_hashmap: false,
680            needs_ahash_hashmap: false,
681            needs_arc: false,
682            needs_rc: false,
683            needs_cow: false,
684            needs_rand: false,
685            needs_slice_random: false,
686            needs_rand_distr: false,
687            needs_serde_json: false,
688            needs_regex: false,
689            needs_chrono: false,
690            needs_tempfile: false,
691            needs_itertools: false,
692            needs_clap: false,
693            needs_csv: false,
694            needs_rust_decimal: false,
695            needs_num_rational: false,
696            needs_base64: false,
697            needs_md5: false,
698            needs_sha1: false,
699            needs_sha2: false,
700            needs_sha3: false,
701            needs_digest: false,
702            needs_blake2: false,
703            needs_hex: false,
704            needs_uuid: false,
705            needs_hmac: false,
706            needs_crc32: false,
707            needs_url_encoding: false,
708            needs_io_read: false,
709            needs_io_write: false,
710            needs_bufread: false,
711            needs_once_cell: false,
712            needs_lazy_lock: false, // DEPYLER-1016
713            needs_trueno: false,
714            numpy_vars: HashSet::new(),
715            needs_tokio: false,
716            needs_glob: false,
717            needs_statrs: false,
718            needs_url: false,
719            declared_vars: vec![HashSet::new()],
720            current_function_can_fail: false,
721            current_return_type: None,
722            module_mapper: crate::module_mapper::ModuleMapper::new(),
723            imported_modules: HashMap::new(),
724            imported_items: HashMap::new(),
725            all_imported_modules: HashSet::new(),
726            module_aliases: HashMap::new(),
727            mutable_vars: HashSet::new(),
728            needs_zerodivisionerror: false,
729            needs_indexerror: false,
730            needs_valueerror: false,
731            needs_argumenttypeerror: false,
732            needs_runtimeerror: false,
733            needs_filenotfounderror: false,
734            needs_syntaxerror: false,
735            needs_typeerror: false,
736            needs_keyerror: false,
737            needs_ioerror: false,
738            needs_attributeerror: false,
739            needs_stopiteration: false,
740            is_classmethod: false,
741            in_generator: false,
742            generator_state_vars: HashSet::new(),
743            generator_iterator_state_vars: HashSet::new(),
744            returns_impl_iterator: false,
745            var_types: HashMap::new(),
746            class_names: HashSet::new(),
747            mutating_methods: HashMap::new(),
748            property_methods: HashSet::new(),
749            function_return_types: HashMap::new(),
750            function_param_borrows: HashMap::new(),
751            function_param_muts: HashMap::new(),
752            function_param_defaults: HashMap::new(),
753            class_field_defaults: HashMap::new(),
754            class_field_types: HashMap::new(),
755            class_method_return_types: HashMap::new(),
756            tuple_iter_vars: HashSet::new(),
757            iterator_vars: HashSet::new(),
758            ref_params: HashSet::new(),
759            mut_ref_params: HashSet::new(),
760            is_final_statement: false,
761            result_bool_functions: HashSet::new(),
762            result_returning_functions: HashSet::new(),
763            option_returning_functions: HashSet::new(),
764            current_error_type: None,
765            exception_scopes: Vec::new(),
766            argparser_tracker: crate::rust_gen::argparse_transform::ArgParserTracker::new(),
767            generated_args_struct: None,
768            generated_commands_enum: None,
769            current_subcommand_fields: None,
770            validator_functions: HashSet::new(),
771            in_json_context: false,
772            stdlib_mappings: crate::stdlib_mappings::StdlibMappings::new(),
773            hoisted_inference_vars: HashSet::new(),
774            none_placeholder_vars: HashSet::new(),
775            precomputed_option_fields: HashSet::new(),
776            cse_subcommand_temps: HashMap::new(),
777            nested_function_params: HashMap::new(),
778            fn_str_params: HashSet::new(),
779            in_cmd_handler: false,
780            cmd_handler_args_fields: Vec::new(),
781            in_subcommand_match_arm: false,
782            boxed_dyn_write_vars: HashSet::new(),
783            subcommand_match_fields: Vec::new(),
784            hoisted_function_names: Vec::new(),
785            is_main_function: false,
786            function_returns_boxed_write: false,
787            option_unwrap_map: HashMap::new(),
788            narrowed_option_vars: HashSet::new(),
789            needs_completed_process: false,
790            vararg_functions: HashSet::new(),
791            slice_params: HashSet::new(),
792            type_substitutions: HashMap::new(),
793            current_assign_type: None,
794            force_dict_value_option_wrap: false,
795            function_param_optionals: HashMap::new(),
796            char_iter_vars: HashSet::new(),
797            char_counter_vars: HashSet::new(),
798            adt_child_to_parent: HashMap::new(),
799            function_param_types: HashMap::new(),
800            mut_option_dict_params: HashSet::new(),
801            mut_option_params: HashSet::new(), // DEPYLER-1126
802            needs_depyler_value_enum: false,
803            needs_python_string_ops: false,        // DEPYLER-1202
804            needs_python_int_ops: false,           // DEPYLER-1202
805            needs_depyler_date: false,             // DEPYLER-1066
806            needs_depyler_datetime: false,         // DEPYLER-1067
807            needs_depyler_timedelta: false,        // DEPYLER-1068
808            needs_depyler_regex_match: false,      // DEPYLER-1070
809            module_constant_types: HashMap::new(), // DEPYLER-1060
810            #[cfg(feature = "sovereign-types")]
811            type_query: None, // DEPYLER-1112
812            last_external_call_return_type: None,  // DEPYLER-1113
813            type_overrides: HashMap::new(),        // DEPYLER-1101
814            vars_used_later: HashSet::new(),       // DEPYLER-1168
815        }
816    }
817}