Skip to main content

decy_parser/
parser.rs

1//! C parser implementation using clang-sys.
2//!
3//! This module provides the core parsing functionality to convert C source code
4//! into an AST representation using LLVM/Clang bindings.
5
6// Allow non-upper-case globals from clang-sys FFI bindings (CXCursor_* constants)
7#![allow(non_upper_case_globals)]
8
9use crate::diagnostic::{Diagnostic, DiagnosticError, Severity};
10use anyhow::{Context, Result};
11use clang_sys::*;
12use std::ffi::{CStr, CString};
13use std::path::Path;
14use std::process::Command;
15use std::ptr;
16
17/// Discover system include paths from the clang compiler.
18///
19/// This function runs `clang -E -x c - -v` to extract the system include
20/// search paths, enabling parsing of code that uses standard headers.
21fn discover_system_includes() -> Vec<String> {
22    let mut includes = Vec::new();
23
24    // Try to get include paths from clang
25    let output = Command::new("clang")
26        .args(["-E", "-x", "c", "-", "-v"])
27        .stdin(std::process::Stdio::null())
28        .output();
29
30    if let Ok(output) = output {
31        let stderr = String::from_utf8_lossy(&output.stderr);
32        let mut in_include_section = false;
33
34        for line in stderr.lines() {
35            if line.contains("#include <...> search starts here:") {
36                in_include_section = true;
37                continue;
38            }
39            if line.contains("End of search list.") {
40                break;
41            }
42            if in_include_section {
43                let path = line.trim();
44                // Skip framework directories (macOS-specific)
45                if !path.is_empty() && !path.contains("(framework directory)") {
46                    includes.push(path.to_string());
47                }
48            }
49        }
50    }
51
52    // Fallback paths if clang detection fails
53    if includes.is_empty() {
54        // macOS paths
55        if cfg!(target_os = "macos") {
56            includes.extend([
57                "/usr/local/include".to_string(),
58                "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include".to_string(),
59            ]);
60        }
61        // Linux paths
62        if cfg!(target_os = "linux") {
63            includes.extend(["/usr/include".to_string(), "/usr/local/include".to_string()]);
64        }
65    }
66
67    includes
68}
69
70/// C parser using clang-sys.
71///
72/// # Examples
73///
74/// ```no_run
75/// use decy_parser::parser::CParser;
76///
77/// let parser = CParser::new()?;
78/// let ast = parser.parse("int main() { return 0; }")?;
79/// assert_eq!(ast.functions().len(), 1);
80/// # Ok::<(), anyhow::Error>(())
81/// ```
82#[derive(Debug)]
83pub struct CParser {
84    index: CXIndex,
85    /// System include paths discovered from the compiler
86    system_includes: Vec<String>,
87}
88
89impl CParser {
90    /// Create a new C parser.
91    ///
92    /// This automatically discovers system include paths from the clang compiler.
93    ///
94    /// # Examples
95    ///
96    /// ```no_run
97    /// use decy_parser::parser::CParser;
98    ///
99    /// let parser = CParser::new()?;
100    /// # Ok::<(), anyhow::Error>(())
101    /// ```
102    pub fn new() -> Result<Self> {
103        // SAFETY: clang_createIndex is safe to call with these parameters
104        let index = unsafe { clang_createIndex(0, 0) };
105        if index.is_null() {
106            anyhow::bail!("Failed to create clang index");
107        }
108
109        // Discover system include paths for standard header support
110        let system_includes = discover_system_includes();
111
112        Ok(Self {
113            index,
114            system_includes,
115        })
116    }
117
118    /// Parse C source code into an AST.
119    ///
120    /// # Arguments
121    ///
122    /// * `source` - The C source code to parse
123    ///
124    /// # Returns
125    ///
126    /// * `Ok(Ast)` - The parsed AST
127    /// * `Err(anyhow::Error)` - If parsing fails
128    ///
129    /// # Examples
130    ///
131    /// ```no_run
132    /// use decy_parser::parser::CParser;
133    ///
134    /// let parser = CParser::new()?;
135    /// let ast = parser.parse("int add(int a, int b) { return a + b; }")?;
136    /// # Ok::<(), anyhow::Error>(())
137    /// ```
138    pub fn parse(&self, source: &str) -> Result<Ast> {
139        let filename = CString::new("input.c").context("Failed to create filename")?;
140        let source_cstr = CString::new(source).context("Failed to convert source to CString")?;
141
142        let mut ast = Ast::new();
143
144        // Handle empty input
145        if source.trim().is_empty() {
146            return Ok(ast);
147        }
148
149        // SAFETY: Creating unsaved file with valid C strings
150        let unsaved_file = CXUnsavedFile {
151            Filename: filename.as_ptr(),
152            Contents: source_cstr.as_ptr(),
153            Length: source.len() as std::os::raw::c_ulong,
154        };
155
156        // Detect if source contains BARE extern "C" (without #ifdef guards)
157        // If it has #ifdef __cplusplus guards, clang can handle it as C
158        // Only enable C++ mode for bare extern "C" blocks
159        let has_extern_c = source.contains("extern \"C\"");
160        let has_ifdef_guard =
161            source.contains("#ifdef __cplusplus") || source.contains("#if defined(__cplusplus)");
162        let needs_cpp_mode = has_extern_c && !has_ifdef_guard;
163
164        // Build system include path arguments
165        // We need to keep CStrings alive for the duration of parsing
166        let isystem_flag = CString::new("-isystem").unwrap();
167        let include_cstrings: Vec<CString> = self
168            .system_includes
169            .iter()
170            .map(|p| CString::new(p.as_str()).unwrap())
171            .collect();
172
173        // Prepare command line arguments for C++ mode if needed
174        let cpp_flag = CString::new("-x").unwrap();
175        let cpp_lang = CString::new("c++").unwrap();
176
177        // DECY-194: Add standard C macro definitions that might not be available
178        // EOF is defined as -1 in stdio.h, NULL as 0
179        let define_eof = CString::new("-DEOF=-1").unwrap();
180        let define_null = CString::new("-DNULL=0").unwrap();
181        // BUFSIZ from stdio.h (typical value)
182        let define_bufsiz = CString::new("-DBUFSIZ=8192").unwrap();
183
184        // Build the complete args vector
185        let mut args_vec: Vec<*const std::os::raw::c_char> = Vec::new();
186
187        // Add C++ mode flags if needed
188        if needs_cpp_mode {
189            args_vec.push(cpp_flag.as_ptr());
190            args_vec.push(cpp_lang.as_ptr());
191        }
192
193        // Add macro definitions
194        args_vec.push(define_eof.as_ptr());
195        args_vec.push(define_null.as_ptr());
196        args_vec.push(define_bufsiz.as_ptr());
197
198        // Add system include paths
199        for include_path in &include_cstrings {
200            args_vec.push(isystem_flag.as_ptr());
201            args_vec.push(include_path.as_ptr());
202        }
203
204        // SAFETY: Parsing with clang_parseTranslationUnit2
205        // Enable DetailedPreprocessingRecord to capture macro definitions
206        // CXTranslationUnit_DetailedPreprocessingRecord = 1
207        let flags = 1;
208
209        let mut tu = ptr::null_mut();
210        let result = unsafe {
211            clang_parseTranslationUnit2(
212                self.index,
213                filename.as_ptr(),
214                if args_vec.is_empty() {
215                    ptr::null()
216                } else {
217                    args_vec.as_ptr()
218                },
219                args_vec.len() as std::os::raw::c_int,
220                &unsaved_file as *const CXUnsavedFile as *mut CXUnsavedFile,
221                1,
222                flags,
223                &mut tu,
224            )
225        };
226
227        if result != CXError_Success || tu.is_null() {
228            anyhow::bail!("Failed to parse C source");
229        }
230
231        // SAFETY: Extract structured diagnostics from clang
232        let diagnostics = extract_diagnostics(tu, source, None);
233        if diagnostics.iter().any(|d| d.severity >= Severity::Error) {
234            unsafe { clang_disposeTranslationUnit(tu) };
235            return Err(DiagnosticError::new(diagnostics).into());
236        }
237
238        // SAFETY: Getting cursor from valid translation unit
239        let cursor = unsafe { clang_getTranslationUnitCursor(tu) };
240
241        // Visit children to extract functions
242        let ast_ptr = &mut ast as *mut Ast;
243
244        // SAFETY: Visiting cursor children with callback
245        unsafe {
246            clang_visitChildren(cursor, visit_function, ast_ptr as CXClientData);
247
248            // Clean up
249            clang_disposeTranslationUnit(tu);
250        }
251
252        Ok(ast)
253    }
254
255    /// Parse a C file into an AST.
256    ///
257    /// # Arguments
258    ///
259    /// * `path` - Path to the C file
260    ///
261    /// # Returns
262    ///
263    /// * `Ok(Ast)` - The parsed AST
264    /// * `Err(anyhow::Error)` - If parsing fails
265    ///
266    /// Parse C source code from a file path.
267    ///
268    /// DECY-237: This method allows includes to resolve properly by parsing
269    /// from the actual file path instead of an in-memory string.
270    pub fn parse_file(&self, path: &Path) -> Result<Ast> {
271        let source = std::fs::read_to_string(path)
272            .with_context(|| format!("Failed to read file: {}", path.display()))?;
273
274        // Convert the path to an absolute path for clang
275        let abs_path = if path.is_absolute() {
276            path.to_path_buf()
277        } else {
278            std::env::current_dir()?.join(path)
279        };
280
281        let filename =
282            CString::new(abs_path.to_string_lossy().as_bytes()).context("Invalid path")?;
283        let source_cstr = CString::new(source.as_str()).context("Invalid source")?;
284
285        let mut ast = Ast::new();
286
287        // Handle empty input
288        if source.trim().is_empty() {
289            return Ok(ast);
290        }
291
292        // SAFETY: Creating unsaved file with valid C strings
293        let unsaved_file = CXUnsavedFile {
294            Filename: filename.as_ptr(),
295            Contents: source_cstr.as_ptr(),
296            Length: source.len() as std::os::raw::c_ulong,
297        };
298
299        // Detect if source contains BARE extern "C"
300        let has_extern_c = source.contains("extern \"C\"");
301        let has_ifdef_guard =
302            source.contains("#ifdef __cplusplus") || source.contains("#if defined(__cplusplus)");
303        let needs_cpp_mode = has_extern_c && !has_ifdef_guard;
304
305        // Prepare command line arguments for C++ mode if needed
306        let cpp_flag = CString::new("-x").unwrap();
307        let cpp_lang = CString::new("c++").unwrap();
308
309        // Standard macro definitions
310        let define_eof = CString::new("-DEOF=-1").unwrap();
311        let define_null = CString::new("-DNULL=0").unwrap();
312        let define_bufsiz = CString::new("-DBUFSIZ=8192").unwrap();
313
314        // DECY-237: Add explicit defines for <limits.h> macros
315        let define_char_bit = CString::new("-DCHAR_BIT=8").unwrap();
316        let define_char_min = CString::new("-DCHAR_MIN=-128").unwrap();
317        let define_char_max = CString::new("-DCHAR_MAX=127").unwrap();
318        let define_schar_min = CString::new("-DSCHAR_MIN=-128").unwrap();
319        let define_schar_max = CString::new("-DSCHAR_MAX=127").unwrap();
320        let define_uchar_max = CString::new("-DUCHAR_MAX=255").unwrap();
321        let define_shrt_min = CString::new("-DSHRT_MIN=-32768").unwrap();
322        let define_shrt_max = CString::new("-DSHRT_MAX=32767").unwrap();
323        let define_ushrt_max = CString::new("-DUSHRT_MAX=65535").unwrap();
324        let define_int_min = CString::new("-DINT_MIN=-2147483648").unwrap();
325        let define_int_max = CString::new("-DINT_MAX=2147483647").unwrap();
326        let define_uint_max = CString::new("-DUINT_MAX=4294967295U").unwrap();
327        let define_long_min = CString::new("-DLONG_MIN=-9223372036854775808L").unwrap();
328        let define_long_max = CString::new("-DLONG_MAX=9223372036854775807L").unwrap();
329        let define_ulong_max = CString::new("-DULONG_MAX=18446744073709551615UL").unwrap();
330
331        // <stdlib.h> macros
332        let define_exit_success = CString::new("-DEXIT_SUCCESS=0").unwrap();
333        let define_exit_failure = CString::new("-DEXIT_FAILURE=1").unwrap();
334        let define_rand_max = CString::new("-DRAND_MAX=2147483647").unwrap();
335
336        // System include paths
337        let include_clang = CString::new("-I/usr/lib/llvm-14/lib/clang/14.0.0/include").unwrap();
338        let include_local = CString::new("-I/usr/local/include").unwrap();
339        let include_arch = CString::new("-I/usr/include/x86_64-linux-gnu").unwrap();
340        let include_usr = CString::new("-I/usr/include").unwrap();
341
342        // Build the args vector with all defines
343        let base_defines: Vec<*const std::os::raw::c_char> = vec![
344            define_eof.as_ptr(),
345            define_null.as_ptr(),
346            define_bufsiz.as_ptr(),
347            define_char_bit.as_ptr(),
348            define_char_min.as_ptr(),
349            define_char_max.as_ptr(),
350            define_schar_min.as_ptr(),
351            define_schar_max.as_ptr(),
352            define_uchar_max.as_ptr(),
353            define_shrt_min.as_ptr(),
354            define_shrt_max.as_ptr(),
355            define_ushrt_max.as_ptr(),
356            define_int_min.as_ptr(),
357            define_int_max.as_ptr(),
358            define_uint_max.as_ptr(),
359            define_long_min.as_ptr(),
360            define_long_max.as_ptr(),
361            define_ulong_max.as_ptr(),
362            define_exit_success.as_ptr(),
363            define_exit_failure.as_ptr(),
364            define_rand_max.as_ptr(),
365            include_clang.as_ptr(),
366            include_local.as_ptr(),
367            include_arch.as_ptr(),
368            include_usr.as_ptr(),
369        ];
370
371        let args_vec: Vec<*const std::os::raw::c_char> = if needs_cpp_mode {
372            let mut args = vec![cpp_flag.as_ptr(), cpp_lang.as_ptr()];
373            args.extend(base_defines);
374            args
375        } else {
376            base_defines
377        };
378
379        // Enable DetailedPreprocessingRecord to capture macro definitions
380        let flags = 1;
381
382        let mut tu = ptr::null_mut();
383        let result = unsafe {
384            clang_parseTranslationUnit2(
385                self.index,
386                filename.as_ptr(),
387                if args_vec.is_empty() {
388                    ptr::null()
389                } else {
390                    args_vec.as_ptr()
391                },
392                args_vec.len() as std::os::raw::c_int,
393                &unsaved_file as *const CXUnsavedFile as *mut CXUnsavedFile,
394                1,
395                flags,
396                &mut tu,
397            )
398        };
399
400        if result != CXError_Success || tu.is_null() {
401            anyhow::bail!("Failed to parse C source file: {}", path.display());
402        }
403
404        // SAFETY: Extract structured diagnostics from clang
405        let file_name = path.to_string_lossy().to_string();
406        let diagnostics = extract_diagnostics(tu, &source, Some(&file_name));
407        if diagnostics.iter().any(|d| d.severity >= Severity::Error) {
408            unsafe { clang_disposeTranslationUnit(tu) };
409            return Err(DiagnosticError::new(diagnostics).into());
410        }
411
412        // Get cursor and visit
413        let cursor = unsafe { clang_getTranslationUnitCursor(tu) };
414        let ast_ptr = &mut ast as *mut Ast;
415
416        unsafe {
417            clang_visitChildren(cursor, visit_function, ast_ptr as CXClientData);
418            clang_disposeTranslationUnit(tu);
419        }
420
421        Ok(ast)
422    }
423}
424
425impl Drop for CParser {
426    fn drop(&mut self) {
427        // SAFETY: Disposing of valid clang index
428        unsafe {
429            clang_disposeIndex(self.index);
430        }
431    }
432}
433
434/// Extract structured diagnostics from a clang translation unit.
435///
436/// Iterates all diagnostics from clang, extracting message, location, category,
437/// fix-it suggestions, and building code snippets with caret indicators.
438///
439/// # Safety
440///
441/// `tu` must be a valid, non-null translation unit from `clang_parseTranslationUnit2`.
442fn extract_diagnostics(
443    tu: CXTranslationUnit,
444    source: &str,
445    file_override: Option<&str>,
446) -> Vec<Diagnostic> {
447    let num_diagnostics = unsafe { clang_getNumDiagnostics(tu) };
448    let mut diagnostics = Vec::new();
449
450    for i in 0..num_diagnostics {
451        let diag = unsafe { clang_getDiagnostic(tu, i) };
452        let raw_severity = unsafe { clang_getDiagnosticSeverity(diag) };
453
454        let severity = match raw_severity {
455            CXDiagnostic_Note => Severity::Note,
456            CXDiagnostic_Warning => Severity::Warning,
457            CXDiagnostic_Error => Severity::Error,
458            CXDiagnostic_Fatal => Severity::Fatal,
459            _ => {
460                unsafe { clang_disposeDiagnostic(diag) };
461                continue; // Skip ignored diagnostics
462            }
463        };
464
465        // Extract message text
466        let diag_str = unsafe { clang_getDiagnosticSpelling(diag) };
467        let c_str = unsafe { CStr::from_ptr(clang_getCString(diag_str)) };
468        let message = c_str.to_str().unwrap_or("unknown error").to_string();
469        unsafe { clang_disposeString(diag_str) };
470
471        let mut d = Diagnostic::new(severity, message);
472
473        // Extract source location (line, column, file)
474        let loc = unsafe { clang_getDiagnosticLocation(diag) };
475        let mut file: CXFile = ptr::null_mut();
476        let mut line: u32 = 0;
477        let mut column: u32 = 0;
478        unsafe {
479            clang_getFileLocation(loc, &mut file, &mut line, &mut column, ptr::null_mut());
480        }
481
482        if line > 0 {
483            d.line = Some(line);
484            d.column = Some(column);
485        }
486
487        // Use file override if provided, otherwise extract from clang
488        if let Some(name) = file_override {
489            d.file = Some(name.to_string());
490        } else if !file.is_null() {
491            let file_name = unsafe {
492                let name_cx = clang_getFileName(file);
493                let name_c = CStr::from_ptr(clang_getCString(name_cx));
494                let name = name_c.to_string_lossy().into_owned();
495                clang_disposeString(name_cx);
496                name
497            };
498            d.file = Some(file_name);
499        } else {
500            d.file = Some("input.c".to_string());
501        }
502
503        // Extract category text
504        let cat_idx = unsafe { clang_getDiagnosticCategory(diag) };
505        if cat_idx != 0 {
506            let cat_str = unsafe { clang_getDiagnosticCategoryText(diag) };
507            let cat_c = unsafe { CStr::from_ptr(clang_getCString(cat_str)) };
508            let category = cat_c.to_str().unwrap_or("").to_string();
509            unsafe { clang_disposeString(cat_str) };
510            if !category.is_empty() {
511                d.category = Some(category);
512            }
513        }
514
515        // Extract fix-it suggestions
516        let num_fix_its = unsafe { clang_getDiagnosticNumFixIts(diag) };
517        for fi in 0..num_fix_its {
518            let mut range = unsafe { std::mem::zeroed::<CXSourceRange>() };
519            let fix_str = unsafe { clang_getDiagnosticFixIt(diag, fi, &mut range) };
520            let fix_c = unsafe { CStr::from_ptr(clang_getCString(fix_str)) };
521            let fix_text = fix_c.to_str().unwrap_or("").to_string();
522            unsafe { clang_disposeString(fix_str) };
523            if !fix_text.is_empty() {
524                d.fix_its.push(format!("insert '{}'", fix_text));
525            }
526        }
527
528        // Build code snippet from source
529        if let Some(line_num) = d.line {
530            d.snippet = Diagnostic::build_snippet(source, line_num, d.column);
531        }
532
533        // Infer note and help from message patterns
534        d.infer_note_and_help();
535
536        unsafe { clang_disposeDiagnostic(diag) };
537        diagnostics.push(d);
538    }
539
540    diagnostics
541}
542
543/// Visitor callback for clang AST traversal.
544///
545/// # Safety
546///
547/// This function is called by clang_visitChildren and must follow C calling conventions.
548extern "C" fn visit_function(
549    cursor: CXCursor,
550    _parent: CXCursor,
551    client_data: CXClientData,
552) -> CXChildVisitResult {
553    // SAFETY: Converting client data back to AST pointer
554    let ast = unsafe { &mut *(client_data as *mut Ast) };
555
556    // SAFETY: Getting cursor kind
557    let kind = unsafe { clang_getCursorKind(cursor) };
558
559    // Handle extern "C" linkage specifications (DECY-055)
560    // CXCursor_LinkageSpec = 23
561    if kind == 23 {
562        // This is extern "C" { ... } - visit its children
563        // Don't process the linkage spec itself, just recurse into declarations
564        unsafe {
565            clang_visitChildren(cursor, visit_function, client_data);
566        }
567        return CXChildVisit_Continue;
568    }
569
570    if kind == CXCursor_FunctionDecl {
571        // Extract function information
572        if let Some(function) = extract_function(cursor) {
573            ast.add_function(function);
574        }
575    } else if kind == CXCursor_TypedefDecl {
576        // Extract typedef information
577        // DECY-147: Handle anonymous struct typedefs
578        let (typedef_opt, struct_opt) = extract_typedef(cursor);
579        if let Some(typedef) = typedef_opt {
580            ast.add_typedef(typedef);
581        }
582        if let Some(struct_def) = struct_opt {
583            ast.add_struct(struct_def);
584        }
585    } else if kind == CXCursor_StructDecl {
586        // Extract struct information
587        if let Some(struct_def) = extract_struct(cursor) {
588            ast.add_struct(struct_def);
589        }
590    } else if kind == CXCursor_VarDecl {
591        // Extract variable declaration - only add if it's at file scope (global)
592        // Check if parent is translation unit (file scope) vs function scope
593        let semantic_parent = unsafe { clang_getCursorSemanticParent(cursor) };
594        let parent_kind = unsafe { clang_getCursorKind(semantic_parent) };
595
596        // Check if parent is file scope: either TranslationUnit or nullptr
597        // Function declarations have parent kind = CXCursor_FunctionDecl (8)
598        // File-scope variables typically have parent kind = CXCursor_TranslationUnit (300 in clang-sys)
599        let is_file_scope = parent_kind != CXCursor_FunctionDecl;
600
601        if is_file_scope {
602            if let Some(variable) = extract_variable(cursor) {
603                ast.add_variable(variable);
604            }
605        }
606        // Local variables in functions are handled by extract_statement in function body parsing
607    } else if kind == 5 {
608        // CXCursor_EnumDecl = 5
609        // DECY-240: Extract enum declaration
610        if let Some(enum_def) = extract_enum(cursor) {
611            ast.add_enum(enum_def);
612        }
613    } else if kind == CXCursor_MacroDefinition {
614        // Extract macro definition (only from main file, not includes)
615        let location = unsafe { clang_getCursorLocation(cursor) };
616        let mut file: CXFile = ptr::null_mut();
617        unsafe {
618            clang_getFileLocation(
619                location,
620                &mut file,
621                ptr::null_mut(),
622                ptr::null_mut(),
623                ptr::null_mut(),
624            );
625        }
626
627        // Only process macros from the main file (not system headers)
628        if !file.is_null() {
629            let file_name = unsafe {
630                let name_cxstring = clang_getFileName(file);
631                let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
632                let name = c_str.to_string_lossy().into_owned();
633                clang_disposeString(name_cxstring);
634                name
635            };
636
637            // Only add macros from input.c (our source file)
638            if file_name.ends_with("input.c") {
639                if let Some(macro_def) = extract_macro(cursor) {
640                    ast.add_macro(macro_def);
641                }
642            }
643        }
644    }
645
646    // Return Recurse to ensure we visit children of all nodes
647    // This is needed in C++ mode to reach LinkageSpec and its children
648    CXChildVisit_Recurse
649}
650
651/// Extract function information from a clang cursor.
652fn extract_function(cursor: CXCursor) -> Option<Function> {
653    // SAFETY: Getting cursor spelling (function name)
654    let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
655    let name = unsafe {
656        let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
657        let name = c_str.to_string_lossy().into_owned();
658        clang_disposeString(name_cxstring);
659        name
660    };
661
662    // SAFETY: Getting return type
663    let cx_type = unsafe { clang_getCursorType(cursor) };
664    let return_cx_type = unsafe { clang_getResultType(cx_type) };
665    let return_type = convert_type(return_cx_type)?;
666
667    // Extract parameters
668    let num_args = unsafe { clang_Cursor_getNumArguments(cursor) };
669    let mut parameters = Vec::new();
670
671    for i in 0..num_args {
672        // SAFETY: Getting argument cursor
673        let arg_cursor = unsafe { clang_Cursor_getArgument(cursor, i as u32) };
674
675        // Get parameter name
676        let param_name_cxstring = unsafe { clang_getCursorSpelling(arg_cursor) };
677        let param_name = unsafe {
678            let c_str = CStr::from_ptr(clang_getCString(param_name_cxstring));
679            let name = c_str.to_string_lossy().into_owned();
680            clang_disposeString(param_name_cxstring);
681            name
682        };
683
684        // Get parameter type
685        let param_cx_type = unsafe { clang_getCursorType(arg_cursor) };
686        if let Some(param_type) = convert_type(param_cx_type) {
687            // DECY-135: Check if this is a pointer with const-qualified pointee
688            let is_pointee_const = unsafe {
689                if param_cx_type.kind == clang_sys::CXType_Pointer {
690                    let pointee = clang_sys::clang_getPointeeType(param_cx_type);
691                    clang_isConstQualifiedType(pointee) != 0
692                } else {
693                    false
694                }
695            };
696            parameters.push(Parameter::new_with_const(
697                param_name,
698                param_type,
699                is_pointee_const,
700            ));
701        }
702    }
703
704    // Extract function body by visiting children
705    let mut body = Vec::new();
706    let body_ptr = &mut body as *mut Vec<Statement>;
707
708    unsafe {
709        clang_visitChildren(cursor, visit_statement, body_ptr as CXClientData);
710    }
711
712    Some(Function::new_with_body(name, return_type, parameters, body))
713}
714
715/// Extract typedef information from a clang cursor.
716/// Returns (Option<Typedef>, Option<Struct>) - struct is Some when typedef is for anonymous struct.
717fn extract_typedef(cursor: CXCursor) -> (Option<Typedef>, Option<Struct>) {
718    // SAFETY: Getting typedef name
719    let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
720    let name = unsafe {
721        let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
722        let name = c_str.to_string_lossy().into_owned();
723        clang_disposeString(name_cxstring);
724        name
725    };
726
727    // SAFETY: Getting underlying type of typedef
728    let cx_type = unsafe { clang_getTypedefDeclUnderlyingType(cursor) };
729
730    // DECY-147: Check if underlying type is anonymous struct
731    // Anonymous struct pattern: typedef struct { ... } Name;
732    let canonical = unsafe { clang_getCanonicalType(cx_type) };
733    if canonical.kind == CXType_Record {
734        let decl = unsafe { clang_getTypeDeclaration(canonical) };
735        let struct_name_cxstring = unsafe { clang_getCursorSpelling(decl) };
736        let struct_name = unsafe {
737            let c_str = CStr::from_ptr(clang_getCString(struct_name_cxstring));
738            let sn = c_str.to_string_lossy().into_owned();
739            clang_disposeString(struct_name_cxstring);
740            sn
741        };
742
743        // If struct name is empty, this is an anonymous struct typedef
744        if struct_name.is_empty() {
745            // Extract struct fields from the declaration
746            let mut fields = Vec::new();
747            let fields_ptr = &mut fields as *mut Vec<StructField>;
748
749            unsafe {
750                clang_visitChildren(decl, visit_struct_fields, fields_ptr as CXClientData);
751            }
752
753            // Return struct with typedef name, no typedef needed
754            return (None, Some(Struct::new(name, fields)));
755        }
756    }
757
758    let underlying_type = convert_type(cx_type);
759    match underlying_type {
760        Some(ut) => (Some(Typedef::new(name, ut)), None),
761        None => (None, None),
762    }
763}
764
765/// Extract struct information from a clang cursor.
766fn extract_struct(cursor: CXCursor) -> Option<Struct> {
767    // SAFETY: Getting struct name
768    let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
769    let name = unsafe {
770        let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
771        let name = c_str.to_string_lossy().into_owned();
772        clang_disposeString(name_cxstring);
773        name
774    };
775
776    // Skip anonymous structs
777    if name.is_empty() {
778        return None;
779    }
780
781    // Extract struct fields by visiting children
782    let mut fields = Vec::new();
783    let fields_ptr = &mut fields as *mut Vec<StructField>;
784
785    unsafe {
786        clang_visitChildren(cursor, visit_struct_fields, fields_ptr as CXClientData);
787    }
788
789    Some(Struct::new(name, fields))
790}
791
792/// DECY-240: Extract enum information from a clang cursor.
793///
794/// Extracts C enum declarations, including explicit values.
795fn extract_enum(cursor: CXCursor) -> Option<Enum> {
796    // SAFETY: Getting enum name
797    let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
798    let name = unsafe {
799        let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
800        let name = c_str.to_string_lossy().into_owned();
801        clang_disposeString(name_cxstring);
802        name
803    };
804
805    // Extract enum variants by visiting children
806    let mut variants: Vec<EnumVariant> = Vec::new();
807    let variants_ptr = &mut variants as *mut Vec<EnumVariant>;
808
809    // Visitor callback for enum constants
810    extern "C" fn visit_enum_constants(
811        cursor: CXCursor,
812        _parent: CXCursor,
813        client_data: CXClientData,
814    ) -> CXChildVisitResult {
815        let variants = unsafe { &mut *(client_data as *mut Vec<EnumVariant>) };
816
817        // SAFETY: Getting cursor kind
818        let kind = unsafe { clang_getCursorKind(cursor) };
819
820        // CXCursor_EnumConstantDecl = 7
821        if kind == 7 {
822            // Get variant name
823            let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
824            let variant_name = unsafe {
825                let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
826                let name = c_str.to_string_lossy().into_owned();
827                clang_disposeString(name_cxstring);
828                name
829            };
830
831            // Get variant value
832            let value = unsafe { clang_getEnumConstantDeclValue(cursor) };
833
834            variants.push(EnumVariant::new(variant_name, Some(value)));
835        }
836
837        CXChildVisit_Continue
838    }
839
840    unsafe {
841        clang_visitChildren(cursor, visit_enum_constants, variants_ptr as CXClientData);
842    }
843
844    // Only return if there are variants (skip empty enums)
845    if variants.is_empty() {
846        return None;
847    }
848
849    Some(Enum::new(name, variants))
850}
851
852/// Extract macro definition from a clang cursor.
853///
854/// Extract variable declaration information from a clang cursor.
855///
856/// Extracts global and local variable declarations, including function pointers.
857///
858/// # Examples
859///
860/// Simple: `int x;`
861/// Function pointer: `int (*callback)(int);`
862fn extract_variable(cursor: CXCursor) -> Option<Variable> {
863    // SAFETY: Getting cursor spelling (variable name)
864    let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
865    let name = unsafe {
866        let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
867        let name = c_str.to_string_lossy().into_owned();
868        clang_disposeString(name_cxstring);
869        name
870    };
871
872    // SAFETY: Getting variable type
873    let cx_type = unsafe { clang_getCursorType(cursor) };
874    let var_type = convert_type(cx_type)?;
875
876    // Extract storage class specifiers
877    // CX_StorageClass values (from clang-sys):
878    // CX_SC_Invalid = 0, CX_SC_None = 1, CX_SC_Extern = 2, CX_SC_Static = 3,
879    // CX_SC_PrivateExtern = 4, CX_SC_OpenCLWorkGroupLocal = 5,
880    // CX_SC_Auto = 6, CX_SC_Register = 7
881    let storage_class = unsafe { clang_Cursor_getStorageClass(cursor) };
882    let is_static = storage_class == 3; // CX_SC_Static
883    let is_extern = storage_class == 2; // CX_SC_Extern
884
885    // Check if type is const-qualified
886    let is_const = unsafe { clang_isConstQualifiedType(cx_type) != 0 };
887
888    // Extract initializer by visiting children
889    let mut initializer: Option<Expression> = None;
890    let initializer_ptr = &mut initializer as *mut Option<Expression>;
891
892    unsafe {
893        clang_visitChildren(
894            cursor,
895            visit_variable_initializer,
896            initializer_ptr as CXClientData,
897        );
898    }
899
900    Some(Variable::new_with_storage_class(
901        name,
902        var_type,
903        initializer,
904        is_static,
905        is_extern,
906        is_const,
907    ))
908}
909
910/// Helper function to extract an expression from a cursor.
911/// Dispatches to the appropriate extract function based on cursor kind.
912#[allow(non_upper_case_globals)]
913fn try_extract_expression(cursor: CXCursor) -> Option<Expression> {
914    let kind = unsafe { clang_getCursorKind(cursor) };
915
916    match kind {
917        CXCursor_IntegerLiteral => extract_int_literal(cursor),
918        107 => extract_float_literal(cursor), // CXCursor_FloatingLiteral
919        CXCursor_StringLiteral => extract_string_literal(cursor),
920        110 => extract_char_literal(cursor), // CXCursor_CharacterLiteral
921        CXCursor_DeclRefExpr => extract_variable_ref(cursor),
922        CXCursor_BinaryOperator => extract_binary_op(cursor),
923        CXCursor_CallExpr => extract_function_call(cursor),
924        CXCursor_UnaryOperator => extract_unary_op(cursor),
925        CXCursor_ArraySubscriptExpr => extract_array_index(cursor),
926        CXCursor_MemberRefExpr => extract_field_access(cursor),
927        116 => extract_conditional_op(cursor), // CXCursor_ConditionalOperator (ternary)
928        117 => extract_cast(cursor),           // CXCursor_CStyleCastExpr
929        118 => extract_compound_literal(cursor), // CXCursor_CompoundLiteralExpr
930        111 => {
931            // CXCursor_ParenExpr - parenthesized expression like (a > b)
932            // Recurse into children to extract the inner expression
933            let mut result: Option<Expression> = None;
934            let result_ptr = &mut result as *mut Option<Expression>;
935            unsafe {
936                clang_visitChildren(
937                    cursor,
938                    visit_variable_initializer,
939                    result_ptr as CXClientData,
940                );
941            }
942            result
943        }
944        CXCursor_UnexposedExpr => {
945            // UnexposedExpr is a wrapper - recurse into children
946            let mut result: Option<Expression> = None;
947            let result_ptr = &mut result as *mut Option<Expression>;
948            unsafe {
949                clang_visitChildren(
950                    cursor,
951                    visit_variable_initializer,
952                    result_ptr as CXClientData,
953                );
954            }
955            result
956        }
957        _ => None,
958    }
959}
960
961/// Visitor callback for variable initializer expressions.
962#[allow(non_upper_case_globals)]
963extern "C" fn visit_variable_initializer(
964    cursor: CXCursor,
965    _parent: CXCursor,
966    client_data: CXClientData,
967) -> CXChildVisitResult {
968    let initializer = unsafe { &mut *(client_data as *mut Option<Expression>) };
969
970    // Extract the first expression found (the initializer)
971    if let Some(expr) = try_extract_expression(cursor) {
972        *initializer = Some(expr);
973        return CXChildVisit_Break;
974    }
975
976    CXChildVisit_Continue
977}
978
979/// This function extracts #define directives, supporting both object-like and function-like macros.
980///
981/// # Examples
982///
983/// Object-like: `#define MAX 100`
984/// Function-like: `#define SQR(x) ((x) * (x))`
985fn extract_macro(cursor: CXCursor) -> Option<MacroDefinition> {
986    // SAFETY: Getting macro name
987    let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
988    let name = unsafe {
989        let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
990        let name = c_str.to_string_lossy().into_owned();
991        clang_disposeString(name_cxstring);
992        name
993    };
994
995    // Skip empty macro names
996    if name.is_empty() {
997        return None;
998    }
999
1000    // Get macro body using clang_Cursor_isMacroFunctionLike and clang token APIs
1001    // For now, we'll check if it's function-like and extract tokens
1002    let is_function_like = unsafe { clang_sys::clang_Cursor_isMacroFunctionLike(cursor) } != 0;
1003
1004    // Get the source range and tokens for the macro
1005    let range = unsafe { clang_getCursorExtent(cursor) };
1006    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
1007
1008    let mut tokens: *mut CXToken = ptr::null_mut();
1009    let mut num_tokens: u32 = 0;
1010
1011    unsafe {
1012        clang_tokenize(tu, range, &mut tokens, &mut num_tokens);
1013    }
1014
1015    // Extract macro body from tokens
1016    // Skip the first token (macro name) and extract the rest
1017    let mut parameters = Vec::new();
1018    let mut body_tokens = Vec::new();
1019    let mut in_params = false;
1020
1021    for i in 0..num_tokens {
1022        let token = unsafe { *tokens.offset(i as isize) };
1023        let token_kind = unsafe { clang_getTokenKind(token) };
1024        let token_spelling = unsafe { clang_getTokenSpelling(tu, token) };
1025        let token_str = unsafe {
1026            let c_str = CStr::from_ptr(clang_getCString(token_spelling));
1027            let s = c_str.to_string_lossy().into_owned();
1028            clang_disposeString(token_spelling);
1029            s
1030        };
1031
1032        // Skip the macro name (first token)
1033        if i == 0 {
1034            continue;
1035        }
1036
1037        // Check for parameter list (function-like macros)
1038        if is_function_like && i == 1 && token_str == "(" {
1039            in_params = true;
1040            continue;
1041        }
1042
1043        if in_params {
1044            if token_str == ")" {
1045                in_params = false;
1046                continue;
1047            } else if token_str != ","
1048                && (token_kind == CXToken_Identifier || token_kind == CXToken_Keyword)
1049            {
1050                // Accept both identifiers and keywords as parameter names
1051                // C allows keywords in macro parameter names since they're in macro scope
1052                parameters.push(token_str);
1053            }
1054        } else {
1055            body_tokens.push(token_str);
1056        }
1057    }
1058
1059    // Clean up tokens
1060    unsafe {
1061        clang_disposeTokens(tu, tokens, num_tokens);
1062    }
1063
1064    // Join body tokens without spaces (preserving original formatting)
1065    let body = body_tokens.join("");
1066
1067    if is_function_like {
1068        Some(MacroDefinition::new_function_like(name, parameters, body))
1069    } else {
1070        Some(MacroDefinition::new_object_like(name, body))
1071    }
1072}
1073
1074/// Visitor callback for struct fields.
1075///
1076/// # Safety
1077///
1078/// This function is called by clang_visitChildren and must follow C calling conventions.
1079#[allow(non_upper_case_globals)]
1080extern "C" fn visit_struct_fields(
1081    cursor: CXCursor,
1082    _parent: CXCursor,
1083    client_data: CXClientData,
1084) -> CXChildVisitResult {
1085    // SAFETY: Converting client data back to fields vector pointer
1086    let fields = unsafe { &mut *(client_data as *mut Vec<StructField>) };
1087
1088    // SAFETY: Getting cursor kind
1089    let kind = unsafe { clang_getCursorKind(cursor) };
1090
1091    if kind == CXCursor_FieldDecl {
1092        // Get field name
1093        let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
1094        let name = unsafe {
1095            let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
1096            let name = c_str.to_string_lossy().into_owned();
1097            clang_disposeString(name_cxstring);
1098            name
1099        };
1100
1101        // Get field type
1102        let cx_type = unsafe { clang_getCursorType(cursor) };
1103        if let Some(field_type) = convert_type(cx_type) {
1104            fields.push(StructField::new(name, field_type));
1105        }
1106    }
1107
1108    CXChildVisit_Continue
1109}
1110
1111/// Visitor callback for extracting statements from function body.
1112///
1113/// # Safety
1114///
1115/// This function is called by clang_visitChildren and must follow C calling conventions.
1116#[allow(non_upper_case_globals)]
1117extern "C" fn visit_statement(
1118    cursor: CXCursor,
1119    _parent: CXCursor,
1120    client_data: CXClientData,
1121) -> CXChildVisitResult {
1122    // SAFETY: Converting client data back to statement vector pointer
1123    let statements = unsafe { &mut *(client_data as *mut Vec<Statement>) };
1124
1125    // SAFETY: Getting cursor kind
1126    let kind = unsafe { clang_getCursorKind(cursor) };
1127
1128    match kind {
1129        CXCursor_CompoundStmt => {
1130            // Compound statement (function body) - recurse into it
1131            CXChildVisit_Recurse
1132        }
1133        CXCursor_DeclStmt => {
1134            // Declaration statement - visit its children to get the actual declaration
1135            CXChildVisit_Recurse
1136        }
1137        CXCursor_VarDecl => {
1138            // Variable declaration
1139            if let Some(stmt) = extract_var_decl(cursor) {
1140                statements.push(stmt);
1141            }
1142            CXChildVisit_Continue
1143        }
1144        CXCursor_ReturnStmt => {
1145            // Return statement
1146            if let Some(stmt) = extract_return_stmt(cursor) {
1147                statements.push(stmt);
1148            }
1149            CXChildVisit_Continue
1150        }
1151        CXCursor_BinaryOperator => {
1152            // Could be an assignment statement (x = 42)
1153            if let Some(stmt) = extract_assignment_stmt(cursor) {
1154                statements.push(stmt);
1155            }
1156            CXChildVisit_Continue
1157        }
1158        CXCursor_IfStmt => {
1159            // If statement
1160            if let Some(stmt) = extract_if_stmt(cursor) {
1161                statements.push(stmt);
1162            }
1163            CXChildVisit_Continue
1164        }
1165        CXCursor_ForStmt => {
1166            // For loop
1167            if let Some(stmt) = extract_for_stmt(cursor) {
1168                statements.push(stmt);
1169            }
1170            CXChildVisit_Continue
1171        }
1172        CXCursor_WhileStmt => {
1173            // While loop
1174            if let Some(stmt) = extract_while_stmt(cursor) {
1175                statements.push(stmt);
1176            }
1177            CXChildVisit_Continue
1178        }
1179        CXCursor_SwitchStmt => {
1180            // Switch statement
1181            if let Some(stmt) = extract_switch_stmt(cursor) {
1182                statements.push(stmt);
1183            }
1184            CXChildVisit_Continue
1185        }
1186        CXCursor_BreakStmt => {
1187            // Break statement
1188            statements.push(Statement::Break);
1189            CXChildVisit_Continue
1190        }
1191        CXCursor_ContinueStmt => {
1192            // Continue statement
1193            statements.push(Statement::Continue);
1194            CXChildVisit_Continue
1195        }
1196        CXCursor_UnaryOperator => {
1197            // Could be ++/-- statement (ptr++, ++ptr, ptr--, --ptr)
1198            if let Some(stmt) = extract_inc_dec_stmt(cursor) {
1199                statements.push(stmt);
1200            }
1201            CXChildVisit_Continue
1202        }
1203        CXCursor_CompoundAssignOperator => {
1204            // Compound assignment (+=, -=, *=, /=, %=)
1205            if let Some(stmt) = extract_compound_assignment_stmt(cursor) {
1206                statements.push(stmt);
1207            }
1208            CXChildVisit_Continue
1209        }
1210        CXCursor_CallExpr => {
1211            // Function call as statement (DECY-066)
1212            // e.g., printf("Hello"); or free(ptr);
1213            if let Some(stmt) = extract_statement(cursor) {
1214                statements.push(stmt);
1215            }
1216            CXChildVisit_Continue
1217        }
1218        _ => CXChildVisit_Recurse, // Recurse into unknown nodes to find statements
1219    }
1220}
1221
1222/// Extract a variable declaration statement.
1223fn extract_var_decl(cursor: CXCursor) -> Option<Statement> {
1224    // DECY-223: Check storage class - skip extern declarations without initializers
1225    // These are references to globals, not new local variables
1226    // CX_SC_Extern = 2
1227    let storage_class = unsafe { clang_Cursor_getStorageClass(cursor) };
1228    let is_extern = storage_class == 2;
1229
1230    // Get variable name
1231    let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
1232    let name = unsafe {
1233        let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
1234        let name = c_str.to_string_lossy().into_owned();
1235        clang_disposeString(name_cxstring);
1236        name
1237    };
1238
1239    // Get variable type
1240    let cx_type = unsafe { clang_getCursorType(cursor) };
1241    let var_type = convert_type(cx_type)?;
1242
1243    // DECY-223: Early check for extern without initializer - check before visiting children
1244    // extern int max; → skip (reference to global)
1245    if is_extern {
1246        // We need to check if there's an initializer - visit children first
1247        let mut has_real_initializer = false;
1248        extern "C" fn check_initializer(
1249            cursor: CXCursor,
1250            _parent: CXCursor,
1251            client_data: CXClientData,
1252        ) -> CXChildVisitResult {
1253            let has_init = unsafe { &mut *(client_data as *mut bool) };
1254            let kind = unsafe { clang_getCursorKind(cursor) };
1255            // Check for expression kinds that indicate a real initializer
1256            if kind == CXCursor_IntegerLiteral
1257                || kind == 107 // CXCursor_FloatingLiteral
1258                || kind == CXCursor_StringLiteral
1259                || kind == CXCursor_CallExpr
1260                || kind == CXCursor_BinaryOperator
1261                || kind == CXCursor_UnaryOperator
1262            {
1263                *has_init = true;
1264                return CXChildVisit_Break;
1265            }
1266            CXChildVisit_Continue
1267        }
1268        let init_ptr = &mut has_real_initializer as *mut bool;
1269        unsafe {
1270            clang_visitChildren(cursor, check_initializer, init_ptr as CXClientData);
1271        }
1272        // If extern without initializer, skip it
1273        if !has_real_initializer {
1274            return None;
1275        }
1276    }
1277
1278    // Extract initializer by visiting children
1279    let mut initializer: Option<Expression> = None;
1280    let init_ptr = &mut initializer as *mut Option<Expression>;
1281
1282    unsafe {
1283        clang_visitChildren(cursor, visit_expression, init_ptr as CXClientData);
1284    }
1285
1286    // DECY-112 FIX: For array types, the visit_expression callback may incorrectly
1287    // capture the array size as an initializer. For example, `int nums[5];` has
1288    // the integer literal 5 as a child node (the array size), which gets captured.
1289    //
1290    // Fix: If the variable is an array type and the initializer is an integer literal
1291    // that matches the array size, clear the initializer (it's the size, not an init).
1292    let initializer = match (&var_type, &initializer) {
1293        (
1294            Type::Array {
1295                size: Some(array_size),
1296                ..
1297            },
1298            Some(Expression::IntLiteral(init_val)),
1299        ) if i64::from(*init_val) == *array_size => {
1300            // The "initializer" is actually the array size expression, not a real initializer
1301            None
1302        }
1303        _ => initializer,
1304    };
1305
1306    Some(Statement::VariableDeclaration {
1307        name,
1308        var_type,
1309        initializer,
1310    })
1311}
1312
1313/// Extract a return statement.
1314fn extract_return_stmt(cursor: CXCursor) -> Option<Statement> {
1315    // Extract return expression by visiting children
1316    let mut return_expr: Option<Expression> = None;
1317    let expr_ptr = &mut return_expr as *mut Option<Expression>;
1318
1319    unsafe {
1320        clang_visitChildren(cursor, visit_expression, expr_ptr as CXClientData);
1321    }
1322
1323    Some(Statement::Return(return_expr))
1324}
1325
1326/// Extract an assignment statement.
1327fn extract_assignment_stmt(cursor: CXCursor) -> Option<Statement> {
1328    // Check if this binary operator is an assignment '=' (not '==', '!=', etc.)
1329    // Get the translation unit
1330    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
1331    if tu.is_null() {
1332        return None;
1333    }
1334
1335    // Get the extent (source range) of the cursor
1336    let extent = unsafe { clang_getCursorExtent(cursor) };
1337
1338    // Tokenize to find the operator
1339    let mut tokens = ptr::null_mut();
1340    let mut num_tokens = 0;
1341
1342    unsafe {
1343        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
1344    }
1345
1346    let mut is_assignment = false;
1347
1348    // Look through tokens to find '=' (and make sure it's not '==', '!=', etc.)
1349    for i in 0..num_tokens {
1350        unsafe {
1351            let token = *tokens.add(i as usize);
1352            let token_kind = clang_getTokenKind(token);
1353
1354            if token_kind == CXToken_Punctuation {
1355                let token_cxstring = clang_getTokenSpelling(tu, token);
1356                let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
1357                if let Ok(token_str) = c_str.to_str() {
1358                    // Only accept single '=' for assignment
1359                    if token_str == "=" {
1360                        is_assignment = true;
1361                        clang_disposeString(token_cxstring);
1362                        break;
1363                    } else if token_str == "=="
1364                        || token_str == "!="
1365                        || token_str == "<="
1366                        || token_str == ">="
1367                    {
1368                        // This is a comparison operator, not assignment
1369                        clang_disposeString(token_cxstring);
1370                        break;
1371                    }
1372                }
1373                clang_disposeString(token_cxstring);
1374            }
1375        }
1376    }
1377
1378    unsafe {
1379        clang_disposeTokens(tu, tokens, num_tokens);
1380    }
1381
1382    if !is_assignment {
1383        return None;
1384    }
1385
1386    // Extract left side (target) and right side (value)
1387    let mut operands: Vec<Expression> = Vec::new();
1388    let operands_ptr = &mut operands as *mut Vec<Expression>;
1389
1390    unsafe {
1391        clang_visitChildren(cursor, visit_binary_operand, operands_ptr as CXClientData);
1392    }
1393
1394    // Assignment should have exactly 2 operands
1395    if operands.len() != 2 {
1396        return None;
1397    }
1398
1399    // Check if left side is a dereference (e.g., *dst = x)
1400    if let Expression::Dereference(inner) = &operands[0] {
1401        return Some(Statement::DerefAssignment {
1402            target: (**inner).clone(), // Extract the inner expression being dereferenced
1403            value: operands[1].clone(),
1404        });
1405    }
1406
1407    // Check if left side is an array index (e.g., arr[i] = value)
1408    if let Expression::ArrayIndex { array, index } = &operands[0] {
1409        return Some(Statement::ArrayIndexAssignment {
1410            array: array.clone(),
1411            index: index.clone(),
1412            value: operands[1].clone(),
1413        });
1414    }
1415
1416    // Check if left side is a field access (e.g., ptr->field = value or obj.field = value)
1417    if matches!(
1418        &operands[0],
1419        Expression::PointerFieldAccess { .. } | Expression::FieldAccess { .. }
1420    ) {
1421        // Extract field name from the expression
1422        let field = match &operands[0] {
1423            Expression::PointerFieldAccess { field, .. } => field.clone(),
1424            Expression::FieldAccess { field, .. } => field.clone(),
1425            _ => unreachable!(),
1426        };
1427
1428        // Extract object from the expression
1429        let object = match &operands[0] {
1430            Expression::PointerFieldAccess { pointer, .. } => (**pointer).clone(),
1431            Expression::FieldAccess { object, .. } => (**object).clone(),
1432            _ => unreachable!(),
1433        };
1434
1435        return Some(Statement::FieldAssignment {
1436            object,
1437            field,
1438            value: operands[1].clone(),
1439        });
1440    }
1441
1442    // Left side must be a variable reference for regular assignment
1443    let target = match &operands[0] {
1444        Expression::Variable(name) => name.clone(),
1445        _ => return None, // Can't assign to non-variables (yet)
1446    };
1447
1448    Some(Statement::Assignment {
1449        target,
1450        value: operands[1].clone(),
1451    })
1452}
1453
1454/// Extract an increment/decrement statement (++, --).
1455fn extract_inc_dec_stmt(cursor: CXCursor) -> Option<Statement> {
1456    // Get the translation unit
1457    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
1458    if tu.is_null() {
1459        return None;
1460    }
1461
1462    // Get the extent (source range) of the cursor
1463    let extent = unsafe { clang_getCursorExtent(cursor) };
1464
1465    // Tokenize to find the operator
1466    let mut tokens = ptr::null_mut();
1467    let mut num_tokens = 0;
1468
1469    unsafe {
1470        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
1471    }
1472
1473    let mut operator: Option<String> = None;
1474    let mut operator_position = 0;
1475
1476    // Look through tokens to find ++ or --
1477    for i in 0..num_tokens {
1478        unsafe {
1479            let token = *tokens.add(i as usize);
1480            let token_kind = clang_getTokenKind(token);
1481
1482            if token_kind == CXToken_Punctuation {
1483                let token_cxstring = clang_getTokenSpelling(tu, token);
1484                let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
1485                if let Ok(token_str) = c_str.to_str() {
1486                    if token_str == "++" || token_str == "--" {
1487                        operator = Some(token_str.to_string()); // Clone the string before disposing
1488                        operator_position = i;
1489                        clang_disposeString(token_cxstring);
1490                        break;
1491                    }
1492                }
1493                clang_disposeString(token_cxstring);
1494            }
1495        }
1496    }
1497
1498    // Determine if this is pre or post increment/decrement
1499    // If operator comes before identifier, it's pre (++ptr)
1500    // If operator comes after identifier, it's post (ptr++)
1501    let is_pre = operator_position == 0;
1502
1503    unsafe {
1504        clang_disposeTokens(tu, tokens, num_tokens);
1505    }
1506
1507    // DECY-166: First check if this is a member expression increment (e.g., sb->length++)
1508    // We need to detect this case and create a FieldAssignment instead of PostIncrement
1509    let mut member_expr: Option<Expression> = None;
1510    let mut simple_var: Option<String> = None;
1511
1512    // Visit children to find MemberRefExpr, ArraySubscriptExpr, or DeclRefExpr
1513    extern "C" fn visit_for_inc_target(
1514        cursor: CXCursor,
1515        _parent: CXCursor,
1516        client_data: CXClientData,
1517    ) -> CXChildVisitResult {
1518        let data = unsafe { &mut *(client_data as *mut (Option<Expression>, Option<String>)) };
1519        let kind = unsafe { clang_getCursorKind(cursor) };
1520
1521        // DECY-219: Check for array subscript expression first (arr[i]++, ndigit[c-'0']++)
1522        // Must be checked before recursing, otherwise we only find the DeclRefExpr
1523        if kind == CXCursor_ArraySubscriptExpr {
1524            if let Some(expr) = extract_array_index(cursor) {
1525                data.0 = Some(expr);
1526                return CXChildVisit_Break;
1527            }
1528        }
1529
1530        // Check for member expression (sb->length, obj.field)
1531        if kind == CXCursor_MemberRefExpr {
1532            if let Some(expr) = extract_field_access(cursor) {
1533                data.0 = Some(expr);
1534                return CXChildVisit_Break;
1535            }
1536        }
1537
1538        // Fall back to simple variable reference
1539        if kind == CXCursor_DeclRefExpr {
1540            let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
1541            let name = unsafe {
1542                let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
1543                let var_name = c_str.to_string_lossy().into_owned();
1544                clang_disposeString(name_cxstring);
1545                var_name
1546            };
1547            data.1 = Some(name);
1548            CXChildVisit_Break
1549        } else {
1550            CXChildVisit_Recurse
1551        }
1552    }
1553
1554    let mut target_data = (member_expr, simple_var);
1555    let target_ptr = &mut target_data as *mut (Option<Expression>, Option<String>);
1556    unsafe {
1557        clang_visitChildren(cursor, visit_for_inc_target, target_ptr as CXClientData);
1558    }
1559    member_expr = target_data.0;
1560    simple_var = target_data.1;
1561
1562    let operator = operator?;
1563    let op_str = operator.as_str();
1564
1565    // DECY-166: If we found a member expression, create a FieldAssignment
1566    // sb->length++ becomes FieldAssignment { object: sb, field: "length", value: sb->length + 1 }
1567    if let Some(expr) = member_expr {
1568        // Determine the delta (+1 or -1) based on operator
1569        let delta = match op_str {
1570            "++" => 1,
1571            "--" => -1,
1572            _ => return None,
1573        };
1574
1575        // Extract object and field from the expression
1576        match expr {
1577            Expression::PointerFieldAccess { pointer, field } => {
1578                // Create the increment/decrement value expression
1579                let value = if delta > 0 {
1580                    Expression::BinaryOp {
1581                        left: Box::new(Expression::PointerFieldAccess {
1582                            pointer: pointer.clone(),
1583                            field: field.clone(),
1584                        }),
1585                        op: BinaryOperator::Add,
1586                        right: Box::new(Expression::IntLiteral(1)),
1587                    }
1588                } else {
1589                    Expression::BinaryOp {
1590                        left: Box::new(Expression::PointerFieldAccess {
1591                            pointer: pointer.clone(),
1592                            field: field.clone(),
1593                        }),
1594                        op: BinaryOperator::Subtract,
1595                        right: Box::new(Expression::IntLiteral(1)),
1596                    }
1597                };
1598
1599                return Some(Statement::FieldAssignment {
1600                    object: *pointer,
1601                    field,
1602                    value,
1603                });
1604            }
1605            Expression::FieldAccess { object, field } => {
1606                // Create the increment/decrement value expression
1607                let value = if delta > 0 {
1608                    Expression::BinaryOp {
1609                        left: Box::new(Expression::FieldAccess {
1610                            object: object.clone(),
1611                            field: field.clone(),
1612                        }),
1613                        op: BinaryOperator::Add,
1614                        right: Box::new(Expression::IntLiteral(1)),
1615                    }
1616                } else {
1617                    Expression::BinaryOp {
1618                        left: Box::new(Expression::FieldAccess {
1619                            object: object.clone(),
1620                            field: field.clone(),
1621                        }),
1622                        op: BinaryOperator::Subtract,
1623                        right: Box::new(Expression::IntLiteral(1)),
1624                    }
1625                };
1626
1627                return Some(Statement::FieldAssignment {
1628                    object: *object,
1629                    field,
1630                    value,
1631                });
1632            }
1633            // DECY-219: Array subscript increment/decrement: arr[i]++ → arr[i] = arr[i] + 1
1634            Expression::ArrayIndex { array, index } => {
1635                // Create the increment/decrement value expression
1636                let value = if delta > 0 {
1637                    Expression::BinaryOp {
1638                        left: Box::new(Expression::ArrayIndex {
1639                            array: array.clone(),
1640                            index: index.clone(),
1641                        }),
1642                        op: BinaryOperator::Add,
1643                        right: Box::new(Expression::IntLiteral(1)),
1644                    }
1645                } else {
1646                    Expression::BinaryOp {
1647                        left: Box::new(Expression::ArrayIndex {
1648                            array: array.clone(),
1649                            index: index.clone(),
1650                        }),
1651                        op: BinaryOperator::Subtract,
1652                        right: Box::new(Expression::IntLiteral(1)),
1653                    }
1654                };
1655
1656                return Some(Statement::ArrayIndexAssignment {
1657                    array,
1658                    index,
1659                    value,
1660                });
1661            }
1662            _ => {} // Fall through to simple variable handling
1663        }
1664    }
1665
1666    // Simple variable increment/decrement
1667    let target = simple_var?;
1668
1669    match op_str {
1670        "++" => {
1671            if is_pre {
1672                Some(Statement::PreIncrement { target })
1673            } else {
1674                Some(Statement::PostIncrement { target })
1675            }
1676        }
1677        "--" => {
1678            if is_pre {
1679                Some(Statement::PreDecrement { target })
1680            } else {
1681                Some(Statement::PostDecrement { target })
1682            }
1683        }
1684        _ => None,
1685    }
1686}
1687
1688/// Extract a compound assignment statement (+=, -=, *=, /=, %=).
1689fn extract_compound_assignment_stmt(cursor: CXCursor) -> Option<Statement> {
1690    // Get the translation unit
1691    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
1692    if tu.is_null() {
1693        return None;
1694    }
1695
1696    // Get the extent (source range) of the cursor
1697    let extent = unsafe { clang_getCursorExtent(cursor) };
1698
1699    // Tokenize to find the operator
1700    let mut tokens = ptr::null_mut();
1701    let mut num_tokens = 0;
1702
1703    unsafe {
1704        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
1705    }
1706
1707    let mut operator: Option<BinaryOperator> = None;
1708
1709    // Look through tokens to find compound assignment operator
1710    for i in 0..num_tokens {
1711        unsafe {
1712            let token = *tokens.add(i as usize);
1713            let token_kind = clang_getTokenKind(token);
1714
1715            if token_kind == CXToken_Punctuation {
1716                let token_cxstring = clang_getTokenSpelling(tu, token);
1717                let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
1718                if let Ok(token_str) = c_str.to_str() {
1719                    operator = match token_str {
1720                        "+=" => Some(BinaryOperator::Add),
1721                        "-=" => Some(BinaryOperator::Subtract),
1722                        "*=" => Some(BinaryOperator::Multiply),
1723                        "/=" => Some(BinaryOperator::Divide),
1724                        "%=" => Some(BinaryOperator::Modulo),
1725                        _ => None,
1726                    };
1727                    if operator.is_some() {
1728                        clang_disposeString(token_cxstring);
1729                        break;
1730                    }
1731                }
1732                clang_disposeString(token_cxstring);
1733            }
1734        }
1735    }
1736
1737    unsafe {
1738        clang_disposeTokens(tu, tokens, num_tokens);
1739    }
1740
1741    let op = operator?;
1742
1743    // Extract left side (target) and right side (value)
1744    let mut operands: Vec<Expression> = Vec::new();
1745    let operands_ptr = &mut operands as *mut Vec<Expression>;
1746
1747    unsafe {
1748        clang_visitChildren(cursor, visit_binary_operand, operands_ptr as CXClientData);
1749    }
1750
1751    // Compound assignment should have exactly 2 operands
1752    if operands.len() != 2 {
1753        return None;
1754    }
1755
1756    // DECY-185: Check for complex targets first (Dereference, PointerFieldAccess, FieldAccess)
1757    // These need DerefCompoundAssignment since target is an Expression, not just a String
1758
1759    // Check if left side is a dereference (e.g., *ptr *= 2)
1760    if let Expression::Dereference(inner) = &operands[0] {
1761        return Some(Statement::DerefCompoundAssignment {
1762            target: (**inner).clone(), // The thing being dereferenced (e.g., 'ptr')
1763            op,
1764            value: operands[1].clone(),
1765        });
1766    }
1767
1768    // Check if left side is a pointer field access (e.g., sb->capacity *= 2)
1769    if let Expression::PointerFieldAccess { .. } = &operands[0] {
1770        return Some(Statement::DerefCompoundAssignment {
1771            target: operands[0].clone(), // The full PointerFieldAccess expression
1772            op,
1773            value: operands[1].clone(),
1774        });
1775    }
1776
1777    // Check if left side is a struct field access (e.g., obj.field *= 2)
1778    if let Expression::FieldAccess { .. } = &operands[0] {
1779        return Some(Statement::DerefCompoundAssignment {
1780            target: operands[0].clone(), // The full FieldAccess expression
1781            op,
1782            value: operands[1].clone(),
1783        });
1784    }
1785
1786    // Check if left side is an array index (e.g., arr[i] *= 2)
1787    if let Expression::ArrayIndex { .. } = &operands[0] {
1788        return Some(Statement::DerefCompoundAssignment {
1789            target: operands[0].clone(), // The full ArrayIndex expression
1790            op,
1791            value: operands[1].clone(),
1792        });
1793    }
1794
1795    // Simple variable target (existing behavior)
1796    let target = match &operands[0] {
1797        Expression::Variable(name) => name.clone(),
1798        _ => return None, // Unknown target type
1799    };
1800
1801    Some(Statement::CompoundAssignment {
1802        target,
1803        op,
1804        value: operands[1].clone(),
1805    })
1806}
1807
1808/// Extract an if statement.
1809fn extract_if_stmt(cursor: CXCursor) -> Option<Statement> {
1810    // An if statement has 2 or 3 children:
1811    // 1. Condition expression
1812    // 2. Then block (compound statement)
1813    // 3. Else block (optional compound statement)
1814
1815    #[repr(C)]
1816    struct IfData {
1817        condition: Option<Expression>,
1818        then_block: Vec<Statement>,
1819        else_block: Option<Vec<Statement>>,
1820        child_index: u32,
1821    }
1822
1823    let mut if_data = IfData {
1824        condition: None,
1825        then_block: Vec::new(),
1826        else_block: None,
1827        child_index: 0,
1828    };
1829
1830    let data_ptr = &mut if_data as *mut IfData;
1831
1832    unsafe {
1833        clang_visitChildren(cursor, visit_if_children, data_ptr as CXClientData);
1834    }
1835
1836    Some(Statement::If {
1837        condition: if_data.condition?,
1838        then_block: if_data.then_block,
1839        else_block: if_data.else_block,
1840    })
1841}
1842
1843/// Visitor for if statement children.
1844#[allow(non_upper_case_globals)]
1845extern "C" fn visit_if_children(
1846    cursor: CXCursor,
1847    _parent: CXCursor,
1848    client_data: CXClientData,
1849) -> CXChildVisitResult {
1850    #[repr(C)]
1851    struct IfData {
1852        condition: Option<Expression>,
1853        then_block: Vec<Statement>,
1854        else_block: Option<Vec<Statement>>,
1855        child_index: u32,
1856    }
1857
1858    let if_data = unsafe { &mut *(client_data as *mut IfData) };
1859    let kind = unsafe { clang_getCursorKind(cursor) };
1860
1861    match if_data.child_index {
1862        0 => {
1863            // First child: condition expression
1864            // The cursor itself IS the condition, extract it directly
1865            if_data.condition = match kind {
1866                CXCursor_BinaryOperator => extract_binary_op(cursor),
1867                CXCursor_IntegerLiteral => extract_int_literal(cursor),
1868                107 => extract_float_literal(cursor), // CXCursor_FloatingLiteral
1869                110 => extract_char_literal(cursor),  // CXCursor_CharacterLiteral
1870                CXCursor_DeclRefExpr => extract_variable_ref(cursor),
1871                CXCursor_CallExpr => extract_function_call(cursor),
1872                CXCursor_UnaryOperator => extract_unary_op(cursor),
1873                _ => {
1874                    // For other expression types, try visiting children
1875                    let mut cond_expr: Option<Expression> = None;
1876                    let expr_ptr = &mut cond_expr as *mut Option<Expression>;
1877                    unsafe {
1878                        clang_visitChildren(cursor, visit_expression, expr_ptr as CXClientData);
1879                    }
1880                    cond_expr
1881                }
1882            };
1883            if_data.child_index += 1;
1884            CXChildVisit_Continue
1885        }
1886        1 => {
1887            // Second child: then block
1888            // DECY-216: Handle both compound statements (with braces) and single statements
1889            if kind == CXCursor_CompoundStmt {
1890                let body_ptr = &mut if_data.then_block as *mut Vec<Statement>;
1891                unsafe {
1892                    clang_visitChildren(cursor, visit_statement, body_ptr as CXClientData);
1893                }
1894            } else {
1895                // Single statement without braces: if (cond) return 1;
1896                if let Some(stmt) = extract_single_statement(cursor) {
1897                    if_data.then_block.push(stmt);
1898                }
1899            }
1900            if_data.child_index += 1;
1901            CXChildVisit_Continue
1902        }
1903        2 => {
1904            // Third child (optional): else block
1905            // DECY-216: Handle compound, if-else chain, and single statement
1906            if kind == CXCursor_CompoundStmt {
1907                let mut else_stmts = Vec::new();
1908                let body_ptr = &mut else_stmts as *mut Vec<Statement>;
1909                unsafe {
1910                    clang_visitChildren(cursor, visit_statement, body_ptr as CXClientData);
1911                }
1912                if_data.else_block = Some(else_stmts);
1913            } else if kind == CXCursor_IfStmt {
1914                // else if chain
1915                let mut else_stmts = Vec::new();
1916                let body_ptr = &mut else_stmts as *mut Vec<Statement>;
1917                unsafe {
1918                    clang_visitChildren(cursor, visit_statement, body_ptr as CXClientData);
1919                }
1920                if_data.else_block = Some(else_stmts);
1921            } else {
1922                // Single statement in else: else return 0;
1923                if let Some(stmt) = extract_single_statement(cursor) {
1924                    if_data.else_block = Some(vec![stmt]);
1925                }
1926            }
1927            if_data.child_index += 1;
1928            CXChildVisit_Continue
1929        }
1930        _ => CXChildVisit_Continue,
1931    }
1932}
1933
1934/// Extract a for loop statement.
1935fn extract_for_stmt(cursor: CXCursor) -> Option<Statement> {
1936    // DECY-200: Two-pass approach to handle for loops with empty parts
1937    // Clang skips empty parts entirely, so we can't rely on fixed indices
1938    //
1939    // Pass 1: Collect all children with their cursor kinds
1940    // Pass 2: Identify what each child represents based on type and position
1941
1942    #[repr(C)]
1943    struct ForChildInfo {
1944        cursor: CXCursor,
1945        kind: i32,
1946    }
1947
1948    #[repr(C)]
1949    struct ForCollector {
1950        children: Vec<ForChildInfo>,
1951    }
1952
1953    // First pass: collect all children
1954    extern "C" fn collect_for_children(
1955        cursor: CXCursor,
1956        _parent: CXCursor,
1957        client_data: CXClientData,
1958    ) -> CXChildVisitResult {
1959        let collector = unsafe { &mut *(client_data as *mut ForCollector) };
1960        let kind = unsafe { clang_getCursorKind(cursor) };
1961        collector.children.push(ForChildInfo { cursor, kind });
1962        CXChildVisit_Continue
1963    }
1964
1965    let mut collector = ForCollector {
1966        children: Vec::new(),
1967    };
1968
1969    unsafe {
1970        clang_visitChildren(
1971            cursor,
1972            collect_for_children,
1973            &mut collector as *mut _ as CXClientData,
1974        );
1975    }
1976
1977    // Second pass: identify what each child is
1978    // DECY-224: Use Vec to support multiple init/increment declarations
1979    let mut init: Vec<Statement> = Vec::new();
1980    let mut condition: Option<Expression> = None;
1981    let mut increment: Vec<Statement> = Vec::new();
1982    let mut body: Vec<Statement> = Vec::new();
1983
1984    let num_children = collector.children.len();
1985
1986    // Body is always the LAST child
1987    // The children before body are init/condition/increment in that order,
1988    // but clang omits empty ones
1989
1990    // Helper to check if a BinaryOperator is an assignment
1991    fn is_assignment_op(cursor: CXCursor) -> bool {
1992        if let Some(op) = extract_binary_operator(cursor) {
1993            matches!(op, BinaryOperator::Assign)
1994        } else {
1995            false
1996        }
1997    }
1998
1999    // Helper to check if a BinaryOperator is a comparison/logical (condition)
2000    fn is_condition_op(cursor: CXCursor) -> bool {
2001        if let Some(op) = extract_binary_operator(cursor) {
2002            matches!(
2003                op,
2004                BinaryOperator::Equal
2005                    | BinaryOperator::NotEqual
2006                    | BinaryOperator::LessThan
2007                    | BinaryOperator::GreaterThan
2008                    | BinaryOperator::LessEqual
2009                    | BinaryOperator::GreaterEqual
2010                    | BinaryOperator::LogicalAnd
2011                    | BinaryOperator::LogicalOr
2012            )
2013        } else {
2014            false
2015        }
2016    }
2017
2018    // DECY-224: Helper to extract increment statements (handles comma operator)
2019    fn extract_increment_stmts(cursor: CXCursor) -> Vec<Statement> {
2020        let kind = unsafe { clang_getCursorKind(cursor) };
2021        let mut stmts = Vec::new();
2022
2023        // Check for comma operator (BinaryOperator with comma)
2024        if kind == CXCursor_BinaryOperator {
2025            // Check if it's a comma operator by looking at the operator
2026            if let Some(BinaryOperator::Comma) = extract_binary_operator(cursor) {
2027                // Comma operator - recursively extract from both sides
2028                let mut children: Vec<CXCursor> = Vec::new();
2029                let children_ptr = &mut children as *mut Vec<CXCursor>;
2030
2031                extern "C" fn collect_children(
2032                    cursor: CXCursor,
2033                    _parent: CXCursor,
2034                    client_data: CXClientData,
2035                ) -> CXChildVisitResult {
2036                    let children = unsafe { &mut *(client_data as *mut Vec<CXCursor>) };
2037                    children.push(cursor);
2038                    CXChildVisit_Continue
2039                }
2040
2041                unsafe {
2042                    clang_visitChildren(cursor, collect_children, children_ptr as CXClientData);
2043                }
2044
2045                for child in children {
2046                    stmts.extend(extract_increment_stmts(child));
2047                }
2048                return stmts;
2049            }
2050            // Assignment increment
2051            if let Some(stmt) = extract_assignment_stmt(cursor) {
2052                stmts.push(stmt);
2053            }
2054        } else if kind == CXCursor_UnaryOperator {
2055            if let Some(stmt) = extract_inc_dec_stmt(cursor) {
2056                stmts.push(stmt);
2057            }
2058        }
2059        stmts
2060    }
2061
2062    if num_children == 0 {
2063        return Some(Statement::For {
2064            init,
2065            condition,
2066            increment,
2067            body,
2068        });
2069    }
2070
2071    // Process children based on count and types
2072    // The LAST child is always the body
2073    let body_idx = num_children - 1;
2074    let body_child = &collector.children[body_idx];
2075
2076    // Extract body
2077    if body_child.kind == CXCursor_CompoundStmt {
2078        let body_ptr = &mut body as *mut Vec<Statement>;
2079        unsafe {
2080            clang_visitChildren(body_child.cursor, visit_statement, body_ptr as CXClientData);
2081        }
2082    } else {
2083        // Single statement body - extract it
2084        if let Some(stmt) = extract_single_statement(body_child.cursor) {
2085            body.push(stmt);
2086        }
2087    }
2088
2089    // Process children before body
2090    let pre_body = &collector.children[..body_idx];
2091
2092    match pre_body.len() {
2093        0 => {
2094            // for (;;) - infinite loop with no init/condition/increment
2095        }
2096        1 => {
2097            // One child before body - could be init, condition, or increment
2098            // Use heuristics to determine which
2099            let child = &pre_body[0];
2100            if child.kind == CXCursor_DeclStmt {
2101                // DeclStmt - always init
2102                let mut init_stmts = Vec::new();
2103                let ptr = &mut init_stmts as *mut Vec<Statement>;
2104                unsafe {
2105                    clang_visitChildren(child.cursor, visit_statement, ptr as CXClientData);
2106                }
2107            } else if child.kind == CXCursor_BinaryOperator {
2108                if is_assignment_op(child.cursor) {
2109                    // Assignment - treat as init
2110                    if let Some(stmt) = extract_assignment_stmt(child.cursor) {
2111                        init.push(stmt);
2112                    }
2113                } else if is_condition_op(child.cursor) {
2114                    // Comparison - treat as condition
2115                    condition = extract_binary_op(child.cursor);
2116                } else {
2117                    // Ambiguous - default to condition
2118                    condition = extract_binary_op(child.cursor);
2119                }
2120            } else if child.kind == CXCursor_UnaryOperator {
2121                increment = extract_increment_stmts(child.cursor);
2122            } else {
2123                // Treat as condition by default
2124                condition = extract_expression_from_cursor(child.cursor);
2125            }
2126        }
2127        2 => {
2128            // Two children before body
2129            // Most common case: condition and increment (init is empty)
2130            let child0 = &pre_body[0];
2131            let child1 = &pre_body[1];
2132
2133            // Check if first child is init (DeclStmt or assignment)
2134            let first_is_init = child0.kind == CXCursor_DeclStmt
2135                || (child0.kind == CXCursor_BinaryOperator && is_assignment_op(child0.cursor));
2136
2137            if first_is_init {
2138                // child0 = init, child1 = condition (skip increment)
2139                if child0.kind == CXCursor_DeclStmt {
2140                    // DECY-224: Collect ALL declarations
2141                    let ptr = &mut init as *mut Vec<Statement>;
2142                    unsafe {
2143                        clang_visitChildren(child0.cursor, visit_statement, ptr as CXClientData);
2144                    }
2145                } else if let Some(stmt) = extract_assignment_stmt(child0.cursor) {
2146                    init.push(stmt);
2147                }
2148                condition = extract_expression_from_cursor(child1.cursor);
2149            } else {
2150                // child0 = condition, child1 = increment (no init)
2151                condition = extract_expression_from_cursor(child0.cursor);
2152                increment = extract_increment_stmts(child1.cursor);
2153            }
2154        }
2155        3 => {
2156            // Three children before body - init, condition, increment all present
2157            let child0 = &pre_body[0];
2158            let child1 = &pre_body[1];
2159            let child2 = &pre_body[2];
2160
2161            // Init - DECY-224: Collect ALL declarations
2162            if child0.kind == CXCursor_DeclStmt {
2163                let ptr = &mut init as *mut Vec<Statement>;
2164                unsafe {
2165                    clang_visitChildren(child0.cursor, visit_statement, ptr as CXClientData);
2166                }
2167            } else if child0.kind == CXCursor_BinaryOperator {
2168                if let Some(stmt) = extract_assignment_stmt(child0.cursor) {
2169                    init.push(stmt);
2170                }
2171            }
2172
2173            // Condition
2174            condition = extract_expression_from_cursor(child1.cursor);
2175
2176            // Increment - DECY-224: Handle comma operators
2177            increment = extract_increment_stmts(child2.cursor);
2178        }
2179        _ => {
2180            // More than 3 children before body - unexpected, handle gracefully
2181        }
2182    }
2183
2184    Some(Statement::For {
2185        init,
2186        condition,
2187        increment,
2188        body,
2189    })
2190}
2191
2192/// Extract expression from cursor for for-loop condition
2193fn extract_expression_from_cursor(cursor: CXCursor) -> Option<Expression> {
2194    let kind = unsafe { clang_getCursorKind(cursor) };
2195    match kind {
2196        CXCursor_BinaryOperator => extract_binary_op(cursor),
2197        CXCursor_IntegerLiteral => extract_int_literal(cursor),
2198        107 => extract_float_literal(cursor), // CXCursor_FloatingLiteral
2199        110 => extract_char_literal(cursor),  // CXCursor_CharacterLiteral
2200        CXCursor_DeclRefExpr => extract_variable_ref(cursor),
2201        CXCursor_CallExpr => extract_function_call(cursor),
2202        CXCursor_UnaryOperator => extract_unary_op(cursor),
2203        _ => {
2204            let mut expr: Option<Expression> = None;
2205            let expr_ptr = &mut expr as *mut Option<Expression>;
2206            unsafe {
2207                clang_visitChildren(cursor, visit_expression, expr_ptr as CXClientData);
2208            }
2209            expr
2210        }
2211    }
2212}
2213
2214/// Extract a single statement from a cursor (for non-compound for bodies)
2215fn extract_single_statement(cursor: CXCursor) -> Option<Statement> {
2216    let kind = unsafe { clang_getCursorKind(cursor) };
2217    match kind {
2218        CXCursor_IfStmt => extract_if_stmt(cursor),
2219        CXCursor_ForStmt => extract_for_stmt(cursor),
2220        CXCursor_WhileStmt => extract_while_stmt(cursor),
2221        CXCursor_ReturnStmt => extract_return_stmt(cursor),
2222        CXCursor_SwitchStmt => extract_switch_stmt(cursor),
2223        CXCursor_UnaryOperator => extract_inc_dec_stmt(cursor),
2224        CXCursor_BinaryOperator => extract_assignment_stmt(cursor),
2225        CXCursor_CallExpr => {
2226            if let Some(Expression::FunctionCall {
2227                function,
2228                arguments,
2229            }) = extract_function_call(cursor)
2230            {
2231                Some(Statement::FunctionCall {
2232                    function,
2233                    arguments,
2234                })
2235            } else {
2236                None
2237            }
2238        }
2239        CXCursor_BreakStmt => Some(Statement::Break),
2240        CXCursor_ContinueStmt => Some(Statement::Continue),
2241        CXCursor_DoStmt | CXCursor_NullStmt => None, // Not supported yet
2242        _ => None,
2243    }
2244}
2245/// Extract a while loop statement.
2246fn extract_while_stmt(cursor: CXCursor) -> Option<Statement> {
2247    // A while loop has 2 children:
2248    // 1. Condition expression
2249    // 2. Body (compound statement)
2250
2251    #[repr(C)]
2252    struct WhileData {
2253        condition: Option<Expression>,
2254        body: Vec<Statement>,
2255        child_index: u32,
2256    }
2257
2258    let mut while_data = WhileData {
2259        condition: None,
2260        body: Vec::new(),
2261        child_index: 0,
2262    };
2263
2264    let data_ptr = &mut while_data as *mut WhileData;
2265
2266    unsafe {
2267        clang_visitChildren(cursor, visit_while_children, data_ptr as CXClientData);
2268    }
2269
2270    Some(Statement::While {
2271        condition: while_data.condition?,
2272        body: while_data.body,
2273    })
2274}
2275
2276/// Visitor for while loop children.
2277#[allow(non_upper_case_globals)]
2278extern "C" fn visit_while_children(
2279    cursor: CXCursor,
2280    _parent: CXCursor,
2281    client_data: CXClientData,
2282) -> CXChildVisitResult {
2283    #[repr(C)]
2284    struct WhileData {
2285        condition: Option<Expression>,
2286        body: Vec<Statement>,
2287        child_index: u32,
2288    }
2289
2290    let while_data = unsafe { &mut *(client_data as *mut WhileData) };
2291    let kind = unsafe { clang_getCursorKind(cursor) };
2292
2293    match while_data.child_index {
2294        0 => {
2295            // First child: condition expression
2296            // The cursor itself IS the condition, extract it directly
2297            while_data.condition = match kind {
2298                CXCursor_BinaryOperator => extract_binary_op(cursor),
2299                CXCursor_IntegerLiteral => extract_int_literal(cursor),
2300                107 => extract_float_literal(cursor), // CXCursor_FloatingLiteral
2301                110 => extract_char_literal(cursor),  // CXCursor_CharacterLiteral
2302                CXCursor_DeclRefExpr => extract_variable_ref(cursor),
2303                CXCursor_CallExpr => extract_function_call(cursor),
2304                CXCursor_UnaryOperator => extract_unary_op(cursor),
2305                _ => {
2306                    let mut cond_expr: Option<Expression> = None;
2307                    let expr_ptr = &mut cond_expr as *mut Option<Expression>;
2308                    unsafe {
2309                        clang_visitChildren(cursor, visit_expression, expr_ptr as CXClientData);
2310                    }
2311                    cond_expr
2312                }
2313            };
2314            while_data.child_index += 1;
2315            CXChildVisit_Continue
2316        }
2317        1 => {
2318            // Second child: body
2319            if kind == CXCursor_CompoundStmt {
2320                let body_ptr = &mut while_data.body as *mut Vec<Statement>;
2321                unsafe {
2322                    clang_visitChildren(cursor, visit_statement, body_ptr as CXClientData);
2323                }
2324            }
2325            while_data.child_index += 1;
2326            CXChildVisit_Continue
2327        }
2328        _ => CXChildVisit_Continue,
2329    }
2330}
2331
2332/// Extract a switch statement from a cursor.
2333///
2334/// Parses switch statements including cases and default labels.
2335#[allow(non_upper_case_globals)]
2336fn extract_switch_stmt(cursor: CXCursor) -> Option<Statement> {
2337    // Switch has 2 children:
2338    // 1. Condition expression
2339    // 2. Body (compound statement containing case/default labels)
2340
2341    #[repr(C)]
2342    struct SwitchData {
2343        condition: Option<Expression>,
2344        cases: Vec<SwitchCase>,
2345        default_case: Option<Vec<Statement>>,
2346        child_index: u32,
2347    }
2348
2349    let mut switch_data = SwitchData {
2350        condition: None,
2351        cases: Vec::new(),
2352        default_case: None,
2353        child_index: 0,
2354    };
2355
2356    let data_ptr = &mut switch_data as *mut SwitchData;
2357
2358    unsafe {
2359        clang_visitChildren(cursor, visit_switch_children, data_ptr as CXClientData);
2360    }
2361
2362    Some(Statement::Switch {
2363        condition: switch_data.condition?,
2364        cases: switch_data.cases,
2365        default_case: switch_data.default_case,
2366    })
2367}
2368
2369/// Visitor callback for switch statement children (condition and body).
2370#[allow(non_upper_case_globals)]
2371extern "C" fn visit_switch_children(
2372    cursor: CXCursor,
2373    _parent: CXCursor,
2374    client_data: CXClientData,
2375) -> CXChildVisitResult {
2376    #[repr(C)]
2377    struct SwitchData {
2378        condition: Option<Expression>,
2379        cases: Vec<SwitchCase>,
2380        default_case: Option<Vec<Statement>>,
2381        child_index: u32,
2382    }
2383
2384    let switch_data = unsafe { &mut *(client_data as *mut SwitchData) };
2385    let kind = unsafe { clang_getCursorKind(cursor) };
2386
2387    match switch_data.child_index {
2388        0 => {
2389            // First child: condition expression
2390            if let Some(expr) = try_extract_expression(cursor) {
2391                switch_data.condition = Some(expr);
2392            }
2393            switch_data.child_index += 1;
2394            CXChildVisit_Continue
2395        }
2396        1 => {
2397            // Second child: compound statement body containing cases
2398            // Need to visit this recursively to find case/default labels
2399            if kind == CXCursor_CompoundStmt {
2400                unsafe {
2401                    clang_visitChildren(cursor, visit_switch_body, client_data);
2402                }
2403            }
2404            switch_data.child_index += 1;
2405            CXChildVisit_Continue
2406        }
2407        _ => CXChildVisit_Continue,
2408    }
2409}
2410
2411/// Visitor callback for switch body to extract cases and default.
2412#[allow(non_upper_case_globals)]
2413extern "C" fn visit_switch_body(
2414    cursor: CXCursor,
2415    _parent: CXCursor,
2416    client_data: CXClientData,
2417) -> CXChildVisitResult {
2418    #[repr(C)]
2419    struct SwitchData {
2420        condition: Option<Expression>,
2421        cases: Vec<SwitchCase>,
2422        default_case: Option<Vec<Statement>>,
2423        child_index: u32,
2424    }
2425
2426    let switch_data = unsafe { &mut *(client_data as *mut SwitchData) };
2427    let kind = unsafe { clang_getCursorKind(cursor) };
2428
2429    match kind {
2430        CXCursor_CaseStmt => {
2431            // Extract case statement
2432            if let Some(case) = extract_case_stmt(cursor) {
2433                switch_data.cases.push(case);
2434            }
2435            CXChildVisit_Continue
2436        }
2437        CXCursor_DefaultStmt => {
2438            // Extract default statement
2439            if let Some(body) = extract_default_stmt(cursor) {
2440                switch_data.default_case = Some(body);
2441            }
2442            CXChildVisit_Continue
2443        }
2444        _ => CXChildVisit_Continue,
2445    }
2446}
2447
2448/// Extract a case statement from a cursor.
2449fn extract_case_stmt(cursor: CXCursor) -> Option<SwitchCase> {
2450    // Case statement has 2 children:
2451    // 1. Case value expression
2452    // 2. Body (statements following the case label)
2453
2454    #[repr(C)]
2455    struct CaseData {
2456        value: Option<Expression>,
2457        body: Vec<Statement>,
2458        child_index: u32,
2459    }
2460
2461    let mut case_data = CaseData {
2462        value: None,
2463        body: Vec::new(),
2464        child_index: 0,
2465    };
2466
2467    let data_ptr = &mut case_data as *mut CaseData;
2468
2469    unsafe {
2470        clang_visitChildren(cursor, visit_case_children, data_ptr as CXClientData);
2471    }
2472
2473    Some(SwitchCase {
2474        value: case_data.value,
2475        body: case_data.body,
2476    })
2477}
2478
2479/// Visitor for case statement children.
2480#[allow(non_upper_case_globals)]
2481extern "C" fn visit_case_children(
2482    cursor: CXCursor,
2483    _parent: CXCursor,
2484    client_data: CXClientData,
2485) -> CXChildVisitResult {
2486    #[repr(C)]
2487    struct CaseData {
2488        value: Option<Expression>,
2489        body: Vec<Statement>,
2490        child_index: u32,
2491    }
2492
2493    let case_data = unsafe { &mut *(client_data as *mut CaseData) };
2494    let _kind = unsafe { clang_getCursorKind(cursor) };
2495
2496    match case_data.child_index {
2497        0 => {
2498            // First child: case value expression
2499            if let Some(expr) = try_extract_expression(cursor) {
2500                case_data.value = Some(expr);
2501            }
2502            case_data.child_index += 1;
2503            CXChildVisit_Continue
2504        }
2505        _ => {
2506            // Subsequent children: statements in case body
2507            // Extract statements until we hit another case or default
2508            if let Some(stmt) = extract_statement(cursor) {
2509                case_data.body.push(stmt);
2510            }
2511            // Continue recursing to find all statements in the case body
2512            CXChildVisit_Recurse
2513        }
2514    }
2515}
2516
2517/// Extract a default statement from a cursor.
2518fn extract_default_stmt(cursor: CXCursor) -> Option<Vec<Statement>> {
2519    // Default statement has body statements as children
2520    let mut body: Vec<Statement> = Vec::new();
2521    let body_ptr = &mut body as *mut Vec<Statement>;
2522
2523    unsafe {
2524        clang_visitChildren(cursor, visit_default_children, body_ptr as CXClientData);
2525    }
2526
2527    Some(body)
2528}
2529
2530/// Visitor for default statement children.
2531#[allow(non_upper_case_globals)]
2532extern "C" fn visit_default_children(
2533    cursor: CXCursor,
2534    _parent: CXCursor,
2535    client_data: CXClientData,
2536) -> CXChildVisitResult {
2537    let body = unsafe { &mut *(client_data as *mut Vec<Statement>) };
2538
2539    // Extract all statements in default body
2540    if let Some(stmt) = extract_statement(cursor) {
2541        body.push(stmt);
2542    }
2543
2544    CXChildVisit_Continue
2545}
2546
2547/// Helper function to extract a statement from a cursor based on its kind.
2548#[allow(non_upper_case_globals)]
2549fn extract_statement(cursor: CXCursor) -> Option<Statement> {
2550    let kind = unsafe { clang_getCursorKind(cursor) };
2551
2552    match kind {
2553        CXCursor_ReturnStmt => extract_return_stmt(cursor),
2554        CXCursor_VarDecl => extract_var_decl(cursor),
2555        CXCursor_IfStmt => extract_if_stmt(cursor),
2556        CXCursor_ForStmt => extract_for_stmt(cursor),
2557        CXCursor_WhileStmt => extract_while_stmt(cursor),
2558        CXCursor_BreakStmt => Some(Statement::Break),
2559        CXCursor_ContinueStmt => Some(Statement::Continue),
2560        CXCursor_UnaryOperator => extract_inc_dec_stmt(cursor),
2561        CXCursor_BinaryOperator => extract_assignment_stmt(cursor),
2562        CXCursor_CallExpr => {
2563            // Function call as statement
2564            if let Some(Expression::FunctionCall {
2565                function,
2566                arguments,
2567            }) = extract_function_call(cursor)
2568            {
2569                return Some(Statement::FunctionCall {
2570                    function,
2571                    arguments,
2572                });
2573            }
2574            None
2575        }
2576        _ => None,
2577    }
2578}
2579
2580/// Visitor callback for extracting expressions.
2581///
2582/// # Safety
2583///
2584/// This function is called by clang_visitChildren and must follow C calling conventions.
2585#[allow(non_upper_case_globals)]
2586extern "C" fn visit_expression(
2587    cursor: CXCursor,
2588    _parent: CXCursor,
2589    client_data: CXClientData,
2590) -> CXChildVisitResult {
2591    // SAFETY: Converting client data back to expression option pointer
2592    let expr_opt = unsafe { &mut *(client_data as *mut Option<Expression>) };
2593
2594    // SAFETY: Getting cursor kind
2595    let kind = unsafe { clang_getCursorKind(cursor) };
2596
2597    match kind {
2598        CXCursor_IntegerLiteral => {
2599            // Integer literal
2600            if let Some(expr) = extract_int_literal(cursor) {
2601                *expr_opt = Some(expr);
2602            }
2603            CXChildVisit_Continue
2604        }
2605        107 => {
2606            // Floating-point literal (CXCursor_FloatingLiteral)
2607            if let Some(expr) = extract_float_literal(cursor) {
2608                *expr_opt = Some(expr);
2609            }
2610            CXChildVisit_Continue
2611        }
2612        CXCursor_StringLiteral => {
2613            // String literal
2614            if let Some(expr) = extract_string_literal(cursor) {
2615                *expr_opt = Some(expr);
2616            }
2617            CXChildVisit_Continue
2618        }
2619        110 => {
2620            // Character literal (CXCursor_CharacterLiteral)
2621            if let Some(expr) = extract_char_literal(cursor) {
2622                *expr_opt = Some(expr);
2623            }
2624            CXChildVisit_Continue
2625        }
2626        CXCursor_DeclRefExpr => {
2627            // Variable reference (e.g., "a" or "b" in "a + b")
2628            if let Some(expr) = extract_variable_ref(cursor) {
2629                *expr_opt = Some(expr);
2630            }
2631            CXChildVisit_Continue
2632        }
2633        CXCursor_BinaryOperator => {
2634            // Binary operation (e.g., a + b)
2635            if let Some(expr) = extract_binary_op(cursor) {
2636                *expr_opt = Some(expr);
2637            }
2638            CXChildVisit_Continue
2639        }
2640        CXCursor_CallExpr => {
2641            // Function call
2642            if let Some(expr) = extract_function_call(cursor) {
2643                *expr_opt = Some(expr);
2644            }
2645            CXChildVisit_Continue
2646        }
2647        CXCursor_UnaryOperator => {
2648            // Unary operator (e.g., *ptr dereference)
2649            if let Some(expr) = extract_unary_op(cursor) {
2650                *expr_opt = Some(expr);
2651            }
2652            CXChildVisit_Continue
2653        }
2654        CXCursor_ArraySubscriptExpr => {
2655            // Array indexing (e.g., arr[i])
2656            if let Some(expr) = extract_array_index(cursor) {
2657                *expr_opt = Some(expr);
2658            }
2659            CXChildVisit_Continue
2660        }
2661        CXCursor_MemberRefExpr => {
2662            // Field access (e.g., ptr->field or obj.field)
2663            if let Some(expr) = extract_field_access(cursor) {
2664                *expr_opt = Some(expr);
2665            }
2666            CXChildVisit_Continue
2667        }
2668        116 => {
2669            // CXCursor_ConditionalOperator (ternary)
2670            // DECY-192: Ternary expressions like (a > b) ? a : b
2671            if let Some(expr) = extract_conditional_op(cursor) {
2672                *expr_opt = Some(expr);
2673            }
2674            CXChildVisit_Continue
2675        }
2676        117 => {
2677            // CXCursor_CStyleCastExpr - cast expression like (int)x or (long)&ptr
2678            // DECY-208: Extract cast expressions to preserve type conversions
2679            if let Some(expr) = extract_cast(cursor) {
2680                *expr_opt = Some(expr);
2681            }
2682            CXChildVisit_Continue
2683        }
2684        CXCursor_UnexposedExpr => {
2685            // Unexposed expressions might wrap other expressions (like ImplicitCastExpr wrapping CallExpr)
2686            // Recurse first to check if there's a more specific expression inside
2687            CXChildVisit_Recurse
2688        }
2689        CXCursor_ParenExpr => {
2690            // Parenthesized expressions wrap other expressions, recurse
2691            CXChildVisit_Recurse
2692        }
2693        136 => {
2694            // CXCursor_UnaryExpr - could be sizeof or other unary expr
2695            if let Some(expr) = extract_sizeof(cursor) {
2696                *expr_opt = Some(expr);
2697                CXChildVisit_Continue
2698            } else {
2699                // Not sizeof, recurse for other unary expressions
2700                CXChildVisit_Recurse
2701            }
2702        }
2703        119 => {
2704            // CXCursor_InitListExpr - initializer list for struct/array
2705            // DECY-133: Handle designated initializers like {.x = 10, .y = 20}
2706            if let Some(expr) = extract_init_list(cursor) {
2707                *expr_opt = Some(expr);
2708            }
2709            CXChildVisit_Continue
2710        }
2711        _ => CXChildVisit_Recurse,
2712    }
2713}
2714
2715/// Extract an integer literal expression.
2716fn extract_int_literal(cursor: CXCursor) -> Option<Expression> {
2717    // SAFETY: Get the extent (source range) of the cursor
2718    let extent = unsafe { clang_getCursorExtent(cursor) };
2719
2720    // SAFETY: Get the translation unit from the cursor
2721    let tu = unsafe {
2722        let loc = clang_getCursorLocation(cursor);
2723        let mut file = ptr::null_mut();
2724        let mut line = 0;
2725        let mut column = 0;
2726        let mut offset = 0;
2727        clang_getFileLocation(loc, &mut file, &mut line, &mut column, &mut offset);
2728
2729        // Get the translation unit containing this cursor
2730        // We need to traverse up to get it, but for now use a different approach
2731        clang_Cursor_getTranslationUnit(cursor)
2732    };
2733
2734    if tu.is_null() {
2735        return Some(Expression::IntLiteral(0));
2736    }
2737
2738    // SAFETY: Tokenize the extent
2739    let mut tokens = ptr::null_mut();
2740    let mut num_tokens = 0;
2741
2742    unsafe {
2743        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
2744    }
2745
2746    let mut value = 0;
2747
2748    if num_tokens > 0 {
2749        // SAFETY: Get the spelling of the first token
2750        unsafe {
2751            let token_cxstring = clang_getTokenSpelling(tu, *tokens);
2752            let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
2753            if let Ok(token_str) = c_str.to_str() {
2754                value = token_str.parse().unwrap_or(0);
2755            }
2756            clang_disposeString(token_cxstring);
2757
2758            // SAFETY: Dispose tokens
2759            clang_disposeTokens(tu, tokens, num_tokens);
2760        }
2761    } else {
2762        // DECY-195: Fallback for system headers where tokenization fails
2763        // Use clang_Cursor_Evaluate to get the constant value
2764        unsafe {
2765            let eval_result = clang_Cursor_Evaluate(cursor);
2766            if !eval_result.is_null() {
2767                value = clang_EvalResult_getAsInt(eval_result);
2768                clang_EvalResult_dispose(eval_result);
2769            }
2770        }
2771    }
2772
2773    Some(Expression::IntLiteral(value))
2774}
2775
2776/// DECY-207: Extract a floating-point literal expression.
2777fn extract_float_literal(cursor: CXCursor) -> Option<Expression> {
2778    // SAFETY: Get the extent (source range) of the cursor
2779    let extent = unsafe { clang_getCursorExtent(cursor) };
2780
2781    // SAFETY: Get the translation unit from the cursor
2782    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
2783
2784    if tu.is_null() {
2785        return Some(Expression::FloatLiteral("0.0".to_string()));
2786    }
2787
2788    // SAFETY: Tokenize the extent
2789    let mut tokens = ptr::null_mut();
2790    let mut num_tokens = 0;
2791
2792    unsafe {
2793        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
2794    }
2795
2796    let mut value = "0.0".to_string();
2797
2798    if num_tokens > 0 {
2799        // SAFETY: Get the spelling of the first token
2800        unsafe {
2801            let token_cxstring = clang_getTokenSpelling(tu, *tokens);
2802            let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
2803            if let Ok(token_str) = c_str.to_str() {
2804                // Keep the string as-is (preserves precision)
2805                value = token_str.to_string();
2806            }
2807            clang_disposeString(token_cxstring);
2808
2809            // SAFETY: Dispose tokens
2810            clang_disposeTokens(tu, tokens, num_tokens);
2811        }
2812    } else {
2813        // Fallback using evaluate
2814        unsafe {
2815            let eval_result = clang_Cursor_Evaluate(cursor);
2816            if !eval_result.is_null() {
2817                let float_val = clang_EvalResult_getAsDouble(eval_result);
2818                value = format!("{}", float_val);
2819                clang_EvalResult_dispose(eval_result);
2820            }
2821        }
2822    }
2823
2824    Some(Expression::FloatLiteral(value))
2825}
2826
2827/// Extract a string literal expression.
2828fn extract_string_literal(cursor: CXCursor) -> Option<Expression> {
2829    // SAFETY: Get the extent (source range) of the cursor
2830    let extent = unsafe { clang_getCursorExtent(cursor) };
2831
2832    // SAFETY: Get the translation unit from the cursor
2833    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
2834
2835    if tu.is_null() {
2836        return Some(Expression::StringLiteral(String::new()));
2837    }
2838
2839    // SAFETY: Tokenize the extent
2840    let mut tokens = ptr::null_mut();
2841    let mut num_tokens = 0;
2842
2843    unsafe {
2844        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
2845    }
2846
2847    let mut value = String::new();
2848
2849    if num_tokens > 0 {
2850        // SAFETY: Get the spelling of the first token
2851        unsafe {
2852            let token_cxstring = clang_getTokenSpelling(tu, *tokens);
2853            let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
2854            if let Ok(token_str) = c_str.to_str() {
2855                // Remove surrounding quotes from string literal
2856                value = token_str.trim_matches('"').to_string();
2857            }
2858            clang_disposeString(token_cxstring);
2859
2860            // SAFETY: Dispose tokens
2861            clang_disposeTokens(tu, tokens, num_tokens);
2862        }
2863    }
2864
2865    Some(Expression::StringLiteral(value))
2866}
2867
2868/// Extract a character literal expression.
2869/// Handles plain characters ('a'), escape sequences ('\0', '\n', '\t', etc.)
2870fn extract_char_literal(cursor: CXCursor) -> Option<Expression> {
2871    // SAFETY: Get the extent (source range) of the cursor
2872    let extent = unsafe { clang_getCursorExtent(cursor) };
2873
2874    // SAFETY: Get the translation unit from the cursor
2875    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
2876
2877    if tu.is_null() {
2878        return Some(Expression::CharLiteral(0));
2879    }
2880
2881    // SAFETY: Tokenize the extent
2882    let mut tokens = ptr::null_mut();
2883    let mut num_tokens = 0;
2884
2885    unsafe {
2886        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
2887    }
2888
2889    let mut value: i8 = 0;
2890
2891    if num_tokens > 0 {
2892        // SAFETY: Get the spelling of the first token
2893        unsafe {
2894            let token_cxstring = clang_getTokenSpelling(tu, *tokens);
2895            let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
2896            if let Ok(token_str) = c_str.to_str() {
2897                // Remove surrounding quotes from character literal
2898                let inner = token_str.trim_matches('\'');
2899                value = parse_char_literal(inner);
2900            }
2901            clang_disposeString(token_cxstring);
2902
2903            // SAFETY: Dispose tokens
2904            clang_disposeTokens(tu, tokens, num_tokens);
2905        }
2906    }
2907
2908    Some(Expression::CharLiteral(value))
2909}
2910
2911/// Parse a character literal string (without quotes) into its i8 value.
2912/// Handles escape sequences like \0, \n, \t, \r, \\, \', \"
2913fn parse_char_literal(s: &str) -> i8 {
2914    if s.is_empty() {
2915        return 0;
2916    }
2917
2918    let mut chars = s.chars();
2919    let first = chars.next().unwrap();
2920
2921    if first == '\\' {
2922        // Escape sequence
2923        match chars.next() {
2924            Some('0') => 0, // null character
2925            Some('n') => b'\n' as i8,
2926            Some('t') => b'\t' as i8,
2927            Some('r') => b'\r' as i8,
2928            Some('\\') => b'\\' as i8,
2929            Some('\'') => b'\'' as i8,
2930            Some('"') => b'"' as i8,
2931            Some('a') => 7,  // bell
2932            Some('b') => 8,  // backspace
2933            Some('f') => 12, // form feed
2934            Some('v') => 11, // vertical tab
2935            Some('x') => {
2936                // Hex escape: \xNN
2937                let hex: String = chars.take(2).collect();
2938                i8::from_str_radix(&hex, 16).unwrap_or(0)
2939            }
2940            Some(c) if c.is_ascii_digit() => {
2941                // Octal escape: \NNN
2942                let mut octal = String::new();
2943                octal.push(c);
2944                for _ in 0..2 {
2945                    if let Some(d) = chars.next() {
2946                        if d.is_ascii_digit() && d < '8' {
2947                            octal.push(d);
2948                        } else {
2949                            break;
2950                        }
2951                    }
2952                }
2953                i8::from_str_radix(&octal, 8).unwrap_or(0)
2954            }
2955            _ => first as i8,
2956        }
2957    } else {
2958        // Plain character
2959        first as i8
2960    }
2961}
2962
2963/// Extract a variable reference expression.
2964fn extract_variable_ref(cursor: CXCursor) -> Option<Expression> {
2965    // Get variable name
2966    let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
2967    let name = unsafe {
2968        let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
2969        let var_name = c_str.to_string_lossy().into_owned();
2970        clang_disposeString(name_cxstring);
2971        var_name
2972    };
2973
2974    Some(Expression::Variable(name))
2975}
2976
2977/// Extract a binary operation expression.
2978fn extract_binary_op(cursor: CXCursor) -> Option<Expression> {
2979    // Extract operator by tokenizing
2980    let op = extract_binary_operator(cursor)?;
2981
2982    // Extract left and right operands by visiting children
2983    let mut operands: Vec<Expression> = Vec::new();
2984    let operands_ptr = &mut operands as *mut Vec<Expression>;
2985
2986    unsafe {
2987        clang_visitChildren(cursor, visit_binary_operand, operands_ptr as CXClientData);
2988    }
2989
2990    // Binary operators should have exactly 2 operands
2991    if operands.len() != 2 {
2992        return None;
2993    }
2994
2995    Some(Expression::BinaryOp {
2996        op,
2997        left: Box::new(operands[0].clone()),
2998        right: Box::new(operands[1].clone()),
2999    })
3000}
3001
3002/// Visitor callback for binary operator operands.
3003#[allow(non_upper_case_globals)]
3004extern "C" fn visit_binary_operand(
3005    cursor: CXCursor,
3006    _parent: CXCursor,
3007    client_data: CXClientData,
3008) -> CXChildVisitResult {
3009    let operands = unsafe { &mut *(client_data as *mut Vec<Expression>) };
3010    let kind = unsafe { clang_getCursorKind(cursor) };
3011
3012    match kind {
3013        CXCursor_IntegerLiteral => {
3014            if let Some(expr) = extract_int_literal(cursor) {
3015                operands.push(expr);
3016            }
3017            CXChildVisit_Continue
3018        }
3019        107 => {
3020            // Floating-point literal (CXCursor_FloatingLiteral)
3021            if let Some(expr) = extract_float_literal(cursor) {
3022                operands.push(expr);
3023            }
3024            CXChildVisit_Continue
3025        }
3026        CXCursor_StringLiteral => {
3027            if let Some(expr) = extract_string_literal(cursor) {
3028                operands.push(expr);
3029            }
3030            CXChildVisit_Continue
3031        }
3032        110 => {
3033            // Character literal (CXCursor_CharacterLiteral)
3034            if let Some(expr) = extract_char_literal(cursor) {
3035                operands.push(expr);
3036            }
3037            CXChildVisit_Continue
3038        }
3039        CXCursor_DeclRefExpr => {
3040            if let Some(expr) = extract_variable_ref(cursor) {
3041                operands.push(expr);
3042            }
3043            CXChildVisit_Continue
3044        }
3045        CXCursor_BinaryOperator => {
3046            // Nested binary operation
3047            if let Some(expr) = extract_binary_op(cursor) {
3048                operands.push(expr);
3049            }
3050            CXChildVisit_Continue
3051        }
3052        CXCursor_UnaryOperator => {
3053            // Unary operation (e.g., *ptr dereference)
3054            if let Some(expr) = extract_unary_op(cursor) {
3055                operands.push(expr);
3056            }
3057            CXChildVisit_Continue
3058        }
3059        CXCursor_ArraySubscriptExpr => {
3060            // Array indexing (e.g., arr[i])
3061            if let Some(expr) = extract_array_index(cursor) {
3062                operands.push(expr);
3063            }
3064            CXChildVisit_Continue
3065        }
3066        CXCursor_MemberRefExpr => {
3067            // Field access (e.g., ptr->field or obj.field)
3068            if let Some(expr) = extract_field_access(cursor) {
3069                operands.push(expr);
3070            }
3071            CXChildVisit_Continue
3072        }
3073        CXCursor_UnexposedExpr | CXCursor_ParenExpr => {
3074            // Unexposed expressions might be sizeof or wrap other expressions
3075            if let Some(expr) = extract_sizeof(cursor) {
3076                operands.push(expr);
3077                CXChildVisit_Continue
3078            } else {
3079                CXChildVisit_Recurse
3080            }
3081        }
3082        136 => {
3083            // CXCursor_UnaryExpr - includes sizeof, alignof, etc.
3084            if let Some(expr) = extract_sizeof(cursor) {
3085                operands.push(expr);
3086                CXChildVisit_Continue
3087            } else {
3088                CXChildVisit_Recurse
3089            }
3090        }
3091        CXCursor_CallExpr => {
3092            // Function call expression (e.g., malloc(size))
3093            if let Some(expr) = extract_function_call(cursor) {
3094                operands.push(expr);
3095            }
3096            CXChildVisit_Continue
3097        }
3098        116 => {
3099            // CXCursor_ConditionalOperator (ternary) - DECY-192
3100            if let Some(expr) = extract_conditional_op(cursor) {
3101                operands.push(expr);
3102            }
3103            CXChildVisit_Continue
3104        }
3105        _ => CXChildVisit_Recurse,
3106    }
3107}
3108
3109/// DECY-234: Extract binary operator by looking between child cursor locations.
3110/// This is more reliable than tokenizing the full extent for macro-expanded expressions.
3111#[allow(non_upper_case_globals)]
3112fn extract_binary_operator_from_children(
3113    cursor: CXCursor,
3114    tu: CXTranslationUnit,
3115) -> Option<BinaryOperator> {
3116    // Collect the two child cursors
3117    let mut children: Vec<CXCursor> = Vec::new();
3118    let children_ptr = &mut children as *mut Vec<CXCursor>;
3119
3120    extern "C" fn collect_children(
3121        cursor: CXCursor,
3122        _parent: CXCursor,
3123        client_data: CXClientData,
3124    ) -> CXChildVisitResult {
3125        let children = unsafe { &mut *(client_data as *mut Vec<CXCursor>) };
3126        children.push(cursor);
3127        CXChildVisit_Continue
3128    }
3129
3130    unsafe {
3131        clang_visitChildren(cursor, collect_children, children_ptr as CXClientData);
3132    }
3133
3134    // Need exactly 2 children for a binary operator
3135    if children.len() != 2 {
3136        return None;
3137    }
3138
3139    // Get the end location of first child and start location of second child
3140    let first_extent = unsafe { clang_getCursorExtent(children[0]) };
3141    let second_extent = unsafe { clang_getCursorExtent(children[1]) };
3142
3143    let first_end = unsafe { clang_getRangeEnd(first_extent) };
3144    let second_start = unsafe { clang_getRangeStart(second_extent) };
3145
3146    // Create a source range between the two children
3147    let operator_range = unsafe { clang_getRange(first_end, second_start) };
3148
3149    // Tokenize this specific range to find the operator
3150    let mut tokens = ptr::null_mut();
3151    let mut num_tokens = 0;
3152
3153    unsafe {
3154        clang_tokenize(tu, operator_range, &mut tokens, &mut num_tokens);
3155    }
3156
3157    if tokens.is_null() || num_tokens == 0 {
3158        return None;
3159    }
3160
3161    // Look for an operator token in this range
3162    let mut result = None;
3163    for i in 0..num_tokens {
3164        unsafe {
3165            let token = *tokens.add(i as usize);
3166            let token_kind = clang_getTokenKind(token);
3167
3168            if token_kind == CXToken_Punctuation {
3169                let token_cxstring = clang_getTokenSpelling(tu, token);
3170                let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
3171                if let Ok(token_str) = c_str.to_str() {
3172                    result = match token_str {
3173                        "+" => Some(BinaryOperator::Add),
3174                        "-" => Some(BinaryOperator::Subtract),
3175                        "*" => Some(BinaryOperator::Multiply),
3176                        "/" => Some(BinaryOperator::Divide),
3177                        "%" => Some(BinaryOperator::Modulo),
3178                        "==" => Some(BinaryOperator::Equal),
3179                        "!=" => Some(BinaryOperator::NotEqual),
3180                        "<" => Some(BinaryOperator::LessThan),
3181                        ">" => Some(BinaryOperator::GreaterThan),
3182                        "<=" => Some(BinaryOperator::LessEqual),
3183                        ">=" => Some(BinaryOperator::GreaterEqual),
3184                        "&&" => Some(BinaryOperator::LogicalAnd),
3185                        "||" => Some(BinaryOperator::LogicalOr),
3186                        "<<" => Some(BinaryOperator::LeftShift),
3187                        ">>" => Some(BinaryOperator::RightShift),
3188                        "&" => Some(BinaryOperator::BitwiseAnd),
3189                        "|" => Some(BinaryOperator::BitwiseOr),
3190                        "^" => Some(BinaryOperator::BitwiseXor),
3191                        "=" => Some(BinaryOperator::Assign),
3192                        "," => Some(BinaryOperator::Comma),
3193                        _ => None,
3194                    };
3195                    if result.is_some() {
3196                        clang_disposeString(token_cxstring);
3197                        break;
3198                    }
3199                }
3200                clang_disposeString(token_cxstring);
3201            }
3202        }
3203    }
3204
3205    // Dispose tokens
3206    unsafe {
3207        clang_disposeTokens(tu, tokens, num_tokens);
3208    }
3209
3210    result
3211}
3212
3213/// Extract the binary operator from a cursor by tokenizing.
3214#[allow(non_upper_case_globals)]
3215fn extract_binary_operator(cursor: CXCursor) -> Option<BinaryOperator> {
3216    // Get the translation unit
3217    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
3218    if tu.is_null() {
3219        return None;
3220    }
3221
3222    // DECY-234: First, try to get operator by looking between child cursors
3223    // This handles macro-expanded expressions better than tokenizing the full extent
3224    if let Some(op) = extract_binary_operator_from_children(cursor, tu) {
3225        return Some(op);
3226    }
3227
3228    // Fallback: Get the extent (source range) of the cursor
3229    let extent = unsafe { clang_getCursorExtent(cursor) };
3230
3231    // Tokenize to find the operator
3232    let mut tokens = ptr::null_mut();
3233    let mut num_tokens = 0;
3234
3235    unsafe {
3236        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
3237    }
3238
3239    // DECY-234: Get the file of the cursor's expansion location to filter macro tokens
3240    // For macro-expanded code, the extent spans multiple locations including macro definitions
3241    // We only want operators from the expansion site, not from macro definitions
3242    let cursor_loc = unsafe { clang_getCursorLocation(cursor) };
3243    let mut cursor_file: CXFile = ptr::null_mut();
3244    let mut _line = 0u32;
3245    let mut _col = 0u32;
3246    let mut _offset = 0u32;
3247    unsafe {
3248        clang_getExpansionLocation(
3249            cursor_loc,
3250            &mut cursor_file,
3251            &mut _line,
3252            &mut _col,
3253            &mut _offset,
3254        );
3255    }
3256
3257    let mut operator = None;
3258
3259    // Look through tokens to find the operator
3260    // For compound expressions like "a > 0 && b > 0", we need to find the LAST
3261    // operator (the one with lowest precedence) which represents THIS binary operation.
3262    // We scan from right to left to find operators with lowest precedence first.
3263    // Precedence (lowest to highest): || > && > == != > < > <= >= > + - > * / %
3264
3265    let mut candidates: Vec<(usize, BinaryOperator)> = Vec::new();
3266    let mut found_first_operand = false;
3267    let mut paren_depth: i32 = 0; // Track parenthesis nesting depth
3268
3269    // DECY-234: Get extent line bounds to filter tokens from macro definitions
3270    let extent_start = unsafe { clang_getRangeStart(extent) };
3271    let extent_end = unsafe { clang_getRangeEnd(extent) };
3272    let mut start_line = 0u32;
3273    let mut end_line = 0u32;
3274    unsafe {
3275        clang_getExpansionLocation(
3276            extent_start,
3277            ptr::null_mut(),
3278            &mut start_line,
3279            ptr::null_mut(),
3280            ptr::null_mut(),
3281        );
3282        clang_getExpansionLocation(
3283            extent_end,
3284            ptr::null_mut(),
3285            &mut end_line,
3286            ptr::null_mut(),
3287            ptr::null_mut(),
3288        );
3289    }
3290
3291    for i in 0..num_tokens {
3292        unsafe {
3293            let token = *tokens.add(i as usize);
3294            let token_kind = clang_getTokenKind(token);
3295
3296            // DECY-234: Skip tokens that are from macro definitions (different file)
3297            let token_loc = clang_getTokenLocation(tu, token);
3298            let mut token_file: CXFile = ptr::null_mut();
3299            let mut token_line = 0u32;
3300            clang_getExpansionLocation(
3301                token_loc,
3302                &mut token_file,
3303                &mut token_line,
3304                &mut _col,
3305                &mut _offset,
3306            );
3307
3308            // Skip tokens from different files (macro definition files)
3309            if !cursor_file.is_null() && !token_file.is_null() && token_file != cursor_file {
3310                continue;
3311            }
3312
3313            // DECY-234: Skip tokens outside the extent's line range
3314            // When macros are involved, clang_tokenize returns tokens from the macro
3315            // definition site (e.g., line 34) even though extent is single-line (e.g., 38-38).
3316            // Filter out these spurious tokens.
3317            if start_line > 0
3318                && end_line > 0
3319                && token_line > 0
3320                && (token_line < start_line || token_line > end_line)
3321            {
3322                continue;
3323            }
3324
3325            // Track when we've seen the first operand (identifier or literal)
3326            if token_kind == CXToken_Identifier || token_kind == CXToken_Literal {
3327                found_first_operand = true;
3328            }
3329
3330            // Track parenthesis depth to avoid operators inside function calls
3331            if token_kind == CXToken_Punctuation {
3332                let token_cxstring = clang_getTokenSpelling(tu, token);
3333                let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
3334                if let Ok(token_str) = c_str.to_str() {
3335                    match token_str {
3336                        "(" => paren_depth += 1,
3337                        ")" => paren_depth = paren_depth.saturating_sub(1),
3338                        _ => {}
3339                    }
3340
3341                    // Only collect operator candidates at depth 0 (outside parentheses)
3342                    // This fixes DECY-116: n * func(n - 1) was picking up the - inside parens
3343                    if found_first_operand && paren_depth == 0 {
3344                        let op = match token_str {
3345                            "+" => Some(BinaryOperator::Add),
3346                            "-" => Some(BinaryOperator::Subtract),
3347                            "*" => Some(BinaryOperator::Multiply),
3348                            "/" => Some(BinaryOperator::Divide),
3349                            "%" => Some(BinaryOperator::Modulo),
3350                            "==" => Some(BinaryOperator::Equal),
3351                            "!=" => Some(BinaryOperator::NotEqual),
3352                            "<" => Some(BinaryOperator::LessThan),
3353                            ">" => Some(BinaryOperator::GreaterThan),
3354                            "<=" => Some(BinaryOperator::LessEqual),
3355                            ">=" => Some(BinaryOperator::GreaterEqual),
3356                            "&&" => Some(BinaryOperator::LogicalAnd),
3357                            "||" => Some(BinaryOperator::LogicalOr),
3358                            // DECY-137: Bitwise and shift operators
3359                            "<<" => Some(BinaryOperator::LeftShift),
3360                            ">>" => Some(BinaryOperator::RightShift),
3361                            "&" => Some(BinaryOperator::BitwiseAnd),
3362                            "|" => Some(BinaryOperator::BitwiseOr),
3363                            "^" => Some(BinaryOperator::BitwiseXor),
3364                            // DECY-195: Assignment operator for embedded assignments like (c=getchar())
3365                            "=" => Some(BinaryOperator::Assign),
3366                            // DECY-224: Comma operator for multi-expression statements (i++, j--)
3367                            "," => Some(BinaryOperator::Comma),
3368                            _ => None,
3369                        };
3370                        if let Some(op) = op {
3371                            candidates.push((i as usize, op));
3372                        }
3373                    }
3374                }
3375                clang_disposeString(token_cxstring);
3376            }
3377        }
3378    }
3379
3380    // Select the operator with lowest precedence (appears last in our search)
3381    // This handles cases like "a > 0 && b > 0" where && should be selected over >
3382    // C precedence (low to high): , > = > || > && > | > ^ > & > == != > < > <= >= > << >> > + - > * / %
3383    if !candidates.is_empty() {
3384        // DECY-234: Filter out comma operators from candidates when other operators are present
3385        // Macro expansion can introduce spurious commas in the token stream
3386        // We should only use comma as operator if it's a genuine comma expression
3387        let has_arithmetic = candidates.iter().any(|(_, op)| {
3388            matches!(
3389                op,
3390                BinaryOperator::Add
3391                    | BinaryOperator::Subtract
3392                    | BinaryOperator::Multiply
3393                    | BinaryOperator::Divide
3394                    | BinaryOperator::Modulo
3395            )
3396        });
3397        let has_comparison = candidates.iter().any(|(_, op)| {
3398            matches!(
3399                op,
3400                BinaryOperator::LessThan
3401                    | BinaryOperator::GreaterThan
3402                    | BinaryOperator::LessEqual
3403                    | BinaryOperator::GreaterEqual
3404                    | BinaryOperator::Equal
3405                    | BinaryOperator::NotEqual
3406            )
3407        });
3408
3409        // Remove comma from candidates if real operators are present
3410        if has_arithmetic || has_comparison {
3411            candidates.retain(|(_, op)| !matches!(op, BinaryOperator::Comma));
3412        }
3413
3414        // DECY-195: Assignment has lowest precedence (now that spurious commas are filtered)
3415        for (_, op) in &candidates {
3416            if matches!(op, BinaryOperator::Assign) {
3417                operator = Some(*op);
3418                break;
3419            }
3420        }
3421        // Find the first || operator (next lowest precedence)
3422        if operator.is_none() {
3423            for (_, op) in &candidates {
3424                if matches!(op, BinaryOperator::LogicalOr) {
3425                    operator = Some(*op);
3426                    break;
3427                }
3428            }
3429        }
3430        // If no ||, find first &&
3431        if operator.is_none() {
3432            for (_, op) in &candidates {
3433                if matches!(op, BinaryOperator::LogicalAnd) {
3434                    operator = Some(*op);
3435                    break;
3436                }
3437            }
3438        }
3439        // DECY-137: Bitwise OR (|)
3440        if operator.is_none() {
3441            for (_, op) in &candidates {
3442                if matches!(op, BinaryOperator::BitwiseOr) {
3443                    operator = Some(*op);
3444                    break;
3445                }
3446            }
3447        }
3448        // DECY-137: Bitwise XOR (^)
3449        if operator.is_none() {
3450            for (_, op) in &candidates {
3451                if matches!(op, BinaryOperator::BitwiseXor) {
3452                    operator = Some(*op);
3453                    break;
3454                }
3455            }
3456        }
3457        // DECY-137: Bitwise AND (&)
3458        if operator.is_none() {
3459            for (_, op) in &candidates {
3460                if matches!(op, BinaryOperator::BitwiseAnd) {
3461                    operator = Some(*op);
3462                    break;
3463                }
3464            }
3465        }
3466        // Equality operators (==, !=)
3467        if operator.is_none() {
3468            for (_, op) in &candidates {
3469                if matches!(op, BinaryOperator::Equal | BinaryOperator::NotEqual) {
3470                    operator = Some(*op);
3471                    break;
3472                }
3473            }
3474        }
3475        // Relational operators (<, >, <=, >=)
3476        if operator.is_none() {
3477            for (_, op) in &candidates {
3478                if matches!(
3479                    op,
3480                    BinaryOperator::LessThan
3481                        | BinaryOperator::GreaterThan
3482                        | BinaryOperator::LessEqual
3483                        | BinaryOperator::GreaterEqual
3484                ) {
3485                    operator = Some(*op);
3486                    break;
3487                }
3488            }
3489        }
3490        // DECY-137: Shift operators (<<, >>)
3491        if operator.is_none() {
3492            for (_, op) in &candidates {
3493                if matches!(op, BinaryOperator::LeftShift | BinaryOperator::RightShift) {
3494                    operator = Some(*op);
3495                    break;
3496                }
3497            }
3498        }
3499        // Additive operators (+, -)
3500        if operator.is_none() {
3501            for (_, op) in &candidates {
3502                if matches!(op, BinaryOperator::Add | BinaryOperator::Subtract) {
3503                    operator = Some(*op);
3504                    break;
3505                }
3506            }
3507        }
3508        // If no additive, take first multiplicative operator (*, /, %)
3509        if operator.is_none() {
3510            operator = Some(candidates[0].1);
3511        }
3512    }
3513
3514    unsafe {
3515        clang_disposeTokens(tu, tokens, num_tokens);
3516    }
3517
3518    operator
3519}
3520
3521/// Extract a function call expression.
3522fn extract_function_call(cursor: CXCursor) -> Option<Expression> {
3523    // Get function name
3524    let name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
3525    let function = unsafe {
3526        let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
3527        let name = c_str.to_string_lossy().into_owned();
3528        clang_disposeString(name_cxstring);
3529        name
3530    };
3531
3532    // Extract arguments by visiting children
3533    // We use a struct to track if we've seen the function reference yet
3534    #[repr(C)]
3535    struct ArgData {
3536        arguments: Vec<Expression>,
3537        skip_first_declref: bool,
3538    }
3539
3540    let mut arg_data = ArgData {
3541        arguments: Vec::new(),
3542        skip_first_declref: true, // Skip the first DeclRefExpr (function name)
3543    };
3544    let args_ptr = &mut arg_data as *mut ArgData;
3545
3546    unsafe {
3547        clang_visitChildren(cursor, visit_call_argument, args_ptr as CXClientData);
3548    }
3549
3550    Some(Expression::FunctionCall {
3551        function,
3552        arguments: arg_data.arguments,
3553    })
3554}
3555
3556/// Visitor callback for function call arguments.
3557///
3558/// # Safety
3559///
3560/// This function is called by clang_visitChildren and must follow C calling conventions.
3561#[allow(non_upper_case_globals)]
3562extern "C" fn visit_call_argument(
3563    cursor: CXCursor,
3564    _parent: CXCursor,
3565    client_data: CXClientData,
3566) -> CXChildVisitResult {
3567    #[repr(C)]
3568    struct ArgData {
3569        arguments: Vec<Expression>,
3570        skip_first_declref: bool,
3571    }
3572
3573    // SAFETY: Converting client data back to ArgData pointer
3574    let arg_data = unsafe { &mut *(client_data as *mut ArgData) };
3575
3576    // SAFETY: Getting cursor kind
3577    let kind = unsafe { clang_getCursorKind(cursor) };
3578
3579    match kind {
3580        CXCursor_IntegerLiteral => {
3581            if let Some(expr) = extract_int_literal(cursor) {
3582                arg_data.arguments.push(expr);
3583            }
3584            CXChildVisit_Continue
3585        }
3586        107 => {
3587            // Floating-point literal (CXCursor_FloatingLiteral)
3588            if let Some(expr) = extract_float_literal(cursor) {
3589                arg_data.arguments.push(expr);
3590            }
3591            CXChildVisit_Continue
3592        }
3593        CXCursor_StringLiteral => {
3594            if let Some(expr) = extract_string_literal(cursor) {
3595                arg_data.arguments.push(expr);
3596            }
3597            CXChildVisit_Continue
3598        }
3599        110 => {
3600            // Character literal (CXCursor_CharacterLiteral)
3601            if let Some(expr) = extract_char_literal(cursor) {
3602                arg_data.arguments.push(expr);
3603            }
3604            CXChildVisit_Continue
3605        }
3606        CXCursor_DeclRefExpr => {
3607            // Variable reference argument
3608            // The first DeclRefExpr is the function being called, skip it
3609            if arg_data.skip_first_declref {
3610                arg_data.skip_first_declref = false;
3611                CXChildVisit_Continue
3612            } else {
3613                if let Some(expr) = extract_variable_ref(cursor) {
3614                    arg_data.arguments.push(expr);
3615                }
3616                CXChildVisit_Continue
3617            }
3618        }
3619        CXCursor_BinaryOperator => {
3620            // Binary operation in argument (e.g., x + 1, y * 2)
3621            if let Some(expr) = extract_binary_op(cursor) {
3622                arg_data.arguments.push(expr);
3623            }
3624            CXChildVisit_Continue
3625        }
3626        CXCursor_CallExpr => {
3627            // Nested function call (e.g., add(add(x, 5), add(10, 20)))
3628            if let Some(expr) = extract_function_call(cursor) {
3629                arg_data.arguments.push(expr);
3630            }
3631            CXChildVisit_Continue
3632        }
3633        CXCursor_UnaryOperator => {
3634            // Unary operation in argument (e.g., -x, !flag)
3635            if let Some(expr) = extract_unary_op(cursor) {
3636                arg_data.arguments.push(expr);
3637            }
3638            CXChildVisit_Continue
3639        }
3640        CXCursor_ArraySubscriptExpr => {
3641            // Array indexing in argument (e.g., arr[i])
3642            if let Some(expr) = extract_array_index(cursor) {
3643                arg_data.arguments.push(expr);
3644            }
3645            CXChildVisit_Continue
3646        }
3647        CXCursor_MemberRefExpr => {
3648            // Field access in argument (e.g., ptr->field or obj.field)
3649            if let Some(expr) = extract_field_access(cursor) {
3650                arg_data.arguments.push(expr);
3651            }
3652            CXChildVisit_Continue
3653        }
3654        116 => {
3655            // CXCursor_ConditionalOperator (ternary) - DECY-192
3656            if let Some(expr) = extract_conditional_op(cursor) {
3657                arg_data.arguments.push(expr);
3658            }
3659            CXChildVisit_Continue
3660        }
3661        CXCursor_UnexposedExpr | CXCursor_ParenExpr => {
3662            // Unexposed expressions might wrap actual expressions or be sizeof, try to extract
3663            if let Some(expr) = extract_sizeof(cursor) {
3664                arg_data.arguments.push(expr);
3665                CXChildVisit_Continue
3666            } else {
3667                CXChildVisit_Recurse
3668            }
3669        }
3670        136 => {
3671            // CXCursor_UnaryExpr - includes sizeof, alignof, etc.
3672            if let Some(expr) = extract_sizeof(cursor) {
3673                arg_data.arguments.push(expr);
3674                CXChildVisit_Continue
3675            } else {
3676                CXChildVisit_Recurse
3677            }
3678        }
3679        117 => {
3680            // CXCursor_CStyleCastExpr - DECY-243: Cast expression in argument (e.g., (int)x, (unsigned char)cp[i])
3681            if let Some(expr) = extract_cast(cursor) {
3682                arg_data.arguments.push(expr);
3683            }
3684            CXChildVisit_Continue
3685        }
3686        _ => CXChildVisit_Continue, // Skip other unknown children
3687    }
3688}
3689
3690/// Extract a unary operator expression.
3691fn extract_unary_op(cursor: CXCursor) -> Option<Expression> {
3692    // Get the translation unit
3693    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
3694    if tu.is_null() {
3695        return None;
3696    }
3697
3698    // Get the extent (source range) of the cursor
3699    let extent = unsafe { clang_getCursorExtent(cursor) };
3700
3701    // Tokenize to find the operator
3702    let mut tokens = ptr::null_mut();
3703    let mut num_tokens = 0;
3704
3705    unsafe {
3706        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
3707    }
3708
3709    let mut operator: Option<UnaryOperator> = None;
3710    let mut is_dereference = false;
3711    let mut is_increment = false;
3712    let mut is_decrement = false;
3713    let mut operator_position = 0;
3714
3715    // DECY-255: For expressions like (*p)++, we need to find ++ as the operator
3716    // The key insight is: ++ or -- at position > 0 is post-increment/decrement
3717    // while * at position 0 is dereference
3718    // So we scan all tokens and pick the right operator based on position
3719    let mut found_star_at_zero = false;
3720    let mut found_open_paren_at_zero = false;
3721    let mut found_increment: Option<u32> = None;
3722    let mut found_decrement: Option<u32> = None;
3723
3724    for i in 0..num_tokens {
3725        unsafe {
3726            let token = *tokens.add(i as usize);
3727            let token_kind = clang_getTokenKind(token);
3728
3729            if token_kind == CXToken_Punctuation {
3730                let token_cxstring = clang_getTokenSpelling(tu, token);
3731                let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
3732                if let Ok(token_str) = c_str.to_str() {
3733                    match token_str {
3734                        "*" if i == 0 => found_star_at_zero = true,
3735                        "(" if i == 0 => found_open_paren_at_zero = true,
3736                        "++" => {
3737                            found_increment = Some(i);
3738                        }
3739                        "--" => {
3740                            found_decrement = Some(i);
3741                        }
3742                        "-" if i == 0 && operator.is_none() => {
3743                            operator = Some(UnaryOperator::Minus);
3744                        }
3745                        "!" if i == 0 && operator.is_none() => {
3746                            operator = Some(UnaryOperator::LogicalNot);
3747                        }
3748                        "~" if i == 0 && operator.is_none() => {
3749                            operator = Some(UnaryOperator::BitwiseNot);
3750                        }
3751                        "&" if i == 0 && operator.is_none() => {
3752                            operator = Some(UnaryOperator::AddressOf);
3753                        }
3754                        _ => {}
3755                    }
3756                }
3757                clang_disposeString(token_cxstring);
3758            }
3759        }
3760    }
3761
3762    // DECY-255: For (*p)++, we have ( at position 0, * at position 1, and ++ at the end
3763    // The ++ is the operator for THIS cursor, * is part of the operand
3764    // For *p++ (without parens), the * is the operator and ++ is handled by a nested cursor
3765    // CRITICAL: If we have * at position 0 (dereference), the ++ belongs to a nested expression
3766    // CRITICAL: If we found a unary operator (-, !, ~, &) at position 0, do NOT override
3767    // with ++/-- found at later positions - those belong to different expressions
3768    let has_unary_op_at_zero = operator.is_some();
3769
3770    if let Some(pos) = found_increment {
3771        // Only use ++ as operator if:
3772        // 1. It's at position 0 (pre-increment), OR
3773        // 2. We have ( at position 0 (parenthesized expression like (*p)++), OR
3774        // 3. No other unary operator at position 0 and no * at position 0
3775        if pos == 0 {
3776            is_increment = true;
3777            operator_position = pos;
3778        } else if found_open_paren_at_zero {
3779            // (*p)++ case - the ++ applies to the whole parenthesized expression
3780            is_increment = true;
3781            operator_position = pos;
3782        } else if !has_unary_op_at_zero && !found_star_at_zero {
3783            // No dereference and no other unary op at position 0
3784            is_increment = true;
3785            operator_position = pos;
3786        }
3787    }
3788    if let Some(pos) = found_decrement {
3789        // Decrement takes precedence if at position 0, after open paren, or no other unary op
3790        if pos == 0 || found_open_paren_at_zero || (!has_unary_op_at_zero && !found_star_at_zero) {
3791            is_decrement = true;
3792            operator_position = pos;
3793        }
3794    }
3795
3796    // If no increment/decrement selected, use dereference if found at position 0
3797    if !is_increment && !is_decrement && found_star_at_zero {
3798        is_dereference = true;
3799    }
3800    // operator is already set if we found -, !, ~, or & at position 0
3801
3802    unsafe {
3803        clang_disposeTokens(tu, tokens, num_tokens);
3804    }
3805
3806    // Extract the operand
3807    let mut operand: Option<Expression> = None;
3808    let operand_ptr = &mut operand as *mut Option<Expression>;
3809
3810    unsafe {
3811        clang_visitChildren(cursor, visit_expression, operand_ptr as CXClientData);
3812    }
3813
3814    let operand_expr = operand?;
3815
3816    // Handle dereference separately (maintains backward compatibility)
3817    if is_dereference {
3818        return Some(Expression::Dereference(Box::new(operand_expr)));
3819    }
3820
3821    // Handle increment/decrement operators
3822    if is_increment {
3823        // Check if pre or post increment
3824        let is_pre = operator_position == 0;
3825        if is_pre {
3826            return Some(Expression::PreIncrement {
3827                operand: Box::new(operand_expr),
3828            });
3829        } else {
3830            return Some(Expression::PostIncrement {
3831                operand: Box::new(operand_expr),
3832            });
3833        }
3834    }
3835
3836    if is_decrement {
3837        // Check if pre or post decrement
3838        let is_pre = operator_position == 0;
3839        if is_pre {
3840            return Some(Expression::PreDecrement {
3841                operand: Box::new(operand_expr),
3842            });
3843        } else {
3844            return Some(Expression::PostDecrement {
3845                operand: Box::new(operand_expr),
3846            });
3847        }
3848    }
3849
3850    // Handle other unary operators
3851    if let Some(op) = operator {
3852        return Some(Expression::UnaryOp {
3853            op,
3854            operand: Box::new(operand_expr),
3855        });
3856    }
3857
3858    // DECY-195: Fallback for system headers where tokenization fails
3859    // If we have a UnaryOperator cursor with an operand but couldn't identify the operator,
3860    // try to infer it from context. For macro expansions like EOF=(-1),
3861    // the unary minus might not be tokenizable.
3862    // Check if the operand is an integer literal - if so, it might be a negation
3863    // For now, return the operand wrapped as unary minus if it's an integer
3864    // This handles the common case of EOF = (-1) from stdio.h
3865    if let Expression::IntLiteral(_) = &operand_expr {
3866        // If we found an integer inside a UnaryOperator, assume it's negation
3867        return Some(Expression::UnaryOp {
3868            op: UnaryOperator::Minus,
3869            operand: Box::new(operand_expr),
3870        });
3871    }
3872
3873    None
3874}
3875
3876/// Extract an array indexing expression.
3877fn extract_array_index(cursor: CXCursor) -> Option<Expression> {
3878    // Extract array and index expressions by visiting children
3879    let mut operands: Vec<Expression> = Vec::new();
3880    let operands_ptr = &mut operands as *mut Vec<Expression>;
3881
3882    unsafe {
3883        clang_visitChildren(cursor, visit_binary_operand, operands_ptr as CXClientData);
3884    }
3885
3886    // Array subscript should have exactly 2 operands: array and index
3887    if operands.len() != 2 {
3888        return None;
3889    }
3890
3891    Some(Expression::ArrayIndex {
3892        array: Box::new(operands[0].clone()),
3893        index: Box::new(operands[1].clone()),
3894    })
3895}
3896
3897/// Extract a field access expression (obj.field or ptr->field).
3898fn extract_field_access(cursor: CXCursor) -> Option<Expression> {
3899    // Get the field name
3900    let field_name_cxstring = unsafe { clang_getCursorSpelling(cursor) };
3901    let field = unsafe {
3902        let c_str = CStr::from_ptr(clang_getCString(field_name_cxstring));
3903        let name = c_str.to_string_lossy().into_owned();
3904        clang_disposeString(field_name_cxstring);
3905        name
3906    };
3907
3908    // Determine if this is -> or . by tokenizing
3909    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
3910    if tu.is_null() {
3911        return None;
3912    }
3913
3914    let extent = unsafe { clang_getCursorExtent(cursor) };
3915    let mut tokens = ptr::null_mut();
3916    let mut num_tokens = 0;
3917
3918    unsafe {
3919        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
3920    }
3921
3922    let mut is_arrow = false;
3923
3924    // Look through tokens to find the LAST '->' or '.' operator
3925    // (the rightmost operator is the one for this specific MemberRefExpr)
3926    // For nested access like r->bottom_right.x, the extent includes all tokens,
3927    // so we need the last operator, not the first
3928    for i in 0..num_tokens {
3929        unsafe {
3930            let token = *tokens.add(i as usize);
3931            let token_kind = clang_getTokenKind(token);
3932
3933            if token_kind == CXToken_Punctuation {
3934                let token_cxstring = clang_getTokenSpelling(tu, token);
3935                let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
3936                if let Ok(token_str) = c_str.to_str() {
3937                    if token_str == "->" {
3938                        is_arrow = true;
3939                        // Don't break - keep looking for later operators
3940                    } else if token_str == "." {
3941                        is_arrow = false;
3942                        // Don't break - keep looking for later operators
3943                    }
3944                }
3945                clang_disposeString(token_cxstring);
3946            }
3947        }
3948    }
3949
3950    unsafe {
3951        clang_disposeTokens(tu, tokens, num_tokens);
3952    }
3953
3954    // Extract the object/pointer expression by visiting children
3955    let mut object_expr: Option<Expression> = None;
3956    let expr_ptr = &mut object_expr as *mut Option<Expression>;
3957
3958    unsafe {
3959        clang_visitChildren(cursor, visit_expression, expr_ptr as CXClientData);
3960    }
3961
3962    let object = object_expr?;
3963
3964    if is_arrow {
3965        Some(Expression::PointerFieldAccess {
3966            pointer: Box::new(object),
3967            field,
3968        })
3969    } else {
3970        Some(Expression::FieldAccess {
3971            object: Box::new(object),
3972            field,
3973        })
3974    }
3975}
3976
3977/// Extract a sizeof expression.
3978/// DECY-119: Only match if sizeof is the FIRST token (not from other statements)
3979fn extract_sizeof(cursor: CXCursor) -> Option<Expression> {
3980    // Get the translation unit
3981    let tu = unsafe { clang_Cursor_getTranslationUnit(cursor) };
3982    if tu.is_null() {
3983        return None;
3984    }
3985
3986    // Get the extent (source range) of the cursor
3987    let extent = unsafe { clang_getCursorExtent(cursor) };
3988
3989    // Tokenize to find "sizeof" keyword
3990    let mut tokens = ptr::null_mut();
3991    let mut num_tokens = 0;
3992
3993    unsafe {
3994        clang_tokenize(tu, extent, &mut tokens, &mut num_tokens);
3995    }
3996
3997    // DECY-119: sizeof must be the FIRST token, otherwise this cursor
3998    // is not a sizeof expression (it might just contain one elsewhere)
3999    if num_tokens == 0 {
4000        unsafe {
4001            clang_disposeTokens(tu, tokens, num_tokens);
4002        }
4003        return None;
4004    }
4005
4006    let first_token_is_sizeof = unsafe {
4007        let token = *tokens.add(0);
4008        let token_cxstring = clang_getTokenSpelling(tu, token);
4009        let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
4010        let is_sizeof = c_str.to_str().map(|s| s == "sizeof").unwrap_or(false);
4011        clang_disposeString(token_cxstring);
4012        is_sizeof
4013    };
4014
4015    if !first_token_is_sizeof {
4016        unsafe {
4017            clang_disposeTokens(tu, tokens, num_tokens);
4018        }
4019        return None;
4020    }
4021
4022    let mut type_name = String::new();
4023    let mut paren_depth = 0;
4024    let mut in_sizeof_parens = false;
4025
4026    // Look through tokens to extract type name (skip first token which is "sizeof")
4027    // DECY-119: Track paren depth to stop at closing paren
4028    for i in 1..num_tokens {
4029        unsafe {
4030            let token = *tokens.add(i as usize);
4031            let token_kind = clang_getTokenKind(token);
4032            let token_cxstring = clang_getTokenSpelling(tu, token);
4033            let c_str = CStr::from_ptr(clang_getCString(token_cxstring));
4034
4035            if let Ok(token_str) = c_str.to_str() {
4036                if token_str == "(" {
4037                    paren_depth += 1;
4038                    in_sizeof_parens = true;
4039                } else if token_str == ")" {
4040                    paren_depth -= 1;
4041                    // DECY-119: Stop when we close the sizeof parenthesis
4042                    if paren_depth == 0 && in_sizeof_parens {
4043                        clang_disposeString(token_cxstring);
4044                        break;
4045                    }
4046                } else if in_sizeof_parens
4047                    && (token_kind == CXToken_Identifier || token_kind == CXToken_Keyword)
4048                {
4049                    // This is part of the type name (e.g., "int", "Data", "struct")
4050                    if !type_name.is_empty() {
4051                        type_name.push(' ');
4052                    }
4053                    type_name.push_str(token_str);
4054                }
4055            }
4056
4057            clang_disposeString(token_cxstring);
4058        }
4059    }
4060
4061    unsafe {
4062        clang_disposeTokens(tu, tokens, num_tokens);
4063    }
4064
4065    // We already verified first token is sizeof, just check we got a type name
4066    if !type_name.is_empty() {
4067        Some(Expression::Sizeof { type_name })
4068    } else {
4069        None
4070    }
4071}
4072
4073/// Convert clang type to our Type enum.
4074#[allow(non_upper_case_globals)]
4075/// Extract a cast expression from a clang cursor.
4076///
4077/// Parses C-style cast expressions like `(int)x` or `(void*)ptr`.
4078/// Extracts the target type and the expression being cast.
4079fn extract_cast(cursor: CXCursor) -> Option<Expression> {
4080    // SAFETY: Getting the type this expression evaluates to (the cast result type)
4081    let target_cx_type = unsafe { clang_getCursorType(cursor) };
4082    let target_type = convert_type(target_cx_type)?;
4083
4084    // Extract the inner expression by visiting children
4085    let mut inner_expr: Option<Expression> = None;
4086    let inner_ptr = &mut inner_expr as *mut Option<Expression>;
4087
4088    unsafe {
4089        clang_visitChildren(cursor, visit_cast_inner, inner_ptr as CXClientData);
4090    }
4091
4092    inner_expr.map(|expr| Expression::Cast {
4093        target_type,
4094        expr: Box::new(expr),
4095    })
4096}
4097
4098/// Visitor callback to extract the inner expression of a cast.
4099#[allow(non_upper_case_globals)]
4100extern "C" fn visit_cast_inner(
4101    cursor: CXCursor,
4102    _parent: CXCursor,
4103    client_data: CXClientData,
4104) -> CXChildVisitResult {
4105    let inner_expr = unsafe { &mut *(client_data as *mut Option<Expression>) };
4106    let kind = unsafe { clang_getCursorKind(cursor) };
4107
4108    // Try to extract any expression
4109    if let Some(expr) = try_extract_expression(cursor) {
4110        *inner_expr = Some(expr);
4111        return CXChildVisit_Break; // Found the inner expression, stop visiting
4112    }
4113
4114    // For some expression types, we need to recurse
4115    match kind {
4116        CXCursor_UnexposedExpr | CXCursor_ParenExpr => CXChildVisit_Recurse,
4117        _ => CXChildVisit_Continue,
4118    }
4119}
4120
4121/// Extract a compound literal expression from a clang cursor.
4122///
4123/// Parses C99 compound literals like `(struct Point){10, 20}` or `(int[]){1, 2, 3}`.
4124/// Extracts the type and initializer expressions.
4125fn extract_compound_literal(cursor: CXCursor) -> Option<Expression> {
4126    // SAFETY: Getting the type of the compound literal
4127    let literal_cx_type = unsafe { clang_getCursorType(cursor) };
4128    let literal_type = convert_type(literal_cx_type)?;
4129
4130    // Extract initializer expressions by visiting children
4131    let mut initializers: Vec<Expression> = Vec::new();
4132    let initializers_ptr = &mut initializers as *mut Vec<Expression>;
4133
4134    unsafe {
4135        clang_visitChildren(
4136            cursor,
4137            visit_compound_literal_initializers,
4138            initializers_ptr as CXClientData,
4139        );
4140    }
4141
4142    Some(Expression::CompoundLiteral {
4143        literal_type,
4144        initializers,
4145    })
4146}
4147
4148/// DECY-192: Extract a ternary/conditional expression.
4149///
4150/// Parses C conditional expressions like `cond ? then_val : else_val`.
4151/// The ternary operator has 3 children: condition, then expression, else expression.
4152fn extract_conditional_op(cursor: CXCursor) -> Option<Expression> {
4153    // Extract all three operands by visiting children
4154    let mut operands: Vec<Expression> = Vec::new();
4155    let operands_ptr = &mut operands as *mut Vec<Expression>;
4156
4157    unsafe {
4158        clang_visitChildren(
4159            cursor,
4160            visit_conditional_operand,
4161            operands_ptr as CXClientData,
4162        );
4163    }
4164
4165    // Ternary operators should have exactly 3 operands: condition, then, else
4166    // However, sometimes clang may emit extra implicit expressions
4167    if operands.len() >= 3 {
4168        Some(Expression::Ternary {
4169            condition: Box::new(operands[0].clone()),
4170            then_expr: Box::new(operands[1].clone()),
4171            else_expr: Box::new(operands[2].clone()),
4172        })
4173    } else if operands.len() == 2 {
4174        // GNU extension: `x ?: y` is equivalent to `x ? x : y`
4175        // Clang may represent this with only 2 children
4176        Some(Expression::Ternary {
4177            condition: Box::new(operands[0].clone()),
4178            then_expr: Box::new(operands[0].clone()),
4179            else_expr: Box::new(operands[1].clone()),
4180        })
4181    } else {
4182        None
4183    }
4184}
4185
4186/// Visitor callback for conditional operator (ternary) operands.
4187/// DECY-192: Collects condition, then_expr, and else_expr.
4188#[allow(non_upper_case_globals)]
4189extern "C" fn visit_conditional_operand(
4190    cursor: CXCursor,
4191    _parent: CXCursor,
4192    client_data: CXClientData,
4193) -> CXChildVisitResult {
4194    let operands = unsafe { &mut *(client_data as *mut Vec<Expression>) };
4195
4196    // Try to extract expression using the general expression extractor
4197    if let Some(expr) = try_extract_expression(cursor) {
4198        operands.push(expr);
4199    }
4200
4201    CXChildVisit_Continue
4202}
4203
4204/// DECY-133: Extract an initializer list expression for struct/array initialization.
4205///
4206/// Handles C99 designated initializers like `{.x = 10, .y = 20}` or `{[2] = 100}`.
4207/// Clang resolves designated initializers to positional order and inserts ImplicitValueInitExpr
4208/// for uninitialized fields.
4209fn extract_init_list(cursor: CXCursor) -> Option<Expression> {
4210    // SAFETY: Getting the type of the initializer list
4211    let literal_cx_type = unsafe { clang_getCursorType(cursor) };
4212    let literal_type = convert_type(literal_cx_type)?;
4213
4214    // Extract initializer expressions by visiting children
4215    let mut initializers: Vec<Expression> = Vec::new();
4216    let initializers_ptr = &mut initializers as *mut Vec<Expression>;
4217
4218    unsafe {
4219        clang_visitChildren(
4220            cursor,
4221            visit_init_list_children,
4222            initializers_ptr as CXClientData,
4223        );
4224    }
4225
4226    Some(Expression::CompoundLiteral {
4227        literal_type,
4228        initializers,
4229    })
4230}
4231
4232/// Visitor callback to extract initializers from an InitListExpr.
4233/// DECY-133: Handles both regular and designated initializers.
4234#[allow(non_upper_case_globals)]
4235extern "C" fn visit_init_list_children(
4236    cursor: CXCursor,
4237    _parent: CXCursor,
4238    client_data: CXClientData,
4239) -> CXChildVisitResult {
4240    let initializers = unsafe { &mut *(client_data as *mut Vec<Expression>) };
4241    let kind = unsafe { clang_getCursorKind(cursor) };
4242
4243    // Handle ImplicitValueInitExpr (115) - default value for uninitialized fields
4244    // This appears when designated initializers skip some fields
4245    if kind == 115 {
4246        // Get the type to determine the default value
4247        let cx_type = unsafe { clang_getCursorType(cursor) };
4248        if let Some(var_type) = convert_type(cx_type) {
4249            // Generate appropriate default based on type
4250            let default_expr = match var_type {
4251                Type::Int => Expression::IntLiteral(0),
4252                Type::Float | Type::Double => Expression::IntLiteral(0), // Will be cast
4253                Type::Char => Expression::IntLiteral(0),
4254                _ => Expression::IntLiteral(0), // Fallback
4255            };
4256            initializers.push(default_expr);
4257        }
4258        return CXChildVisit_Continue;
4259    }
4260
4261    // DECY-133b: Handle designated initializers
4262    // Array: [idx] = value  → UnexposedExpr with children [IntLiteral(idx), value]
4263    // Struct: .field = value → UnexposedExpr with children [MemberRef, value]
4264    if kind == CXCursor_UnexposedExpr {
4265        // Collect cursor kinds and expressions for all children
4266        #[repr(C)]
4267        struct ChildInfo {
4268            kinds: Vec<u32>,
4269            exprs: Vec<Expression>,
4270        }
4271
4272        let mut info = ChildInfo {
4273            kinds: Vec::new(),
4274            exprs: Vec::new(),
4275        };
4276        let info_ptr = &mut info as *mut ChildInfo;
4277
4278        extern "C" fn collect_child_info(
4279            cursor: CXCursor,
4280            _parent: CXCursor,
4281            client_data: CXClientData,
4282        ) -> CXChildVisitResult {
4283            let info = unsafe { &mut *(client_data as *mut ChildInfo) };
4284            let kind = unsafe { clang_getCursorKind(cursor) };
4285            info.kinds.push(kind as u32);
4286
4287            // Try to extract expression, including InitListExpr
4288            if kind == 119 {
4289                // InitListExpr - extract as CompoundLiteral
4290                if let Some(expr) = extract_init_list(cursor) {
4291                    info.exprs.push(expr);
4292                }
4293            } else if let Some(expr) = try_extract_expression(cursor) {
4294                info.exprs.push(expr);
4295            }
4296            CXChildVisit_Continue
4297        }
4298
4299        unsafe {
4300            clang_visitChildren(cursor, collect_child_info, info_ptr as CXClientData);
4301        }
4302
4303        // Array designated init: [idx] = value → 2 children, first is IntLiteral
4304        if info.exprs.len() == 2 && matches!(&info.exprs[0], Expression::IntLiteral(_)) {
4305            initializers.push(info.exprs[1].clone());
4306            return CXChildVisit_Continue;
4307        }
4308
4309        // Struct field designated init: .field = value → first kind is MemberRef (47)
4310        // Second child is the value (could be InitListExpr or other expression)
4311        if info.kinds.len() == 2 && info.kinds[0] == 47 && !info.exprs.is_empty() {
4312            // Take the last expression (the value)
4313            initializers.push(info.exprs.last().unwrap().clone());
4314            return CXChildVisit_Continue;
4315        }
4316
4317        // Not a designated initializer - fall through to recursion
4318        return CXChildVisit_Recurse;
4319    }
4320
4321    // Try to extract any expression as an initializer
4322    if let Some(expr) = try_extract_expression(cursor) {
4323        initializers.push(expr);
4324        return CXChildVisit_Continue;
4325    }
4326
4327    // For some expression types, recurse
4328    match kind {
4329        CXCursor_ParenExpr => CXChildVisit_Recurse,
4330        _ => CXChildVisit_Continue,
4331    }
4332}
4333
4334/// Visitor callback to extract initializers from a compound literal.
4335#[allow(non_upper_case_globals)]
4336extern "C" fn visit_compound_literal_initializers(
4337    cursor: CXCursor,
4338    _parent: CXCursor,
4339    client_data: CXClientData,
4340) -> CXChildVisitResult {
4341    let initializers = unsafe { &mut *(client_data as *mut Vec<Expression>) };
4342    let kind = unsafe { clang_getCursorKind(cursor) };
4343
4344    // The compound literal typically has an InitListExpr child
4345    // CXCursor_InitListExpr = 119
4346    if kind == 119 {
4347        // This is the initializer list - visit its children to get individual initializers
4348        return CXChildVisit_Recurse;
4349    }
4350
4351    // Try to extract any expression as an initializer
4352    if let Some(expr) = try_extract_expression(cursor) {
4353        initializers.push(expr);
4354        return CXChildVisit_Continue;
4355    }
4356
4357    // For some expression types, recurse
4358    match kind {
4359        CXCursor_UnexposedExpr | CXCursor_ParenExpr => CXChildVisit_Recurse,
4360        _ => CXChildVisit_Continue,
4361    }
4362}
4363
4364#[allow(non_upper_case_globals)]
4365fn convert_type(cx_type: CXType) -> Option<Type> {
4366    // SAFETY: Getting type kind
4367    match cx_type.kind {
4368        CXType_Void => Some(Type::Void),
4369        3 => Some(Type::Bool), // CXType_Bool = 3 — C99 _Bool
4370        CXType_Int => Some(Type::Int),
4371        CXType_UInt => Some(Type::UnsignedInt), // DECY-158: unsigned int → u32
4372        CXType_UChar => Some(Type::Char),       // unsigned char → u8 (DECY-057 fix)
4373        CXType_UShort => Some(Type::UnsignedInt), // unsigned short → u32 (safe approximation)
4374        CXType_ULong => Some(Type::UnsignedInt), // unsigned long → u32 (safe approximation)
4375        CXType_Short => Some(Type::Int),        // short → i32
4376        CXType_Long => Some(Type::Int),         // long → i32
4377        CXType_LongLong => Some(Type::Int),     // long long → i32 (simplified)
4378        CXType_ULongLong => Some(Type::UnsignedInt), // DECY-158: unsigned long long → u32
4379        CXType_Float => Some(Type::Float),
4380        CXType_Double => Some(Type::Double),
4381        23 => Some(Type::Double), // CXType_LongDouble → f64 (Rust has no long double)
4382        CXType_Char_S | CXType_Char_U => Some(Type::Char),
4383        14 => Some(Type::SignedChar), // CXType_SChar - explicitly signed char → i8 (DECY-250)
4384        CXType_Pointer => {
4385            // SAFETY: Getting pointee type
4386            let pointee = unsafe { clang_getPointeeType(cx_type) };
4387
4388            // Check if the pointee is a function - this is a function pointer
4389            if pointee.kind == CXType_FunctionProto || pointee.kind == CXType_FunctionNoProto {
4390                // This is a function pointer type
4391                // Extract return type
4392                let return_cx_type = unsafe { clang_getResultType(pointee) };
4393                let return_type = convert_type(return_cx_type)?;
4394
4395                // Extract parameter types
4396                let num_args = unsafe { clang_getNumArgTypes(pointee) };
4397                let mut param_types = Vec::new();
4398
4399                for i in 0..num_args {
4400                    let arg_type = unsafe { clang_getArgType(pointee, i as u32) };
4401                    if let Some(param_type) = convert_type(arg_type) {
4402                        param_types.push(param_type);
4403                    }
4404                }
4405
4406                return Some(Type::FunctionPointer {
4407                    param_types,
4408                    return_type: Box::new(return_type),
4409                });
4410            }
4411
4412            // Regular pointer (not function pointer)
4413            convert_type(pointee).map(|t| Type::Pointer(Box::new(t)))
4414        }
4415        CXType_FunctionProto | CXType_FunctionNoProto => {
4416            // Function type (not a pointer to function, but the function type itself)
4417            // This can occur in typedefs like: typedef int Func(int);
4418            // Extract return type
4419            let return_cx_type = unsafe { clang_getResultType(cx_type) };
4420            let return_type = convert_type(return_cx_type)?;
4421
4422            // Extract parameter types
4423            let num_args = unsafe { clang_getNumArgTypes(cx_type) };
4424            let mut param_types = Vec::new();
4425
4426            for i in 0..num_args {
4427                let arg_type = unsafe { clang_getArgType(cx_type, i as u32) };
4428                if let Some(param_type) = convert_type(arg_type) {
4429                    param_types.push(param_type);
4430                }
4431            }
4432
4433            Some(Type::FunctionPointer {
4434                param_types,
4435                return_type: Box::new(return_type),
4436            })
4437        }
4438        CXType_Record => {
4439            // SAFETY: Getting type declaration to extract struct name
4440            let decl = unsafe { clang_getTypeDeclaration(cx_type) };
4441            let name_cxstring = unsafe { clang_getCursorSpelling(decl) };
4442            let name = unsafe {
4443                let c_str = CStr::from_ptr(clang_getCString(name_cxstring));
4444                let struct_name = c_str.to_string_lossy().into_owned();
4445                clang_disposeString(name_cxstring);
4446                struct_name
4447            };
4448            Some(Type::Struct(name))
4449        }
4450        CXType_Elaborated => {
4451            // Elaborated types wrap other types (e.g., "struct Point" wraps the Record type)
4452            // Get the canonical type to unwrap it
4453            let canonical = unsafe { clang_getCanonicalType(cx_type) };
4454            convert_type(canonical)
4455        }
4456        CXType_Typedef => {
4457            // DECY-172: Get typedef name first to check for known type aliases
4458            let typedef_decl = unsafe { clang_getTypeDeclaration(cx_type) };
4459            let typedef_name_cxstring = unsafe { clang_getCursorSpelling(typedef_decl) };
4460            let typedef_name = unsafe {
4461                let c_str = CStr::from_ptr(clang_getCString(typedef_name_cxstring));
4462                let tn = c_str.to_string_lossy().into_owned();
4463                clang_disposeString(typedef_name_cxstring);
4464                tn
4465            };
4466
4467            // DECY-172: Preserve size_t, ssize_t, ptrdiff_t as TypeAlias
4468            // These need to map to usize/isize in Rust for compatibility with .len() etc.
4469            match typedef_name.as_str() {
4470                "size_t" | "ssize_t" | "ptrdiff_t" => {
4471                    return Some(Type::TypeAlias(typedef_name));
4472                }
4473                _ => {}
4474            }
4475
4476            // DECY-147: For typedefs to anonymous structs, use typedef name as struct name
4477            // Example: typedef struct { int x; } Point; → Type::Struct("Point")
4478            let canonical = unsafe { clang_getCanonicalType(cx_type) };
4479
4480            // Check if this is a typedef to an anonymous struct
4481            if canonical.kind == CXType_Record {
4482                let decl = unsafe { clang_getTypeDeclaration(canonical) };
4483                let struct_name_cxstring = unsafe { clang_getCursorSpelling(decl) };
4484                let struct_name = unsafe {
4485                    let c_str = CStr::from_ptr(clang_getCString(struct_name_cxstring));
4486                    let sn = c_str.to_string_lossy().into_owned();
4487                    clang_disposeString(struct_name_cxstring);
4488                    sn
4489                };
4490
4491                // If struct is anonymous, use the typedef name instead
4492                if struct_name.is_empty() {
4493                    return Some(Type::Struct(typedef_name));
4494                }
4495            }
4496
4497            // Default: recursively convert the canonical type
4498            convert_type(canonical)
4499        }
4500        CXType_ConstantArray => {
4501            // Array type - extract element type and size
4502            let element_cx_type = unsafe { clang_getArrayElementType(cx_type) };
4503            let element_type = convert_type(element_cx_type)?;
4504
4505            // Get array size
4506            let array_size = unsafe { clang_getArraySize(cx_type) };
4507            let size = if array_size >= 0 {
4508                Some(array_size)
4509            } else {
4510                None
4511            };
4512
4513            Some(Type::Array {
4514                element_type: Box::new(element_type),
4515                size,
4516            })
4517        }
4518        114 => {
4519            // CXType_IncompleteArray - flexible array member (C99 §6.7.2.1)
4520            // DECY-136: char data[] → Vec<u8>
4521            // Flexible array members have no size specified
4522            let element_cx_type = unsafe { clang_getArrayElementType(cx_type) };
4523            let element_type = convert_type(element_cx_type)?;
4524
4525            // Generate as Array with size None (will be transformed to Vec in codegen)
4526            Some(Type::Array {
4527                element_type: Box::new(element_type),
4528                size: None,
4529            })
4530        }
4531        106 => {
4532            // CXType_Enum - C enums are integers
4533            // DECY-240: Map enum types to i32 for Rust compatibility
4534            Some(Type::Int)
4535        }
4536        _ => None,
4537    }
4538}
4539
4540/// Represents a single case in a switch statement.
4541#[derive(Debug, Clone, PartialEq)]
4542pub struct SwitchCase {
4543    /// Case value expression (None for default case)
4544    pub value: Option<Expression>,
4545    /// Statements to execute for this case
4546    pub body: Vec<Statement>,
4547}
4548
4549/// Represents a C statement.
4550#[derive(Debug, Clone, PartialEq)]
4551pub enum Statement {
4552    /// Variable declaration: `int* ptr = malloc(4);`
4553    VariableDeclaration {
4554        /// Variable name
4555        name: String,
4556        /// Variable type
4557        var_type: Type,
4558        /// Optional initializer expression
4559        initializer: Option<Expression>,
4560    },
4561    /// Return statement: `return expr;`
4562    Return(Option<Expression>),
4563    /// Assignment statement: `x = 42;`
4564    Assignment {
4565        /// Target variable name
4566        target: String,
4567        /// Value expression to assign
4568        value: Expression,
4569    },
4570    /// If statement: `if (cond) { ... } else { ... }`
4571    If {
4572        /// Condition expression
4573        condition: Expression,
4574        /// Then block
4575        then_block: Vec<Statement>,
4576        /// Optional else block
4577        else_block: Option<Vec<Statement>>,
4578    },
4579    /// For loop: `for (init; cond; inc) { ... }`
4580    For {
4581        /// Init statements (can be multiple with comma: int i = 0, j = 10)
4582        init: Vec<Statement>,
4583        /// Optional condition expression
4584        condition: Option<Expression>,
4585        /// Optional increment statements (can be multiple with comma: i++, j--)
4586        increment: Vec<Statement>,
4587        /// Loop body
4588        body: Vec<Statement>,
4589    },
4590    /// While loop: `while (cond) { ... }`
4591    While {
4592        /// Condition expression
4593        condition: Expression,
4594        /// Loop body
4595        body: Vec<Statement>,
4596    },
4597    /// Pointer dereference assignment: `*ptr = value;`
4598    DerefAssignment {
4599        /// Target expression to dereference
4600        target: Expression,
4601        /// Value expression to assign
4602        value: Expression,
4603    },
4604    /// Array index assignment: `arr[i] = value;`
4605    ArrayIndexAssignment {
4606        /// Array expression
4607        array: Box<Expression>,
4608        /// Index expression
4609        index: Box<Expression>,
4610        /// Value expression to assign
4611        value: Expression,
4612    },
4613    /// Field assignment: `ptr->field = value;` or `obj.field = value;`
4614    FieldAssignment {
4615        /// Object/pointer expression
4616        object: Expression,
4617        /// Field name
4618        field: String,
4619        /// Value expression to assign
4620        value: Expression,
4621    },
4622    /// Break statement: `break;`
4623    Break,
4624    /// Continue statement: `continue;`
4625    Continue,
4626    /// Switch statement: `switch (expr) { case 1: ...; default: ...; }`
4627    Switch {
4628        /// Condition expression to switch on
4629        condition: Expression,
4630        /// List of case statements
4631        cases: Vec<SwitchCase>,
4632        /// Optional default case body
4633        default_case: Option<Vec<Statement>>,
4634    },
4635    /// Post-increment statement: `ptr++;`
4636    PostIncrement {
4637        /// Target variable name
4638        target: String,
4639    },
4640    /// Pre-increment statement: `++ptr;`
4641    PreIncrement {
4642        /// Target variable name
4643        target: String,
4644    },
4645    /// Post-decrement statement: `ptr--;`
4646    PostDecrement {
4647        /// Target variable name
4648        target: String,
4649    },
4650    /// Pre-decrement statement: `--ptr;`
4651    PreDecrement {
4652        /// Target variable name
4653        target: String,
4654    },
4655    /// Compound assignment: `ptr += offset;`, `x *= 2;`, etc.
4656    CompoundAssignment {
4657        /// Target variable name
4658        target: String,
4659        /// Binary operator to apply
4660        op: BinaryOperator,
4661        /// Value expression
4662        value: Expression,
4663    },
4664    /// DECY-185: Compound assignment to expression target: `*ptr *= 2;`, `sb->capacity *= 2;`
4665    /// Used when target is not a simple variable (Dereference, PointerFieldAccess, etc.)
4666    DerefCompoundAssignment {
4667        /// Target expression (e.g., the dereferenced pointer or field access)
4668        target: Expression,
4669        /// Binary operator to apply
4670        op: BinaryOperator,
4671        /// Value expression
4672        value: Expression,
4673    },
4674    /// Function call statement: `strlen(s);`, `strcpy(dst, src);`
4675    FunctionCall {
4676        /// Function name
4677        function: String,
4678        /// Arguments
4679        arguments: Vec<Expression>,
4680    },
4681}
4682
4683impl Statement {
4684    /// Check if this statement is a string function call.
4685    pub fn is_string_function_call(&self) -> bool {
4686        match self {
4687            Statement::FunctionCall { function, .. } => {
4688                matches!(function.as_str(), "strlen" | "strcmp" | "strcpy" | "strdup")
4689            }
4690            _ => false,
4691        }
4692    }
4693
4694    /// Check if this statement is a function call.
4695    pub fn is_function_call(&self) -> bool {
4696        matches!(self, Statement::FunctionCall { .. })
4697    }
4698
4699    /// Convert this statement to a function call expression if it is one.
4700    ///
4701    /// # Implementation Status
4702    ///
4703    /// Stub implementation - always returns `None`.
4704    /// The `Statement::FunctionCall` variant doesn't store the call as an `Expression`,
4705    /// so conversion would require reconstructing an `Expression::FunctionCall` from
4706    /// the statement's fields.
4707    pub fn as_function_call(&self) -> Option<&Expression> {
4708        None
4709    }
4710}
4711
4712/// Unary operators for C expressions.
4713#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4714pub enum UnaryOperator {
4715    /// Unary minus (-x)
4716    Minus,
4717    /// Logical NOT (!x)
4718    LogicalNot,
4719    /// Bitwise NOT (~x)
4720    BitwiseNot,
4721    /// Address-of (&x)
4722    AddressOf,
4723}
4724
4725/// Binary operators for C expressions.
4726#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4727pub enum BinaryOperator {
4728    /// Addition (+)
4729    Add,
4730    /// Subtraction (-)
4731    Subtract,
4732    /// Multiplication (*)
4733    Multiply,
4734    /// Division (/)
4735    Divide,
4736    /// Modulo (%)
4737    Modulo,
4738    /// Equality (==)
4739    Equal,
4740    /// Inequality (!=)
4741    NotEqual,
4742    /// Less than (<)
4743    LessThan,
4744    /// Greater than (>)
4745    GreaterThan,
4746    /// Less than or equal (<=)
4747    LessEqual,
4748    /// Greater than or equal (>=)
4749    GreaterEqual,
4750    /// Logical AND (&&)
4751    LogicalAnd,
4752    /// Logical OR (||)
4753    LogicalOr,
4754    /// Left shift (<<)
4755    LeftShift,
4756    /// Right shift (>>)
4757    RightShift,
4758    /// Bitwise AND (&)
4759    BitwiseAnd,
4760    /// Bitwise OR (|)
4761    BitwiseOr,
4762    /// Bitwise XOR (^)
4763    BitwiseXor,
4764    /// Assignment (=) - used for embedded assignments like (c=getchar())
4765    Assign,
4766    /// Comma operator (,) - DECY-224: for multi-expression statements
4767    Comma,
4768}
4769
4770/// Represents a C expression.
4771#[derive(Debug, Clone, PartialEq)]
4772pub enum Expression {
4773    /// Integer literal: `42`
4774    IntLiteral(i32),
4775    /// Float literal: `3.14` (stored as string to preserve precision)
4776    FloatLiteral(String),
4777    /// String literal: `"hello"`
4778    StringLiteral(String),
4779    /// Character literal: `'a'`, `'\0'`, `'\n'`
4780    CharLiteral(i8),
4781    /// Variable reference: `x`
4782    Variable(String),
4783    /// Binary operation: `a + b`
4784    BinaryOp {
4785        /// Operator
4786        op: BinaryOperator,
4787        /// Left operand
4788        left: Box<Expression>,
4789        /// Right operand
4790        right: Box<Expression>,
4791    },
4792    /// Function call: `malloc(4)`
4793    FunctionCall {
4794        /// Function name
4795        function: String,
4796        /// Arguments
4797        arguments: Vec<Expression>,
4798    },
4799    /// Pointer dereference: `*ptr`
4800    Dereference(Box<Expression>),
4801    /// Unary operation: `-x`, `!x`
4802    UnaryOp {
4803        /// Operator
4804        op: UnaryOperator,
4805        /// Operand
4806        operand: Box<Expression>,
4807    },
4808    /// Array indexing: `arr[i]`
4809    ArrayIndex {
4810        /// Array expression
4811        array: Box<Expression>,
4812        /// Index expression
4813        index: Box<Expression>,
4814    },
4815    /// Struct field access: `obj.field`
4816    FieldAccess {
4817        /// Object expression
4818        object: Box<Expression>,
4819        /// Field name
4820        field: String,
4821    },
4822    /// Pointer field access: `ptr->field`
4823    PointerFieldAccess {
4824        /// Pointer expression
4825        pointer: Box<Expression>,
4826        /// Field name
4827        field: String,
4828    },
4829    /// Post-increment expression: `ptr++`
4830    PostIncrement {
4831        /// Operand expression
4832        operand: Box<Expression>,
4833    },
4834    /// Pre-increment expression: `++ptr`
4835    PreIncrement {
4836        /// Operand expression
4837        operand: Box<Expression>,
4838    },
4839    /// Post-decrement expression: `ptr--`
4840    PostDecrement {
4841        /// Operand expression
4842        operand: Box<Expression>,
4843    },
4844    /// Pre-decrement expression: `--ptr`
4845    PreDecrement {
4846        /// Operand expression
4847        operand: Box<Expression>,
4848    },
4849    /// Sizeof expression: `sizeof(int)` or `sizeof(struct Data)`
4850    Sizeof {
4851        /// Type name as a string (e.g., "int", "struct Data")
4852        type_name: String,
4853    },
4854    /// Cast expression: `(int)x` or `(void*)ptr`
4855    ///
4856    /// C-style cast that converts an expression to a target type.
4857    /// Maps to Rust `as` operator for safe casts, or `transmute` for unsafe casts.
4858    ///
4859    /// # Examples
4860    ///
4861    /// ```c
4862    /// int x = (int)3.14;           // float to int
4863    /// void* ptr = (void*)buffer;   // pointer cast
4864    /// long l = (long)small_int;    // widening cast
4865    /// ```
4866    Cast {
4867        /// Target type to cast to
4868        target_type: Type,
4869        /// Expression being cast
4870        expr: Box<Expression>,
4871    },
4872    /// Compound literal: `(struct Point){10, 20}` or `(int[]){1, 2, 3}`
4873    ///
4874    /// C99 compound literals create anonymous objects of a specified type.
4875    /// Useful for passing struct values to functions or creating temporary objects.
4876    ///
4877    /// # Examples
4878    ///
4879    /// ```c
4880    /// struct Point p = (struct Point){10, 20};       // struct compound literal
4881    /// int* arr = (int[]){1, 2, 3, 4, 5};             // array compound literal
4882    /// draw((struct Rect){.x=0, .y=0, .w=100, .h=50}); // with designated initializers
4883    /// ```
4884    CompoundLiteral {
4885        /// Type of the compound literal (struct Point, int[], etc.)
4886        literal_type: Type,
4887        /// Initializer expressions (values for struct fields or array elements)
4888        initializers: Vec<Expression>,
4889    },
4890    /// Ternary/Conditional expression: `cond ? then_val : else_val`
4891    ///
4892    /// The C ternary operator evaluates the condition and returns either
4893    /// the then_val or else_val based on whether condition is truthy.
4894    ///
4895    /// # DECY-192
4896    ///
4897    /// Added to support K&R Chapter 2.11 Conditional Expressions.
4898    ///
4899    /// # Examples
4900    ///
4901    /// ```c
4902    /// int max = (a > b) ? a : b;
4903    /// char* msg = (x == 0) ? "zero" : "nonzero";
4904    /// ```
4905    Ternary {
4906        /// Condition expression (evaluated as boolean)
4907        condition: Box<Expression>,
4908        /// Value if condition is true
4909        then_expr: Box<Expression>,
4910        /// Value if condition is false
4911        else_expr: Box<Expression>,
4912    },
4913}
4914
4915impl Expression {
4916    /// Check if this expression is a string function call (strlen, strcmp, strcpy, strdup).
4917    pub fn is_string_function_call(&self) -> bool {
4918        match self {
4919            Expression::FunctionCall { function, .. } => {
4920                matches!(function.as_str(), "strlen" | "strcmp" | "strcpy" | "strdup")
4921            }
4922            _ => false,
4923        }
4924    }
4925
4926    /// Get the string function name if this is a string function call.
4927    pub fn string_function_name(&self) -> Option<&str> {
4928        match self {
4929            Expression::FunctionCall { function, .. } if self.is_string_function_call() => {
4930                Some(function.as_str())
4931            }
4932            _ => None,
4933        }
4934    }
4935
4936    /// Check if this expression has a string literal argument.
4937    pub fn has_string_literal_argument(&self) -> bool {
4938        match self {
4939            Expression::FunctionCall { arguments, .. } => arguments
4940                .iter()
4941                .any(|arg| matches!(arg, Expression::StringLiteral(_))),
4942            _ => false,
4943        }
4944    }
4945}
4946
4947/// Represents a C typedef declaration.
4948#[derive(Debug, Clone, PartialEq)]
4949pub struct Typedef {
4950    /// Typedef name (the alias)
4951    pub name: String,
4952    /// Underlying type being aliased
4953    pub underlying_type: Type,
4954}
4955
4956impl Typedef {
4957    /// Create a new typedef.
4958    pub fn new(name: String, underlying_type: Type) -> Self {
4959        Self {
4960            name,
4961            underlying_type,
4962        }
4963    }
4964
4965    /// Get the typedef name.
4966    pub fn name(&self) -> &str {
4967        &self.name
4968    }
4969
4970    /// Get the underlying type as a string representation.
4971    pub fn underlying_type(&self) -> &str {
4972        // Return a string representation of the type
4973        match &self.underlying_type {
4974            Type::Void => "void",
4975            Type::Bool => "_Bool",
4976            Type::Int => "int",
4977            Type::UnsignedInt => "unsigned int", // DECY-158
4978            Type::Float => "float",
4979            Type::Double => "double",
4980            Type::Char => "char",
4981            Type::SignedChar => "signed char", // DECY-250
4982            Type::Pointer(inner) => match **inner {
4983                Type::Char => "char*",
4984                Type::SignedChar => "signed char*", // DECY-250
4985                Type::Int => "int*",
4986                Type::UnsignedInt => "unsigned int*", // DECY-158
4987                Type::Float => "float*",
4988                Type::Double => "double*",
4989                Type::Void => "void*",
4990                _ => "pointer",
4991            },
4992            Type::Struct(name) => name,
4993            Type::FunctionPointer { .. } => "function pointer",
4994            Type::Array { .. } => "array",
4995            // DECY-172: TypeAlias returns the alias name
4996            Type::TypeAlias(name) => name,
4997        }
4998    }
4999
5000    /// Check if this typedef is a pointer type.
5001    pub fn is_pointer(&self) -> bool {
5002        matches!(self.underlying_type, Type::Pointer(_))
5003    }
5004
5005    /// Check if this typedef is a struct type.
5006    pub fn is_struct(&self) -> bool {
5007        matches!(self.underlying_type, Type::Struct(_))
5008    }
5009
5010    /// Check if this typedef is a function pointer type.
5011    pub fn is_function_pointer(&self) -> bool {
5012        matches!(self.underlying_type, Type::FunctionPointer { .. })
5013    }
5014
5015    /// Check if this typedef is an array type.
5016    pub fn is_array(&self) -> bool {
5017        // Arrays are not yet in the Type enum, so return false for now
5018        false
5019    }
5020}
5021
5022/// Represents a struct field.
5023#[derive(Debug, Clone, PartialEq)]
5024pub struct StructField {
5025    /// Field name
5026    pub name: String,
5027    /// Field type
5028    pub field_type: Type,
5029}
5030
5031impl StructField {
5032    /// Create a new struct field.
5033    pub fn new(name: String, field_type: Type) -> Self {
5034        Self { name, field_type }
5035    }
5036
5037    /// Get the field name.
5038    pub fn name(&self) -> &str {
5039        &self.name
5040    }
5041
5042    /// Check if this field is a function pointer.
5043    pub fn is_function_pointer(&self) -> bool {
5044        matches!(self.field_type, Type::FunctionPointer { .. })
5045    }
5046}
5047
5048/// Represents a struct definition.
5049#[derive(Debug, Clone, PartialEq)]
5050pub struct Struct {
5051    /// Struct name
5052    pub name: String,
5053    /// Struct fields
5054    pub fields: Vec<StructField>,
5055}
5056
5057impl Struct {
5058    /// Create a new struct.
5059    pub fn new(name: String, fields: Vec<StructField>) -> Self {
5060        Self { name, fields }
5061    }
5062
5063    /// Get the struct name.
5064    pub fn name(&self) -> &str {
5065        &self.name
5066    }
5067
5068    /// Get the struct fields.
5069    pub fn fields(&self) -> &[StructField] {
5070        &self.fields
5071    }
5072}
5073
5074/// Represents a variable declaration.
5075#[derive(Debug, Clone, PartialEq)]
5076pub struct Variable {
5077    /// Variable name
5078    name: String,
5079    /// Variable type
5080    var_type: Type,
5081    /// Optional initializer expression
5082    initializer: Option<Expression>,
5083    /// Static storage class (file-local)
5084    is_static: bool,
5085    /// Extern storage class (external linkage)
5086    is_extern: bool,
5087    /// Const qualifier (immutable)
5088    is_const: bool,
5089}
5090
5091impl Variable {
5092    /// Create a new variable.
5093    pub fn new(name: String, var_type: Type) -> Self {
5094        Self {
5095            name,
5096            var_type,
5097            initializer: None,
5098            is_static: false,
5099            is_extern: false,
5100            is_const: false,
5101        }
5102    }
5103
5104    /// Create a new variable with an initializer.
5105    pub fn new_with_initializer(name: String, var_type: Type, initializer: Expression) -> Self {
5106        Self {
5107            name,
5108            var_type,
5109            initializer: Some(initializer),
5110            is_static: false,
5111            is_extern: false,
5112            is_const: false,
5113        }
5114    }
5115
5116    /// Create a new variable with storage class specifiers.
5117    pub fn new_with_storage_class(
5118        name: String,
5119        var_type: Type,
5120        initializer: Option<Expression>,
5121        is_static: bool,
5122        is_extern: bool,
5123        is_const: bool,
5124    ) -> Self {
5125        Self {
5126            name,
5127            var_type,
5128            initializer,
5129            is_static,
5130            is_extern,
5131            is_const,
5132        }
5133    }
5134
5135    /// Get the variable name.
5136    pub fn name(&self) -> &str {
5137        &self.name
5138    }
5139
5140    /// Get the variable type.
5141    pub fn var_type(&self) -> &Type {
5142        &self.var_type
5143    }
5144
5145    /// Check if this variable is a function pointer.
5146    pub fn is_function_pointer(&self) -> bool {
5147        matches!(self.var_type, Type::FunctionPointer { .. })
5148    }
5149
5150    /// Get the number of parameters if this is a function pointer.
5151    pub fn function_pointer_param_count(&self) -> usize {
5152        match &self.var_type {
5153            Type::FunctionPointer { param_types, .. } => param_types.len(),
5154            _ => 0,
5155        }
5156    }
5157
5158    /// Check if this function pointer has a void return type.
5159    pub fn function_pointer_has_void_return(&self) -> bool {
5160        match &self.var_type {
5161            Type::FunctionPointer { return_type, .. } => matches!(**return_type, Type::Void),
5162            _ => false,
5163        }
5164    }
5165
5166    /// Check if this variable is a string literal (char* with literal initializer).
5167    ///
5168    /// Detects patterns like: `const char* msg = "Hello";`
5169    ///
5170    /// # Implementation
5171    ///
5172    /// Checks if:
5173    /// - Type is a pointer to char (`char*`)
5174    /// - Has an initializer that is a `StringLiteral` expression
5175    ///
5176    /// Note: Const qualifier detection not yet implemented - checks all char* pointers.
5177    pub fn is_string_literal(&self) -> bool {
5178        // Check if type is char*
5179        let is_char_ptr =
5180            matches!(self.var_type, Type::Pointer(ref inner) if **inner == Type::Char);
5181
5182        // Check if initializer is a string literal
5183        if let Some(initializer) = &self.initializer {
5184            is_char_ptr && matches!(initializer, Expression::StringLiteral(_))
5185        } else {
5186            false
5187        }
5188    }
5189
5190    /// Check if this variable is a string buffer (char* allocated with malloc).
5191    ///
5192    /// Detects patterns like: `char* buffer = malloc(100);`
5193    ///
5194    /// # Implementation
5195    ///
5196    /// Checks if:
5197    /// - Type is a pointer to char (`char*`)
5198    /// - Has an initializer that is a malloc/calloc function call
5199    pub fn is_string_buffer(&self) -> bool {
5200        // Check if type is char*
5201        let is_char_ptr =
5202            matches!(self.var_type, Type::Pointer(ref inner) if **inner == Type::Char);
5203
5204        // Check if initializer is malloc/calloc call
5205        if let Some(Expression::FunctionCall { function, .. }) = &self.initializer {
5206            is_char_ptr && (function == "malloc" || function == "calloc")
5207        } else {
5208            false
5209        }
5210    }
5211
5212    /// Get the initializer expression for this variable.
5213    ///
5214    /// Returns `Some(&Expression)` if the variable has an initializer, `None` otherwise.
5215    pub fn initializer(&self) -> Option<&Expression> {
5216        self.initializer.as_ref()
5217    }
5218
5219    /// Check if this variable has static storage class (file-local).
5220    pub fn is_static(&self) -> bool {
5221        self.is_static
5222    }
5223
5224    /// Check if this variable is extern (external linkage).
5225    pub fn is_extern(&self) -> bool {
5226        self.is_extern
5227    }
5228
5229    /// Check if this variable is const (immutable).
5230    pub fn is_const(&self) -> bool {
5231        self.is_const
5232    }
5233}
5234
5235/// Represents an enum variant (constant) in C.
5236#[derive(Debug, Clone, PartialEq)]
5237pub struct EnumVariant {
5238    /// Variant name
5239    pub name: String,
5240    /// Explicit value if specified
5241    pub value: Option<i64>,
5242}
5243
5244impl EnumVariant {
5245    /// Create a new enum variant.
5246    pub fn new(name: String, value: Option<i64>) -> Self {
5247        Self { name, value }
5248    }
5249}
5250
5251/// Represents a C enum definition.
5252#[derive(Debug, Clone, PartialEq)]
5253pub struct Enum {
5254    /// Enum name (empty string for anonymous enums)
5255    pub name: String,
5256    /// Enum variants
5257    pub variants: Vec<EnumVariant>,
5258}
5259
5260impl Enum {
5261    /// Create a new enum.
5262    pub fn new(name: String, variants: Vec<EnumVariant>) -> Self {
5263        Self { name, variants }
5264    }
5265}
5266
5267/// Abstract Syntax Tree representing parsed C code.
5268#[derive(Debug, Clone, PartialEq)]
5269pub struct Ast {
5270    functions: Vec<Function>,
5271    typedefs: Vec<Typedef>,
5272    structs: Vec<Struct>,
5273    macros: Vec<MacroDefinition>,
5274    variables: Vec<Variable>,
5275    enums: Vec<Enum>,
5276}
5277
5278/// Represents a C macro definition (#define).
5279///
5280/// C macros come in two forms:
5281/// - **Object-like**: Simple text replacement (e.g., `#define MAX 100`)
5282/// - **Function-like**: Parameterized text replacement (e.g., `#define SQR(x) ((x) * (x))`)
5283///
5284/// # Examples
5285///
5286/// ```no_run
5287/// use decy_parser::parser::{CParser, MacroDefinition};
5288///
5289/// // Parse a simple object-like macro
5290/// let parser = CParser::new()?;
5291/// let ast = parser.parse("#define MAX 100\nint main() { return 0; }")?;
5292/// assert_eq!(ast.macros().len(), 1);
5293/// assert_eq!(ast.macros()[0].name(), "MAX");
5294/// assert!(ast.macros()[0].is_object_like());
5295///
5296/// // Parse a function-like macro
5297/// let ast2 = parser.parse("#define SQR(x) ((x) * (x))\nint main() { return 0; }")?;
5298/// assert_eq!(ast2.macros()[0].name(), "SQR");
5299/// assert!(ast2.macros()[0].is_function_like());
5300/// assert_eq!(ast2.macros()[0].parameters(), &["x"]);
5301/// # Ok::<(), anyhow::Error>(())
5302/// ```
5303///
5304/// # Reference
5305///
5306/// K&R §4.11, ISO C99 §6.10.3
5307#[derive(Debug, Clone, PartialEq)]
5308pub struct MacroDefinition {
5309    /// Macro name
5310    pub name: String,
5311    /// Parameters (empty for object-like macros)
5312    pub parameters: Vec<String>,
5313    /// Macro body (unparsed, tokenized without spaces)
5314    pub body: String,
5315}
5316
5317impl MacroDefinition {
5318    /// Create a new object-like macro.
5319    pub fn new_object_like(name: String, body: String) -> Self {
5320        Self {
5321            name,
5322            parameters: vec![],
5323            body,
5324        }
5325    }
5326
5327    /// Create a new function-like macro.
5328    pub fn new_function_like(name: String, parameters: Vec<String>, body: String) -> Self {
5329        Self {
5330            name,
5331            parameters,
5332            body,
5333        }
5334    }
5335
5336    /// Get the macro name.
5337    pub fn name(&self) -> &str {
5338        &self.name
5339    }
5340
5341    /// Get the macro parameters.
5342    pub fn parameters(&self) -> &[String] {
5343        &self.parameters
5344    }
5345
5346    /// Get the macro body.
5347    pub fn body(&self) -> &str {
5348        &self.body
5349    }
5350
5351    /// Check if this is a function-like macro.
5352    pub fn is_function_like(&self) -> bool {
5353        !self.parameters.is_empty()
5354    }
5355
5356    /// Check if this is an object-like macro.
5357    pub fn is_object_like(&self) -> bool {
5358        self.parameters.is_empty()
5359    }
5360}
5361
5362impl Ast {
5363    /// Create a new empty AST.
5364    pub fn new() -> Self {
5365        Self {
5366            functions: Vec::new(),
5367            typedefs: Vec::new(),
5368            structs: Vec::new(),
5369            macros: Vec::new(),
5370            variables: Vec::new(),
5371            enums: Vec::new(),
5372        }
5373    }
5374
5375    /// Get the functions in the AST.
5376    pub fn functions(&self) -> &[Function] {
5377        &self.functions
5378    }
5379
5380    /// Add a function to the AST.
5381    pub fn add_function(&mut self, function: Function) {
5382        self.functions.push(function);
5383    }
5384
5385    /// Get the typedefs in the AST.
5386    pub fn typedefs(&self) -> &[Typedef] {
5387        &self.typedefs
5388    }
5389
5390    /// Add a typedef to the AST.
5391    pub fn add_typedef(&mut self, typedef: Typedef) {
5392        self.typedefs.push(typedef);
5393    }
5394
5395    /// Get the structs in the AST.
5396    pub fn structs(&self) -> &[Struct] {
5397        &self.structs
5398    }
5399
5400    /// Add a struct to the AST.
5401    /// Deduplicates by name to avoid duplicate definitions from system includes.
5402    pub fn add_struct(&mut self, struct_def: Struct) {
5403        // Deduplicate: don't add if a struct with the same name already exists
5404        if !self.structs.iter().any(|s| s.name() == struct_def.name()) {
5405            self.structs.push(struct_def);
5406        }
5407    }
5408
5409    /// Get the macro definitions in the AST.
5410    pub fn macros(&self) -> &[MacroDefinition] {
5411        &self.macros
5412    }
5413
5414    /// Add a macro definition to the AST.
5415    pub fn add_macro(&mut self, macro_def: MacroDefinition) {
5416        self.macros.push(macro_def);
5417    }
5418
5419    /// Get the variables in the AST.
5420    pub fn variables(&self) -> &[Variable] {
5421        &self.variables
5422    }
5423
5424    /// Add a variable to the AST.
5425    pub fn add_variable(&mut self, variable: Variable) {
5426        self.variables.push(variable);
5427    }
5428
5429    /// Get the enums in the AST.
5430    pub fn enums(&self) -> &[Enum] {
5431        &self.enums
5432    }
5433
5434    /// Add an enum to the AST.
5435    pub fn add_enum(&mut self, enum_def: Enum) {
5436        self.enums.push(enum_def);
5437    }
5438}
5439
5440impl Default for Ast {
5441    fn default() -> Self {
5442        Self::new()
5443    }
5444}
5445
5446/// Represents a C function.
5447#[derive(Debug, Clone, PartialEq)]
5448pub struct Function {
5449    /// Function name
5450    pub name: String,
5451    /// Return type
5452    pub return_type: Type,
5453    /// Parameters
5454    pub parameters: Vec<Parameter>,
5455    /// Function body (statements)
5456    pub body: Vec<Statement>,
5457}
5458
5459impl Function {
5460    /// Create a new function.
5461    pub fn new(name: String, return_type: Type, parameters: Vec<Parameter>) -> Self {
5462        Self {
5463            name,
5464            return_type,
5465            parameters,
5466            body: Vec::new(),
5467        }
5468    }
5469
5470    /// Create a new function with body.
5471    pub fn new_with_body(
5472        name: String,
5473        return_type: Type,
5474        parameters: Vec<Parameter>,
5475        body: Vec<Statement>,
5476    ) -> Self {
5477        Self {
5478            name,
5479            return_type,
5480            parameters,
5481            body,
5482        }
5483    }
5484}
5485
5486/// Represents a C type.
5487#[derive(Debug, Clone, PartialEq)]
5488#[allow(clippy::enum_variant_names)] // TypeAlias is a meaningful variant name
5489pub enum Type {
5490    /// void
5491    Void,
5492    /// C99 _Bool type (maps to bool in Rust)
5493    Bool,
5494    /// int
5495    Int,
5496    /// unsigned int (DECY-158)
5497    UnsignedInt,
5498    /// float
5499    Float,
5500    /// double
5501    Double,
5502    /// char (unsigned char or plain char → u8)
5503    Char,
5504    /// signed char → i8 (DECY-250)
5505    SignedChar,
5506    /// Pointer to a type
5507    Pointer(Box<Type>),
5508    /// Struct type (e.g., struct Point)
5509    Struct(String),
5510    /// Function pointer type (e.g., int (*callback)(int))
5511    FunctionPointer {
5512        /// Parameter types
5513        param_types: Vec<Type>,
5514        /// Return type
5515        return_type: Box<Type>,
5516    },
5517    /// Array type (e.g., `int arr[10]`)
5518    /// For typedef assertions like: `typedef char check[sizeof(int) == 4 ? 1 : -1]`
5519    Array {
5520        /// Element type
5521        element_type: Box<Type>,
5522        /// Array size (None for unknown/expression-based size)
5523        size: Option<i64>,
5524    },
5525    /// Type alias (typedef) - preserves the alias name
5526    /// DECY-172: Used for size_t, ssize_t, ptrdiff_t, etc.
5527    TypeAlias(String),
5528}
5529
5530/// Represents a function parameter.
5531#[derive(Debug, Clone, PartialEq)]
5532pub struct Parameter {
5533    /// Parameter name
5534    pub name: String,
5535    /// Parameter type
5536    pub param_type: Type,
5537    /// Whether the pointee type is const (for pointer params like `const char*`)
5538    /// DECY-135: Track const qualifier to enable const char* → &str transformation
5539    pub is_pointee_const: bool,
5540}
5541
5542impl Parameter {
5543    /// Create a new parameter.
5544    pub fn new(name: String, param_type: Type) -> Self {
5545        Self {
5546            name,
5547            param_type,
5548            is_pointee_const: false,
5549        }
5550    }
5551
5552    /// Create a new parameter with const pointee information.
5553    /// DECY-135: Used for const char* parameters
5554    pub fn new_with_const(name: String, param_type: Type, is_pointee_const: bool) -> Self {
5555        Self {
5556            name,
5557            param_type,
5558            is_pointee_const,
5559        }
5560    }
5561
5562    /// Check if this parameter is a function pointer.
5563    pub fn is_function_pointer(&self) -> bool {
5564        matches!(self.param_type, Type::FunctionPointer { .. })
5565    }
5566
5567    /// Check if this parameter is a const char pointer (const char*).
5568    ///
5569    /// DECY-135: Now properly checks if pointee is const-qualified.
5570    /// Returns `true` only for `const char*` parameters, not `char*`.
5571    pub fn is_const_char_pointer(&self) -> bool {
5572        self.is_pointee_const
5573            && matches!(self.param_type, Type::Pointer(ref inner) if matches!(**inner, Type::Char))
5574    }
5575
5576    /// Check if this parameter is any char pointer (char* or const char*).
5577    pub fn is_char_pointer(&self) -> bool {
5578        matches!(self.param_type, Type::Pointer(ref inner) if matches!(**inner, Type::Char))
5579    }
5580}
5581
5582#[cfg(test)]
5583#[path = "parser_tests.rs"]
5584mod parser_tests;
5585
5586#[cfg(test)]
5587#[path = "pointer_arithmetic_tests.rs"]
5588mod pointer_arithmetic_tests;
5589
5590#[cfg(test)]
5591#[path = "break_continue_tests.rs"]
5592mod break_continue_tests;