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
11pub(crate) fn receiver_is_self_like(mut node: Node<'_>, reference_is_c: bool) -> bool {
17 loop {
18 match node.kind() {
19 "this" => return !reference_is_c,
20 "pointer_expression" | "parenthesized_expression" => {
21 let Some(inner) = node
22 .child_by_field_name("argument")
23 .or_else(|| node.named_child(0))
24 else {
25 return false;
26 };
27 node = inner;
28 }
29 _ => return false,
30 }
31 }
32}
33
34pub(crate) fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
35 while let Some(parent) = node.parent() {
36 if matches!(parent.kind(), "function_definition" | "lambda_expression") {
37 return true;
38 }
39 node = parent;
40 }
41 false
42}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
45pub struct MacroReplacementTypeReference {
46 pub components: Vec<String>,
47 pub component_ranges: Vec<Range<usize>>,
48 pub global: bool,
49}
50
51pub fn is_cpp_recovered_callable_declaration_reference(node: Node<'_>) -> bool {
62 if !matches!(node.kind(), "identifier" | "field_identifier") {
63 return false;
64 }
65 let Some(error) = node.parent().filter(|parent| parent.is_error()) else {
66 return false;
67 };
68 let Some(pointer) = error.parent().filter(|parent| {
69 parent.kind() == "pointer_declarator"
70 && parent.named_child(0) == Some(error)
71 && parent
72 .child_by_field_name("declarator")
73 .is_some_and(|declarator| declarator.kind() == "function_declarator")
74 }) else {
75 return false;
76 };
77 pointer.parent().is_some_and(|declaration| {
78 declaration.kind() == "declaration"
79 && declaration.child_by_field_name("declarator") == Some(pointer)
80 })
81}
82
83#[derive(Clone, Debug, PartialEq, Eq)]
88pub struct MacroReplacementField {
89 pub name: String,
90 pub declaration: String,
91 pub field_type: Option<CppDeclaredFieldTypeFact>,
92 pub names_function_type: bool,
93}
94
95#[derive(Clone, Debug, Default, PartialEq, Eq)]
101pub struct ObjectMacroReplacement {
102 pub fields: Vec<MacroReplacementField>,
103 pub nested: Vec<String>,
104}
105
106impl ObjectMacroReplacement {
107 pub fn is_empty(&self) -> bool {
108 self.fields.is_empty() && self.nested.is_empty()
109 }
110
111 pub fn intersect(&self, other: &Self) -> Self {
119 Self {
120 fields: self
121 .fields
122 .iter()
123 .filter(|field| other.fields.contains(field))
124 .cloned()
125 .collect(),
126 nested: self
127 .nested
128 .iter()
129 .filter(|name| other.nested.contains(name))
130 .cloned()
131 .collect(),
132 }
133 }
134}
135
136pub fn object_macro_replacement(replacement: &str) -> ObjectMacroReplacement {
149 if replacement.trim().is_empty() {
150 return ObjectMacroReplacement::default();
151 }
152 let normalized_replacement = normalize_macro_continuations(replacement);
153 const PREFIX: &str = "struct __bifrost_macro_fields { ";
154 let synthetic = format!("{PREFIX}{normalized_replacement} }};");
155 let mut parser = Parser::new();
156 if parser
157 .set_language(&tree_sitter_cpp::LANGUAGE.into())
158 .is_err()
159 {
160 return ObjectMacroReplacement::default();
161 }
162 let Some(tree) = parser.parse(&synthetic, None) else {
163 return ObjectMacroReplacement::default();
164 };
165 let mut stack = vec![tree.root_node()];
166 let body = loop {
167 let Some(current) = stack.pop() else {
168 return ObjectMacroReplacement::default();
169 };
170 if current.kind() == "struct_specifier"
171 && let Some(body) = current.child_by_field_name("body")
172 {
173 break body;
174 }
175 let mut cursor = current.walk();
176 for child in current.named_children(&mut cursor) {
177 stack.push(child);
178 }
179 };
180 let mut recovered = ObjectMacroReplacement::default();
181 let mut composed_terminators = Vec::new();
182 let mut cursor = body.walk();
183 for declaration in body.named_children(&mut cursor) {
184 if !matches!(declaration.kind(), "declaration" | "field_declaration") {
185 continue;
186 }
187 if let Some((name, terminator)) = nested_object_macro_invocation(declaration, &synthetic) {
188 recovered.nested.push(name);
189 composed_terminators.push(terminator.id());
190 continue;
191 }
192 let Some(declarator) = declaration
193 .child_by_field_name("declarator")
194 .or_else(|| declaration.named_child(1))
195 else {
196 continue;
197 };
198 let Some(name) = macro_replacement_declarator_name(declarator, &synthetic) else {
199 continue;
200 };
201 let Some(declaration_text) =
202 declaration_text_without_synthetic_prefix(declaration, replacement, PREFIX.len())
203 else {
204 continue;
205 };
206 let (field_type, names_function_type) =
207 crate::source_facts::capture_field_type(declaration, &name, &synthetic);
208 recovered.fields.push(MacroReplacementField {
209 name,
210 declaration: declaration_text,
211 field_type,
212 names_function_type,
213 });
214 }
215 if !malformed_regions_are_composition(tree.root_node(), &composed_terminators) {
216 return ObjectMacroReplacement::default();
217 }
218 recovered
219}
220
221fn nested_object_macro_invocation<'tree>(
226 declaration: Node<'tree>,
227 source: &str,
228) -> Option<(String, Node<'tree>)> {
229 if declaration.kind() != "field_declaration" {
230 return None;
231 }
232 let mut cursor = declaration.walk();
233 let children = declaration.children(&mut cursor).collect::<Vec<_>>();
234 let [name, terminator] = children.as_slice() else {
235 return None;
236 };
237 if name.kind() != "type_identifier" || terminator.kind() != ";" || !terminator.is_missing() {
238 return None;
239 }
240 let text = node_text(*name, source).trim();
241 (!text.is_empty()).then(|| (text.to_string(), *terminator))
242}
243
244fn malformed_regions_are_composition(root: Node<'_>, composed_terminators: &[usize]) -> bool {
249 let mut stack = vec![root];
250 while let Some(node) = stack.pop() {
251 if !node.has_error() && !node.is_missing() {
252 continue;
253 }
254 if (node.is_error() || node.is_missing()) && !composed_terminators.contains(&node.id()) {
255 return false;
256 }
257 let mut cursor = node.walk();
258 let children = node.children(&mut cursor).collect::<Vec<_>>();
259 stack.extend(children);
260 }
261 true
262}
263
264#[derive(Clone, Debug, PartialEq, Eq)]
267pub struct RecoveredAggregateField {
268 pub name: String,
269 pub declaration: String,
270 pub range: Range<usize>,
271 pub field_type: Option<CppDeclaredFieldTypeFact>,
272 pub names_function_type: bool,
273}
274
275pub fn recovered_aggregate_fields(
285 source: &str,
286 span: Range<usize>,
287) -> Vec<RecoveredAggregateField> {
288 let Some(region) = source.get(span.clone()) else {
289 return Vec::new();
290 };
291 if region.trim().is_empty() {
292 return Vec::new();
293 }
294 const PREFIX: &str = "struct __bifrost_recovered_members { ";
295 let synthetic = format!("{PREFIX}{region} }};");
296 let mut parser = Parser::new();
297 if parser
298 .set_language(&tree_sitter_cpp::LANGUAGE.into())
299 .is_err()
300 {
301 return Vec::new();
302 }
303 let Some(tree) = parser.parse(&synthetic, None) else {
304 return Vec::new();
305 };
306 if tree.root_node().has_error() {
307 return Vec::new();
308 }
309 let mut stack = vec![tree.root_node()];
310 let body = loop {
311 let Some(current) = stack.pop() else {
312 return Vec::new();
313 };
314 if current.kind() == "struct_specifier"
315 && let Some(body) = current.child_by_field_name("body")
316 {
317 break body;
318 }
319 let mut cursor = current.walk();
320 for child in current.named_children(&mut cursor) {
321 stack.push(child);
322 }
323 };
324 let mut fields = Vec::new();
325 let mut cursor = body.walk();
326 for declaration in body.named_children(&mut cursor) {
327 if !matches!(declaration.kind(), "declaration" | "field_declaration") {
328 continue;
329 }
330 let Some(declarator) = declaration
331 .child_by_field_name("declarator")
332 .or_else(|| declaration.named_child(1))
333 else {
334 continue;
335 };
336 let Some(name) = macro_replacement_declarator_name(declarator, &synthetic) else {
337 continue;
338 };
339 let Some(declaration_text) =
340 declaration_text_without_synthetic_prefix(declaration, region, PREFIX.len())
341 else {
342 continue;
343 };
344 let (field_type, names_function_type) =
345 crate::source_facts::capture_field_type(declaration, &name, &synthetic);
346 let start = span.start + declaration.start_byte() - PREFIX.len();
347 let end = span.start + declaration.end_byte() - PREFIX.len();
348 fields.push(RecoveredAggregateField {
349 name,
350 declaration: declaration_text,
351 range: start..end,
352 field_type,
353 names_function_type,
354 });
355 }
356 fields
357}
358
359pub fn object_macro_replacement_span(node: Node<'_>, source: &str) -> Option<Range<usize>> {
370 if node.kind() != "preproc_def" {
371 return None;
372 }
373 let start = node.child_by_field_name("name")?.end_byte();
374 macro_replacement_span(source, start)
375}
376
377pub fn function_macro_replacement_span(node: Node<'_>, source: &str) -> Option<Range<usize>> {
386 let parameters = match node.kind() {
387 "preproc_function_def" => node.child_by_field_name("parameters"),
388 "preproc_def" => {
389 let mut stack = vec![node];
392 let mut parameters = None;
393 while let Some(part) = stack.pop() {
394 if part.kind() == "preproc_params" {
395 parameters = Some(part);
396 break;
397 }
398 if part == node || part.is_error() {
399 push_named_children_reversed(part, &mut stack);
400 }
401 }
402 parameters
403 }
404 _ => None,
405 }?;
406 macro_replacement_span(source, parameters.end_byte())
407}
408
409fn macro_replacement_span(source: &str, start: usize) -> Option<Range<usize>> {
410 (start <= source.len()).then(|| start..logical_line_end(start, source))
411}
412
413fn logical_line_end(start: usize, source: &str) -> usize {
417 let bytes = source.as_bytes();
418 let mut index = start;
419 while index < bytes.len() {
420 if bytes[index] != b'\n' {
421 index += 1;
422 continue;
423 }
424 let mut previous = index;
425 if previous > start && bytes[previous - 1] == b'\r' {
426 previous -= 1;
427 }
428 if previous > start && bytes[previous - 1] == b'\\' {
429 index += 1;
430 continue;
431 }
432 break;
433 }
434 index
435}
436
437pub(crate) fn normalize_macro_continuations(replacement: &str) -> String {
443 let source = replacement.as_bytes();
444 let mut normalized = source.to_vec();
445 let mut index = 0;
446 while index + 1 < source.len() {
447 if source[index] == b'\\' && source[index + 1] == b'\n' {
448 normalized[index] = b' ';
449 normalized[index + 1] = b' ';
450 index += 2;
451 } else if index + 2 < source.len()
452 && source[index] == b'\\'
453 && source[index + 1] == b'\r'
454 && source[index + 2] == b'\n'
455 {
456 normalized[index] = b' ';
457 normalized[index + 1] = b' ';
458 normalized[index + 2] = b' ';
459 index += 3;
460 } else {
461 index += 1;
462 }
463 }
464 String::from_utf8(normalized).expect("source text must remain valid UTF-8")
465}
466
467fn macro_replacement_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
468 match node.kind() {
469 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
470 let name = node_text(node, source).trim();
471 (!name.is_empty()).then(|| name.to_string())
472 }
473 "function_declarator" => None,
474 _ => node
475 .child_by_field_name("declarator")
476 .or_else(|| node.child_by_field_name("name"))
477 .and_then(|child| macro_replacement_declarator_name(child, source)),
478 }
479}
480
481fn declaration_text_without_synthetic_prefix(
482 node: Node<'_>,
483 source: &str,
484 prefix_len: usize,
485) -> Option<String> {
486 let start = node.start_byte().checked_sub(prefix_len)?;
487 let end = node.end_byte().checked_sub(prefix_len)?;
488 (end <= source.len()).then(|| source[start..end].to_string())
489}
490
491pub fn object_macro_replacement_type_references(
499 node: Node<'_>,
500 source: &str,
501) -> Vec<MacroReplacementTypeReference> {
502 if node.kind() != "preproc_arg"
503 || !node.parent().is_some_and(|parent| {
504 parent.kind() == "preproc_def"
505 && parent
506 .child_by_field_name("value")
507 .is_some_and(|value| value == node)
508 })
509 {
510 return Vec::new();
511 }
512 let Some(replacement) = source.get(node.start_byte()..node.end_byte()) else {
513 return Vec::new();
514 };
515 const PREFIX: &str = "void __bifrost_macro_reference() { ";
516 let synthetic = format!("{PREFIX}{replacement}; }}");
517 let mut parser = Parser::new();
518 if parser
519 .set_language(&tree_sitter_cpp::LANGUAGE.into())
520 .is_err()
521 {
522 return Vec::new();
523 }
524 let Some(tree) = parser.parse(&synthetic, None) else {
525 return Vec::new();
526 };
527 if tree.root_node().has_error() {
528 return Vec::new();
529 }
530
531 let mut references = Vec::new();
532 let mut stack = vec![tree.root_node()];
533 while let Some(current) = stack.pop() {
534 let structured = if matches!(
535 current.kind(),
536 "type_identifier" | "scoped_type_identifier" | "template_type"
537 ) && !is_nested_type_node(current)
538 {
539 cpp_type_name_components(current, &synthetic)
540 .zip(cpp_name_component_nodes(current))
541 .map(|(components, nodes)| {
542 (components, nodes, is_globally_qualified_cpp_name(current))
543 })
544 } else if current.kind() == "qualified_identifier"
545 && !current.parent().is_some_and(|parent| {
546 matches!(
547 parent.kind(),
548 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
549 )
550 })
551 {
552 qualified_owner_components(current, &synthetic)
553 .map(|owner| (owner.names, owner.nodes, owner.global))
554 } else {
555 None
556 };
557 if let Some((components, component_nodes, global)) = structured {
558 let component_ranges = component_nodes
559 .into_iter()
560 .map(|component| {
561 let start = component.start_byte().checked_sub(PREFIX.len())?;
562 let end = component.end_byte().checked_sub(PREFIX.len())?;
563 (end <= replacement.len())
564 .then_some(node.start_byte() + start..node.start_byte() + end)
565 })
566 .collect::<Option<Vec<_>>>();
567 if let Some(component_ranges) = component_ranges
568 && component_ranges.len() == components.len()
569 {
570 let reference = MacroReplacementTypeReference {
571 components,
572 component_ranges,
573 global,
574 };
575 if !references.contains(&reference) {
576 references.push(reference);
577 }
578 }
579 }
580 push_named_children_reversed(current, &mut stack);
581 }
582 references
583}
584
585#[derive(Clone)]
586pub struct QualifiedCallableValue<'tree> {
587 pub qualified: Node<'tree>,
588 pub global: bool,
589 pub owner_components: Vec<Node<'tree>>,
590 pub member: Node<'tree>,
591}
592
593pub fn explicit_qualified_callable_value(node: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
600 if node.kind() != "pointer_expression" || node.child_by_field_name("operator")?.kind() != "&" {
601 return None;
602 }
603 let qualified = node.child_by_field_name("argument")?;
604 qualified_callable_value_from_node(qualified)
605}
606
607pub fn qualified_callable_value(node: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
613 if let Some(value) = explicit_qualified_callable_value(node) {
614 return Some(value);
615 }
616 if node.kind() != "qualified_identifier" {
617 return None;
618 }
619 if crate::graph::resolver::is_declaration_name(node) {
620 return None;
621 }
622 if node.parent().is_some_and(|parent| {
623 parent.child_by_field_name("type") == Some(node)
624 || (parent.kind() == "call_expression"
625 && parent.child_by_field_name("function") == Some(node))
626 || (parent.kind() == "pointer_expression"
627 && parent.child_by_field_name("argument") == Some(node))
628 || matches!(
629 parent.kind(),
630 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
631 )
632 }) {
633 return None;
634 }
635 qualified_callable_value_from_node(node)
636}
637
638fn qualified_callable_value_from_node(qualified: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
639 if qualified.kind() != "qualified_identifier" {
640 return None;
641 }
642 let mut components = Vec::new();
643 let global = qualified.child_by_field_name("scope").is_none()
644 && qualified.child(0).is_some_and(|child| child.kind() == "::");
645 append_qualified_components(qualified, &mut components)?;
646 let member = components.pop()?;
647 if components.is_empty() {
648 return None;
649 }
650 Some(QualifiedCallableValue {
651 qualified,
652 global,
653 owner_components: components,
654 member,
655 })
656}
657
658fn append_qualified_components<'tree>(node: Node<'tree>, out: &mut Vec<Node<'tree>>) -> Option<()> {
659 let mut stack = vec![node];
660 while let Some(current) = stack.pop() {
661 match current.kind() {
662 "identifier" | "namespace_identifier" | "type_identifier" | "operator_name" => {
663 out.push(current)
664 }
665 "qualified_identifier" | "scoped_identifier" => {
666 let (scope, name) = qualified_name_parts(current)?;
667 stack.push(name);
668 if let Some(scope) = scope {
669 stack.push(scope);
670 } else if current.child(0).is_none_or(|child| child.kind() != "::") {
671 return None;
672 }
673 }
674 "template_type" | "template_function" => {
675 stack.push(current.child_by_field_name("name")?);
676 }
677 "nested_namespace_specifier" => {
678 for index in (0..current.named_child_count()).rev() {
679 stack.push(current.named_child(index)?);
680 }
681 }
682 _ => return None,
683 }
684 }
685 Some(())
686}
687
688pub(super) fn qualified_name_parts<'tree>(
689 node: Node<'tree>,
690) -> Option<(Option<Node<'tree>>, Node<'tree>)> {
691 Some((
692 node.child_by_field_name("scope"),
693 node.child_by_field_name("name")?,
694 ))
695}
696
697pub fn function_macro_included_ranges(
706 source: &str,
707 tree: &tree_sitter::Tree,
708 mut should_stop: impl FnMut() -> bool,
709) -> ControlFlow<(), Option<Vec<tree_sitter::Range>>> {
710 let mut replacements = Vec::new();
711 let mut comments = Vec::new();
712 let mut stack = vec![tree.root_node()];
713 while let Some(node) = stack.pop() {
714 if should_stop() {
715 return ControlFlow::Break(());
716 }
717 if let Some(span) = function_macro_replacement_span(node, source) {
718 replacements.push(span);
719 }
720 if node.kind() == "comment" {
721 comments.push(node.range());
722 }
723 push_named_children_reversed(node, &mut stack);
724 }
725 let mut masked_comments = Vec::new();
726 for comment in comments {
727 if should_stop() {
728 return ControlFlow::Break(());
729 }
730 for span in &replacements {
731 if should_stop() {
732 return ControlFlow::Break(());
733 }
734 if span.start <= comment.start_byte && span.end >= comment.end_byte {
735 masked_comments.push(comment);
736 break;
737 }
738 }
739 }
740 let mut comments = masked_comments;
741 if comments.is_empty() {
742 return ControlFlow::Continue(None);
743 }
744 if should_stop() {
745 return ControlFlow::Break(());
746 }
747 comments.sort_by_key(|range| range.start_byte);
748 if should_stop() {
749 return ControlFlow::Break(());
750 }
751 let mut included = Vec::new();
752 let mut start_byte = 0;
753 let mut start_point = tree_sitter::Point::new(0, 0);
754 for comment in comments {
755 if should_stop() {
756 return ControlFlow::Break(());
757 }
758 if start_byte < comment.start_byte {
759 included.push(tree_sitter::Range {
760 start_byte,
761 end_byte: comment.start_byte,
762 start_point,
763 end_point: comment.start_point,
764 });
765 }
766 start_byte = comment.end_byte;
767 start_point = comment.end_point;
768 }
769 if start_byte < source.len() {
770 included.push(tree_sitter::Range {
771 start_byte,
772 end_byte: source.len(),
773 start_point,
774 end_point: tree.root_node().end_position(),
775 });
776 }
777 ControlFlow::Continue(Some(included))
778}
779
780#[cfg(test)]
781mod tests {
782 use brokk_bifrost_test_macros::hardwon;
783 #[test]
784 #[hardwon(
785 "05fbd05c6bd7: C/C++ macro recovery previously lost or invented structured declarations"
786 )]
787 fn issue_3089_macro_comments_do_not_consume_caller_function() {
788 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";
789 let mut parser = tree_sitter::Parser::new();
790 parser
791 .set_language(&tree_sitter_cpp::LANGUAGE.into())
792 .unwrap();
793 let tree = parser.parse(source, None).unwrap();
794 let ranges = super::function_macro_included_ranges(source, &tree, || false)
795 .continue_value()
796 .expect("complete discovery")
797 .expect("macro comments");
798 let mut parser = tree_sitter::Parser::new();
799 parser
800 .set_language(&tree_sitter_cpp::LANGUAGE.into())
801 .unwrap();
802 parser.set_included_ranges(&ranges).unwrap();
803 let tree = parser.parse(source, None).unwrap();
804 let mut stack = vec![tree.root_node()];
805 let mut functions = Vec::new();
806 while let Some(node) = stack.pop() {
807 if node.kind() == "function_definition" {
808 functions.push(node);
809 }
810 super::push_named_children_reversed(node, &mut stack);
811 }
812 assert_eq!(functions.len(), 1, "{}", tree.root_node().to_sexp());
813 assert_eq!(
814 super::node_text(
815 functions[0]
816 .child_by_field_name("declarator")
817 .unwrap()
818 .child_by_field_name("declarator")
819 .unwrap(),
820 source
821 ),
822 "caller"
823 );
824 }
825 use super::*;
826
827 fn references(source: &str) -> Vec<MacroReplacementTypeReference> {
828 let mut parser = Parser::new();
829 parser
830 .set_language(&tree_sitter_cpp::LANGUAGE.into())
831 .expect("C++ grammar");
832 let tree = parser.parse(source, None).expect("macro fixture tree");
833 let value = tree
834 .root_node()
835 .named_child(0)
836 .and_then(|definition| definition.child_by_field_name("value"))
837 .expect("macro replacement");
838 object_macro_replacement_type_references(value, source)
839 }
840
841 #[test]
842 #[hardwon(
843 "3d330d262275: C/C++ macro recovery previously lost or invented structured declarations"
844 )]
845 fn object_macro_replacement_reparse_preserves_type_ranges() {
846 let source = "#define SETTINGS (*api::SettingsImpl::GetInstance())\n";
847 let references = references(source);
848 let reference = references
849 .iter()
850 .find(|reference| reference.components == ["api", "SettingsImpl"])
851 .expect("qualified callable owner");
852 let rendered = reference
853 .component_ranges
854 .iter()
855 .map(|range| &source[range.clone()])
856 .collect::<Vec<_>>();
857 assert_eq!(rendered, ["api", "SettingsImpl"]);
858 }
859
860 #[test]
861 #[hardwon(
862 "3d77aac19607: C/C++ macro recovery previously lost or invented structured declarations"
863 )]
864 fn object_macro_replacement_fields_are_structured_and_direct_only() {
865 let replacement = object_macro_replacement(
866 r#"int public_value; \
867 union { int nested_value; }; \
868 unsigned private_value;"#,
869 );
870 assert_eq!(
871 replacement.fields,
872 vec![
873 MacroReplacementField {
874 name: "public_value".to_string(),
875 declaration: "int public_value;".to_string(),
876 field_type: Some(CppDeclaredFieldTypeFact {
877 type_text: "int".to_string(),
878 indirection: 0,
879 binds_indirectly: false,
880 template_arguments: None,
881 }),
882 names_function_type: false,
883 },
884 MacroReplacementField {
885 name: "private_value".to_string(),
886 declaration: "unsigned private_value;".to_string(),
887 field_type: Some(CppDeclaredFieldTypeFact {
888 type_text: "unsigned".to_string(),
889 indirection: 0,
890 binds_indirectly: false,
891 template_arguments: None,
892 }),
893 names_function_type: false,
894 },
895 ]
896 );
897 assert!(replacement.nested.is_empty());
898 assert!(object_macro_replacement("not a declaration").is_empty());
899 }
900
901 #[test]
906 #[hardwon(
907 "5e7361a53510: C/C++ macro recovery previously lost or invented structured declarations"
908 )]
909 fn comment_split_replacement_keeps_every_member_and_its_composition() {
910 let source = "#define UV_HANDLE_FIELDS \\\n\
911 \x20 /* public */ \\\n\
912 \x20 void* data; \\\n\
913 \x20 /* read-only */ \\\n\
914 \x20 uv_loop_t* loop; \\\n\
915 \x20 UV_HANDLE_PRIVATE_FIELDS \\\n\
916 \nstruct uv_handle_s { UV_HANDLE_FIELDS };\n";
917 let mut parser = Parser::new();
918 parser
919 .set_language(&tree_sitter_cpp::LANGUAGE.into())
920 .expect("C++ grammar");
921 let tree = parser.parse(source, None).expect("macro fixture tree");
922 let mut stack = vec![tree.root_node()];
923 let definition = loop {
924 let current = stack.pop().expect("the fixture defines one object macro");
925 if current.kind() == "preproc_def" {
926 break current;
927 }
928 let mut cursor = current.walk();
929 for child in current.named_children(&mut cursor) {
930 stack.push(child);
931 }
932 };
933 let value = definition
934 .child_by_field_name("value")
935 .expect("truncated replacement token");
936 assert_eq!(
937 source[value.byte_range()]
938 .trim_end()
939 .trim_end_matches('\\')
940 .trim_end(),
941 "void* data;"
942 );
943
944 let span = object_macro_replacement_span(definition, source).expect("replacement span");
945 let replacement = object_macro_replacement(&source[span]);
946 assert_eq!(
947 replacement
948 .fields
949 .iter()
950 .map(|field| field.name.as_str())
951 .collect::<Vec<_>>(),
952 ["data", "loop"]
953 );
954 assert_eq!(replacement.nested, ["UV_HANDLE_PRIVATE_FIELDS"]);
955 }
956
957 #[test]
958 #[hardwon(
959 "5e7361a53510: C/C++ macro recovery previously lost or invented structured declarations"
960 )]
961 fn malformed_replacement_regions_still_refuse_the_whole_replacement() {
962 assert!(object_macro_replacement("int ok; struct {").is_empty());
963 }
964
965 #[test]
966 #[hardwon(
967 "5e7361a53510: C/C++ macro recovery previously lost or invented structured declarations"
968 )]
969 fn conflicting_replacements_keep_only_the_members_both_declare() {
970 let unix = object_macro_replacement("uv_handle_t* next_closing; unsigned int flags;");
971 let windows = object_macro_replacement("uv_handle_t* endgame_next; unsigned int flags;");
972 assert_eq!(
973 unix.intersect(&windows).fields,
974 vec![MacroReplacementField {
975 name: "flags".to_string(),
976 declaration: "unsigned int flags;".to_string(),
977 field_type: Some(CppDeclaredFieldTypeFact {
978 type_text: "unsigned int".to_string(),
979 indirection: 0,
980 binds_indirectly: false,
981 template_arguments: None,
982 }),
983 names_function_type: false,
984 }]
985 );
986 }
987
988 #[test]
989 #[hardwon(
990 "5e7361a53510: C/C++ macro recovery previously lost or invented structured declarations"
991 )]
992 fn collapsed_aggregate_members_recover_their_names_and_ranges() {
993 let source = "struct uv_signal_s {\n UV_HANDLE_FIELDS\n uv_signal_cb signal_cb;\n};";
994 let span = source.find("uv_signal_cb").expect("member start")
995 ..source.find("signal_cb;").expect("member end") + "signal_cb;".len();
996 let fields = recovered_aggregate_fields(source, span);
997 assert_eq!(
998 fields
999 .iter()
1000 .map(|field| (field.name.as_str(), field.declaration.as_str()))
1001 .collect::<Vec<_>>(),
1002 [("signal_cb", "uv_signal_cb signal_cb;")]
1003 );
1004 assert_eq!(&source[fields[0].range.clone()], "uv_signal_cb signal_cb;");
1005 }
1006}