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}