brokk_bifrost_cpp/graph/
syntax.rs1use crate::declarations::node_text;
2use crate::graph::resolver::{
3 cpp_name_component_nodes, cpp_type_name_components, is_globally_qualified_cpp_name,
4 is_nested_type_node, qualified_owner_components,
5};
6use std::ops::Range;
7use tree_sitter::{Node, Parser};
8
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct MacroReplacementTypeReference {
11 pub components: Vec<String>,
12 pub component_ranges: Vec<Range<usize>>,
13 pub global: bool,
14}
15
16#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct MacroReplacementField {
22 pub name: String,
23 pub declaration: String,
24}
25
26pub fn object_macro_replacement_fields(replacement: &str) -> Vec<MacroReplacementField> {
33 if replacement.trim().is_empty() {
34 return Vec::new();
35 }
36 let normalized_replacement = normalize_macro_continuations(replacement);
37 const PREFIX: &str = "struct __bifrost_macro_fields { ";
38 let synthetic = format!("{PREFIX}{normalized_replacement} }};");
39 let mut parser = Parser::new();
40 if parser
41 .set_language(&tree_sitter_cpp::LANGUAGE.into())
42 .is_err()
43 {
44 return Vec::new();
45 }
46 let Some(tree) = parser.parse(&synthetic, None) else {
47 return Vec::new();
48 };
49 if tree.root_node().has_error() {
50 return Vec::new();
51 }
52 let mut stack = vec![tree.root_node()];
53 let body = loop {
54 let Some(current) = stack.pop() else {
55 return Vec::new();
56 };
57 if current.kind() == "struct_specifier"
58 && let Some(body) = current.child_by_field_name("body")
59 {
60 break body;
61 }
62 let mut cursor = current.walk();
63 for child in current.named_children(&mut cursor) {
64 stack.push(child);
65 }
66 };
67 let mut fields = Vec::new();
68 let mut cursor = body.walk();
69 for declaration in body.named_children(&mut cursor) {
70 if !matches!(declaration.kind(), "declaration" | "field_declaration") {
71 continue;
72 }
73 let Some(declarator) = declaration
74 .child_by_field_name("declarator")
75 .or_else(|| declaration.named_child(1))
76 else {
77 continue;
78 };
79 let Some(name) = macro_replacement_declarator_name(declarator, &synthetic) else {
80 continue;
81 };
82 let Some(declaration_text) =
83 declaration_text_without_synthetic_prefix(declaration, replacement, PREFIX.len())
84 else {
85 continue;
86 };
87 fields.push(MacroReplacementField {
88 name,
89 declaration: declaration_text,
90 });
91 }
92 fields
93}
94
95fn normalize_macro_continuations(replacement: &str) -> String {
101 let source = replacement.as_bytes();
102 let mut normalized = source.to_vec();
103 let mut index = 0;
104 while index + 1 < source.len() {
105 if source[index] == b'\\' && source[index + 1] == b'\n' {
106 normalized[index] = b' ';
107 normalized[index + 1] = b' ';
108 index += 2;
109 } else if index + 2 < source.len()
110 && source[index] == b'\\'
111 && source[index + 1] == b'\r'
112 && source[index + 2] == b'\n'
113 {
114 normalized[index] = b' ';
115 normalized[index + 1] = b' ';
116 normalized[index + 2] = b' ';
117 index += 3;
118 } else {
119 index += 1;
120 }
121 }
122 String::from_utf8(normalized).expect("source text must remain valid UTF-8")
123}
124
125fn macro_replacement_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
126 match node.kind() {
127 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
128 let name = node_text(node, source).trim();
129 (!name.is_empty()).then(|| name.to_string())
130 }
131 "function_declarator" => None,
132 _ => node
133 .child_by_field_name("declarator")
134 .or_else(|| node.child_by_field_name("name"))
135 .and_then(|child| macro_replacement_declarator_name(child, source)),
136 }
137}
138
139fn declaration_text_without_synthetic_prefix(
140 node: Node<'_>,
141 source: &str,
142 prefix_len: usize,
143) -> Option<String> {
144 let start = node.start_byte().checked_sub(prefix_len)?;
145 let end = node.end_byte().checked_sub(prefix_len)?;
146 (end <= source.len()).then(|| source[start..end].to_string())
147}
148
149pub fn object_macro_replacement_type_references(
157 node: Node<'_>,
158 source: &str,
159) -> Vec<MacroReplacementTypeReference> {
160 if node.kind() != "preproc_arg"
161 || !node.parent().is_some_and(|parent| {
162 parent.kind() == "preproc_def"
163 && parent
164 .child_by_field_name("value")
165 .is_some_and(|value| value == node)
166 })
167 {
168 return Vec::new();
169 }
170 let Some(replacement) = source.get(node.start_byte()..node.end_byte()) else {
171 return Vec::new();
172 };
173 const PREFIX: &str = "void __bifrost_macro_reference() { ";
174 let synthetic = format!("{PREFIX}{replacement}; }}");
175 let mut parser = Parser::new();
176 if parser
177 .set_language(&tree_sitter_cpp::LANGUAGE.into())
178 .is_err()
179 {
180 return Vec::new();
181 }
182 let Some(tree) = parser.parse(&synthetic, None) else {
183 return Vec::new();
184 };
185 if tree.root_node().has_error() {
186 return Vec::new();
187 }
188
189 let mut references = Vec::new();
190 let mut stack = vec![tree.root_node()];
191 while let Some(current) = stack.pop() {
192 let structured = if matches!(
193 current.kind(),
194 "type_identifier" | "scoped_type_identifier" | "template_type"
195 ) && !is_nested_type_node(current)
196 {
197 cpp_type_name_components(current, &synthetic)
198 .zip(cpp_name_component_nodes(current))
199 .map(|(components, nodes)| {
200 (components, nodes, is_globally_qualified_cpp_name(current))
201 })
202 } else if current.kind() == "qualified_identifier"
203 && !current.parent().is_some_and(|parent| {
204 matches!(
205 parent.kind(),
206 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
207 )
208 })
209 {
210 qualified_owner_components(current, &synthetic)
211 .map(|owner| (owner.names, owner.nodes, owner.global))
212 } else {
213 None
214 };
215 if let Some((components, component_nodes, global)) = structured {
216 let component_ranges = component_nodes
217 .into_iter()
218 .map(|component| {
219 let start = component.start_byte().checked_sub(PREFIX.len())?;
220 let end = component.end_byte().checked_sub(PREFIX.len())?;
221 (end <= replacement.len())
222 .then_some(node.start_byte() + start..node.start_byte() + end)
223 })
224 .collect::<Option<Vec<_>>>();
225 if let Some(component_ranges) = component_ranges
226 && component_ranges.len() == components.len()
227 {
228 let reference = MacroReplacementTypeReference {
229 components,
230 component_ranges,
231 global,
232 };
233 if !references.contains(&reference) {
234 references.push(reference);
235 }
236 }
237 }
238 for index in (0..current.named_child_count()).rev() {
239 if let Some(child) = current.named_child(index) {
240 stack.push(child);
241 }
242 }
243 }
244 references
245}
246
247#[derive(Clone)]
248pub struct QualifiedCallableValue<'tree> {
249 pub qualified: Node<'tree>,
250 pub global: bool,
251 pub owner_components: Vec<Node<'tree>>,
252 pub member: Node<'tree>,
253}
254
255pub fn explicit_qualified_callable_value(node: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
262 if node.kind() != "pointer_expression" || node.child_by_field_name("operator")?.kind() != "&" {
263 return None;
264 }
265 let qualified = node.child_by_field_name("argument")?;
266 qualified_callable_value_from_node(qualified)
267}
268
269pub fn qualified_callable_value(node: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
275 if let Some(value) = explicit_qualified_callable_value(node) {
276 return Some(value);
277 }
278 if node.kind() != "qualified_identifier" {
279 return None;
280 }
281 if crate::graph::resolver::is_declaration_name(node) {
282 return None;
283 }
284 if node.parent().is_some_and(|parent| {
285 parent.child_by_field_name("type") == Some(node)
286 || (parent.kind() == "call_expression"
287 && parent.child_by_field_name("function") == Some(node))
288 || (parent.kind() == "pointer_expression"
289 && parent.child_by_field_name("argument") == Some(node))
290 || matches!(
291 parent.kind(),
292 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
293 )
294 }) {
295 return None;
296 }
297 qualified_callable_value_from_node(node)
298}
299
300fn qualified_callable_value_from_node(qualified: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
301 if qualified.kind() != "qualified_identifier" {
302 return None;
303 }
304 let mut components = Vec::new();
305 let global = qualified.child_by_field_name("scope").is_none()
306 && qualified.child(0).is_some_and(|child| child.kind() == "::");
307 append_qualified_components(qualified, &mut components)?;
308 let member = components.pop()?;
309 if components.is_empty() {
310 return None;
311 }
312 Some(QualifiedCallableValue {
313 qualified,
314 global,
315 owner_components: components,
316 member,
317 })
318}
319
320fn append_qualified_components<'tree>(node: Node<'tree>, out: &mut Vec<Node<'tree>>) -> Option<()> {
321 let mut stack = vec![node];
322 while let Some(current) = stack.pop() {
323 match current.kind() {
324 "identifier" | "namespace_identifier" | "type_identifier" | "operator_name" => {
325 out.push(current)
326 }
327 "qualified_identifier" | "scoped_identifier" => {
328 stack.push(current.child_by_field_name("name")?);
329 if let Some(scope) = current.child_by_field_name("scope") {
330 stack.push(scope);
331 } else if current.child(0).is_none_or(|child| child.kind() != "::") {
332 return None;
333 }
334 }
335 "template_type" | "template_function" => {
336 stack.push(current.child_by_field_name("name")?);
337 }
338 "nested_namespace_specifier" => {
339 for index in (0..current.named_child_count()).rev() {
340 stack.push(current.named_child(index)?);
341 }
342 }
343 _ => return None,
344 }
345 }
346 Some(())
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352
353 fn references(source: &str) -> Vec<MacroReplacementTypeReference> {
354 let mut parser = Parser::new();
355 parser
356 .set_language(&tree_sitter_cpp::LANGUAGE.into())
357 .expect("C++ grammar");
358 let tree = parser.parse(source, None).expect("macro fixture tree");
359 let value = tree
360 .root_node()
361 .named_child(0)
362 .and_then(|definition| definition.child_by_field_name("value"))
363 .expect("macro replacement");
364 object_macro_replacement_type_references(value, source)
365 }
366
367 #[test]
368 fn object_macro_replacement_reparse_preserves_type_ranges() {
369 let source = "#define SETTINGS (*api::SettingsImpl::GetInstance())\n";
370 let references = references(source);
371 let reference = references
372 .iter()
373 .find(|reference| reference.components == ["api", "SettingsImpl"])
374 .expect("qualified callable owner");
375 let rendered = reference
376 .component_ranges
377 .iter()
378 .map(|range| &source[range.clone()])
379 .collect::<Vec<_>>();
380 assert_eq!(rendered, ["api", "SettingsImpl"]);
381 }
382
383 #[test]
384 fn object_macro_replacement_fields_are_structured_and_direct_only() {
385 let fields = object_macro_replacement_fields(
386 r#"int public_value; \
387 union { int nested_value; }; \
388 unsigned private_value;"#,
389 );
390 assert_eq!(
391 fields,
392 vec![
393 MacroReplacementField {
394 name: "public_value".to_string(),
395 declaration: "int public_value;".to_string(),
396 },
397 MacroReplacementField {
398 name: "private_value".to_string(),
399 declaration: "unsigned private_value;".to_string(),
400 },
401 ]
402 );
403 assert!(object_macro_replacement_fields("not a declaration").is_empty());
404 }
405
406 #[test]
407 fn macro_reparse_ignores_function_like_and_non_code_text() {
408 let function_like = "#define SETTINGS(Type) (*Type::GetInstance())\n";
409 assert!(references(function_like).is_empty());
410
411 let text = "#define SETTINGS \"SettingsImpl::GetInstance()\"\n";
412 assert!(references(text).is_empty());
413 }
414}