Skip to content

Commit 39ffd10

Browse files
Copilotaschackmull
andauthored
Include elided tokens in yeast ranges
Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com>
1 parent 53db3bd commit 39ffd10

2 files changed

Lines changed: 53 additions & 3 deletions

File tree

shared/yeast/src/lib.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -571,11 +571,17 @@ impl Ast {
571571
let source_range = match &content {
572572
// Parsed nodes already carry an exact source range in their content.
573573
NodeContent::Range(_) => source_range,
574-
// Synthesized nodes derive location from children when possible,
575-
// and fall back to the inherited rule-match range otherwise.
574+
// Synthesized nodes derive location from both their children and
575+
// the inherited rule-match range, so tokens matched by a rule but
576+
// elided from its output still contribute to the replacement range.
576577
_ => self
577578
.union_source_range_of_children(&fields)
578-
.or(source_range),
579+
.map_or(source_range, |child_range| {
580+
Some(match source_range {
581+
Some(source_range) => union_source_ranges(child_range, source_range),
582+
None => child_range,
583+
})
584+
}),
579585
};
580586
let id = self.nodes.len();
581587
self.nodes.push(Node {
@@ -766,6 +772,25 @@ impl Ast {
766772
}
767773
}
768774

775+
fn union_source_ranges(first: Range, second: Range) -> Range {
776+
let (start_byte, start_point) = if first.start_byte <= second.start_byte {
777+
(first.start_byte, first.start_point)
778+
} else {
779+
(second.start_byte, second.start_point)
780+
};
781+
let (end_byte, end_point) = if first.end_byte >= second.end_byte {
782+
(first.end_byte, first.end_point)
783+
} else {
784+
(second.end_byte, second.end_point)
785+
};
786+
Range {
787+
start_byte,
788+
end_byte,
789+
start_point,
790+
end_point,
791+
}
792+
}
793+
769794
/// A node in our AST
770795
#[derive(PartialEq, Eq, Debug, Clone, Serialize)]
771796
pub struct Node {

shared/yeast/tests/test.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1663,6 +1663,31 @@ fn test_hash_brace_uses_capture_location_for_leaf() {
16631663
assert_eq!(bar.end_byte(), 7);
16641664
}
16651665

1666+
/// Regression test: tokens matched by a rule but elided from the output still
1667+
/// contribute to the source location of the synthesized replacement node.
1668+
#[test]
1669+
fn test_elided_tokens_contribute_to_replacement_location() {
1670+
let rule: Rule = rule!(
1671+
(call
1672+
method: (identifier) @name
1673+
receiver: (identifier) @recv
1674+
)
1675+
=>
1676+
(call
1677+
method: {name}
1678+
)
1679+
);
1680+
1681+
let ast = run_and_ast("foo.bar()", vec![rule]);
1682+
let root = ast.get_node(ast.get_root()).unwrap();
1683+
let stmt_field = ast.field_id_for_name("stmt").unwrap();
1684+
let call_id = root.field_children(stmt_field)[0];
1685+
let call = ast.get_node(call_id).unwrap();
1686+
1687+
assert_eq!(call.start_byte(), 0);
1688+
assert_eq!(call.end_byte(), 7);
1689+
}
1690+
16661691
// ---- `rules!` macro tests (compile-time type-checking) ----
16671692

16681693
/// `rules!` should accept well-typed rules using the bare-rule-body

0 commit comments

Comments
 (0)