1use 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 brokk_bifrost_core::analyzer::cpp_facts::CppDeclaredFieldTypeFact;
7use brokk_bifrost_core::analyzer::tree_walk::push_named_children_reversed;
8use std::ops::{ControlFlow, Range};
9use tree_sitter::{Node, Parser};
10
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct MacroReplacementTypeReference {
13 pub components: Vec<String>,
14 pub component_ranges: Vec<Range<usize>>,
15 pub global: bool,
16}
17
18pub fn is_cpp_recovered_callable_declaration_reference(node: Node<'_>) -> bool {
29 if !matches!(node.kind(), "identifier" | "field_identifier") {
30 return false;
31 }
32 let Some(error) = node.parent().filter(|parent| parent.is_error()) else {
33 return false;
34 };
35 let Some(pointer) = error.parent().filter(|parent| {
36 parent.kind() == "pointer_declarator"
37 && parent.named_child(0) == Some(error)
38 && parent
39 .child_by_field_name("declarator")
40 .is_some_and(|declarator| declarator.kind() == "function_declarator")
41 }) else {
42 return false;
43 };
44 pointer.parent().is_some_and(|declaration| {
45 declaration.kind() == "declaration"
46 && declaration.child_by_field_name("declarator") == Some(pointer)
47 })
48}
49
50#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct MacroReplacementField {
56 pub name: String,
57 pub declaration: String,
58 pub field_type: Option<CppDeclaredFieldTypeFact>,
59 pub names_function_type: bool,
60}
61
62#[derive(Clone, Debug, Default, PartialEq, Eq)]
68pub struct ObjectMacroReplacement {
69 pub fields: Vec<MacroReplacementField>,
70 pub nested: Vec<String>,
71}
72
73impl ObjectMacroReplacement {
74 pub fn is_empty(&self) -> bool {
75 self.fields.is_empty() && self.nested.is_empty()
76 }
77
78 pub fn intersect(&self, other: &Self) -> Self {
86 Self {
87 fields: self
88 .fields
89 .iter()
90 .filter(|field| other.fields.contains(field))
91 .cloned()
92 .collect(),
93 nested: self
94 .nested
95 .iter()
96 .filter(|name| other.nested.contains(name))
97 .cloned()
98 .collect(),
99 }
100 }
101}
102
103pub fn object_macro_replacement(replacement: &str) -> ObjectMacroReplacement {
116 if replacement.trim().is_empty() {
117 return ObjectMacroReplacement::default();
118 }
119 let normalized_replacement = normalize_macro_continuations(replacement);
120 const PREFIX: &str = "struct __bifrost_macro_fields { ";
121 let synthetic = format!("{PREFIX}{normalized_replacement} }};");
122 let mut parser = Parser::new();
123 if parser
124 .set_language(&tree_sitter_cpp::LANGUAGE.into())
125 .is_err()
126 {
127 return ObjectMacroReplacement::default();
128 }
129 let Some(tree) = parser.parse(&synthetic, None) else {
130 return ObjectMacroReplacement::default();
131 };
132 let mut stack = vec![tree.root_node()];
133 let body = loop {
134 let Some(current) = stack.pop() else {
135 return ObjectMacroReplacement::default();
136 };
137 if current.kind() == "struct_specifier"
138 && let Some(body) = current.child_by_field_name("body")
139 {
140 break body;
141 }
142 let mut cursor = current.walk();
143 for child in current.named_children(&mut cursor) {
144 stack.push(child);
145 }
146 };
147 let mut recovered = ObjectMacroReplacement::default();
148 let mut composed_terminators = Vec::new();
149 let mut cursor = body.walk();
150 for declaration in body.named_children(&mut cursor) {
151 if !matches!(declaration.kind(), "declaration" | "field_declaration") {
152 continue;
153 }
154 if let Some((name, terminator)) = nested_object_macro_invocation(declaration, &synthetic) {
155 recovered.nested.push(name);
156 composed_terminators.push(terminator.id());
157 continue;
158 }
159 let Some(declarator) = declaration
160 .child_by_field_name("declarator")
161 .or_else(|| declaration.named_child(1))
162 else {
163 continue;
164 };
165 let Some(name) = macro_replacement_declarator_name(declarator, &synthetic) else {
166 continue;
167 };
168 let Some(declaration_text) =
169 declaration_text_without_synthetic_prefix(declaration, replacement, PREFIX.len())
170 else {
171 continue;
172 };
173 let (field_type, names_function_type) =
174 crate::source_facts::capture_field_type(declaration, &name, &synthetic);
175 recovered.fields.push(MacroReplacementField {
176 name,
177 declaration: declaration_text,
178 field_type,
179 names_function_type,
180 });
181 }
182 if !malformed_regions_are_composition(tree.root_node(), &composed_terminators) {
183 return ObjectMacroReplacement::default();
184 }
185 recovered
186}
187
188fn nested_object_macro_invocation<'tree>(
193 declaration: Node<'tree>,
194 source: &str,
195) -> Option<(String, Node<'tree>)> {
196 if declaration.kind() != "field_declaration" {
197 return None;
198 }
199 let mut cursor = declaration.walk();
200 let children = declaration.children(&mut cursor).collect::<Vec<_>>();
201 let [name, terminator] = children.as_slice() else {
202 return None;
203 };
204 if name.kind() != "type_identifier" || terminator.kind() != ";" || !terminator.is_missing() {
205 return None;
206 }
207 let text = node_text(*name, source).trim();
208 (!text.is_empty()).then(|| (text.to_string(), *terminator))
209}
210
211fn malformed_regions_are_composition(root: Node<'_>, composed_terminators: &[usize]) -> bool {
216 let mut stack = vec![root];
217 while let Some(node) = stack.pop() {
218 if !node.has_error() && !node.is_missing() {
219 continue;
220 }
221 if (node.is_error() || node.is_missing()) && !composed_terminators.contains(&node.id()) {
222 return false;
223 }
224 let mut cursor = node.walk();
225 let children = node.children(&mut cursor).collect::<Vec<_>>();
226 stack.extend(children);
227 }
228 true
229}
230
231#[derive(Clone, Debug, PartialEq, Eq)]
234pub struct RecoveredAggregateField {
235 pub name: String,
236 pub declaration: String,
237 pub range: Range<usize>,
238 pub field_type: Option<CppDeclaredFieldTypeFact>,
239 pub names_function_type: bool,
240}
241
242pub fn recovered_aggregate_fields(
252 source: &str,
253 span: Range<usize>,
254) -> Vec<RecoveredAggregateField> {
255 let Some(region) = source.get(span.clone()) else {
256 return Vec::new();
257 };
258 if region.trim().is_empty() {
259 return Vec::new();
260 }
261 const PREFIX: &str = "struct __bifrost_recovered_members { ";
262 let synthetic = format!("{PREFIX}{region} }};");
263 let mut parser = Parser::new();
264 if parser
265 .set_language(&tree_sitter_cpp::LANGUAGE.into())
266 .is_err()
267 {
268 return Vec::new();
269 }
270 let Some(tree) = parser.parse(&synthetic, None) else {
271 return Vec::new();
272 };
273 if tree.root_node().has_error() {
274 return Vec::new();
275 }
276 let mut stack = vec![tree.root_node()];
277 let body = loop {
278 let Some(current) = stack.pop() else {
279 return Vec::new();
280 };
281 if current.kind() == "struct_specifier"
282 && let Some(body) = current.child_by_field_name("body")
283 {
284 break body;
285 }
286 let mut cursor = current.walk();
287 for child in current.named_children(&mut cursor) {
288 stack.push(child);
289 }
290 };
291 let mut fields = Vec::new();
292 let mut cursor = body.walk();
293 for declaration in body.named_children(&mut cursor) {
294 if !matches!(declaration.kind(), "declaration" | "field_declaration") {
295 continue;
296 }
297 let Some(declarator) = declaration
298 .child_by_field_name("declarator")
299 .or_else(|| declaration.named_child(1))
300 else {
301 continue;
302 };
303 let Some(name) = macro_replacement_declarator_name(declarator, &synthetic) else {
304 continue;
305 };
306 let Some(declaration_text) =
307 declaration_text_without_synthetic_prefix(declaration, region, PREFIX.len())
308 else {
309 continue;
310 };
311 let (field_type, names_function_type) =
312 crate::source_facts::capture_field_type(declaration, &name, &synthetic);
313 let start = span.start + declaration.start_byte() - PREFIX.len();
314 let end = span.start + declaration.end_byte() - PREFIX.len();
315 fields.push(RecoveredAggregateField {
316 name,
317 declaration: declaration_text,
318 range: start..end,
319 field_type,
320 names_function_type,
321 });
322 }
323 fields
324}
325
326pub fn object_macro_replacement_span(node: Node<'_>, source: &str) -> Option<Range<usize>> {
337 if node.kind() != "preproc_def" {
338 return None;
339 }
340 let start = node.child_by_field_name("name")?.end_byte();
341 (start <= source.len()).then(|| start..logical_line_end(start, source))
342}
343
344pub fn function_macro_replacement_span(node: Node<'_>, source: &str) -> Option<Range<usize>> {
353 let parameters = match node.kind() {
354 "preproc_function_def" => node.child_by_field_name("parameters"),
355 "preproc_def" => {
356 let mut stack = vec![node];
359 let mut parameters = None;
360 while let Some(part) = stack.pop() {
361 if part.kind() == "preproc_params" {
362 parameters = Some(part);
363 break;
364 }
365 if part == node || part.is_error() {
366 push_named_children_reversed(part, &mut stack);
367 }
368 }
369 parameters
370 }
371 _ => None,
372 }?;
373 let start = parameters.end_byte();
374 (start <= source.len()).then(|| start..logical_line_end(start, source))
375}
376
377fn logical_line_end(start: usize, source: &str) -> usize {
381 let bytes = source.as_bytes();
382 let mut index = start;
383 while index < bytes.len() {
384 if bytes[index] != b'\n' {
385 index += 1;
386 continue;
387 }
388 let mut previous = index;
389 if previous > start && bytes[previous - 1] == b'\r' {
390 previous -= 1;
391 }
392 if previous > start && bytes[previous - 1] == b'\\' {
393 index += 1;
394 continue;
395 }
396 break;
397 }
398 index
399}
400
401pub(crate) fn normalize_macro_continuations(replacement: &str) -> String {
407 let source = replacement.as_bytes();
408 let mut normalized = source.to_vec();
409 let mut index = 0;
410 while index + 1 < source.len() {
411 if source[index] == b'\\' && source[index + 1] == b'\n' {
412 normalized[index] = b' ';
413 normalized[index + 1] = b' ';
414 index += 2;
415 } else if index + 2 < source.len()
416 && source[index] == b'\\'
417 && source[index + 1] == b'\r'
418 && source[index + 2] == b'\n'
419 {
420 normalized[index] = b' ';
421 normalized[index + 1] = b' ';
422 normalized[index + 2] = b' ';
423 index += 3;
424 } else {
425 index += 1;
426 }
427 }
428 String::from_utf8(normalized).expect("source text must remain valid UTF-8")
429}
430
431fn macro_replacement_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
432 match node.kind() {
433 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
434 let name = node_text(node, source).trim();
435 (!name.is_empty()).then(|| name.to_string())
436 }
437 "function_declarator" => None,
438 _ => node
439 .child_by_field_name("declarator")
440 .or_else(|| node.child_by_field_name("name"))
441 .and_then(|child| macro_replacement_declarator_name(child, source)),
442 }
443}
444
445fn declaration_text_without_synthetic_prefix(
446 node: Node<'_>,
447 source: &str,
448 prefix_len: usize,
449) -> Option<String> {
450 let start = node.start_byte().checked_sub(prefix_len)?;
451 let end = node.end_byte().checked_sub(prefix_len)?;
452 (end <= source.len()).then(|| source[start..end].to_string())
453}
454
455pub fn object_macro_replacement_type_references(
463 node: Node<'_>,
464 source: &str,
465) -> Vec<MacroReplacementTypeReference> {
466 if node.kind() != "preproc_arg"
467 || !node.parent().is_some_and(|parent| {
468 parent.kind() == "preproc_def"
469 && parent
470 .child_by_field_name("value")
471 .is_some_and(|value| value == node)
472 })
473 {
474 return Vec::new();
475 }
476 let Some(replacement) = source.get(node.start_byte()..node.end_byte()) else {
477 return Vec::new();
478 };
479 const PREFIX: &str = "void __bifrost_macro_reference() { ";
480 let synthetic = format!("{PREFIX}{replacement}; }}");
481 let mut parser = Parser::new();
482 if parser
483 .set_language(&tree_sitter_cpp::LANGUAGE.into())
484 .is_err()
485 {
486 return Vec::new();
487 }
488 let Some(tree) = parser.parse(&synthetic, None) else {
489 return Vec::new();
490 };
491 if tree.root_node().has_error() {
492 return Vec::new();
493 }
494
495 let mut references = Vec::new();
496 let mut stack = vec![tree.root_node()];
497 while let Some(current) = stack.pop() {
498 let structured = if matches!(
499 current.kind(),
500 "type_identifier" | "scoped_type_identifier" | "template_type"
501 ) && !is_nested_type_node(current)
502 {
503 cpp_type_name_components(current, &synthetic)
504 .zip(cpp_name_component_nodes(current))
505 .map(|(components, nodes)| {
506 (components, nodes, is_globally_qualified_cpp_name(current))
507 })
508 } else if current.kind() == "qualified_identifier"
509 && !current.parent().is_some_and(|parent| {
510 matches!(
511 parent.kind(),
512 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
513 )
514 })
515 {
516 qualified_owner_components(current, &synthetic)
517 .map(|owner| (owner.names, owner.nodes, owner.global))
518 } else {
519 None
520 };
521 if let Some((components, component_nodes, global)) = structured {
522 let component_ranges = component_nodes
523 .into_iter()
524 .map(|component| {
525 let start = component.start_byte().checked_sub(PREFIX.len())?;
526 let end = component.end_byte().checked_sub(PREFIX.len())?;
527 (end <= replacement.len())
528 .then_some(node.start_byte() + start..node.start_byte() + end)
529 })
530 .collect::<Option<Vec<_>>>();
531 if let Some(component_ranges) = component_ranges
532 && component_ranges.len() == components.len()
533 {
534 let reference = MacroReplacementTypeReference {
535 components,
536 component_ranges,
537 global,
538 };
539 if !references.contains(&reference) {
540 references.push(reference);
541 }
542 }
543 }
544 push_named_children_reversed(current, &mut stack);
545 }
546 references
547}
548
549#[derive(Clone)]
550pub struct QualifiedCallableValue<'tree> {
551 pub qualified: Node<'tree>,
552 pub global: bool,
553 pub owner_components: Vec<Node<'tree>>,
554 pub member: Node<'tree>,
555}
556
557pub fn explicit_qualified_callable_value(node: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
564 if node.kind() != "pointer_expression" || node.child_by_field_name("operator")?.kind() != "&" {
565 return None;
566 }
567 let qualified = node.child_by_field_name("argument")?;
568 qualified_callable_value_from_node(qualified)
569}
570
571pub fn qualified_callable_value(node: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
577 if let Some(value) = explicit_qualified_callable_value(node) {
578 return Some(value);
579 }
580 if node.kind() != "qualified_identifier" {
581 return None;
582 }
583 if crate::graph::resolver::is_declaration_name(node) {
584 return None;
585 }
586 if node.parent().is_some_and(|parent| {
587 parent.child_by_field_name("type") == Some(node)
588 || (parent.kind() == "call_expression"
589 && parent.child_by_field_name("function") == Some(node))
590 || (parent.kind() == "pointer_expression"
591 && parent.child_by_field_name("argument") == Some(node))
592 || matches!(
593 parent.kind(),
594 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
595 )
596 }) {
597 return None;
598 }
599 qualified_callable_value_from_node(node)
600}
601
602fn qualified_callable_value_from_node(qualified: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
603 if qualified.kind() != "qualified_identifier" {
604 return None;
605 }
606 let mut components = Vec::new();
607 let global = qualified.child_by_field_name("scope").is_none()
608 && qualified.child(0).is_some_and(|child| child.kind() == "::");
609 append_qualified_components(qualified, &mut components)?;
610 let member = components.pop()?;
611 if components.is_empty() {
612 return None;
613 }
614 Some(QualifiedCallableValue {
615 qualified,
616 global,
617 owner_components: components,
618 member,
619 })
620}
621
622fn append_qualified_components<'tree>(node: Node<'tree>, out: &mut Vec<Node<'tree>>) -> Option<()> {
623 let mut stack = vec![node];
624 while let Some(current) = stack.pop() {
625 match current.kind() {
626 "identifier" | "namespace_identifier" | "type_identifier" | "operator_name" => {
627 out.push(current)
628 }
629 "qualified_identifier" | "scoped_identifier" => {
630 stack.push(current.child_by_field_name("name")?);
631 if let Some(scope) = current.child_by_field_name("scope") {
632 stack.push(scope);
633 } else if current.child(0).is_none_or(|child| child.kind() != "::") {
634 return None;
635 }
636 }
637 "template_type" | "template_function" => {
638 stack.push(current.child_by_field_name("name")?);
639 }
640 "nested_namespace_specifier" => {
641 for index in (0..current.named_child_count()).rev() {
642 stack.push(current.named_child(index)?);
643 }
644 }
645 _ => return None,
646 }
647 }
648 Some(())
649}
650
651pub fn function_macro_included_ranges(
660 source: &str,
661 tree: &tree_sitter::Tree,
662 mut should_stop: impl FnMut() -> bool,
663) -> ControlFlow<(), Option<Vec<tree_sitter::Range>>> {
664 let mut replacements = Vec::new();
665 let mut comments = Vec::new();
666 let mut stack = vec![tree.root_node()];
667 while let Some(node) = stack.pop() {
668 if should_stop() {
669 return ControlFlow::Break(());
670 }
671 if let Some(span) = function_macro_replacement_span(node, source) {
672 replacements.push(span);
673 }
674 if node.kind() == "comment" {
675 comments.push(node.range());
676 }
677 push_named_children_reversed(node, &mut stack);
678 }
679 let mut masked_comments = Vec::new();
680 for comment in comments {
681 if should_stop() {
682 return ControlFlow::Break(());
683 }
684 for span in &replacements {
685 if should_stop() {
686 return ControlFlow::Break(());
687 }
688 if span.start <= comment.start_byte && span.end >= comment.end_byte {
689 masked_comments.push(comment);
690 break;
691 }
692 }
693 }
694 let mut comments = masked_comments;
695 if comments.is_empty() {
696 return ControlFlow::Continue(None);
697 }
698 if should_stop() {
699 return ControlFlow::Break(());
700 }
701 comments.sort_by_key(|range| range.start_byte);
702 if should_stop() {
703 return ControlFlow::Break(());
704 }
705 let mut included = Vec::new();
706 let mut start_byte = 0;
707 let mut start_point = tree_sitter::Point::new(0, 0);
708 for comment in comments {
709 if should_stop() {
710 return ControlFlow::Break(());
711 }
712 if start_byte < comment.start_byte {
713 included.push(tree_sitter::Range {
714 start_byte,
715 end_byte: comment.start_byte,
716 start_point,
717 end_point: comment.start_point,
718 });
719 }
720 start_byte = comment.end_byte;
721 start_point = comment.end_point;
722 }
723 if start_byte < source.len() {
724 included.push(tree_sitter::Range {
725 start_byte,
726 end_byte: source.len(),
727 start_point,
728 end_point: tree.root_node().end_position(),
729 });
730 }
731 ControlFlow::Continue(Some(included))
732}
733
734#[cfg(test)]
735mod tests {
736 #[test]
737 fn issue_3089_macro_comments_do_not_consume_caller_function() {
738 let source = "#define PROCESS(handle, block) \\\ndo { /* comment */ \\\n int event; \\\n if (handle) block \\\n} while (0)\nstatic void caller(int handle) { PROCESS(handle, { event; }); }\n";
739 let mut parser = tree_sitter::Parser::new();
740 parser
741 .set_language(&tree_sitter_cpp::LANGUAGE.into())
742 .unwrap();
743 let tree = parser.parse(source, None).unwrap();
744 let ranges = super::function_macro_included_ranges(source, &tree, || false)
745 .continue_value()
746 .expect("complete discovery")
747 .expect("macro comments");
748 let mut parser = tree_sitter::Parser::new();
749 parser
750 .set_language(&tree_sitter_cpp::LANGUAGE.into())
751 .unwrap();
752 parser.set_included_ranges(&ranges).unwrap();
753 let tree = parser.parse(source, None).unwrap();
754 let mut stack = vec![tree.root_node()];
755 let mut functions = Vec::new();
756 while let Some(node) = stack.pop() {
757 if node.kind() == "function_definition" {
758 functions.push(node);
759 }
760 super::push_named_children_reversed(node, &mut stack);
761 }
762 assert_eq!(functions.len(), 1, "{}", tree.root_node().to_sexp());
763 assert_eq!(
764 super::node_text(
765 functions[0]
766 .child_by_field_name("declarator")
767 .unwrap()
768 .child_by_field_name("declarator")
769 .unwrap(),
770 source
771 ),
772 "caller"
773 );
774 }
775 use super::*;
776
777 fn cpp_tree(source: &str) -> tree_sitter::Tree {
778 let mut parser = Parser::new();
779 parser
780 .set_language(&tree_sitter_cpp::LANGUAGE.into())
781 .expect("C++ grammar");
782 parser.parse(source, None).expect("C++ fixture tree")
783 }
784
785 #[test]
786 fn function_macro_included_ranges_masks_only_replacement_comments() {
787 let source = "#define PROCESS(handle, block) \\\ndo { /* comment */ \\\n int event; \\\n if (handle) block \\\n} while (0)\nstatic void caller(int handle) { PROCESS(handle, { event; }); }\n";
788 let tree = cpp_tree(source);
789 let ranges = function_macro_included_ranges(source, &tree, || false)
790 .continue_value()
791 .expect("complete discovery")
792 .expect("macro comments");
793 let included = ranges
794 .iter()
795 .map(|range| &source[range.start_byte..range.end_byte])
796 .collect::<String>();
797
798 assert_eq!(included, source.replace("/* comment */", ""));
799 assert!(
800 ranges.iter().all(|range| {
801 !source[range.start_byte..range.end_byte].contains("/* comment */")
802 })
803 );
804 }
805
806 #[test]
807 fn function_macro_included_ranges_ignores_nearby_non_replacement_comment() {
808 let source = "// outside the replacement\n#define PROCESS(handle, block) \\\ndo { handle; \\\n block \\\n} while (0)\n";
809 let tree = cpp_tree(source);
810
811 assert_eq!(
812 function_macro_included_ranges(source, &tree, || false),
813 ControlFlow::Continue(None)
814 );
815 }
816
817 #[test]
818 fn function_macro_range_discovery_never_publishes_interrupted_work() {
819 let source =
820 "#define APPLY(x) do { /* first */ x; } while (0)\nint caller() { return 1; }\n";
821 let tree = cpp_tree(source);
822 let mut visits = 0;
823 let result = function_macro_included_ranges(source, &tree, || {
824 visits += 1;
825 visits == 3
826 });
827 assert_eq!(visits, 3);
828 assert_eq!(result, ControlFlow::Break(()));
829 assert!(matches!(
830 function_macro_included_ranges(source, &tree, || false),
831 ControlFlow::Continue(Some(_))
832 ));
833 }
834
835 #[test]
836 fn function_macro_included_ranges_preserves_source_coordinates() {
837 let source = "#define PROCESS(handle, block) \\\ndo { /* first */ \\\n int event; /* second */ \\\n if (handle) block \\\n} while (0)\nstatic void caller(int handle) { PROCESS(handle, { event; }); }\n";
838 let tree = cpp_tree(source);
839 let ranges = function_macro_included_ranges(source, &tree, || false)
840 .continue_value()
841 .expect("complete discovery")
842 .expect("macro comments");
843 let first = source.find("/* first */").expect("first comment");
844 let second = source.find("/* second */").expect("second comment");
845 let first_end = first + "/* first */".len();
846 let second_end = second + "/* second */".len();
847
848 assert_eq!(ranges.len(), 3);
849 assert_eq!(ranges[0].start_byte, 0);
850 assert_eq!(ranges[0].end_byte, first);
851 assert_eq!(ranges[0].start_point, tree_sitter::Point::new(0, 0));
852 assert_eq!(ranges[0].end_point, tree_sitter::Point::new(1, 5));
853 assert_eq!(ranges[1].start_byte, first_end);
854 assert_eq!(ranges[1].end_byte, second);
855 assert_eq!(ranges[1].start_point, tree_sitter::Point::new(1, 16));
856 assert_eq!(ranges[1].end_point, tree_sitter::Point::new(2, 13));
857 assert_eq!(ranges[2].start_byte, second_end);
858 assert_eq!(ranges[2].end_byte, source.len());
859 assert_eq!(ranges[2].start_point, tree_sitter::Point::new(2, 25));
860 assert_eq!(ranges[2].end_point, tree.root_node().end_position());
861 }
862
863 fn references(source: &str) -> Vec<MacroReplacementTypeReference> {
864 let mut parser = Parser::new();
865 parser
866 .set_language(&tree_sitter_cpp::LANGUAGE.into())
867 .expect("C++ grammar");
868 let tree = parser.parse(source, None).expect("macro fixture tree");
869 let value = tree
870 .root_node()
871 .named_child(0)
872 .and_then(|definition| definition.child_by_field_name("value"))
873 .expect("macro replacement");
874 object_macro_replacement_type_references(value, source)
875 }
876
877 #[test]
878 fn object_macro_replacement_reparse_preserves_type_ranges() {
879 let source = "#define SETTINGS (*api::SettingsImpl::GetInstance())\n";
880 let references = references(source);
881 let reference = references
882 .iter()
883 .find(|reference| reference.components == ["api", "SettingsImpl"])
884 .expect("qualified callable owner");
885 let rendered = reference
886 .component_ranges
887 .iter()
888 .map(|range| &source[range.clone()])
889 .collect::<Vec<_>>();
890 assert_eq!(rendered, ["api", "SettingsImpl"]);
891 }
892
893 #[test]
894 fn object_macro_replacement_fields_are_structured_and_direct_only() {
895 let replacement = object_macro_replacement(
896 r#"int public_value; \
897 union { int nested_value; }; \
898 unsigned private_value;"#,
899 );
900 assert_eq!(
901 replacement.fields,
902 vec![
903 MacroReplacementField {
904 name: "public_value".to_string(),
905 declaration: "int public_value;".to_string(),
906 field_type: Some(CppDeclaredFieldTypeFact {
907 type_text: "int".to_string(),
908 indirection: 0,
909 binds_indirectly: false,
910 template_arguments: None,
911 }),
912 names_function_type: false,
913 },
914 MacroReplacementField {
915 name: "private_value".to_string(),
916 declaration: "unsigned private_value;".to_string(),
917 field_type: Some(CppDeclaredFieldTypeFact {
918 type_text: "unsigned".to_string(),
919 indirection: 0,
920 binds_indirectly: false,
921 template_arguments: None,
922 }),
923 names_function_type: false,
924 },
925 ]
926 );
927 assert!(replacement.nested.is_empty());
928 assert!(object_macro_replacement("not a declaration").is_empty());
929 }
930
931 #[test]
936 fn comment_split_replacement_keeps_every_member_and_its_composition() {
937 let source = "#define UV_HANDLE_FIELDS \\\n\
938 \x20 /* public */ \\\n\
939 \x20 void* data; \\\n\
940 \x20 /* read-only */ \\\n\
941 \x20 uv_loop_t* loop; \\\n\
942 \x20 UV_HANDLE_PRIVATE_FIELDS \\\n\
943 \nstruct uv_handle_s { UV_HANDLE_FIELDS };\n";
944 let mut parser = Parser::new();
945 parser
946 .set_language(&tree_sitter_cpp::LANGUAGE.into())
947 .expect("C++ grammar");
948 let tree = parser.parse(source, None).expect("macro fixture tree");
949 let mut stack = vec![tree.root_node()];
950 let definition = loop {
951 let current = stack.pop().expect("the fixture defines one object macro");
952 if current.kind() == "preproc_def" {
953 break current;
954 }
955 let mut cursor = current.walk();
956 for child in current.named_children(&mut cursor) {
957 stack.push(child);
958 }
959 };
960 let value = definition
961 .child_by_field_name("value")
962 .expect("truncated replacement token");
963 assert_eq!(
964 source[value.byte_range()]
965 .trim_end()
966 .trim_end_matches('\\')
967 .trim_end(),
968 "void* data;"
969 );
970
971 let span = object_macro_replacement_span(definition, source).expect("replacement span");
972 let replacement = object_macro_replacement(&source[span]);
973 assert_eq!(
974 replacement
975 .fields
976 .iter()
977 .map(|field| field.name.as_str())
978 .collect::<Vec<_>>(),
979 ["data", "loop"]
980 );
981 assert_eq!(replacement.nested, ["UV_HANDLE_PRIVATE_FIELDS"]);
982 }
983
984 #[test]
985 fn malformed_replacement_regions_still_refuse_the_whole_replacement() {
986 assert!(object_macro_replacement("int ok; struct {").is_empty());
987 }
988
989 #[test]
990 fn conflicting_replacements_keep_only_the_members_both_declare() {
991 let unix = object_macro_replacement("uv_handle_t* next_closing; unsigned int flags;");
992 let windows = object_macro_replacement("uv_handle_t* endgame_next; unsigned int flags;");
993 assert_eq!(
994 unix.intersect(&windows).fields,
995 vec![MacroReplacementField {
996 name: "flags".to_string(),
997 declaration: "unsigned int flags;".to_string(),
998 field_type: Some(CppDeclaredFieldTypeFact {
999 type_text: "unsigned int".to_string(),
1000 indirection: 0,
1001 binds_indirectly: false,
1002 template_arguments: None,
1003 }),
1004 names_function_type: false,
1005 }]
1006 );
1007 }
1008
1009 #[test]
1010 fn collapsed_aggregate_members_recover_their_names_and_ranges() {
1011 let source = "struct uv_signal_s {\n UV_HANDLE_FIELDS\n uv_signal_cb signal_cb;\n};";
1012 let span = source.find("uv_signal_cb").expect("member start")
1013 ..source.find("signal_cb;").expect("member end") + "signal_cb;".len();
1014 let fields = recovered_aggregate_fields(source, span);
1015 assert_eq!(
1016 fields
1017 .iter()
1018 .map(|field| (field.name.as_str(), field.declaration.as_str()))
1019 .collect::<Vec<_>>(),
1020 [("signal_cb", "uv_signal_cb signal_cb;")]
1021 );
1022 assert_eq!(&source[fields[0].range.clone()], "uv_signal_cb signal_cb;");
1023 }
1024
1025 #[test]
1026 fn macro_reparse_ignores_function_like_and_non_code_text() {
1027 let function_like = "#define SETTINGS(Type) (*Type::GetInstance())\n";
1028 assert!(references(function_like).is_empty());
1029
1030 let text = "#define SETTINGS \"SettingsImpl::GetInstance()\"\n";
1031 assert!(references(text).is_empty());
1032 }
1033}