1use llmcc_core::context::CompileUnit;
2use llmcc_core::ir::{HirId, HirNode};
3use tree_sitter::Node;
4
5use super::function::{parse_type_expr, FnVisibility, TypeExpr};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum StructKind {
10 Named,
11 Tuple,
12 Unit,
13}
14
15#[derive(Debug, Clone)]
17pub struct StructDescriptor {
18 pub hir_id: HirId,
19 pub name: String,
20 pub fqn: String,
21 pub visibility: FnVisibility,
22 pub generics: Option<String>,
23 pub fields: Vec<StructField>,
24 pub kind: StructKind,
25}
26
27#[derive(Debug, Clone)]
29pub struct StructField {
30 pub name: Option<String>,
31 pub ty: Option<TypeExpr>,
32}
33
34impl StructDescriptor {
35 pub fn from_struct<'tcx>(
36 unit: CompileUnit<'tcx>,
37 node: &HirNode<'tcx>,
38 fqn: String,
39 ) -> Option<Self> {
40 let ts_node = match node.inner_ts_node() {
41 ts if ts.kind() == "struct_item" => ts,
42 _ => return None,
43 };
44
45 #[cfg(test)]
46 eprintln!(
47 "struct_item fields? {} tuple? {} sexp: {}",
48 ts_node.child_by_field_name("fields").is_some(),
49 ts_node.child_by_field_name("body").is_some(),
50 ts_node.to_sexp()
51 );
52
53 let name_node = ts_node.child_by_field_name("name")?;
54 let name = clean(&node_text(unit, name_node));
55 let header_text = unit
56 .file()
57 .get_text(ts_node.start_byte(), name_node.start_byte());
58 let visibility = FnVisibility::from_header(&header_text);
59
60 let generics = ts_node
61 .child_by_field_name("type_parameters")
62 .map(|n| clean(&node_text(unit, n)));
63
64 let (fields, kind) = parse_struct_fields(unit, ts_node);
65
66 Some(StructDescriptor {
67 hir_id: node.hir_id(),
68 name,
69 fqn,
70 visibility,
71 generics,
72 fields,
73 kind,
74 })
75 }
76}
77
78fn parse_struct_fields<'tcx>(
79 unit: CompileUnit<'tcx>,
80 node: Node<'tcx>,
81) -> (Vec<StructField>, StructKind) {
82 match node.kind() {
83 "field_declaration_list" => return (parse_named_fields(unit, node), StructKind::Named),
84 "tuple_field_declaration_list" | "ordered_field_declaration_list" => {
85 return (parse_tuple_fields(unit, node), StructKind::Tuple)
86 }
87 _ => {}
88 }
89
90 let mut named = Vec::new();
91 let mut tuple = Vec::new();
92 let child_count = node.child_count();
93 for i in 0..child_count {
94 if let Some(child) = node.child(i) {
95 match child.kind() {
96 "field_declaration_list" => {
97 return (parse_named_fields(unit, child), StructKind::Named)
98 }
99 "tuple_field_declaration_list" | "ordered_field_declaration_list" => {
100 return (parse_tuple_fields(unit, child), StructKind::Tuple)
101 }
102 "field_declaration" => named.push(parse_named_field_node(unit, child)),
103 "tuple_field_declaration" | "ordered_field_declaration" => {
104 tuple.push(parse_tuple_field_node(unit, child))
105 }
106 _ => {
107 let (fields, kind) = parse_struct_fields(unit, child);
108 match kind {
109 StructKind::Named if !fields.is_empty() => {
110 return (fields, StructKind::Named)
111 }
112 StructKind::Tuple if !fields.is_empty() => {
113 return (fields, StructKind::Tuple)
114 }
115 _ => {}
116 }
117 }
118 }
119 }
120 }
121
122 if !named.is_empty() {
123 return (named, StructKind::Named);
124 }
125 if !tuple.is_empty() {
126 return (tuple, StructKind::Tuple);
127 }
128
129 (Vec::new(), StructKind::Unit)
130}
131
132fn parse_named_fields<'tcx>(unit: CompileUnit<'tcx>, list: Node<'tcx>) -> Vec<StructField> {
133 let mut fields = Vec::new();
134 let mut cursor = list.walk();
135 for child in list.named_children(&mut cursor) {
136 if child.kind() == "field_declaration" {
137 let name = child
138 .child_by_field_name("name")
139 .map(|n| clean(&node_text(unit, n)));
140 let ty = child
141 .child_by_field_name("type")
142 .map(|n| parse_type_expr(unit, n));
143 fields.push(StructField { name, ty });
144 }
145 }
146 fields
147}
148
149fn parse_tuple_fields<'tcx>(unit: CompileUnit<'tcx>, list: Node<'tcx>) -> Vec<StructField> {
150 let mut fields = Vec::new();
151 let mut cursor = list.walk();
152 for child in list.children(&mut cursor) {
153 match child.kind() {
154 "tuple_field_declaration" | "ordered_field_declaration" => {
155 let ty = child
156 .child_by_field_name("type")
157 .map(|n| parse_type_expr(unit, n))
158 .or_else(|| {
159 child
161 .children(&mut child.walk())
162 .find_map(|n| match n.kind() {
163 "type_identifier"
164 | "primitive_type"
165 | "scoped_type_identifier"
166 | "generic_type"
167 | "tuple_type"
168 | "reference_type"
169 | "impl_trait_type" => Some(parse_type_expr(unit, n)),
170 _ => None,
171 })
172 });
173 fields.push(StructField { name: None, ty });
174 }
175 kind if is_type_kind(kind) => {
176 fields.push(StructField {
177 name: None,
178 ty: Some(parse_type_expr(unit, child)),
179 });
180 }
181 _ => {}
182 }
183 }
184 fields
185}
186
187fn is_type_kind(kind: &str) -> bool {
188 matches!(
189 kind,
190 "type_identifier"
191 | "primitive_type"
192 | "scoped_type_identifier"
193 | "generic_type"
194 | "tuple_type"
195 | "reference_type"
196 | "impl_trait_type"
197 )
198}
199
200fn node_text<'tcx>(unit: CompileUnit<'tcx>, node: Node<'tcx>) -> String {
201 unit.file().get_text(node.start_byte(), node.end_byte())
202}
203
204fn clean(text: &str) -> String {
205 let mut out = String::new();
206 let mut last_was_ws = false;
207 for ch in text.chars() {
208 if ch.is_whitespace() {
209 if !last_was_ws && !out.is_empty() {
210 out.push(' ');
211 }
212 last_was_ws = true;
213 } else {
214 out.push(ch);
215 last_was_ws = false;
216 }
217 }
218 out.trim().to_string()
219}
220
221fn parse_named_field_node<'tcx>(unit: CompileUnit<'tcx>, node: Node<'tcx>) -> StructField {
222 let name = node
223 .child_by_field_name("name")
224 .map(|n| clean(&node_text(unit, n)));
225 let ty = node
226 .child_by_field_name("type")
227 .map(|n| parse_type_expr(unit, n));
228 StructField { name, ty }
229}
230
231fn parse_tuple_field_node<'tcx>(unit: CompileUnit<'tcx>, node: Node<'tcx>) -> StructField {
232 let ty = node
233 .child_by_field_name("type")
234 .map(|n| parse_type_expr(unit, n));
235 StructField { name: None, ty }
236}