1use std::mem;
2
3use llmcc_core::context::CompileUnit;
4use llmcc_core::ir::{HirId, HirNode};
5use tree_sitter::Node;
6
7use super::function::{parse_type_expr, TypeExpr};
8
9#[derive(Debug, Clone)]
11pub struct CallDescriptor {
12 pub hir_id: HirId,
14 pub target: CallTarget,
16 pub arguments: Vec<CallArgument>,
18 pub enclosing_function: Option<String>,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub enum CallTarget {
25 Path {
27 segments: Vec<String>,
28 generics: Vec<TypeExpr>,
29 },
30 Method {
32 receiver: String,
33 method: String,
34 generics: Vec<TypeExpr>,
35 },
36 Chain {
38 base: String,
39 segments: Vec<ChainSegment>,
40 },
41 Unknown(String),
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
47pub struct ChainSegment {
48 pub method: String,
49 pub generics: Vec<TypeExpr>,
50 pub arguments: Vec<CallArgument>,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct CallArgument {
56 pub text: String,
57}
58
59impl CallDescriptor {
60 pub fn from_call<'tcx>(
61 unit: CompileUnit<'tcx>,
62 node: &HirNode<'tcx>,
63 enclosing_function: Option<String>,
64 ) -> Self {
65 let ts_node = node.inner_ts_node();
66 let function_node = ts_node.child_by_field_name("function");
67 let call_generics = ts_node
68 .child_by_field_name("type_arguments")
69 .map(|n| parse_type_arguments(unit, n))
70 .unwrap_or_default();
71
72 let target = function_node
73 .and_then(|func| parse_chain(unit, func, call_generics.clone()))
74 .or_else(|| parse_chain(unit, ts_node, call_generics.clone()))
75 .map(|(base, segments)| CallTarget::Chain { base, segments })
76 .unwrap_or_else(|| match function_node {
77 Some(func) => parse_call_target(unit, func, call_generics.clone()),
78 None => CallTarget::Unknown(clean(&node_text(unit, ts_node))),
79 });
80
81 let arguments = ts_node
82 .child_by_field_name("arguments")
83 .map(|args| parse_arguments(unit, args))
84 .unwrap_or_default();
85
86 CallDescriptor {
87 hir_id: node.hir_id(),
88 target,
89 arguments,
90 enclosing_function,
91 }
92 }
93}
94
95fn parse_arguments<'tcx>(unit: CompileUnit<'tcx>, args_node: Node<'tcx>) -> Vec<CallArgument> {
96 let mut cursor = args_node.walk();
97 args_node
98 .named_children(&mut cursor)
99 .map(|arg| CallArgument {
100 text: clean(&node_text(unit, arg)),
101 })
102 .collect()
103}
104
105fn parse_call_target<'tcx>(
106 unit: CompileUnit<'tcx>,
107 node: Node<'tcx>,
108 call_generics: Vec<TypeExpr>,
109) -> CallTarget {
110 match node.kind() {
111 "identifier" | "scoped_identifier" | "type_identifier" => {
112 let segments: Vec<String> = clean(&node_text(unit, node))
113 .split("::")
114 .map(|s| s.to_string())
115 .collect();
116 CallTarget::Path {
117 segments,
118 generics: call_generics,
119 }
120 }
121 "generic_type" => {
122 let base = node.child_by_field_name("type").unwrap_or(node);
123 let mut segments: Vec<String> = clean(&node_text(unit, base))
124 .split("::")
125 .map(|s| s.to_string())
126 .collect();
127 if segments.is_empty() {
128 segments.push(clean(&node_text(unit, base)));
129 }
130 let generics = node
131 .child_by_field_name("type_arguments")
132 .map(|args| parse_type_arguments(unit, args))
133 .unwrap_or(call_generics);
134 CallTarget::Path { segments, generics }
135 }
136 "generic_function" => {
137 let generics = node
138 .child_by_field_name("type_arguments")
139 .map(|args| parse_type_arguments(unit, args))
140 .unwrap_or_default();
141 let inner = node
142 .child_by_field_name("function")
143 .unwrap_or(node.child(0).unwrap_or(node));
144 let mut target = parse_call_target(unit, inner, call_generics);
145 match &mut target {
146 CallTarget::Path { generics: g, .. } => *g = generics,
147 CallTarget::Method { generics: g, .. } => *g = generics,
148 _ => {}
149 }
150 target
151 }
152 "field_expression" => {
153 let receiver = node
154 .child_by_field_name("value")
155 .map(|n| clean(&node_text(unit, n)))
156 .unwrap_or_else(|| clean(&node_text(unit, node)));
157 let method = node
158 .child_by_field_name("field")
159 .map(|n| clean(&node_text(unit, n)))
160 .unwrap_or_default();
161 let generics = node
162 .child_by_field_name("type_arguments")
163 .map(|n| parse_type_arguments(unit, n))
164 .unwrap_or(call_generics);
165 CallTarget::Method {
166 receiver,
167 method,
168 generics,
169 }
170 }
171 _ => {
172 let text = clean(&node_text(unit, node));
173 if let Some((receiver, method)) = parse_method_from_text(&text) {
174 CallTarget::Method {
175 receiver,
176 method,
177 generics: call_generics,
178 }
179 } else {
180 CallTarget::Unknown(text)
181 }
182 }
183 }
184}
185
186fn parse_chain<'tcx>(
187 unit: CompileUnit<'tcx>,
188 mut node: Node<'tcx>,
189 call_generics: Vec<TypeExpr>,
190) -> Option<(String, Vec<ChainSegment>)> {
191 let mut segments = Vec::new();
192 let mut pending_generics = call_generics;
193 let mut pending_arguments = Vec::new();
194
195 loop {
196 match node.kind() {
197 "call_expression" => {
198 pending_generics = node
199 .child_by_field_name("type_arguments")
200 .map(|n| parse_type_arguments(unit, n))
201 .unwrap_or_default();
202 pending_arguments = node
203 .child_by_field_name("arguments")
204 .map(|args| parse_arguments(unit, args))
205 .unwrap_or_default();
206 let function_node = node.child_by_field_name("function")?;
207 node = function_node;
208 }
209 "generic_function" => {
210 pending_generics = node
211 .child_by_field_name("type_arguments")
212 .map(|n| parse_type_arguments(unit, n))
213 .unwrap_or_default();
214 let inner_function = node.child_by_field_name("function")?;
215 node = inner_function;
216 }
217 "field_expression" => {
218 let method = node
219 .child_by_field_name("field")
220 .map(|n| clean(&node_text(unit, n)))
221 .unwrap_or_default();
222 let generics = mem::take(&mut pending_generics);
223 let arguments = mem::take(&mut pending_arguments);
224 segments.push(ChainSegment {
225 method,
226 generics,
227 arguments,
228 });
229 let argument_node = node.child_by_field_name("value")?;
230 node = argument_node;
231 }
232 _ => {
233 if segments.len() < 2 {
234 return None;
235 }
236 let base = clean(&node_text(unit, node));
237 segments.reverse();
238 return Some((base, segments));
239 }
240 }
241 }
242}
243
244fn parse_type_arguments<'tcx>(unit: CompileUnit<'tcx>, node: Node<'tcx>) -> Vec<TypeExpr> {
245 let mut args = Vec::new();
246 let mut cursor = node.walk();
247 for child in node.named_children(&mut cursor) {
248 match child.kind() {
249 "type_argument" => {
250 if let Some(inner) = child.child_by_field_name("type") {
251 args.push(parse_type_expr(unit, inner));
252 }
253 }
254 kind if is_type_node(kind) => {
255 args.push(parse_type_expr(unit, child));
256 }
257 _ => {}
258 }
259 }
260 if args.is_empty() {
261 let mut cursor = node.walk();
262 for child in node.children(&mut cursor) {
263 if is_type_node(child.kind()) {
264 args.push(parse_type_expr(unit, child));
265 }
266 }
267 }
268 args
269}
270
271fn is_type_node(kind: &str) -> bool {
272 matches!(
273 kind,
274 "type_identifier"
275 | "scoped_type_identifier"
276 | "generic_type"
277 | "reference_type"
278 | "tuple_type"
279 | "primitive_type"
280 | "impl_trait_type"
281 )
282}
283
284fn node_text<'tcx>(unit: CompileUnit<'tcx>, node: Node<'tcx>) -> String {
285 unit.file().get_text(node.start_byte(), node.end_byte())
286}
287
288fn clean(text: &str) -> String {
289 let mut out = String::new();
290 let mut last_was_ws = false;
291 for ch in text.chars() {
292 if ch.is_whitespace() {
293 if !last_was_ws && !out.is_empty() {
294 out.push(' ');
295 }
296 last_was_ws = true;
297 } else {
298 out.push(ch);
299 last_was_ws = false;
300 }
301 }
302 out.trim().to_string()
303}
304
305fn parse_method_from_text(text: &str) -> Option<(String, String)> {
306 let idx = text.rfind('.')?;
307 let (receiver, method_part) = text.split_at(idx);
308 Some((
309 receiver.trim().to_string(),
310 method_part.trim_start_matches('.').to_string(),
311 ))
312}