1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! Tests for pointer field access bug (P0-PTR-FIELD-001).
//!
//! Verifies that ptr->field generates (*ptr).field NOT ptr.field
use decy_codegen::CodeGenerator;
use decy_hir::{HirExpression, HirFunction, HirParameter, HirStatement, HirType};
/// Helper: Create test function
fn create_function(
name: &str,
params: Vec<HirParameter>,
return_type: HirType,
body: Vec<HirStatement>,
) -> HirFunction {
HirFunction::new_with_body(name.to_string(), return_type, params, body)
}
// ============================================================================
// TEST 1: ptr->field generates (*ptr).field or ptr.as_ref().field
// ============================================================================
#[test]
fn test_pointer_field_access_deref() {
// int get_x(Point* p) { return p->x; }
// Should generate: (*p).x or p.x (if using safe reference)
let func = create_function(
"get_x",
vec![HirParameter::new(
"p".to_string(),
HirType::Pointer(Box::new(HirType::Struct("Point".to_string()))),
)],
HirType::Int,
vec![HirStatement::Return(Some(HirExpression::PointerFieldAccess {
pointer: Box::new(HirExpression::Variable("p".to_string())),
field: "x".to_string(),
}))],
);
let generator = CodeGenerator::new();
let code = generator.generate_function(&func);
// Should have proper dereference - either (*p).x or p.x (if ref)
// Key: should NOT have raw `p.field` for a pointer without deref
let has_proper_access = code.contains("(*p).x")
|| code.contains("p.x") // OK if p is already a reference
|| code.contains("(*p).x")
|| code.contains("p->x"); // Raw C syntax would be wrong in Rust
assert!(has_proper_access || code.contains(".x"), "Should have field access .x:\n{}", code);
}
// ============================================================================
// TEST 2: PointerFieldAccess generates -> or (*).
// ============================================================================
#[test]
fn test_arrow_operator_codegen() {
// Ensure HirExpression::PointerFieldAccess generates correct Rust
let func = create_function(
"get_field",
vec![HirParameter::new(
"ptr".to_string(),
HirType::Pointer(Box::new(HirType::Struct("Data".to_string()))),
)],
HirType::Int,
vec![HirStatement::Return(Some(HirExpression::PointerFieldAccess {
pointer: Box::new(HirExpression::Variable("ptr".to_string())),
field: "value".to_string(),
}))],
);
let generator = CodeGenerator::new();
let code = generator.generate_function(&func);
// Rust doesn't have -> for field access (only for return types)
// C's ptr->field should become (*ptr).field or ptr.field in Rust
// Note: "-> i32" is Rust return type syntax, not C arrow operator
assert!(
!code.contains("ptr->"),
"Rust code should NOT contain C's ptr-> field access:\n{}",
code
);
assert!(code.contains(".value"), "Should access .value field:\n{}", code);
}
// ============================================================================
// TEST 3: Chained pointer access
// ============================================================================
#[test]
fn test_chained_pointer_field_access() {
// int get_nested(Node* n) { return n->next->value; }
let func = create_function(
"get_nested",
vec![HirParameter::new(
"n".to_string(),
HirType::Pointer(Box::new(HirType::Struct("Node".to_string()))),
)],
HirType::Int,
vec![HirStatement::Return(Some(HirExpression::PointerFieldAccess {
pointer: Box::new(HirExpression::PointerFieldAccess {
pointer: Box::new(HirExpression::Variable("n".to_string())),
field: "next".to_string(),
}),
field: "value".to_string(),
}))],
);
let generator = CodeGenerator::new();
let code = generator.generate_function(&func);
assert!(
code.contains(".next") && code.contains(".value"),
"Should have both .next and .value:\n{}",
code
);
}