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) struct SentinelParse {
16 pub source: String,
17 pub tree: tree_sitter::Tree,
18 pub original_offsets: Vec<Range<usize>>,
21 prefix_len: usize,
22}
23
24impl SentinelParse {
25 pub fn original_range(&self, node: Node<'_>) -> Option<Range<usize>> {
26 let start = node.start_byte().checked_sub(self.prefix_len)?;
27 let end = node.end_byte().checked_sub(self.prefix_len)?;
28 Some(
29 self.original_offsets.get(start)?.start
30 ..self.original_offsets.get(end.checked_sub(1)?)?.end,
31 )
32 }
33}
34
35pub(crate) struct SentinelInput {
38 pub source: String,
39 pub original_offsets: Vec<Range<usize>>,
40}
41
42impl SentinelInput {
43 pub fn replacement(source: &str) -> Self {
44 let (source, original_offsets) = normalize_macro_continuations(source);
45 Self {
46 source,
47 original_offsets,
48 }
49 }
50
51 pub fn region(source: &str) -> Self {
52 let mut original_offsets = (0..source.len()).map(|i| i..i + 1).collect::<Vec<_>>();
53 original_offsets.push(source.len()..source.len());
54 Self {
55 source: source.to_owned(),
56 original_offsets,
57 }
58 }
59}
60
61pub(crate) fn parse_sentinel(prefix: &str, embedded: SentinelInput, suffix: &str) -> SentinelParse {
62 let SentinelInput {
63 source: replacement,
64 mut original_offsets,
65 } = embedded;
66 let original_end = original_offsets
67 .last()
68 .expect("embedded source includes its final boundary")
69 .end;
70 debug_assert_eq!(original_offsets.len(), replacement.len() + 1);
71 let source = format!("{prefix}{replacement}\n{suffix}");
74 original_offsets.extend(std::iter::repeat_n(
75 original_end..original_end,
76 1 + suffix.len(),
77 ));
78 let mut parser = Parser::new();
79 parser
80 .set_language(&tree_sitter_cpp::LANGUAGE.into())
81 .expect("C++ grammar matches the parser ABI");
82 let tree = parser
83 .parse(&source, None)
84 .expect("sentinel parse has no cancellation or timeout");
85 SentinelParse {
86 source,
87 tree,
88 original_offsets,
89 prefix_len: prefix.len(),
90 }
91}
92
93pub(crate) fn receiver_is_self_like(mut node: Node<'_>, reference_is_c: bool) -> bool {
99 loop {
100 match node.kind() {
101 "this" => return !reference_is_c,
102 "pointer_expression" | "parenthesized_expression" => {
103 let Some(inner) = node
104 .child_by_field_name("argument")
105 .or_else(|| node.named_child(0))
106 else {
107 return false;
108 };
109 node = inner;
110 }
111 _ => return false,
112 }
113 }
114}
115
116pub(crate) fn has_function_scope_ancestor(mut node: Node<'_>) -> bool {
117 while let Some(parent) = node.parent() {
118 if matches!(parent.kind(), "function_definition" | "lambda_expression") {
119 return true;
120 }
121 node = parent;
122 }
123 false
124}
125
126#[derive(Clone, Debug, PartialEq, Eq)]
127pub struct MacroReplacementTypeReference {
128 pub components: Vec<String>,
129 pub component_ranges: Vec<Range<usize>>,
130 pub global: bool,
131}
132
133pub fn is_cpp_recovered_callable_declaration_reference(node: Node<'_>) -> bool {
144 if !matches!(node.kind(), "identifier" | "field_identifier") {
145 return false;
146 }
147 let Some(error) = node.parent().filter(|parent| parent.is_error()) else {
148 return false;
149 };
150 let Some(pointer) = error.parent().filter(|parent| {
151 parent.kind() == "pointer_declarator"
152 && parent.named_child(0) == Some(error)
153 && parent
154 .child_by_field_name("declarator")
155 .is_some_and(|declarator| declarator.kind() == "function_declarator")
156 }) else {
157 return false;
158 };
159 pointer.parent().is_some_and(|declaration| {
160 declaration.kind() == "declaration"
161 && declaration.child_by_field_name("declarator") == Some(pointer)
162 })
163}
164
165#[derive(Clone, Debug, PartialEq, Eq)]
170pub struct MacroReplacementField {
171 pub name: String,
172 pub declaration: String,
173 pub field_type: Option<CppDeclaredFieldTypeFact>,
174 pub names_function_type: bool,
175}
176
177#[derive(Clone, Debug, Default, PartialEq, Eq)]
183pub struct ObjectMacroReplacement {
184 pub fields: Vec<MacroReplacementField>,
185 pub nested: Vec<String>,
186}
187
188impl ObjectMacroReplacement {
189 pub fn is_empty(&self) -> bool {
190 self.fields.is_empty() && self.nested.is_empty()
191 }
192
193 pub fn intersect(&self, other: &Self) -> Self {
201 Self {
202 fields: self
203 .fields
204 .iter()
205 .filter(|field| other.fields.contains(field))
206 .cloned()
207 .collect(),
208 nested: self
209 .nested
210 .iter()
211 .filter(|name| other.nested.contains(name))
212 .cloned()
213 .collect(),
214 }
215 }
216}
217
218pub fn object_macro_replacement(replacement: &str) -> ObjectMacroReplacement {
231 if replacement.trim().is_empty() {
232 return ObjectMacroReplacement::default();
233 }
234 const PREFIX: &str = "struct __bifrost_macro_fields { ";
235 let parsed = parse_sentinel(PREFIX, SentinelInput::replacement(replacement), " };");
236 let synthetic = parsed.source.as_str();
237 let tree = &parsed.tree;
238 let original_offsets = &parsed.original_offsets;
239 let mut stack = vec![tree.root_node()];
240 let body = loop {
241 let Some(current) = stack.pop() else {
242 return ObjectMacroReplacement::default();
243 };
244 if current.kind() == "struct_specifier"
245 && let Some(body) = current.child_by_field_name("body")
246 {
247 break body;
248 }
249 let mut cursor = current.walk();
250 for child in current.named_children(&mut cursor) {
251 stack.push(child);
252 }
253 };
254 let mut recovered = ObjectMacroReplacement::default();
255 let mut composed_terminators = Vec::new();
256 let mut cursor = body.walk();
257 for declaration in body.named_children(&mut cursor) {
258 if !matches!(declaration.kind(), "declaration" | "field_declaration") {
259 continue;
260 }
261 if let Some((name, terminator)) = nested_object_macro_invocation(declaration, synthetic) {
262 recovered.nested.push(name);
263 composed_terminators.push(terminator.id());
264 continue;
265 }
266 let Some(declarator) = declaration.child_by_field_name("declarator").or_else(|| {
267 let mut cursor = declaration.walk();
268 declaration
269 .named_children(&mut cursor)
270 .filter(|child| !child.is_extra())
271 .nth(1)
272 }) else {
273 continue;
274 };
275 let Some(name) = macro_replacement_declarator_name(declarator, synthetic) else {
276 continue;
277 };
278 let start = declaration
279 .start_byte()
280 .checked_sub(PREFIX.len())
281 .and_then(|start| original_offsets.get(start));
282 let end = declaration
283 .end_byte()
284 .checked_sub(PREFIX.len())
285 .and_then(|end| original_offsets.get(end.checked_sub(1)?));
286 let Some((start, end)) = start.zip(end) else {
287 continue;
288 };
289 let declaration_text = replacement[start.start..end.end].to_string();
290 let (field_type, names_function_type) =
291 crate::source_facts::capture_field_type(declaration, &name, synthetic);
292 recovered.fields.push(MacroReplacementField {
293 name,
294 declaration: declaration_text,
295 field_type,
296 names_function_type,
297 });
298 }
299 if !malformed_regions_are_composition(tree.root_node(), &composed_terminators) {
300 return ObjectMacroReplacement::default();
301 }
302 recovered
303}
304
305fn nested_object_macro_invocation<'tree>(
310 declaration: Node<'tree>,
311 source: &str,
312) -> Option<(String, Node<'tree>)> {
313 if declaration.kind() != "field_declaration" {
314 return None;
315 }
316 let mut cursor = declaration.walk();
317 let children = declaration.children(&mut cursor).collect::<Vec<_>>();
318 let [name, terminator] = children.as_slice() else {
319 return None;
320 };
321 if name.kind() != "type_identifier" || terminator.kind() != ";" || !terminator.is_missing() {
322 return None;
323 }
324 let text = node_text(*name, source).trim();
325 (!text.is_empty()).then(|| (text.to_string(), *terminator))
326}
327
328fn malformed_regions_are_composition(root: Node<'_>, composed_terminators: &[usize]) -> bool {
333 let mut stack = vec![root];
334 while let Some(node) = stack.pop() {
335 if !node.has_error() && !node.is_missing() {
336 continue;
337 }
338 if (node.is_error() || node.is_missing()) && !composed_terminators.contains(&node.id()) {
339 return false;
340 }
341 let mut cursor = node.walk();
342 let children = node.children(&mut cursor).collect::<Vec<_>>();
343 stack.extend(children);
344 }
345 true
346}
347
348#[derive(Clone, Debug, PartialEq, Eq)]
351pub struct RecoveredAggregateField {
352 pub name: String,
353 pub declaration: String,
354 pub range: Range<usize>,
355 pub field_type: Option<CppDeclaredFieldTypeFact>,
356 pub names_function_type: bool,
357}
358
359pub fn recovered_aggregate_fields(
369 source: &str,
370 span: Range<usize>,
371) -> Vec<RecoveredAggregateField> {
372 let Some(region) = source.get(span.clone()) else {
373 return Vec::new();
374 };
375 if region.trim().is_empty() {
376 return Vec::new();
377 }
378 const PREFIX: &str = "struct __bifrost_recovered_members { ";
379 let parsed = parse_sentinel(PREFIX, SentinelInput::region(region), " };");
380 let synthetic = parsed.source.as_str();
381 let tree = &parsed.tree;
382 if tree.root_node().has_error() {
383 return Vec::new();
384 }
385 let mut stack = vec![tree.root_node()];
386 let body = loop {
387 let Some(current) = stack.pop() else {
388 return Vec::new();
389 };
390 if current.kind() == "struct_specifier"
391 && let Some(body) = current.child_by_field_name("body")
392 {
393 break body;
394 }
395 let mut cursor = current.walk();
396 for child in current.named_children(&mut cursor) {
397 stack.push(child);
398 }
399 };
400 let mut fields = Vec::new();
401 let mut cursor = body.walk();
402 for declaration in body.named_children(&mut cursor) {
403 if !matches!(declaration.kind(), "declaration" | "field_declaration") {
404 continue;
405 }
406 let mut declarator_cursor = declaration.walk();
407 for declarator in declaration.children_by_field_name("declarator", &mut declarator_cursor) {
408 let Some(name) = macro_replacement_declarator_name(declarator, synthetic) else {
409 continue;
410 };
411 let Some(range) = parsed.original_range(declaration) else {
412 continue;
413 };
414 let Some(declaration_text) = region.get(range.clone()).map(str::to_owned) else {
415 continue;
416 };
417 let (field_type, names_function_type) =
418 crate::source_facts::capture_field_type(declaration, &name, synthetic);
419 let start = span.start + range.start;
420 let end = span.start + range.end;
421 fields.push(RecoveredAggregateField {
422 name,
423 declaration: declaration_text,
424 range: start..end,
425 field_type,
426 names_function_type,
427 });
428 }
429 }
430 fields
431}
432
433pub fn object_macro_replacement_span(node: Node<'_>, source: &str) -> Option<Range<usize>> {
444 if node.kind() != "preproc_def" {
445 return None;
446 }
447 let start = node.child_by_field_name("name")?.end_byte();
448 macro_replacement_span(source, start)
449}
450
451pub fn function_macro_replacement_span(node: Node<'_>, source: &str) -> Option<Range<usize>> {
460 let parameters = match node.kind() {
461 "preproc_function_def" => node.child_by_field_name("parameters"),
462 "preproc_def" => {
463 let mut stack = vec![node];
466 let mut parameters = None;
467 while let Some(part) = stack.pop() {
468 if part.kind() == "preproc_params" {
469 parameters = Some(part);
470 break;
471 }
472 if part == node || part.is_error() {
473 push_named_children_reversed(part, &mut stack);
474 }
475 }
476 parameters
477 }
478 _ => None,
479 }?;
480 macro_replacement_span(source, parameters.end_byte())
481}
482
483fn macro_replacement_span(source: &str, start: usize) -> Option<Range<usize>> {
484 (start <= source.len()).then(|| start..logical_line_end(start, source))
485}
486
487fn logical_line_end(start: usize, source: &str) -> usize {
491 let bytes = source.as_bytes();
492 let mut index = start;
493 while index < bytes.len() {
494 if bytes[index] != b'\n' {
495 index += 1;
496 continue;
497 }
498 let mut previous = index;
499 if previous > start && bytes[previous - 1] == b'\r' {
500 previous -= 1;
501 }
502 if previous > start && bytes[previous - 1] == b'\\' {
503 index += 1;
504 continue;
505 }
506 break;
507 }
508 index
509}
510
511fn normalize_macro_continuations(replacement: &str) -> (String, Vec<Range<usize>>) {
516 let source = replacement.as_bytes();
517 let mut normalized = Vec::with_capacity(source.len());
518 let mut original_offsets = Vec::with_capacity(source.len() + 1);
519 let mut index = 0;
520 while index < source.len() {
521 if source[index..].starts_with(b"\\\n") {
522 index += 2;
523 } else if source[index..].starts_with(b"\\\r\n") {
524 index += 3;
525 } else {
526 original_offsets.push(index..index + 1);
527 normalized.push(source[index]);
528 index += 1;
529 }
530 }
531 original_offsets.push(source.len()..source.len());
532 (
533 String::from_utf8(normalized).expect("source text remains valid UTF-8"),
534 original_offsets,
535 )
536}
537
538fn macro_replacement_declarator_name(node: Node<'_>, source: &str) -> Option<String> {
539 match node.kind() {
540 "identifier" | "field_identifier" | "type_identifier" | "qualified_identifier" => {
541 let name = node_text(node, source).trim();
542 (!name.is_empty()).then(|| name.to_string())
543 }
544 "function_declarator" => None,
545 _ => node
546 .child_by_field_name("declarator")
547 .or_else(|| node.child_by_field_name("name"))
548 .and_then(|child| macro_replacement_declarator_name(child, source)),
549 }
550}
551
552pub fn object_macro_replacement_type_references(
560 node: Node<'_>,
561 source: &str,
562) -> Vec<MacroReplacementTypeReference> {
563 if node.kind() != "preproc_arg"
564 || !node.parent().is_some_and(|parent| {
565 parent.kind() == "preproc_def"
566 && parent
567 .child_by_field_name("value")
568 .is_some_and(|value| value == node)
569 })
570 {
571 return Vec::new();
572 }
573 let Some(replacement) = source.get(node.start_byte()..node.end_byte()) else {
574 return Vec::new();
575 };
576 const PREFIX: &str = "void __bifrost_macro_reference() { ";
577 let parsed = parse_sentinel(PREFIX, SentinelInput::replacement(replacement), "; }");
578 let synthetic = parsed.source.as_str();
579 let tree = &parsed.tree;
580 if tree.root_node().has_error() {
581 return Vec::new();
582 }
583
584 let mut references = Vec::new();
585 let mut stack = vec![tree.root_node()];
586 while let Some(current) = stack.pop() {
587 let structured = if matches!(
588 current.kind(),
589 "type_identifier" | "scoped_type_identifier" | "template_type"
590 ) && !is_nested_type_node(current)
591 {
592 cpp_type_name_components(current, synthetic)
593 .zip(cpp_name_component_nodes(current))
594 .map(|(components, nodes)| {
595 (components, nodes, is_globally_qualified_cpp_name(current))
596 })
597 } else if current.kind() == "qualified_identifier"
598 && !current.parent().is_some_and(|parent| {
599 matches!(
600 parent.kind(),
601 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
602 )
603 })
604 {
605 qualified_owner_components(current, synthetic)
606 .map(|owner| (owner.names, owner.nodes, owner.global))
607 } else {
608 None
609 };
610 if let Some((components, component_nodes, global)) = structured {
611 let component_ranges = component_nodes
612 .into_iter()
613 .map(|component| {
614 let range = parsed.original_range(component)?;
615 (range.end <= replacement.len())
616 .then_some(node.start_byte() + range.start..node.start_byte() + range.end)
617 })
618 .collect::<Option<Vec<_>>>();
619 if let Some(component_ranges) = component_ranges
620 && component_ranges.len() == components.len()
621 {
622 let reference = MacroReplacementTypeReference {
623 components,
624 component_ranges,
625 global,
626 };
627 if !references.contains(&reference) {
628 references.push(reference);
629 }
630 }
631 }
632 push_named_children_reversed(current, &mut stack);
633 }
634 references
635}
636
637#[derive(Clone)]
638pub struct QualifiedCallableValue<'tree> {
639 pub qualified: Node<'tree>,
640 pub global: bool,
641 pub owner_components: Vec<Node<'tree>>,
642 pub member: Node<'tree>,
643}
644
645pub fn explicit_qualified_callable_value(node: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
652 if node.kind() != "pointer_expression" || node.child_by_field_name("operator")?.kind() != "&" {
653 return None;
654 }
655 let qualified = node.child_by_field_name("argument")?;
656 qualified_callable_value_from_node(qualified)
657}
658
659pub fn qualified_callable_value(node: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
665 if let Some(value) = explicit_qualified_callable_value(node) {
666 return Some(value);
667 }
668 if node.kind() != "qualified_identifier" {
669 return None;
670 }
671 if crate::graph::resolver::is_declaration_name(node) {
672 return None;
673 }
674 if node.parent().is_some_and(|parent| {
675 parent.child_by_field_name("type") == Some(node)
676 || (parent.kind() == "call_expression"
677 && parent.child_by_field_name("function") == Some(node))
678 || (parent.kind() == "pointer_expression"
679 && parent.child_by_field_name("argument") == Some(node))
680 || matches!(
681 parent.kind(),
682 "qualified_identifier" | "scoped_identifier" | "scoped_type_identifier"
683 )
684 }) {
685 return None;
686 }
687 qualified_callable_value_from_node(node)
688}
689
690fn qualified_callable_value_from_node(qualified: Node<'_>) -> Option<QualifiedCallableValue<'_>> {
691 if qualified.kind() != "qualified_identifier" {
692 return None;
693 }
694 let mut components = Vec::new();
695 let global = qualified.child_by_field_name("scope").is_none()
696 && qualified.child(0).is_some_and(|child| child.kind() == "::");
697 append_qualified_components(qualified, &mut components)?;
698 let member = components.pop()?;
699 if components.is_empty() {
700 return None;
701 }
702 Some(QualifiedCallableValue {
703 qualified,
704 global,
705 owner_components: components,
706 member,
707 })
708}
709
710fn append_qualified_components<'tree>(node: Node<'tree>, out: &mut Vec<Node<'tree>>) -> Option<()> {
711 let mut stack = vec![node];
712 while let Some(current) = stack.pop() {
713 match current.kind() {
714 "identifier" | "namespace_identifier" | "type_identifier" | "operator_name" => {
715 out.push(current)
716 }
717 "qualified_identifier" | "scoped_identifier" => {
718 let (scope, name) = qualified_name_parts(current)?;
719 stack.push(name);
720 if let Some(scope) = scope {
721 stack.push(scope);
722 } else if current.child(0).is_none_or(|child| child.kind() != "::") {
723 return None;
724 }
725 }
726 "template_type" | "template_function" => {
727 stack.push(current.child_by_field_name("name")?);
728 }
729 "nested_namespace_specifier" => {
730 for index in (0..current.named_child_count()).rev() {
731 stack.push(current.named_child(index)?);
732 }
733 }
734 _ => return None,
735 }
736 }
737 Some(())
738}
739
740pub(super) fn qualified_name_parts<'tree>(
741 node: Node<'tree>,
742) -> Option<(Option<Node<'tree>>, Node<'tree>)> {
743 Some((
744 node.child_by_field_name("scope"),
745 node.child_by_field_name("name")?,
746 ))
747}
748
749pub fn function_macro_included_ranges(
758 source: &str,
759 tree: &tree_sitter::Tree,
760 mut should_stop: impl FnMut() -> bool,
761) -> ControlFlow<(), Option<Vec<tree_sitter::Range>>> {
762 let mut replacements = Vec::new();
763 let mut comments = Vec::new();
764 let mut stack = vec![tree.root_node()];
765 while let Some(node) = stack.pop() {
766 if should_stop() {
767 return ControlFlow::Break(());
768 }
769 if let Some(span) = function_macro_replacement_span(node, source) {
770 replacements.push(span);
771 }
772 if node.kind() == "comment" {
773 comments.push(node.range());
774 }
775 push_named_children_reversed(node, &mut stack);
776 }
777 let mut masked_comments = Vec::new();
778 for comment in comments {
779 if should_stop() {
780 return ControlFlow::Break(());
781 }
782 for span in &replacements {
783 if should_stop() {
784 return ControlFlow::Break(());
785 }
786 if span.start <= comment.start_byte && span.end >= comment.end_byte {
787 masked_comments.push(comment);
788 break;
789 }
790 }
791 }
792 let mut comments = masked_comments;
793 if comments.is_empty() {
794 return ControlFlow::Continue(None);
795 }
796 if should_stop() {
797 return ControlFlow::Break(());
798 }
799 comments.sort_by_key(|range| range.start_byte);
800 if should_stop() {
801 return ControlFlow::Break(());
802 }
803 let mut included = Vec::new();
804 let mut start_byte = 0;
805 let mut start_point = tree_sitter::Point::new(0, 0);
806 for comment in comments {
807 if should_stop() {
808 return ControlFlow::Break(());
809 }
810 if start_byte < comment.start_byte {
811 included.push(tree_sitter::Range {
812 start_byte,
813 end_byte: comment.start_byte,
814 start_point,
815 end_point: comment.start_point,
816 });
817 }
818 start_byte = comment.end_byte;
819 start_point = comment.end_point;
820 }
821 if start_byte < source.len() {
822 included.push(tree_sitter::Range {
823 start_byte,
824 end_byte: source.len(),
825 start_point,
826 end_point: tree.root_node().end_position(),
827 });
828 }
829 ControlFlow::Continue(Some(included))
830}
831
832#[cfg(test)]
833mod tests {
834 use brokk_bifrost_test_macros::hardwon;
835 #[test]
836 #[hardwon(
837 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: line splicing split macro field tokens"
838 )]
839 fn macro_splices_join_tokens() {
840 for newline in ["\n", "\r\n"] {
841 let source = format!("in\\{newline}t val\\{newline}ue;\\{newline}int after;");
842 let replacement = object_macro_replacement(&source);
843 assert_eq!(
844 replacement
845 .fields
846 .iter()
847 .map(|field| field.name.as_str())
848 .collect::<Vec<_>>(),
849 ["value", "after"]
850 );
851 assert_eq!(
852 replacement.fields[0].declaration,
853 source[..source.find(';').unwrap() + 1]
854 );
855 assert_eq!(replacement.fields[1].declaration, "int after;");
856 assert_eq!(
857 replacement.fields[0].field_type.as_ref().unwrap().type_text,
858 "int"
859 );
860 }
861 }
862
863 #[test]
864 #[hardwon(
865 "e3c20990a105: quicksilver round 4 on brokk-bifrost-cpp: recovered aggregate lost repeated declarators"
866 )]
867 fn quicksilver_recovered_aggregate_multi_declarators() {
868 let source = "int left, right;";
869 let fields = recovered_aggregate_fields(source, 0..source.len());
870 assert_eq!(
871 fields
872 .iter()
873 .map(|field| field.name.as_str())
874 .collect::<Vec<_>>(),
875 ["left", "right"]
876 );
877 for field in fields {
878 assert_eq!(field.declaration, source);
879 assert_eq!(&source[field.range], source);
880 assert_eq!(field.field_type.unwrap().type_text, "int");
881 }
882 }
883
884 #[test]
885 #[hardwon(
886 "05fbd05c6bd7: C/C++ macro recovery previously lost or invented structured declarations"
887 )]
888 fn issue_3089_macro_comments_do_not_consume_caller_function() {
889 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";
890 let mut parser = tree_sitter::Parser::new();
891 parser
892 .set_language(&tree_sitter_cpp::LANGUAGE.into())
893 .unwrap();
894 let tree = parser.parse(source, None).unwrap();
895 let ranges = super::function_macro_included_ranges(source, &tree, || false)
896 .continue_value()
897 .expect("complete discovery")
898 .expect("macro comments");
899 let mut parser = tree_sitter::Parser::new();
900 parser
901 .set_language(&tree_sitter_cpp::LANGUAGE.into())
902 .unwrap();
903 parser.set_included_ranges(&ranges).unwrap();
904 let tree = parser.parse(source, None).unwrap();
905 let mut stack = vec![tree.root_node()];
906 let mut functions = Vec::new();
907 while let Some(node) = stack.pop() {
908 if node.kind() == "function_definition" {
909 functions.push(node);
910 }
911 super::push_named_children_reversed(node, &mut stack);
912 }
913 assert_eq!(functions.len(), 1, "{}", tree.root_node().to_sexp());
914 assert_eq!(
915 super::node_text(
916 functions[0]
917 .child_by_field_name("declarator")
918 .unwrap()
919 .child_by_field_name("declarator")
920 .unwrap(),
921 source
922 ),
923 "caller"
924 );
925 }
926 use super::*;
927
928 fn references(source: &str) -> Vec<MacroReplacementTypeReference> {
929 let mut parser = Parser::new();
930 parser
931 .set_language(&tree_sitter_cpp::LANGUAGE.into())
932 .expect("C++ grammar");
933 let tree = parser.parse(source, None).expect("macro fixture tree");
934 let value = tree
935 .root_node()
936 .named_child(0)
937 .and_then(|definition| definition.child_by_field_name("value"))
938 .expect("macro replacement");
939 object_macro_replacement_type_references(value, source)
940 }
941
942 #[test]
943 #[hardwon(
944 "3d330d262275: C/C++ macro recovery previously lost or invented structured declarations"
945 )]
946 fn object_macro_replacement_reparse_preserves_type_ranges() {
947 let source = "#define SETTINGS (*api::SettingsImpl::GetInstance())\n";
948 let references = references(source);
949 let reference = references
950 .iter()
951 .find(|reference| reference.components == ["api", "SettingsImpl"])
952 .expect("qualified callable owner");
953 let rendered = reference
954 .component_ranges
955 .iter()
956 .map(|range| &source[range.clone()])
957 .collect::<Vec<_>>();
958 assert_eq!(rendered, ["api", "SettingsImpl"]);
959 }
960
961 #[test]
962 #[hardwon(
963 "3d77aac19607: C/C++ macro recovery previously lost or invented structured declarations"
964 )]
965 fn object_macro_replacement_fields_are_structured_and_direct_only() {
966 let replacement = object_macro_replacement(
967 r#"int public_value; \
968 union { int nested_value; }; \
969 unsigned private_value;"#,
970 );
971 assert_eq!(
972 replacement.fields,
973 vec![
974 MacroReplacementField {
975 name: "public_value".to_string(),
976 declaration: "int public_value;".to_string(),
977 field_type: Some(CppDeclaredFieldTypeFact {
978 type_text: "int".to_string(),
979 indirection: 0,
980 binds_indirectly: false,
981 template_arguments: None,
982 }),
983 names_function_type: false,
984 },
985 MacroReplacementField {
986 name: "private_value".to_string(),
987 declaration: "unsigned private_value;".to_string(),
988 field_type: Some(CppDeclaredFieldTypeFact {
989 type_text: "unsigned".to_string(),
990 indirection: 0,
991 binds_indirectly: false,
992 template_arguments: None,
993 }),
994 names_function_type: false,
995 },
996 ]
997 );
998 assert!(replacement.nested.is_empty());
999 assert!(object_macro_replacement("not a declaration").is_empty());
1000 }
1001
1002 #[test]
1007 #[hardwon(
1008 "5e7361a53510: C/C++ macro recovery previously lost or invented structured declarations"
1009 )]
1010 fn comment_split_replacement_keeps_every_member_and_its_composition() {
1011 let source = "#define UV_HANDLE_FIELDS \\\n\
1012 \x20 /* public */ \\\n\
1013 \x20 void* data; \\\n\
1014 \x20 /* read-only */ \\\n\
1015 \x20 uv_loop_t* loop; \\\n\
1016 \x20 UV_HANDLE_PRIVATE_FIELDS \\\n\
1017 \nstruct uv_handle_s { UV_HANDLE_FIELDS };\n";
1018 let mut parser = Parser::new();
1019 parser
1020 .set_language(&tree_sitter_cpp::LANGUAGE.into())
1021 .expect("C++ grammar");
1022 let tree = parser.parse(source, None).expect("macro fixture tree");
1023 let mut stack = vec![tree.root_node()];
1024 let definition = loop {
1025 let current = stack.pop().expect("the fixture defines one object macro");
1026 if current.kind() == "preproc_def" {
1027 break current;
1028 }
1029 let mut cursor = current.walk();
1030 for child in current.named_children(&mut cursor) {
1031 stack.push(child);
1032 }
1033 };
1034 let value = definition
1035 .child_by_field_name("value")
1036 .expect("truncated replacement token");
1037 assert_eq!(
1038 source[value.byte_range()]
1039 .trim_end()
1040 .trim_end_matches('\\')
1041 .trim_end(),
1042 "void* data;"
1043 );
1044
1045 let span = object_macro_replacement_span(definition, source).expect("replacement span");
1046 let replacement = object_macro_replacement(&source[span]);
1047 assert_eq!(
1048 replacement
1049 .fields
1050 .iter()
1051 .map(|field| field.name.as_str())
1052 .collect::<Vec<_>>(),
1053 ["data", "loop"]
1054 );
1055 assert_eq!(replacement.nested, ["UV_HANDLE_PRIVATE_FIELDS"]);
1056 }
1057
1058 #[test]
1059 #[hardwon(
1060 "5e7361a53510: C/C++ macro recovery previously lost or invented structured declarations"
1061 )]
1062 fn malformed_replacement_regions_still_refuse_the_whole_replacement() {
1063 assert!(object_macro_replacement("int ok; struct {").is_empty());
1064 }
1065
1066 #[test]
1067 #[hardwon(
1068 "5e7361a53510: C/C++ macro recovery previously lost or invented structured declarations"
1069 )]
1070 fn conflicting_replacements_keep_only_the_members_both_declare() {
1071 let unix = object_macro_replacement("uv_handle_t* next_closing; unsigned int flags;");
1072 let windows = object_macro_replacement("uv_handle_t* endgame_next; unsigned int flags;");
1073 assert_eq!(
1074 unix.intersect(&windows).fields,
1075 vec![MacroReplacementField {
1076 name: "flags".to_string(),
1077 declaration: "unsigned int flags;".to_string(),
1078 field_type: Some(CppDeclaredFieldTypeFact {
1079 type_text: "unsigned int".to_string(),
1080 indirection: 0,
1081 binds_indirectly: false,
1082 template_arguments: None,
1083 }),
1084 names_function_type: false,
1085 }]
1086 );
1087 }
1088
1089 #[test]
1090 #[hardwon(
1091 "5e7361a53510: C/C++ macro recovery previously lost or invented structured declarations"
1092 )]
1093 fn collapsed_aggregate_members_recover_their_names_and_ranges() {
1094 let source = "struct uv_signal_s {\n UV_HANDLE_FIELDS\n uv_signal_cb signal_cb;\n};";
1095 let span = source.find("uv_signal_cb").expect("member start")
1096 ..source.find("signal_cb;").expect("member end") + "signal_cb;".len();
1097 let fields = recovered_aggregate_fields(source, span);
1098 assert_eq!(
1099 fields
1100 .iter()
1101 .map(|field| (field.name.as_str(), field.declaration.as_str()))
1102 .collect::<Vec<_>>(),
1103 [("signal_cb", "uv_signal_cb signal_cb;")]
1104 );
1105 assert_eq!(&source[fields[0].range.clone()], "uv_signal_cb signal_cb;");
1106 }
1107
1108 #[test]
1109 fn function_macro_comment_boundary_grammar() {
1110 for replacement in [
1114 "/*head*/ int phantom;",
1115 "int phantom; /*tail*/",
1116 "int /*middle*/ phantom;",
1117 ] {
1118 let source = format!("#define F(){replacement}\nvoid caller() {{ F(); }}\n");
1119 let mut parser = tree_sitter::Parser::new();
1120 parser
1121 .set_language(&tree_sitter_cpp::LANGUAGE.into())
1122 .unwrap();
1123 let raw = parser.parse(&source, None).unwrap();
1124 let ranges = super::function_macro_included_ranges(&source, &raw, || false)
1125 .continue_value()
1126 .unwrap()
1127 .expect("comment masking is required");
1128 parser.set_included_ranges(&ranges).unwrap();
1129 let tree = parser.parse(&source, None).unwrap();
1130 let declarations: Vec<_> = tree
1131 .root_node()
1132 .named_children(&mut tree.root_node().walk())
1133 .map(|n| n.kind())
1134 .collect();
1135 assert_eq!(
1136 declarations,
1137 ["preproc_function_def", "function_definition"],
1138 "{source}: {}",
1139 tree.root_node().to_sexp()
1140 );
1141 let caller = tree.root_node().named_child(1).unwrap();
1142 assert_eq!(&source[caller.byte_range()], "void caller() { F(); }");
1143 }
1144 }
1145}