rusty_cpp/parser/ast_visitor.rs
1use clang::{Entity, EntityKind, Type, TypeKind};
2use crate::debug_println;
3
4/// Check if a C++ method is deleted (= delete) or defaulted (= default)
5/// These special member functions don't count as regular methods for @interface validation.
6///
7/// Uses libclang's native APIs:
8/// - `is_defaulted()` for `= default` detection (libclang 3.9+)
9/// - `clang_CXXMethod_isDeleted()` for `= delete` detection (libclang 16.0+)
10///
11/// No source file parsing needed - works with temp files and multi-line declarations.
12fn is_deleted_or_defaulted_method(entity: &Entity) -> bool {
13 // Check for = default using clang crate's wrapper
14 if entity.is_defaulted() {
15 debug_println!("DEBUG PARSE: Method {:?} is defaulted via libclang", entity.get_name());
16 return true;
17 }
18
19 // Check for = delete using availability
20 // Deleted methods have Unavailable availability
21 if entity.get_availability() == clang::Availability::Unavailable {
22 debug_println!("DEBUG PARSE: Method {:?} is deleted (Unavailable)", entity.get_name());
23 return true;
24 }
25
26 // Fallback: Check for = delete using libclang 16+ native API
27 if is_deleted_via_libclang(entity) {
28 debug_println!("DEBUG PARSE: Method {:?} is deleted via libclang FFI", entity.get_name());
29 return true;
30 }
31
32 false
33}
34
35/// Check if method is deleted (= delete) using libclang's native API.
36///
37/// Uses clang_CXXMethod_isDeleted from libclang 16+.
38/// The clang crate doesn't wrap this function, so we call clang-sys directly.
39fn is_deleted_via_libclang(entity: &Entity) -> bool {
40 // Entity layout: { raw: CXCursor, tu: &'tu TranslationUnit<'tu> }
41 // We extract the CXCursor (first field) to call the FFI function directly.
42 //
43 // SAFETY:
44 // - Entity is repr(Rust), but raw is the first field
45 // - CXCursor is Copy, Clone
46 // - We're only reading, not modifying
47 //
48 // Use transmute_copy to extract the raw cursor bytes
49 let raw_cursor: clang_sys::CXCursor = unsafe {
50 // CXCursor is 32 bytes (kind: c_int, xdata: c_int, data: [*const c_void; 3])
51 // Entity starts with the raw CXCursor field
52 std::mem::transmute_copy(entity)
53 };
54
55 // Call the libclang 16+ function directly
56 let result = unsafe {
57 clang_sys::clang_CXXMethod_isDeleted(raw_cursor)
58 };
59
60 result != 0
61}
62
63/// Check if a function name is std::move, rusty::move, or a namespace-qualified move
64///
65/// rusty::move provides Rust-like move semantics where moving a reference
66/// invalidates the reference variable itself (not just the underlying object).
67fn is_move_function(name: &str) -> bool {
68 name == "move"
69 || name == "std::move"
70 || name == "rusty::move"
71 || name.ends_with("::move")
72}
73
74/// Determine the kind of move function (std::move vs rusty::move)
75fn get_move_kind(name: &str) -> MoveKind {
76 if name == "rusty::move" || name.ends_with("::rusty::move") {
77 MoveKind::RustyMove
78 } else {
79 // std::move, move, or any other ::move variant is treated as std::move
80 MoveKind::StdMove
81 }
82}
83
84/// Check if a function name is std::forward or a namespace-qualified forward
85fn is_forward_function(name: &str) -> bool {
86 name == "forward" || name == "std::forward" || name.ends_with("::forward")
87}
88
89/// Check if an entity represents a call to an overloaded operator->
90///
91/// Key insight: If the entity is a call to "operator->", it means the type has
92/// an overloaded operator->. This is the definition of a smart pointer.
93/// Raw pointers use the built-in -> operator which does NOT create a CallExpr.
94///
95/// So the detection is simple:
96/// - If we see a CallExpr/UnexposedExpr named "operator->", it's a smart pointer
97/// - Raw pointers never create such a call
98fn has_overloaded_arrow_operator(entity: &Entity) -> bool {
99 // Check if the entity's name is "operator->"
100 // This indicates an overloaded operator-> was called, meaning it's a smart pointer
101 if let Some(name) = entity.get_name() {
102 if name == "operator->" {
103 debug_println!("DEBUG: Found overloaded operator-> call");
104 return true;
105 }
106 }
107
108 // For nested cases (like method calls), check children recursively
109 // e.g., box->get_value() has nested operator-> calls
110 for child in entity.get_children() {
111 if let Some(child_name) = child.get_name() {
112 if child_name == "operator->" {
113 debug_println!("DEBUG: Found overloaded operator-> in child");
114 return true;
115 }
116 }
117 }
118
119 false
120}
121
122/// Check if an entity has an @unsafe annotation by reading source file
123fn check_for_unsafe_annotation(entity: &Entity) -> bool {
124 use std::fs::File;
125 use std::io::{BufRead, BufReader};
126
127 // Try get_comment() first (works for some entity types)
128 if let Some(comment) = entity.get_comment() {
129 if comment.contains("@unsafe") {
130 return true;
131 }
132 }
133
134 // For CompoundStmt and other entities, check the source file directly
135 let location = match entity.get_location() {
136 Some(loc) => loc,
137 None => return false,
138 };
139
140 let file_location = location.get_file_location();
141 let file = match file_location.file {
142 Some(f) => f,
143 None => return false,
144 };
145
146 let file_path = file.get_path();
147 let block_line = file_location.line as usize;
148
149 // Read the source file and check the line before the block
150 let file_handle = match File::open(&file_path) {
151 Ok(f) => f,
152 Err(_) => return false,
153 };
154
155 let reader = BufReader::new(file_handle);
156 let mut current_line = 0;
157 let mut prev_line = String::new();
158
159 for line_result in reader.lines() {
160 current_line += 1;
161 let line = match line_result {
162 Ok(l) => l,
163 Err(_) => continue,
164 };
165
166 // Check if we're at the block's line
167 if current_line == block_line {
168 // Check the previous line for @unsafe annotation
169 let trimmed = prev_line.trim();
170 if trimmed.starts_with("//") && trimmed.contains("@unsafe") {
171 debug_println!("DEBUG UNSAFE: Found @unsafe annotation for block at line {}", block_line);
172 return true;
173 }
174 // Also check for /* @unsafe */ style comments
175 if trimmed.contains("/*") && trimmed.contains("@unsafe") && trimmed.contains("*/") {
176 debug_println!("DEBUG UNSAFE: Found @unsafe annotation for block at line {}", block_line);
177 return true;
178 }
179 return false;
180 }
181
182 prev_line = line;
183 }
184
185 false
186}
187
188/// Check if a field declaration has the 'mutable' keyword
189/// Read the source code around the declaration to detect 'mutable'
190fn check_for_mutable_keyword(entity: &Entity) -> bool {
191 use std::fs::File;
192 use std::io::{BufRead, BufReader};
193
194 let location = match entity.get_location() {
195 Some(loc) => loc,
196 None => return false,
197 };
198
199 let file_location = location.get_file_location();
200 let file = match file_location.file {
201 Some(f) => f,
202 None => return false,
203 };
204
205 let file_path = file.get_path();
206 let decl_line = file_location.line as usize;
207
208 // Read the source file and check the line with the declaration
209 let file_handle = match File::open(&file_path) {
210 Ok(f) => f,
211 Err(_) => return false,
212 };
213
214 let reader = BufReader::new(file_handle);
215 let mut current_line = 0;
216
217 for line_result in reader.lines() {
218 current_line += 1;
219 let line = match line_result {
220 Ok(l) => l,
221 Err(_) => continue,
222 };
223
224 // Check if we're at the declaration line
225 if current_line == decl_line {
226 // Check if the line contains the 'mutable' keyword
227 // Look for word boundary to avoid matching "immutable" or similar
228 let trimmed = line.trim();
229 let has_mutable = trimmed.starts_with("mutable ") ||
230 trimmed.contains(" mutable ") ||
231 (trimmed == "mutable");
232
233 debug_println!("DEBUG MUTABLE: Line {}: '{}' -> has_mutable = {}",
234 decl_line, trimmed, has_mutable);
235
236 return has_mutable;
237 }
238 }
239
240 false
241}
242
243/// Get the qualified name of an entity (including namespace/class context)
244pub fn get_qualified_name(entity: &Entity) -> String {
245 let simple_name = entity.get_name().unwrap_or_else(|| "anonymous".to_string());
246
247 // Try to build qualified name by walking up the semantic parents
248 let mut parts = vec![simple_name.clone()];
249 let mut current = entity.get_semantic_parent();
250
251 while let Some(parent) = current {
252 match parent.get_kind() {
253 EntityKind::Namespace | EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::ClassTemplate => {
254 if let Some(parent_name) = parent.get_name() {
255 if !parent_name.is_empty() {
256 parts.push(parent_name);
257 }
258 }
259 }
260 _ => {}
261 }
262 current = parent.get_semantic_parent();
263 }
264
265 // Reverse to get the correct order (namespace::class::method)
266 parts.reverse();
267
268 // Join with :: but skip if we only have the simple name
269 if parts.len() > 1 {
270 parts.join("::")
271 } else {
272 simple_name
273 }
274}
275
276/// Extract template type parameters from a template entity
277///
278/// For `template<typename T, typename U>`, this returns ["T", "U"]
279/// Works with ClassTemplateDecl and FunctionTemplateDecl
280pub fn extract_template_parameters(entity: &Entity) -> Vec<String> {
281 use clang::EntityVisitResult;
282
283 let mut params = Vec::new();
284
285 // Visit direct children to find TemplateTypeParameter or NonTypeTemplateParameter
286 entity.visit_children(|child, _| {
287 match child.get_kind() {
288 EntityKind::TemplateTypeParameter | EntityKind::NonTypeTemplateParameter => {
289 if let Some(name) = child.get_name() {
290 debug_println!("TEMPLATE: Found type parameter: {}", name);
291 params.push(name);
292 }
293 }
294 _ => {}
295 }
296 EntityVisitResult::Continue
297 });
298
299 params
300}
301
302// Phase 3: Class template representation
303#[derive(Debug, Clone)]
304pub struct Class {
305 pub name: String,
306 pub template_parameters: Vec<String>, // e.g., ["T", "Args"] for template<typename T, typename... Args>
307 pub is_template: bool,
308 pub members: Vec<Variable>, // Member fields
309 pub methods: Vec<Function>, // Member methods
310 pub base_classes: Vec<String>, // Base class names (may contain packs like "Bases...")
311 pub location: SourceLocation,
312 // RAII Phase 2: Track if class has a destructor
313 pub has_destructor: bool, // True if class has ~ClassName()
314 // Inheritance safety: Interface-related fields
315 pub is_interface: bool, // Has @interface annotation
316 pub has_virtual_destructor: bool, // virtual ~Class() or virtual ~Class() = default
317 pub destructor_is_defaulted: bool, // True if destructor is = default
318 pub all_methods_pure_virtual: bool, // All methods are = 0 (pure virtual)
319 pub has_non_virtual_methods: bool, // Has any non-virtual methods (excluding destructor)
320 pub safety_annotation: Option<crate::parser::safety_annotations::SafetyMode>, // @safe or @unsafe on class
321 // Copy semantics: @safe classes should not have copy operations (Rust-like move semantics)
322 pub has_copy_constructor: bool, // True if class has ClassName(const ClassName&)
323 pub has_copy_assignment: bool, // True if class has operator=(const ClassName&)
324 pub copy_constructor_deleted: bool, // True if copy constructor is explicitly deleted
325 pub copy_assignment_deleted: bool, // True if copy assignment is explicitly deleted
326}
327
328#[derive(Debug, Clone)]
329pub struct CppAst {
330 pub functions: Vec<Function>,
331 pub global_variables: Vec<Variable>,
332 pub classes: Vec<Class>, // Phase 3: Track template classes
333}
334
335impl CppAst {
336 pub fn new() -> Self {
337 Self {
338 functions: Vec::new(),
339 global_variables: Vec::new(),
340 classes: Vec::new(), // Phase 3
341 }
342 }
343}
344
345#[derive(Debug, Clone, PartialEq)]
346pub enum MethodQualifier {
347 Const, // const method (like Rust's &self)
348 NonConst, // regular method (like Rust's &mut self)
349 RvalueRef, // && qualified method (like Rust's self)
350}
351
352#[derive(Debug, Clone)]
353pub struct Function {
354 pub name: String,
355 pub parameters: Vec<Variable>,
356 #[allow(dead_code)]
357 pub return_type: String,
358 #[allow(dead_code)]
359 pub body: Vec<Statement>,
360 #[allow(dead_code)]
361 pub location: SourceLocation,
362 // Method information
363 pub is_method: bool,
364 pub method_qualifier: Option<MethodQualifier>,
365 pub class_name: Option<String>,
366 // Template information
367 pub template_parameters: Vec<String>, // e.g., ["T", "U"] for template<typename T, typename U>
368 // Safety annotation for method safety contract checking
369 pub safety_annotation: Option<crate::parser::safety_annotations::SafetyMode>,
370 pub has_explicit_safety_annotation: bool, // true if annotation was in source code
371}
372
373#[derive(Debug, Clone)]
374pub struct Variable {
375 pub name: String,
376 pub type_name: String,
377 pub is_reference: bool,
378 #[allow(dead_code)]
379 pub is_pointer: bool,
380 pub is_const: bool,
381 pub is_unique_ptr: bool,
382 #[allow(dead_code)]
383 pub is_shared_ptr: bool,
384 pub is_static: bool,
385 pub is_mutable: bool, // C++ mutable keyword (for interior mutability)
386 #[allow(dead_code)]
387 pub location: SourceLocation,
388 // Variadic template support (Phase 1)
389 pub is_pack: bool, // Is this a parameter pack (e.g., Args... args)?
390 pub pack_element_type: Option<String>, // Type of pack elements (e.g., "Args&&" from "Args&&...")
391}
392
393#[derive(Debug, Clone)]
394#[allow(dead_code)]
395pub enum Statement {
396 VariableDecl(Variable),
397 Assignment {
398 lhs: Expression, // Changed to Expression to support dereference: *ptr = value
399 rhs: Expression,
400 location: SourceLocation,
401 },
402 ReferenceBinding {
403 name: String,
404 target: Expression,
405 is_mutable: bool,
406 location: SourceLocation,
407 },
408 Return(Option<Expression>),
409 FunctionCall {
410 name: String,
411 args: Vec<Expression>,
412 location: SourceLocation,
413 },
414 Block(Vec<Statement>),
415 // Scope markers
416 EnterScope,
417 ExitScope,
418 // Loop markers
419 EnterLoop,
420 ExitLoop,
421 // Safety markers
422 EnterUnsafe,
423 ExitUnsafe,
424 // Conditional statements
425 If {
426 condition: Expression,
427 then_branch: Vec<Statement>,
428 else_branch: Option<Vec<Statement>>,
429 location: SourceLocation,
430 },
431 // Expression statements (e.g., standalone dereference, method calls)
432 ExpressionStatement {
433 expr: Expression,
434 location: SourceLocation,
435 },
436 // Phase 2: Pack expansion statement (fold expressions, pack usage)
437 PackExpansion {
438 pack_name: String, // Name of the pack being expanded (e.g., "args")
439 operation: String, // Type of operation: "forward", "move", "use"
440 location: SourceLocation,
441 },
442 // Lambda expression with captures (for safety checking)
443 LambdaExpr {
444 captures: Vec<LambdaCaptureKind>,
445 location: SourceLocation,
446 },
447}
448
449/// Represents a lambda capture
450#[derive(Debug, Clone)]
451pub enum LambdaCaptureKind {
452 /// [&] - default reference capture
453 DefaultRef,
454 /// [=] - default copy capture
455 DefaultCopy,
456 /// [&x] - explicit reference capture
457 ByRef(String),
458 /// [x] - explicit copy capture
459 ByCopy(String),
460 /// [x = expr] - init capture (includes move captures)
461 Init { name: String, is_move: bool },
462 /// [this] - captures this pointer
463 This,
464 /// [*this] - captures this by copy (C++17)
465 ThisCopy,
466}
467
468/// Indicates which move function was used
469#[derive(Debug, Clone, PartialEq, Eq)]
470pub enum MoveKind {
471 /// std::move - standard C++ move, unsafe for references in @safe code
472 StdMove,
473 /// rusty::move - Rust-like move semantics, safe for all types
474 RustyMove,
475}
476
477#[derive(Debug, Clone)]
478#[allow(dead_code)]
479pub enum Expression {
480 Variable(String),
481 Move {
482 inner: Box<Expression>,
483 kind: MoveKind,
484 },
485 Dereference(Box<Expression>),
486 AddressOf(Box<Expression>),
487 FunctionCall {
488 name: String,
489 args: Vec<Expression>,
490 },
491 Literal(String),
492 /// String literal expression ("hello", L"wide", u8"utf8", etc.)
493 /// String literals are safe in @safe code because they have static lifetime.
494 /// They cannot dangle because they're stored in .rodata (read-only data segment).
495 StringLiteral(String),
496 BinaryOp {
497 left: Box<Expression>,
498 op: String,
499 right: Box<Expression>,
500 },
501 // NEW: Member access (obj.field)
502 MemberAccess {
503 object: Box<Expression>,
504 field: String,
505 },
506 // Lambda expression with captures
507 Lambda {
508 captures: Vec<LambdaCaptureKind>,
509 },
510 // C++ cast expression (static_cast, dynamic_cast, reinterpret_cast, const_cast, C-style)
511 // All casts are considered unsafe operations in @safe code
512 Cast(Box<Expression>),
513}
514
515#[derive(Debug, Clone)]
516pub struct SourceLocation {
517 #[allow(dead_code)]
518 pub file: String,
519 #[allow(dead_code)]
520 pub line: u32,
521 #[allow(dead_code)]
522 pub column: u32,
523}
524
525pub fn extract_function(entity: &Entity) -> Function {
526 use crate::parser::safety_annotations::check_method_safety_annotation;
527
528 let kind = entity.get_kind();
529 let is_method = kind == EntityKind::Method || kind == EntityKind::Constructor;
530
531 // Parse safety annotation from comments (uses source file reading fallback)
532 let safety_annotation = check_method_safety_annotation(entity);
533 let has_explicit_safety_annotation = safety_annotation.is_some();
534
535 // Use qualified name for ALL functions (methods AND free functions in namespaces)
536 // This ensures:
537 // 1. Methods get qualified names like "MyClass::method"
538 // 2. Namespaced functions get qualified names like "network::send_message"
539 // 3. External library functions get qualified names like "YAML::detail::node_data::get"
540 // This prevents false matches where unqualified "get" incorrectly matches "rusty::Cell::get"
541 let name = get_qualified_name(entity);
542 let location = extract_location(entity);
543
544 let mut parameters = Vec::new();
545 for child in entity.get_children() {
546 if child.get_kind() == EntityKind::ParmDecl {
547 let mut param = extract_variable(&child);
548
549 // Phase 1: Detect variadic parameter packs
550 // Check if this is a parameter pack by examining the type
551 if let Some(param_type) = child.get_type() {
552 let type_str = type_to_string(¶m_type);
553
554 // Check if type contains "..." (e.g., "Args...", "Args &&...", "const T &...")
555 let is_pack = type_str.contains("...") || child.is_variadic();
556
557 if is_pack {
558 param.is_pack = true;
559
560 // Extract element type by removing "..." from the type string
561 let element_type = type_str.trim_end_matches("...").trim().to_string();
562 param.pack_element_type = Some(element_type.clone());
563
564 debug_println!("DEBUG PARSE: Found parameter pack '{}' with element type '{}'",
565 param.name, element_type);
566 }
567 }
568
569 parameters.push(param);
570 }
571 }
572
573 let return_type = entity
574 .get_result_type()
575 .map(|t| type_to_string(&t))
576 .unwrap_or_else(|| "void".to_string());
577
578 let body = extract_function_body(entity);
579
580 // Detect method qualifier and class name
581 let (method_qualifier, class_name) = if is_method {
582 let qualifier = detect_method_qualifier(entity);
583 let class_name = entity.get_semantic_parent()
584 .and_then(|parent| parent.get_name());
585 (Some(qualifier), class_name)
586 } else {
587 (None, None)
588 };
589
590 // Extract template parameters from:
591 // 1. ClassTemplate parent (for template class methods)
592 // 2. FunctionTemplate entity itself (for free template functions)
593 // 3. FunctionTemplate parent (fallback for nested cases)
594 let template_parameters = if is_method {
595 // Check if parent is a ClassTemplate
596 if let Some(parent) = entity.get_semantic_parent() {
597 if parent.get_kind() == EntityKind::ClassTemplate {
598 debug_println!("TEMPLATE: Method in template class, extracting parameters");
599 extract_template_parameters(&parent)
600 } else {
601 Vec::new()
602 }
603 } else {
604 Vec::new()
605 }
606 } else if entity.get_kind() == EntityKind::FunctionTemplate {
607 // Entity IS a FunctionTemplate - extract parameters directly from it
608 debug_println!("TEMPLATE: Free template function (entity is FunctionTemplate), extracting parameters");
609 extract_template_parameters(entity)
610 } else {
611 // For free functions, check if parent is a FunctionTemplate (fallback)
612 if let Some(parent) = entity.get_semantic_parent() {
613 if parent.get_kind() == EntityKind::FunctionTemplate {
614 debug_println!("TEMPLATE: Free template function, extracting parameters from FunctionTemplate parent");
615 extract_template_parameters(&parent)
616 } else {
617 Vec::new()
618 }
619 } else {
620 Vec::new()
621 }
622 };
623
624 Function {
625 name,
626 parameters,
627 return_type,
628 body,
629 location,
630 is_method,
631 method_qualifier,
632 class_name,
633 template_parameters,
634 safety_annotation,
635 has_explicit_safety_annotation,
636 }
637}
638
639// Phase 3: Extract class template information
640pub fn extract_class(entity: &Entity) -> Class {
641 use crate::debug_println;
642 use crate::parser::safety_annotations::{check_class_interface_annotation, parse_entity_safety};
643
644 // Bug #8 fix: Use qualified name for classes to prevent namespace collision
645 // e.g., "yaml::Node" instead of just "Node"
646 let name = get_qualified_name(entity);
647 let location = extract_location(entity);
648 let is_template = entity.get_kind() == EntityKind::ClassTemplate;
649
650 debug_println!("DEBUG PARSE: Extracting class '{}', is_template={}", name, is_template);
651
652 // Extract template parameters from ClassTemplate
653 let template_parameters = if is_template {
654 extract_template_parameters(entity)
655 } else {
656 Vec::new()
657 };
658
659 debug_println!("DEBUG PARSE: Class '{}' has {} template parameters: {:?}",
660 name, template_parameters.len(), template_parameters);
661
662 // Check for @interface annotation
663 let is_interface = check_class_interface_annotation(entity);
664 if is_interface {
665 debug_println!("DEBUG PARSE: Class '{}' is marked as @interface", name);
666 }
667
668 // Check for @safe/@unsafe annotation on the class
669 let safety_annotation = parse_entity_safety(entity);
670
671 let mut members = Vec::new();
672 let mut methods = Vec::new();
673 let mut base_classes = Vec::new();
674 let mut has_destructor = false; // RAII Phase 2: Track destructors
675 let mut has_virtual_destructor = false;
676 let mut destructor_is_defaulted = false;
677 let mut has_non_virtual_methods = false;
678 let mut all_methods_pure_virtual = true; // Start true, set false if we find non-pure method
679 let mut has_any_method = false; // Track if there are any methods to check
680 // Copy semantics tracking
681 let mut has_copy_constructor = false;
682 let mut has_copy_assignment = false;
683 let mut copy_constructor_deleted = false;
684 let mut copy_assignment_deleted = false;
685
686 // LibClang's get_children() flattens the hierarchy and returns class members directly
687 // (FieldDecl, Method, etc.) rather than going through CXXRecordDecl
688 for child in entity.get_children() {
689 debug_println!("DEBUG PARSE: ClassTemplate child kind: {:?}", child.get_kind());
690 match child.get_kind() {
691 EntityKind::FieldDecl => {
692 // Member field
693 let mut member = extract_variable(&child);
694
695 // Phase 3: Check if member type contains pack expansion
696 if let Some(field_type) = child.get_type() {
697 let type_str = type_to_string(&field_type);
698 if type_str.contains("...") {
699 debug_println!("DEBUG PARSE: Found member field with pack expansion: '{}' of type '{}'",
700 member.name, type_str);
701 member.is_pack = true;
702 member.pack_element_type = Some(type_str.clone());
703 }
704 }
705
706 members.push(member);
707 }
708 EntityKind::Destructor => {
709 // RAII Phase 2: Mark class as having a destructor
710 has_destructor = true;
711 debug_println!("DEBUG PARSE: Class '{}' has user-defined destructor", name);
712
713 // Check if destructor is virtual
714 if child.is_virtual_method() {
715 has_virtual_destructor = true;
716 debug_println!("DEBUG PARSE: Class '{}' has virtual destructor", name);
717 }
718
719 // Check if destructor is defaulted (= default)
720 if child.is_defaulted() {
721 destructor_is_defaulted = true;
722 debug_println!("DEBUG PARSE: Class '{}' has defaulted destructor", name);
723 }
724
725 let method = extract_function(&child);
726 methods.push(method);
727 }
728 EntityKind::Method | EntityKind::Constructor => {
729 // Member method
730 let method = extract_function(&child);
731 let is_deleted = is_deleted_or_defaulted_method(&child);
732
733 // Check for copy constructor
734 if child.get_kind() == EntityKind::Constructor {
735 if child.is_copy_constructor() {
736 has_copy_constructor = true;
737 if is_deleted {
738 copy_constructor_deleted = true;
739 debug_println!("DEBUG PARSE: Class '{}' has deleted copy constructor", name);
740 } else {
741 debug_println!("DEBUG PARSE: Class '{}' has copy constructor", name);
742 }
743 }
744 }
745
746 // Skip constructors for pure virtual check
747 if child.get_kind() == EntityKind::Method {
748 has_any_method = true;
749
750 // Check for copy assignment operator: operator=(const ClassName&)
751 if let Some(method_name) = child.get_name() {
752 if method_name == "operator=" {
753 // Check if parameter is const reference to same class type
754 let children = child.get_children();
755 for param_child in &children {
756 if param_child.get_kind() == EntityKind::ParmDecl {
757 if let Some(param_type) = param_child.get_type() {
758 let type_str = type_to_string(¶m_type);
759 // Copy assignment takes const ClassName& or ClassName const&
760 // Get the unqualified class name for comparison
761 let simple_name = name.split("::").last().unwrap_or(&name);
762 if type_str.contains(simple_name) &&
763 type_str.contains("const") &&
764 type_str.contains("&") &&
765 !type_str.contains("&&") {
766 has_copy_assignment = true;
767 if is_deleted {
768 copy_assignment_deleted = true;
769 debug_println!("DEBUG PARSE: Class '{}' has deleted copy assignment", name);
770 } else {
771 debug_println!("DEBUG PARSE: Class '{}' has copy assignment", name);
772 }
773 }
774 }
775 break; // Only check first parameter
776 }
777 }
778 }
779 }
780
781 // Check if method is virtual, deleted, or defaulted
782 let is_virtual = child.is_virtual_method();
783 let is_pure_virtual = child.is_pure_virtual_method();
784
785 // Deleted/defaulted methods (= delete, = default) don't count as
786 // non-virtual methods for @interface validation. They are special
787 // member functions, not regular callable methods.
788 if !is_virtual && !is_deleted {
789 has_non_virtual_methods = true;
790 debug_println!("DEBUG PARSE: Class '{}' has non-virtual method: {:?}", name, child.get_name());
791 }
792
793 // Deleted/defaulted methods also don't count for pure virtual check
794 if !is_pure_virtual && !is_deleted {
795 all_methods_pure_virtual = false;
796 debug_println!("DEBUG PARSE: Class '{}' has non-pure-virtual method: {:?}", name, child.get_name());
797 }
798 }
799
800 methods.push(method);
801 }
802 EntityKind::FunctionTemplate => {
803 // Template method
804 if child.is_definition() {
805 let method = extract_function(&child);
806 methods.push(method);
807 // Template methods cannot be pure virtual
808 has_any_method = true;
809 all_methods_pure_virtual = false;
810 has_non_virtual_methods = true;
811 }
812 }
813 EntityKind::BaseSpecifier => {
814 // Base class
815 if let Some(base_type) = child.get_type() {
816 let base_name = type_to_string(&base_type);
817 debug_println!("DEBUG PARSE: Found base class: '{}'", base_name);
818 base_classes.push(base_name);
819 }
820 }
821 _ => {}
822 }
823 }
824
825 // If no methods at all, all_methods_pure_virtual should be true (vacuously true for interfaces)
826 if !has_any_method {
827 all_methods_pure_virtual = true;
828 }
829
830 debug_println!("DEBUG PARSE: Class '{}' has {} members, {} methods, {} base classes, has_destructor={}, is_interface={}, has_virtual_destructor={}, destructor_is_defaulted={}, all_methods_pure_virtual={}, has_non_virtual_methods={}, has_copy_constructor={} (deleted={}), has_copy_assignment={} (deleted={})",
831 name, members.len(), methods.len(), base_classes.len(), has_destructor, is_interface, has_virtual_destructor, destructor_is_defaulted, all_methods_pure_virtual, has_non_virtual_methods,
832 has_copy_constructor, copy_constructor_deleted, has_copy_assignment, copy_assignment_deleted);
833
834 Class {
835 name,
836 template_parameters,
837 is_template,
838 members,
839 methods,
840 base_classes,
841 location,
842 has_destructor, // RAII Phase 2
843 // Inheritance safety fields
844 is_interface,
845 has_virtual_destructor,
846 destructor_is_defaulted,
847 all_methods_pure_virtual,
848 has_non_virtual_methods,
849 safety_annotation,
850 // Copy semantics
851 has_copy_constructor,
852 has_copy_assignment,
853 copy_constructor_deleted,
854 copy_assignment_deleted,
855 }
856}
857
858/// Detect the qualifier of a method (const, non-const, or rvalue-ref)
859fn detect_method_qualifier(entity: &Entity) -> MethodQualifier {
860 // Check if this is a const method
861 let is_const = entity.is_const_method();
862
863 // Check for && qualifier (rvalue reference qualifier)
864 // LibClang doesn't expose this directly, so we check the function type
865 let has_rvalue_ref_qualifier = if let Some(func_type) = entity.get_type() {
866 // Check the display name for && qualifier
867 let type_str = type_to_string(&func_type);
868 debug_println!("DEBUG METHOD: type_str = {}", type_str);
869
870 // The type string may contain "&&" for rvalue ref qualifier
871 // Example: "void () &&" or "void (int) &&"
872 type_str.contains(" &&") || type_str.ends_with("&&")
873 } else {
874 false
875 };
876
877 debug_println!("DEBUG METHOD: is_const={}, has_rvalue_ref={}", is_const, has_rvalue_ref_qualifier);
878
879 // Determine the qualifier
880 if has_rvalue_ref_qualifier {
881 MethodQualifier::RvalueRef
882 } else if is_const {
883 MethodQualifier::Const
884 } else {
885 MethodQualifier::NonConst
886 }
887}
888
889pub fn extract_variable(entity: &Entity) -> Variable {
890 let name = entity.get_name().unwrap_or_else(|| "anonymous".to_string());
891 let location = extract_location(entity);
892
893 let type_info = entity.get_type().unwrap();
894 let type_name = type_to_string(&type_info);
895
896 let is_reference = matches!(type_info.get_kind(), TypeKind::LValueReference | TypeKind::RValueReference);
897 let is_pointer = matches!(type_info.get_kind(), TypeKind::Pointer);
898
899 // For references, check if the pointee type is const
900 let is_const = if is_reference {
901 if let Some(pointee) = type_info.get_pointee_type() {
902 pointee.is_const_qualified()
903 } else {
904 type_info.is_const_qualified()
905 }
906 } else {
907 type_info.is_const_qualified()
908 };
909
910 let is_unique_ptr = type_name.contains("unique_ptr");
911 let is_shared_ptr = type_name.contains("shared_ptr");
912
913 // Check if this is a static variable
914 // In clang, static variables have StorageClass::Static
915 let is_static = entity.get_storage_class() == Some(clang::StorageClass::Static);
916
917 // Check if this is a mutable field (C++ mutable keyword)
918 // We need to read the source code to check for the 'mutable' keyword
919 // because libclang doesn't expose mutable as a storage class
920 let is_mutable = check_for_mutable_keyword(entity);
921 debug_println!("DEBUG MUTABLE: Field '{}' is_mutable = {}", name, is_mutable);
922
923 Variable {
924 name,
925 type_name,
926 is_reference,
927 is_pointer,
928 is_const,
929 is_unique_ptr,
930 is_shared_ptr,
931 is_static,
932 is_mutable,
933 location,
934 is_pack: false, // Will be set properly for function parameters
935 pack_element_type: None, // Will be set properly for function parameters
936 }
937}
938
939fn extract_function_body(entity: &Entity) -> Vec<Statement> {
940 let mut statements = Vec::new();
941
942 for child in entity.get_children() {
943 if child.get_kind() == EntityKind::CompoundStmt {
944 statements.extend(extract_compound_statement(&child));
945 }
946 }
947
948 statements
949}
950
951/// Helper function to extract function name from a CallExpr
952/// Returns None if the function name cannot be determined
953fn extract_function_name(call_expr: &Entity) -> Option<String> {
954 debug_println!("DEBUG: Extracting function name from CallExpr");
955
956 // Try to get name from the first child (usually the callee)
957 for child in call_expr.get_children() {
958 // Check DeclRefExpr or UnexposedExpr for function name
959 if matches!(child.get_kind(), EntityKind::DeclRefExpr | EntityKind::UnexposedExpr) {
960 if let Some(ref_entity) = child.get_reference() {
961 if let Some(name) = ref_entity.get_name() {
962 debug_println!("DEBUG: Found function name '{}' from reference", name);
963 return Some(name);
964 }
965 }
966 if let Some(name) = child.get_name() {
967 debug_println!("DEBUG: Found function name '{}' from child name", name);
968 return Some(name);
969 }
970 }
971 }
972
973 None
974}
975
976fn extract_compound_statement(entity: &Entity) -> Vec<Statement> {
977 let mut statements = Vec::new();
978
979 for child in entity.get_children() {
980 debug_println!("DEBUG STMT: Compound child kind: {:?}", child.get_kind());
981 match child.get_kind() {
982 EntityKind::DeclStmt => {
983 for decl_child in child.get_children() {
984 if decl_child.get_kind() == EntityKind::VarDecl {
985 let var = extract_variable(&decl_child);
986
987 // Always add the variable declaration first
988 statements.push(Statement::VariableDecl(var.clone()));
989
990 // Check if this variable has an initializer
991 for init_child in decl_child.get_children() {
992 if let Some(expr) = extract_expression(&init_child) {
993
994 // Check if this is a reference binding
995 if var.is_reference {
996 statements.push(Statement::ReferenceBinding {
997 name: var.name.clone(),
998 target: expr,
999 is_mutable: !var.is_const,
1000 location: extract_location(&decl_child),
1001 });
1002 } else {
1003 // Regular assignment/initialization
1004 statements.push(Statement::Assignment {
1005 lhs: Expression::Variable(var.name.clone()),
1006 rhs: expr,
1007 location: extract_location(&decl_child),
1008 });
1009 }
1010 break;
1011 }
1012 }
1013 }
1014 }
1015 }
1016 EntityKind::BinaryOperator => {
1017 // Handle assignments
1018 let children: Vec<Entity> = child.get_children().into_iter().collect();
1019 debug_println!("DEBUG STMT: BinaryOperator has {} children", children.len());
1020 if children.len() == 2 {
1021 debug_println!("DEBUG STMT: BinaryOperator child[0] kind: {:?}", children[0].get_kind());
1022 debug_println!("DEBUG STMT: BinaryOperator child[1] kind: {:?}", children[1].get_kind());
1023 let lhs_expr = extract_expression(&children[0]);
1024 let rhs_expr = extract_expression(&children[1]);
1025 debug_println!("DEBUG STMT: BinaryOperator LHS: {:?}", lhs_expr);
1026 debug_println!("DEBUG STMT: BinaryOperator RHS: {:?}", rhs_expr);
1027 if let (Some(lhs), Some(rhs)) = (lhs_expr, rhs_expr) {
1028 debug_println!("DEBUG STMT: Creating Assignment statement");
1029 statements.push(Statement::Assignment {
1030 lhs, // Now supports dereference: *ptr = value
1031 rhs,
1032 location: extract_location(&child),
1033 });
1034 } else {
1035 debug_println!("DEBUG STMT: Failed to extract expressions from BinaryOperator");
1036 }
1037 }
1038 }
1039 EntityKind::CallExpr => {
1040 let children: Vec<Entity> = child.get_children().into_iter().collect();
1041 let mut name = "unknown".to_string();
1042 let mut args = Vec::new();
1043
1044 // Check if this might be a variable declaration disguised as a CallExpr
1045 // This happens with constructs like "struct timeval now;" or "ClassName obj;"
1046 let mut is_likely_var_decl = false;
1047
1048 // Debug: Log all CallExprs
1049 debug_println!("DEBUG AST: Found CallExpr with {} children", children.len());
1050
1051 // First check if the CallExpr itself has a reference
1052 if let Some(ref_entity) = child.get_reference() {
1053 debug_println!("DEBUG AST: CallExpr references entity kind: {:?}, name: {:?}",
1054 ref_entity.get_kind(), ref_entity.get_name());
1055
1056 // Check if it references a type (struct/class/typedef)
1057 if ref_entity.get_kind() == EntityKind::StructDecl ||
1058 ref_entity.get_kind() == EntityKind::ClassDecl ||
1059 ref_entity.get_kind() == EntityKind::TypedefDecl ||
1060 ref_entity.get_kind() == EntityKind::TypeAliasDecl {
1061 // This is likely a variable declaration, not a function call
1062 debug_println!("DEBUG AST: CallExpr appears to be a variable declaration of type {:?}",
1063 ref_entity.get_name());
1064 is_likely_var_decl = true;
1065 }
1066
1067 if let Some(_n) = ref_entity.get_name() {
1068 // Build qualified name for ALL functions (methods AND free functions)
1069 // This ensures:
1070 // 1. Methods get qualified names like "MyClass::method"
1071 // 2. Namespaced functions get qualified names like "mylib::dangerous_op"
1072 // This prevents false matches between same-named functions in different namespaces
1073 name = get_qualified_name(&ref_entity);
1074 }
1075 }
1076
1077 // If this looks like a variable declaration, skip it
1078 if is_likely_var_decl && children.is_empty() {
1079 debug_println!("DEBUG AST: Skipping variable declaration disguised as CallExpr: {}", name);
1080 continue;
1081 }
1082
1083 // Try to extract the function name from children
1084 // CRITICAL: EXCLUDE entities that reference variables/parameters
1085 // (which caused the "rhs"/"schema"/"vv" bugs where args were mistaken for function names)
1086 for (idx, c) in children.iter().enumerate() {
1087 debug_println!("DEBUG AST: CallExpr child[{}] kind: {:?}, name: {:?}, display_name: {:?}, reference: {:?}",
1088 idx, c.get_kind(), c.get_name(), c.get_display_name(),
1089 c.get_reference().map(|r| (r.get_kind(), r.get_name())));
1090
1091 if c.get_kind() == EntityKind::UnexposedExpr || c.get_kind() == EntityKind::DeclRefExpr {
1092 // Check if this entity references a variable/parameter - if so, skip it
1093 if let Some(ref_entity) = c.get_reference() {
1094 let ref_kind = ref_entity.get_kind();
1095 // EXCLUDE variables and parameters - these are arguments, not function names
1096 if ref_kind == EntityKind::VarDecl || ref_kind == EntityKind::ParmDecl {
1097 debug_println!("DEBUG AST: Skipping variable/parameter reference: {:?}", ref_kind);
1098 continue;
1099 }
1100 // This is a function reference - use qualified name
1101 if name == "unknown" {
1102 let qualified = get_qualified_name(&ref_entity);
1103 debug_println!("DEBUG AST: Got qualified name from child reference: {}", qualified);
1104 name = qualified;
1105 }
1106 } else {
1107 // No reference (template-dependent) - use unqualified name as fallback
1108 if let Some(n) = c.get_name() {
1109 if name == "unknown" {
1110 debug_println!("DEBUG AST: Got name from child (template-dependent): {}", n);
1111 name = n;
1112 }
1113 }
1114 }
1115 }
1116 }
1117
1118 // Check if this is a type name being used as a constructor/declaration
1119 // Common pattern: TypeName varname; is parsed as CallExpr
1120 if children.len() == 1 && name != "unknown" {
1121 // Check if the name matches known type patterns
1122 if name.ends_with("val") || name.ends_with("spec") ||
1123 name.starts_with("struct") || name.starts_with("class") {
1124 debug_println!("DEBUG AST: Likely variable declaration based on name pattern: {}", name);
1125 is_likely_var_decl = true;
1126 }
1127 }
1128
1129 // If this looks like a variable declaration, skip it
1130 if is_likely_var_decl {
1131 debug_println!("DEBUG AST: Skipping variable declaration disguised as CallExpr: {}", name);
1132 continue;
1133 }
1134
1135 // Two-pass approach: identify name-providing child, then extract args
1136 let mut name_providing_child_idx: Option<usize> = None;
1137
1138 // Pass 1: If name still unknown, find it; otherwise identify which child has it
1139 if name == "unknown" {
1140 for (i, c) in children.iter().enumerate() {
1141 debug_println!("DEBUG AST: Child {}: kind={:?}, name={:?}", i, c.get_kind(), c.get_name());
1142 match c.get_kind() {
1143 EntityKind::MemberRefExpr => {
1144 debug_println!("DEBUG AST: Found MemberRefExpr!");
1145 if let Some(ref_entity) = c.get_reference() {
1146 debug_println!("DEBUG AST: MemberRefExpr has reference: kind={:?}, name={:?}",
1147 ref_entity.get_kind(), ref_entity.get_name());
1148 if let Some(_n) = ref_entity.get_name() {
1149 // Use qualified name for ALL function calls
1150 name = get_qualified_name(&ref_entity);
1151 name_providing_child_idx = Some(i);
1152 break;
1153 }
1154 } else {
1155 debug_println!("DEBUG AST: MemberRefExpr has NO reference!");
1156 }
1157 }
1158 EntityKind::DeclRefExpr | EntityKind::UnexposedExpr => {
1159 // CRITICAL FIX: EXCLUDE variables/parameters
1160 // (which caused the "rhs"/"schema"/"vv" bugs)
1161 if let Some(ref_entity) = c.get_reference() {
1162 let ref_kind = ref_entity.get_kind();
1163 if ref_kind == EntityKind::VarDecl || ref_kind == EntityKind::ParmDecl {
1164 continue; // Skip variables/parameters
1165 }
1166 // Use qualified name for function calls
1167 let qualified = get_qualified_name(&ref_entity);
1168 name = qualified;
1169 name_providing_child_idx = Some(i);
1170 break;
1171 } else if let Some(n) = c.get_name() {
1172 // No reference (template-dependent) - use unqualified name
1173 name = n;
1174 name_providing_child_idx = Some(i);
1175 break;
1176 }
1177 }
1178 _ => {}
1179 }
1180 }
1181 } else {
1182 // Name already known, find which child provides it
1183 debug_println!("DEBUG AST: Name already known: '{}', searching {} children", name, children.len());
1184 for (i, c) in children.iter().enumerate() {
1185 debug_println!("DEBUG AST: Child {}: kind={:?}, name={:?}", i, c.get_kind(), c.get_name());
1186 match c.get_kind() {
1187 EntityKind::MemberRefExpr => {
1188 // For .method() calls, MemberRefExpr contains the method name
1189 debug_println!("DEBUG AST: Exploring MemberRefExpr for receiver object:");
1190 debug_println!(" - name: {:?}", c.get_name());
1191 debug_println!(" - display_name: {:?}", c.get_display_name());
1192 debug_println!(" - num children: {}", c.get_children().len());
1193
1194 // Check if MemberRefExpr has children
1195 for (child_idx, member_child) in c.get_children().iter().enumerate() {
1196 debug_println!(" - MemberRefExpr child {}: kind={:?}, name={:?}",
1197 child_idx, member_child.get_kind(), member_child.get_name());
1198 }
1199
1200 // Check semantic parent
1201 if let Some(semantic_parent) = c.get_semantic_parent() {
1202 debug_println!(" - semantic_parent: kind={:?}, name={:?}",
1203 semantic_parent.get_kind(), semantic_parent.get_name());
1204 }
1205
1206 // Check lexical parent
1207 if let Some(lexical_parent) = c.get_lexical_parent() {
1208 debug_println!(" - lexical_parent: kind={:?}, name={:?}",
1209 lexical_parent.get_kind(), lexical_parent.get_name());
1210 }
1211
1212 if let Some(child_name) = c.get_name() {
1213 debug_println!("DEBUG AST: Checking if '{}' matches name '{}'", child_name, name);
1214 if name.ends_with(&child_name) || name == child_name {
1215 debug_println!("DEBUG AST: Match found! Setting name_providing_child_idx = {}", i);
1216 name_providing_child_idx = Some(i);
1217 break;
1218 }
1219 }
1220 }
1221 EntityKind::DeclRefExpr | EntityKind::UnexposedExpr => {
1222 if let Some(child_name) = c.get_name() {
1223 debug_println!("DEBUG AST: Checking if '{}' matches name '{}'", child_name, name);
1224 if name.ends_with(&child_name) || name == child_name {
1225 debug_println!("DEBUG AST: Match found! Setting name_providing_child_idx = {}", i);
1226 name_providing_child_idx = Some(i);
1227 break;
1228 }
1229 }
1230 }
1231 _ => {}
1232 }
1233 }
1234 debug_println!("DEBUG AST: name_providing_child_idx = {:?}", name_providing_child_idx);
1235 }
1236
1237 // Pass 2: Extract arguments, skipping name-providing child
1238 for (i, c) in children.into_iter().enumerate() {
1239 if Some(i) == name_providing_child_idx {
1240 // For MemberRefExpr, extract the receiver from its children
1241 if c.get_kind() == EntityKind::MemberRefExpr {
1242 debug_println!("DEBUG AST: Extracting receiver from MemberRefExpr");
1243 let member_children: Vec<Entity> = c.get_children().into_iter().collect();
1244 if !member_children.is_empty() {
1245 // Has children - extract receiver from first child
1246 if let Some(receiver_expr) = extract_expression(&member_children[0]) {
1247 // Check if receiver type is a pointer (means -> was used)
1248 // ptr->method() is semantically (*ptr).method(), so wrap in Dereference
1249 // EXCEPT for safe smart pointers whose operator-> is safe
1250 let is_arrow = member_children[0].get_type()
1251 .map(|t| matches!(t.get_kind(), TypeKind::Pointer))
1252 .unwrap_or(false);
1253
1254 // Check if this is an overloaded operator-> (smart pointer)
1255 // Raw pointers use built-in ->, smart pointers call operator->
1256 let has_overloaded_arrow = if is_arrow {
1257 has_overloaded_arrow_operator(&member_children[0])
1258 } else {
1259 false
1260 };
1261
1262 if is_arrow && !has_overloaded_arrow {
1263 debug_println!("DEBUG AST: Raw pointer arrow method call: (*{:?})", receiver_expr);
1264 args.push(Expression::Dereference(Box::new(receiver_expr)));
1265 } else {
1266 debug_println!("DEBUG AST: Dot/smart pointer method call: {:?}", receiver_expr);
1267 args.push(receiver_expr);
1268 }
1269 }
1270 } else {
1271 // No children - the receiver is a simple variable/parameter
1272 // Extract it from the MemberRefExpr's name or display name
1273 if let Some(member_name) = c.get_name() {
1274 // The name might be like "m" for m.content_size()
1275 // But we need to be careful - the name might be the method name
1276 // Check if this is different from our extracted function name
1277 if member_name != name && !name.ends_with(&member_name) {
1278 debug_println!("DEBUG AST: Found receiver from MemberRefExpr name: {}", member_name);
1279 args.push(Expression::Variable(member_name));
1280 }
1281 } else if let Some(display) = c.get_display_name() {
1282 // Try display name as fallback
1283 if display != name && !name.ends_with(&display) && !display.is_empty() {
1284 debug_println!("DEBUG AST: Found receiver from MemberRefExpr display: {}", display);
1285 args.push(Expression::Variable(display));
1286 }
1287 }
1288 }
1289 }
1290 continue; // Skip the MemberRefExpr itself
1291 }
1292
1293 // Phase 2: Check if this argument is a PackExpansionExpr
1294 if c.get_kind() == EntityKind::PackExpansionExpr {
1295 debug_println!("DEBUG STMT: Found PackExpansionExpr as function argument");
1296
1297 let mut pack_name = String::new();
1298 let mut operation = "use".to_string();
1299
1300 // Look for the pack name and operation type
1301 for pack_child in c.get_children() {
1302 // Check if it's a CallExpr (could be std::forward or std::move)
1303 if pack_child.get_kind() == EntityKind::CallExpr {
1304 if let Some(callee_name) = extract_function_name(&pack_child) {
1305 debug_println!("DEBUG STMT: PackExpansion contains call to: {}", callee_name);
1306 if is_forward_function(&callee_name) {
1307 operation = "forward".to_string();
1308 } else if is_move_function(&callee_name) {
1309 operation = "move".to_string();
1310 }
1311 }
1312
1313 // Find pack name inside the call
1314 // Skip the first child (function name) and look for parameter references
1315 debug_println!("DEBUG STMT: Searching for pack name in CallExpr children (count: {})",
1316 pack_child.get_children().len());
1317 for call_child in pack_child.get_children() {
1318 debug_println!("DEBUG STMT: CallExpr child kind: {:?}", call_child.get_kind());
1319 if call_child.get_kind() == EntityKind::DeclRefExpr {
1320 if let Some(ref_entity) = call_child.get_reference() {
1321 debug_println!("DEBUG STMT: DeclRefExpr references entity kind: {:?}", ref_entity.get_kind());
1322 // Only accept if it references a parameter (ParmDecl), not a function
1323 if ref_entity.get_kind() == EntityKind::ParmDecl {
1324 if let Some(name) = ref_entity.get_name() {
1325 debug_println!("DEBUG STMT: Found pack name '{}' inside CallExpr", name);
1326 pack_name = name;
1327 break;
1328 }
1329 }
1330 }
1331 }
1332 }
1333 }
1334 // Direct DeclRefExpr (pack used without forward/move)
1335 else if pack_child.get_kind() == EntityKind::DeclRefExpr {
1336 debug_println!("DEBUG STMT: Found direct DeclRefExpr in PackExpansionExpr");
1337 if let Some(ref_entity) = pack_child.get_reference() {
1338 if let Some(name) = ref_entity.get_name() {
1339 debug_println!("DEBUG STMT: Pack name from direct DeclRefExpr: '{}'", name);
1340 pack_name = name;
1341 }
1342 }
1343 }
1344 }
1345
1346 if !pack_name.is_empty() {
1347 debug_println!("DEBUG STMT: Pack expansion detected: pack='{}', operation='{}'",
1348 pack_name, operation);
1349 statements.push(Statement::PackExpansion {
1350 pack_name,
1351 operation,
1352 location: extract_location(&c),
1353 });
1354 }
1355 }
1356 // Regular argument extraction
1357 else if let Some(expr) = extract_expression(&c) {
1358 args.push(expr);
1359 }
1360 }
1361
1362 debug_println!("DEBUG STMT: Creating FunctionCall statement: name='{}', args={:?}", name, args);
1363 statements.push(Statement::FunctionCall {
1364 name,
1365 args,
1366 location: extract_location(&child),
1367 });
1368 }
1369 EntityKind::ReturnStmt => {
1370 // Extract the return value expression
1371 let return_expr = child.get_children()
1372 .into_iter()
1373 .find_map(|c| extract_expression(&c));
1374 statements.push(Statement::Return(return_expr));
1375 }
1376 EntityKind::CompoundStmt => {
1377 // Regular nested block scope - add scope markers
1378 statements.push(Statement::EnterScope);
1379
1380 // Check if this block is preceded by @unsafe comment
1381 let is_unsafe = check_for_unsafe_annotation(&child);
1382 if is_unsafe {
1383 debug_println!("DEBUG UNSAFE: Found @unsafe block");
1384 statements.push(Statement::EnterUnsafe);
1385 }
1386
1387 statements.extend(extract_compound_statement(&child));
1388
1389 if is_unsafe {
1390 statements.push(Statement::ExitUnsafe);
1391 }
1392
1393 statements.push(Statement::ExitScope);
1394 }
1395 EntityKind::ForStmt | EntityKind::WhileStmt | EntityKind::DoStmt => {
1396 // Loop detected - add loop markers
1397 statements.push(Statement::EnterLoop);
1398 // Extract loop body (usually a compound statement)
1399 for loop_child in child.get_children() {
1400 if loop_child.get_kind() == EntityKind::CompoundStmt {
1401 statements.extend(extract_compound_statement(&loop_child));
1402 }
1403 }
1404 statements.push(Statement::ExitLoop);
1405 }
1406 EntityKind::IfStmt => {
1407 // Extract if statement
1408 let children: Vec<Entity> = child.get_children().into_iter().collect();
1409 let mut condition = Expression::Literal("true".to_string());
1410 let mut then_branch = Vec::new();
1411 let mut else_branch = None;
1412
1413 // Parse the if statement structure
1414 let mut i = 0;
1415 while i < children.len() {
1416 let child_kind = children[i].get_kind();
1417
1418 if child_kind == EntityKind::UnexposedExpr || child_kind == EntityKind::BinaryOperator {
1419 // This is likely the condition
1420 if let Some(expr) = extract_expression(&children[i]) {
1421 condition = expr;
1422 }
1423 } else if child_kind == EntityKind::CompoundStmt {
1424 // This is a branch
1425 if then_branch.is_empty() {
1426 then_branch = extract_compound_statement(&children[i]);
1427 } else {
1428 else_branch = Some(extract_compound_statement(&children[i]));
1429 }
1430 }
1431 i += 1;
1432 }
1433
1434 statements.push(Statement::If {
1435 condition,
1436 then_branch,
1437 else_branch,
1438 location: extract_location(&child),
1439 });
1440 }
1441 EntityKind::UnaryOperator => {
1442 // Handle standalone dereference operations
1443 if let Some(expr) = extract_expression(&child) {
1444 // Only add as statement if it's a dereference or address-of
1445 match &expr {
1446 Expression::Dereference(_) | Expression::AddressOf(_) => {
1447 statements.push(Statement::ExpressionStatement {
1448 expr,
1449 location: extract_location(&child),
1450 });
1451 }
1452 _ => {} // Ignore other unary operators for now
1453 }
1454 }
1455 }
1456 EntityKind::UnexposedExpr => {
1457 // UnexposedExpr can contain function calls or other expressions
1458 // Also check if this contains a PackExpansionExpr (fold expression)
1459
1460 // First check for pack expansions in children
1461 let has_pack_expansion = child.get_children().iter()
1462 .any(|c| c.get_kind() == EntityKind::PackExpansionExpr);
1463
1464 if has_pack_expansion {
1465 debug_println!("DEBUG STMT: UnexposedExpr contains PackExpansionExpr (fold expression)");
1466 // Process PackExpansionExpr children
1467 for ue_child in child.get_children() {
1468 if ue_child.get_kind() == EntityKind::PackExpansionExpr {
1469 // Recursively process the pack expansion
1470 let pack_stmts = extract_compound_statement(&ue_child);
1471 statements.extend(pack_stmts);
1472 }
1473 }
1474 } else {
1475 // Regular UnexposedExpr handling
1476 if let Some(expr) = extract_expression(&child) {
1477 match expr {
1478 Expression::FunctionCall { name, args } => {
1479 statements.push(Statement::FunctionCall {
1480 name,
1481 args,
1482 location: extract_location(&child),
1483 });
1484 }
1485 _ => {
1486 // Other expression types - add as expression statement
1487 statements.push(Statement::ExpressionStatement {
1488 expr,
1489 location: extract_location(&child),
1490 });
1491 }
1492 }
1493 }
1494 }
1495 }
1496 _ => {}
1497 }
1498 }
1499
1500 statements
1501}
1502
1503fn extract_expression(entity: &Entity) -> Option<Expression> {
1504 match entity.get_kind() {
1505 EntityKind::DeclRefExpr => {
1506 // Check what kind of entity this DeclRefExpr references
1507 if let Some(ref_entity) = entity.get_reference() {
1508 if ref_entity.get_kind() == EntityKind::FieldDecl {
1509 // This is a member field access - convert to this.field
1510 if let Some(field_name) = entity.get_name() {
1511 debug_println!("DEBUG: DeclRefExpr to FieldDecl '{}' - converting to this.{}", field_name, field_name);
1512 return Some(Expression::MemberAccess {
1513 object: Box::new(Expression::Variable("this".to_string())),
1514 field: field_name,
1515 });
1516 }
1517 }
1518 // Check if this references a method (member function)
1519 if ref_entity.get_kind() == EntityKind::Method || ref_entity.get_kind() == EntityKind::FunctionDecl {
1520 // This is a function/method reference - include class qualifier if available
1521 if let Some(func_name) = entity.get_name() {
1522 // Try to get the class name from semantic parent
1523 if let Some(parent) = ref_entity.get_semantic_parent() {
1524 if matches!(parent.get_kind(), EntityKind::ClassDecl | EntityKind::StructDecl | EntityKind::ClassTemplate) {
1525 if let Some(class_name) = parent.get_name() {
1526 // Return qualified name: ClassName::method
1527 return Some(Expression::Variable(format!("{}::{}", class_name, func_name)));
1528 }
1529 }
1530 }
1531 // No class parent, return just the function name
1532 return Some(Expression::Variable(func_name));
1533 }
1534 }
1535 }
1536 // Regular variable reference
1537 entity.get_name().map(Expression::Variable)
1538 }
1539 EntityKind::ThisExpr => {
1540 // The 'this' pointer in C++ methods
1541 Some(Expression::Variable("this".to_string()))
1542 }
1543 EntityKind::CallExpr => {
1544 // Extract function call as expression
1545 let children: Vec<Entity> = entity.get_children().into_iter().collect();
1546 let mut name = "unknown".to_string();
1547 let mut args = Vec::new();
1548
1549 // Check if this might be a variable declaration disguised as a CallExpr
1550 // This happens with constructs like "struct timeval now;" or "ClassName obj;"
1551 // The pattern is: CallExpr with 0 children that references a type name
1552
1553 // First check if the CallExpr itself has a reference
1554 let mut method_name_from_callexpr = false;
1555 if let Some(ref_entity) = entity.get_reference() {
1556 debug_println!("DEBUG AST: CallExpr itself references: {:?}", ref_entity.get_name());
1557
1558 // Check if it references a type (struct/class/typedef)
1559 // BUT: A CallExpr with 0 children is likely a constructor/declaration
1560 if children.is_empty() {
1561 if let Some(n) = ref_entity.get_name() {
1562 name = n;
1563 }
1564 debug_println!("DEBUG AST: CallExpr with 0 children referencing '{}' - likely a variable declaration", name);
1565 return None; // Not a function call, it's a variable declaration
1566 }
1567
1568 // Build qualified name for all functions to avoid namespace collisions
1569 // This ensures ns1::func and ns2::func are distinguished
1570 if ref_entity.get_kind() == EntityKind::Method
1571 || ref_entity.get_kind() == EntityKind::Constructor
1572 || ref_entity.get_kind() == EntityKind::FunctionDecl
1573 || ref_entity.get_kind() == EntityKind::FunctionTemplate {
1574 name = get_qualified_name(&ref_entity);
1575 if ref_entity.get_kind() == EntityKind::Method || ref_entity.get_kind() == EntityKind::Constructor {
1576 method_name_from_callexpr = true;
1577 }
1578 debug_println!("DEBUG AST: Function name extracted from CallExpr reference: {}", name);
1579 } else if let Some(n) = ref_entity.get_name() {
1580 name = n;
1581 }
1582 }
1583
1584 // Debug: print all child entity kinds
1585 for (idx, c) in children.iter().enumerate() {
1586 debug_println!("DEBUG AST: CallExpr child[{}] kind: {:?}, name: {:?}, display_name: {:?}, reference: {:?}",
1587 idx, c.get_kind(), c.get_name(), c.get_display_name(),
1588 c.get_reference().map(|r| (r.get_kind(), r.get_name())));
1589
1590 // For member function calls, check for MemberRefExpr first
1591 if c.get_kind() == EntityKind::MemberRefExpr {
1592 // MemberRefExpr can be either a method call OR a field access
1593 // We need to distinguish between them
1594 if let Some(ref_entity) = c.get_reference() {
1595 debug_println!("DEBUG AST: MemberRefExpr references: {:?}", ref_entity.get_name());
1596 if let Some(n) = ref_entity.get_name() {
1597 if name == "unknown" {
1598 // Build qualified name for member functions and constructors
1599 if ref_entity.get_kind() == EntityKind::Method || ref_entity.get_kind() == EntityKind::Constructor {
1600 name = get_qualified_name(&ref_entity);
1601 } else if ref_entity.get_kind() == EntityKind::FieldDecl {
1602 // This is a field access, not a function call
1603 // Don't use it as the function name - it will be extracted as MemberAccess
1604 debug_println!("DEBUG AST: MemberRefExpr is field access, not function name");
1605 } else {
1606 name = n;
1607 }
1608 }
1609 }
1610 }
1611
1612 // Debug: Check if MemberRefExpr has children (which might be the receiver object)
1613 let member_children = c.get_children();
1614 debug_println!("DEBUG AST: MemberRefExpr has {} children", member_children.len());
1615 for (i, mc) in member_children.iter().enumerate() {
1616 debug_println!(" DEBUG AST: MemberRefExpr child[{}]: kind={:?}, name={:?}",
1617 i, mc.get_kind(), mc.get_name());
1618 }
1619 } else if c.get_kind() == EntityKind::UnexposedExpr {
1620 // Try to get the referenced entity
1621 if let Some(ref_entity) = c.get_reference() {
1622 debug_println!("DEBUG AST: UnexposedExpr references: {:?}", ref_entity.get_name());
1623 if let Some(n) = ref_entity.get_name() {
1624 if name == "unknown" {
1625 name = n;
1626 }
1627 }
1628 }
1629 // Also try children of UnexposedExpr
1630 for ue_child in c.get_children() {
1631 debug_println!("DEBUG AST: UnexposedExpr child: kind={:?}, name={:?}, display_name={:?}",
1632 ue_child.get_kind(), ue_child.get_name(), ue_child.get_display_name());
1633
1634 // Try to extract member function name from MemberRefExpr
1635 if let Some(n) = ue_child.get_name() {
1636 if name == "unknown" {
1637 name = n;
1638 }
1639 } else if let Some(dn) = ue_child.get_display_name() {
1640 if name == "unknown" && !dn.is_empty() {
1641 name = dn;
1642 }
1643 }
1644
1645 // Also check if this child has a reference
1646 if let Some(ref_entity) = ue_child.get_reference() {
1647 debug_println!("DEBUG AST: Child references: {:?}", ref_entity.get_name());
1648 if let Some(n) = ref_entity.get_name() {
1649 if name == "unknown" {
1650 name = n;
1651 }
1652 }
1653 }
1654 }
1655 }
1656 }
1657
1658 // Two-pass approach:
1659 // Pass 1: Identify which child provides the function name
1660 // Pass 2: Extract arguments, handling MemberRefExpr specially if it's the name provider
1661
1662 let mut name_providing_child_idx: Option<usize> = None;
1663
1664 // CRITICAL FIX: If the method name was extracted from CallExpr's own reference
1665 // (common for template class methods), we need to find the MemberRefExpr child
1666 // and mark it as the name provider so the receiver can be extracted
1667 if method_name_from_callexpr {
1668 for (i, c) in children.iter().enumerate() {
1669 if c.get_kind() == EntityKind::MemberRefExpr {
1670 if let Some(ref_entity) = c.get_reference() {
1671 if ref_entity.get_kind() == EntityKind::Method {
1672 debug_println!("DEBUG AST: Found MemberRefExpr at index {} for method call", i);
1673 name_providing_child_idx = Some(i);
1674 break;
1675 }
1676 }
1677 }
1678 }
1679 }
1680
1681 // Pass 1: Find the name-providing child
1682 if name_providing_child_idx.is_none() {
1683 if name == "unknown" {
1684 for (i, c) in children.iter().enumerate() {
1685 match c.get_kind() {
1686 EntityKind::DeclRefExpr | EntityKind::UnexposedExpr => {
1687 // CRITICAL FIX: EXCLUDE variables/parameters
1688 // (which caused the "rhs"/"schema"/"vv" bugs)
1689 if let Some(ref_entity) = c.get_reference() {
1690 let ref_kind = ref_entity.get_kind();
1691 if ref_kind == EntityKind::VarDecl || ref_kind == EntityKind::ParmDecl {
1692 continue; // Skip variables/parameters
1693 }
1694 // Bug #8 fix: Use qualified name for free functions too
1695 // This ensures namespace::function is captured correctly
1696 let n = get_qualified_name(&ref_entity);
1697 debug_println!("DEBUG AST: Got function name '{}' from reference (kind: {:?})", n, ref_entity.get_kind());
1698 name = n;
1699 name_providing_child_idx = Some(i);
1700 break;
1701 }
1702 // No reference - might be template-dependent, use name directly
1703 if let Some(n) = c.get_name() {
1704 debug_println!("DEBUG AST: Got function name '{}' from name field", n);
1705 name = n;
1706 name_providing_child_idx = Some(i);
1707 break;
1708 }
1709 }
1710 _ => {}
1711 }
1712 }
1713 } else {
1714 // Name was already extracted, identify which child represents it
1715 for (i, c) in children.iter().enumerate() {
1716 match c.get_kind() {
1717 EntityKind::DeclRefExpr | EntityKind::UnexposedExpr => {
1718 if let Some(child_name) = c.get_name() {
1719 if name.ends_with(&child_name) || name == child_name {
1720 name_providing_child_idx = Some(i);
1721 break;
1722 }
1723 }
1724 }
1725 _ => {}
1726 }
1727 }
1728 }
1729 }
1730
1731 // Pass 2: Extract arguments, handling MemberRefExpr specially if it's the name provider
1732 for (i, c) in children.into_iter().enumerate() {
1733 // If this child provided the function name
1734 if Some(i) == name_providing_child_idx {
1735 // For method calls (MemberRefExpr is name provider), extract the receiver
1736 if c.get_kind() == EntityKind::MemberRefExpr {
1737 debug_println!("DEBUG AST: MemberRefExpr is name provider - extracting receiver");
1738 // Extract receiver from MemberRefExpr's children
1739 let member_children = c.get_children();
1740 if !member_children.is_empty() {
1741 // First child of MemberRefExpr is the receiver object
1742 if let Some(recv_expr) = extract_expression(&member_children[0]) {
1743 // Check if receiver type is a pointer (means -> was used)
1744 // ptr->method() is semantically (*ptr).method(), so wrap in Dereference
1745 // EXCEPT for safe smart pointers whose operator-> is safe
1746 let is_arrow = member_children[0].get_type()
1747 .map(|t| matches!(t.get_kind(), TypeKind::Pointer))
1748 .unwrap_or(false);
1749
1750 // Check if this is an overloaded operator-> (smart pointer)
1751 // Raw pointers use built-in ->, smart pointers call operator->
1752 let has_overloaded_arrow = if is_arrow {
1753 has_overloaded_arrow_operator(&member_children[0])
1754 } else {
1755 false
1756 };
1757
1758 if is_arrow && !has_overloaded_arrow {
1759 debug_println!("DEBUG AST: Raw pointer arrow method call: (*{:?})", recv_expr);
1760 args.push(Expression::Dereference(Box::new(recv_expr)));
1761 } else {
1762 debug_println!("DEBUG AST: Dot/smart pointer method call: {:?}", recv_expr);
1763 args.push(recv_expr);
1764 }
1765 }
1766 } else {
1767 // No children - the receiver might be implicit or a simple variable
1768 // For a MemberRefExpr like m.content_size(), we need to extract "m"
1769 // Unfortunately libclang doesn't always give us this directly
1770 // We'll need to handle this case differently
1771 debug_println!("DEBUG AST: MemberRefExpr has no children, cannot extract receiver");
1772 }
1773 }
1774 // Skip the name-providing child itself
1775 continue;
1776 }
1777
1778 // Extract all other children as normal arguments
1779 if let Some(expr) = extract_expression(&c) {
1780 args.push(expr);
1781 }
1782 }
1783
1784 // Check if this is std::move
1785 debug_println!("DEBUG: Found function call: name='{}', args_count={}", name, args.len());
1786 for (i, arg) in args.iter().enumerate() {
1787 debug_println!(" DEBUG: arg[{}] = {:?}", i, arg);
1788 }
1789 if is_move_function(&name) {
1790 debug_println!("DEBUG: Detected move function!");
1791 // std::move/rusty::move takes one argument and we treat it as a Move expression
1792 if args.len() == 1 {
1793 let kind = get_move_kind(&name);
1794 debug_println!("DEBUG: Creating Move expression with kind {:?}", kind);
1795 return Some(Expression::Move {
1796 inner: Box::new(args.into_iter().next().unwrap()),
1797 kind,
1798 });
1799 }
1800 }
1801
1802 Some(Expression::FunctionCall { name, args })
1803 }
1804 EntityKind::UnexposedExpr => {
1805 // UnexposedExpr often wraps other expressions, so look at its children
1806 let children: Vec<Entity> = entity.get_children().into_iter().collect();
1807 debug_println!("DEBUG EXTRACT: UnexposedExpr with name={:?}, {} children",
1808 entity.get_name(), children.len());
1809
1810 // Check if this UnexposedExpr has a reference (might be a method call)
1811 if let Some(ref_entity) = entity.get_reference() {
1812 debug_println!(" DEBUG EXTRACT: UnexposedExpr has reference: kind={:?}, name={:?}",
1813 ref_entity.get_kind(), ref_entity.get_name());
1814 }
1815
1816 // If there are exactly 2 children, this might be a binary operation (e.g., assignment)
1817 if children.len() == 2 {
1818 debug_println!(" DEBUG EXTRACT: UnexposedExpr with 2 children - checking for binary op");
1819 if let (Some(left), Some(right)) =
1820 (extract_expression(&children[0]), extract_expression(&children[1])) {
1821 debug_println!(" DEBUG EXTRACT: Extracted both children, treating as assignment");
1822 // UnexposedExpr with 2 operands is typically an assignment in a const method
1823 // (C++ allows it syntactically even though it's a semantic error)
1824 return Some(Expression::BinaryOp {
1825 left: Box::new(left),
1826 op: "=".to_string(),
1827 right: Box::new(right),
1828 });
1829 }
1830 }
1831
1832 // Otherwise, try to extract single child expression
1833 for child in children {
1834 debug_println!(" DEBUG EXTRACT: Child kind={:?}, name={:?}",
1835 child.get_kind(), child.get_name());
1836
1837 // Check if child has a reference
1838 if let Some(child_ref) = child.get_reference() {
1839 debug_println!(" DEBUG EXTRACT: Child has reference: kind={:?}, name={:?}",
1840 child_ref.get_kind(), child_ref.get_name());
1841 }
1842
1843 if let Some(expr) = extract_expression(&child) {
1844 debug_println!(" DEBUG EXTRACT: Returning {:?}", expr);
1845 return Some(expr);
1846 }
1847 }
1848 debug_println!(" DEBUG EXTRACT: Returning None");
1849 None
1850 }
1851 EntityKind::BinaryOperator => {
1852 // Extract binary operation (e.g., i < 2, x == 0)
1853 let children: Vec<Entity> = entity.get_children().into_iter().collect();
1854 if children.len() == 2 {
1855 if let (Some(left), Some(right)) =
1856 (extract_expression(&children[0]), extract_expression(&children[1])) {
1857 // Try to get the operator from the entity's spelling
1858 let op = entity.get_name().unwrap_or_else(|| "==".to_string());
1859 return Some(Expression::BinaryOp {
1860 left: Box::new(left),
1861 op,
1862 right: Box::new(right),
1863 });
1864 }
1865 }
1866 None
1867 }
1868 EntityKind::IntegerLiteral => {
1869 // IntegerLiterals often have name=None, try display_name or tokens
1870 if let Some(name) = entity.get_name() {
1871 Some(Expression::Literal(name))
1872 } else if let Some(display) = entity.get_display_name() {
1873 Some(Expression::Literal(display))
1874 } else {
1875 // For integer literals, we can use a placeholder since we don't
1876 // need the actual value for ownership/borrow checking
1877 Some(Expression::Literal("0".to_string()))
1878 }
1879 }
1880 EntityKind::StringLiteral => {
1881 // String literals ("hello", L"wide", u8"utf8", etc.) have static lifetime
1882 // and are safe in @safe code - they cannot dangle.
1883 // Extract the string value from the entity
1884 debug_println!("DEBUG: Found StringLiteral entity");
1885 if let Some(name) = entity.get_name() {
1886 Some(Expression::StringLiteral(name))
1887 } else if let Some(display) = entity.get_display_name() {
1888 Some(Expression::StringLiteral(display))
1889 } else {
1890 // Even without the exact value, we know it's a string literal
1891 Some(Expression::StringLiteral("<string literal>".to_string()))
1892 }
1893 }
1894 EntityKind::ParenExpr => {
1895 // Parenthesized expression - just extract the inner expression
1896 // e.g., (*n) in (*n).value_ - we want to get the Dereference(n) inside
1897 let children: Vec<Entity> = entity.get_children().into_iter().collect();
1898 if !children.is_empty() {
1899 debug_println!("DEBUG: ParenExpr has child: kind={:?}", children[0].get_kind());
1900 return extract_expression(&children[0]);
1901 }
1902 None
1903 }
1904 // C++ cast expressions - extract the inner expression
1905 // static_cast<T*>(ptr), dynamic_cast<T*>(ptr), reinterpret_cast<T*>(ptr), const_cast<T*>(ptr)
1906 // These are transparent for borrow checking - we care about what's being cast
1907 EntityKind::StaticCastExpr
1908 | EntityKind::DynamicCastExpr
1909 | EntityKind::ReinterpretCastExpr
1910 | EntityKind::ConstCastExpr
1911 | EntityKind::CStyleCastExpr => {
1912 // C++ cast expressions are unsafe operations in @safe code
1913 // Wrap the inner expression in Cast to track this
1914 let children: Vec<Entity> = entity.get_children().into_iter().collect();
1915 // Find the expression being cast (not the type reference)
1916 for child in &children {
1917 if child.get_kind() != EntityKind::TypeRef {
1918 if let Some(inner_expr) = extract_expression(child) {
1919 return Some(Expression::Cast(Box::new(inner_expr)));
1920 }
1921 }
1922 }
1923 // Even if we can't extract the inner expression, the cast itself is unsafe
1924 None
1925 }
1926 EntityKind::UnaryOperator => {
1927 // Check if it's address-of (&) or dereference (*)
1928 // Other unary operators (!, ~, -, +) should be treated as simple expressions
1929 let children: Vec<Entity> = entity.get_children().into_iter().collect();
1930 if !children.is_empty() {
1931 if let Some(inner) = extract_expression(&children[0]) {
1932 // Try to determine the operator type
1933 // LibClang doesn't give us the operator directly, but we can check the types
1934 if let Some(result_type) = entity.get_type() {
1935 let type_str = type_to_string(&result_type);
1936
1937 if let Some(child_type) = children[0].get_type() {
1938 let child_type_str = type_to_string(&child_type);
1939
1940 // If child is pointer and result is not, it's dereference (*)
1941 if child_type_str.contains('*') && !type_str.contains('*') {
1942 return Some(Expression::Dereference(Box::new(inner)));
1943 }
1944 // If child is not pointer but result is, it's address-of (&)
1945 else if !child_type_str.contains('*') && type_str.contains('*') {
1946 return Some(Expression::AddressOf(Box::new(inner)));
1947 }
1948 // Otherwise, it's a non-pointer unary operator (!, ~, -, +)
1949 // These don't affect ownership/borrowing, so just return the inner expression
1950 // wrapped in a BinaryOp with the operator for completeness
1951 else {
1952 // For borrow checking purposes, these operators are transparent
1953 // Just return the inner expression since we don't need to track
1954 // the arithmetic/logical operation
1955 return Some(inner);
1956 }
1957 }
1958 }
1959 // If we couldn't get types, return inner expression (conservative: don't assume pointer op)
1960 return Some(inner);
1961 }
1962 }
1963 None
1964 }
1965 EntityKind::MemberRefExpr => {
1966 // NEW: Parse member access expressions (obj.field)
1967 // MemberRefExpr is used for both field access and method calls
1968 // For field access, we extract: object from first child, field name from reference
1969 debug_println!("DEBUG: Found MemberRefExpr");
1970
1971 // Get the field/member name from the entity's reference or name
1972 let field_name = if let Some(ref_entity) = entity.get_reference() {
1973 debug_println!("DEBUG: MemberRefExpr references kind={:?}, name={:?}",
1974 ref_entity.get_kind(), ref_entity.get_name());
1975 // Check if it's a field (not a method)
1976 if ref_entity.get_kind() == EntityKind::FieldDecl {
1977 ref_entity.get_name().unwrap_or_else(|| "unknown_field".to_string())
1978 } else {
1979 // It's a method call, not field access - return None to let CallExpr handle it
1980 debug_println!("DEBUG: MemberRefExpr is method, not field");
1981 return None;
1982 }
1983 } else if let Some(name) = entity.get_name() {
1984 debug_println!("DEBUG: MemberRefExpr has name={}", name);
1985 name
1986 } else {
1987 debug_println!("DEBUG: MemberRefExpr has no reference or name");
1988 "unknown_field".to_string()
1989 };
1990
1991 let children: Vec<Entity> = entity.get_children().into_iter().collect();
1992 if !children.is_empty() {
1993 // First child is the object being accessed (explicit object.field or ptr->field)
1994 if let Some(object_expr) = extract_expression(&children[0]) {
1995 // Check if object type is a pointer (means -> was used, not .)
1996 // ptr->field is semantically (*ptr).field, so wrap in Dereference
1997 let child_type = children[0].get_type();
1998
1999 let is_arrow = child_type
2000 .map(|t| matches!(t.get_kind(), TypeKind::Pointer))
2001 .unwrap_or(false);
2002
2003 // Check if this is an overloaded operator-> (smart pointer)
2004 // Raw pointers use built-in ->, smart pointers call operator->
2005 let has_overloaded_arrow = if is_arrow {
2006 has_overloaded_arrow_operator(&children[0])
2007 } else {
2008 false
2009 };
2010
2011 if is_arrow && !has_overloaded_arrow {
2012 debug_println!("DEBUG: MemberRefExpr raw pointer arrow: (*{:?}).{}", object_expr, field_name);
2013 return Some(Expression::MemberAccess {
2014 object: Box::new(Expression::Dereference(Box::new(object_expr))),
2015 field: field_name,
2016 });
2017 } else {
2018 debug_println!("DEBUG: MemberRefExpr {} access: {:?}.{}",
2019 if has_overloaded_arrow { "smart pointer" } else { "dot" },
2020 object_expr, field_name);
2021 return Some(Expression::MemberAccess {
2022 object: Box::new(object_expr),
2023 field: field_name,
2024 });
2025 }
2026 }
2027 } else {
2028 // No children means implicit 'this->field' access in a method
2029 // 'this' is guaranteed valid inside member functions, so NOT unsafe
2030 // (Unlike arbitrary raw pointers, 'this' cannot be null/invalid in well-formed code)
2031 debug_println!("DEBUG: MemberRefExpr implicit 'this' access: this.{}", field_name);
2032 return Some(Expression::MemberAccess {
2033 object: Box::new(Expression::Variable("this".to_string())),
2034 field: field_name,
2035 });
2036 }
2037 None
2038 }
2039 EntityKind::LambdaExpr => {
2040 // Extract lambda captures by analyzing the AST structure
2041 // In libclang, lambda captures appear as:
2042 // - Reference capture: VariableRef only (no DeclRefExpr for that variable)
2043 // - Copy capture: VariableRef + DeclRefExpr for the same variable
2044 // - Default capture ([&] or [=]): No VariableRef/DeclRefExpr, must check type fields
2045 // - The body is a CompoundStmt
2046 debug_println!("DEBUG LAMBDA PARSER: Found LambdaExpr!");
2047
2048 let mut captures = Vec::new();
2049
2050 // Collect all VariableRef entries (these are the captured variables)
2051 let mut var_refs: Vec<String> = Vec::new();
2052 // Collect all DeclRefExpr entries (these indicate copy captures)
2053 let mut decl_refs: std::collections::HashSet<String> = std::collections::HashSet::new();
2054 // Track if we found any explicit captures
2055 let mut has_explicit_captures = false;
2056 // Track if we found a move call (indicates move capture)
2057 let mut has_move_call = false;
2058
2059 for child in entity.get_children() {
2060 debug_println!("DEBUG LAMBDA child: kind={:?} name={:?}", child.get_kind(), child.get_name());
2061 match child.get_kind() {
2062 EntityKind::VariableRef => {
2063 has_explicit_captures = true;
2064 if let Some(var_name) = child.get_name() {
2065 var_refs.push(var_name);
2066 }
2067 }
2068 EntityKind::DeclRefExpr => {
2069 // DeclRefExpr indicates a copy capture (part of copy init expr)
2070 if let Some(var_name) = child.get_name() {
2071 decl_refs.insert(var_name);
2072 }
2073 }
2074 EntityKind::CallExpr => {
2075 // CallExpr with 'move' indicates a move capture [y = std::move(x)]
2076 if let Some(name) = child.get_name() {
2077 if name == "move" {
2078 has_move_call = true;
2079 }
2080 }
2081 }
2082 EntityKind::ThisExpr => {
2083 // Capturing 'this'
2084 has_explicit_captures = true;
2085 captures.push(LambdaCaptureKind::This);
2086 }
2087 _ => {}
2088 }
2089 }
2090
2091 // If no explicit captures found, check if lambda uses default capture
2092 // by looking at the source code range
2093 debug_println!("DEBUG LAMBDA: has_explicit_captures={}", has_explicit_captures);
2094 if !has_explicit_captures {
2095 // Try to get the source range and parse the capture specifier
2096 if let Some(range) = entity.get_range() {
2097 if let Some(file) = range.get_start().get_file_location().file {
2098 if let Ok(content) = std::fs::read_to_string(file.get_path()) {
2099 let start_line = range.get_start().get_file_location().line as usize;
2100 let start_col = range.get_start().get_file_location().column as usize;
2101 debug_println!("DEBUG LAMBDA: Source parsing at line={} col={}", start_line, start_col);
2102
2103 if let Some(line) = content.lines().nth(start_line.saturating_sub(1)) {
2104 debug_println!("DEBUG LAMBDA: Line content: '{}'", line);
2105 // Find the capture list: [...]
2106 if let Some(bracket_start) = line.get(start_col.saturating_sub(1)..).and_then(|s| s.find('[')) {
2107 let search_start = start_col.saturating_sub(1) + bracket_start;
2108 if let Some(rest) = line.get(search_start..) {
2109 if let Some(bracket_end) = rest.find(']') {
2110 let capture_list = &rest[1..bracket_end];
2111 debug_println!("DEBUG LAMBDA: Capture list from source: '{}'", capture_list);
2112
2113 // Check for default reference capture [&]
2114 if capture_list.trim() == "&" {
2115 debug_println!("DEBUG LAMBDA: Default reference capture [&] detected");
2116 captures.push(LambdaCaptureKind::DefaultRef);
2117 }
2118 // Check for default copy capture [=]
2119 else if capture_list.trim() == "=" {
2120 debug_println!("DEBUG LAMBDA: Default copy capture [=] detected");
2121 captures.push(LambdaCaptureKind::DefaultCopy);
2122 }
2123 // Check for 'this' capture [this]
2124 else if capture_list.trim() == "this" {
2125 debug_println!("DEBUG LAMBDA: 'this' capture [this] detected");
2126 captures.push(LambdaCaptureKind::This);
2127 }
2128 // Check for '*this' capture [*this]
2129 else if capture_list.trim() == "*this" {
2130 debug_println!("DEBUG LAMBDA: '*this' capture [*this] detected");
2131 captures.push(LambdaCaptureKind::ThisCopy);
2132 }
2133 // Check for init captures [x = expr] or [x = std::move(y)]
2134 else if capture_list.contains('=') && !capture_list.starts_with('&') {
2135 // This is an init capture - safe (copy or move)
2136 debug_println!("DEBUG LAMBDA: Init capture detected");
2137 // Extract variable name before the '='
2138 if let Some(eq_pos) = capture_list.find('=') {
2139 let var_name = capture_list[..eq_pos].trim().to_string();
2140 let is_move = capture_list.contains("std::move") ||
2141 capture_list.contains("move(");
2142 captures.push(LambdaCaptureKind::Init {
2143 name: var_name,
2144 is_move,
2145 });
2146 }
2147 }
2148 // Check for explicit reference captures [&x, &y, ...]
2149 else if capture_list.starts_with('&') && !capture_list.contains(',') {
2150 // [&x] - single explicit reference capture
2151 // Already handled by VariableRef detection above
2152 }
2153 }
2154 }
2155 }
2156 }
2157 }
2158 }
2159 }
2160 }
2161
2162 // Determine capture type for each VariableRef (explicit captures)
2163 // Key insight from libclang patterns:
2164 // - Reference capture [&x]: VariableRef 'x' only (no DeclRefExpr, no CallExpr)
2165 // - Copy capture [x]: VariableRef 'x' + DeclRefExpr 'x' (same name)
2166 // - Init copy capture [y = x]: VariableRef 'y' + DeclRefExpr 'x' (different names, NO overlap!)
2167 // - Init move capture [y = std::move(x)]: VariableRef 'y' + CallExpr 'move'
2168 // - Mixed capture [x, &y]: VariableRef 'x' & 'y', DeclRefExpr 'x' only (partial overlap)
2169
2170 // Check if this is an init capture situation:
2171 // For init captures [y = x], var_refs and decl_refs have NO overlapping names
2172 let var_ref_set: std::collections::HashSet<_> = var_refs.iter().cloned().collect();
2173 let has_any_overlap = var_ref_set.intersection(&decl_refs).next().is_some();
2174 let is_init_capture_pattern = !decl_refs.is_empty() && !has_any_overlap;
2175
2176 for var_name in var_refs {
2177 if decl_refs.contains(&var_name) {
2178 // Has corresponding DeclRefExpr with SAME name = copy capture
2179 debug_println!("DEBUG LAMBDA: Copy capture of '{}'", var_name);
2180 captures.push(LambdaCaptureKind::ByCopy(var_name));
2181 } else if has_move_call {
2182 // Has move() call = init move capture [y = std::move(x)]
2183 debug_println!("DEBUG LAMBDA: Init move capture '{}'", var_name);
2184 captures.push(LambdaCaptureKind::Init {
2185 name: var_name,
2186 is_move: true,
2187 });
2188 } else if is_init_capture_pattern {
2189 // Has DeclRefExpr with entirely DIFFERENT names = init capture [y = x]
2190 // The VariableRef is the new capture name, DeclRefExpr is the source
2191 debug_println!("DEBUG LAMBDA: Init copy capture '{}'", var_name);
2192 captures.push(LambdaCaptureKind::Init {
2193 name: var_name,
2194 is_move: false,
2195 });
2196 } else {
2197 // No matching DeclRefExpr = reference capture
2198 debug_println!("DEBUG LAMBDA: Reference capture of '{}'", var_name);
2199 captures.push(LambdaCaptureKind::ByRef(var_name));
2200 }
2201 }
2202
2203 debug_println!("DEBUG LAMBDA: Found lambda with {} captures: {:?}",
2204 captures.len(), captures);
2205
2206 Some(Expression::Lambda { captures })
2207 }
2208 EntityKind::ArraySubscriptExpr => {
2209 // Array subscript: arr[i], data[idx], etc.
2210 // The first child is the array/pointer, second is the index
2211 // For lifetime purposes, the source is the array (first child)
2212 let children: Vec<Entity> = entity.get_children().into_iter().collect();
2213 if !children.is_empty() {
2214 // First child is the array or pointer being indexed
2215 if let Some(array_expr) = extract_expression(&children[0]) {
2216 debug_println!("DEBUG: ArraySubscriptExpr - array/pointer source: {:?}", array_expr);
2217 // Return the array expression as the source
2218 // This handles cases like data[i] returning reference to member data
2219 return Some(array_expr);
2220 }
2221 }
2222 None
2223 }
2224 _ => None
2225 }
2226}
2227
2228fn extract_location(entity: &Entity) -> SourceLocation {
2229 let location = entity.get_location().unwrap();
2230 let file_location = location.get_file_location();
2231
2232 SourceLocation {
2233 file: file_location
2234 .file
2235 .map(|f| f.get_path().display().to_string())
2236 .unwrap_or_else(|| "unknown".to_string()),
2237 line: file_location.line,
2238 column: file_location.column,
2239 }
2240}
2241
2242fn type_to_string(ty: &Type) -> String {
2243 ty.get_display_name()
2244}