Skip to content

Commit 3f79f52

Browse files
tausbnCopilot
andcommitted
yeast: Update documentation on location: none usage and add test
Clarified usage of `location: none` for synthetic nodes and its effects on parent node ranges. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 41e5eff commit 3f79f52

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

‎shared/yeast/doc/yeast.md‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,12 @@ leaf nodes:
300300
The override applies only to the node carrying the `location` field; nested
301301
nodes still derive their locations normally.
302302

303-
Use `location: none` for a synthetic node that intentionally has no source
304-
location. This also prevents a returned rule result from inheriting the matched
305-
input range:
303+
As an exception to the Rust-block requirement above, use `location: none` for a
304+
synthetic node that intentionally has no source location. The field-combination
305+
rules above apply only to block-valued fields: `location: none` cannot be
306+
combined with any other `location` field on the same node. A locationless node
307+
does not contribute to its parent's derived range, and a returned rule result
308+
marked this way does not inherit the matched input range:
306309

307310
```rust
308311
(metadata_node location: none)

‎shared/yeast/tests/test.rs‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,6 +1916,42 @@ fn test_location_none_makes_result_root_locationless() {
19161916
assert_eq!(call.source_range(), None);
19171917
}
19181918

1919+
/// A nested `location: none` node stays locationless and does not widen its
1920+
/// parent's derived range with an empty range at the match start.
1921+
#[test]
1922+
fn test_location_none_nested_node_does_not_widen_parent() {
1923+
let rule: Rule = rule!(
1924+
(call method: (identifier) @method)
1925+
=>
1926+
(call
1927+
method: (identifier "outer")
1928+
receiver: (call
1929+
method: {method}
1930+
arguments: (argument_list location: none)))
1931+
);
1932+
1933+
let ast = run_and_ast("foo.bar()", vec![rule]);
1934+
let nodes: Vec<_> = ast
1935+
.reachable_node_ids()
1936+
.into_iter()
1937+
.filter_map(|id| ast.get_node(id))
1938+
.collect();
1939+
let arguments = nodes
1940+
.iter()
1941+
.find(|node| node.kind_name() == "argument_list")
1942+
.expect("argument list exists");
1943+
assert_eq!(arguments.source_range(), None);
1944+
1945+
let mut call_ranges: Vec<_> = nodes
1946+
.iter()
1947+
.filter(|node| node.kind_name() == "call")
1948+
.map(|node| node.byte_range())
1949+
.collect();
1950+
call_ranges.sort_by_key(|range| (range.start, range.end));
1951+
// Without `location: none`, the inner call would be widened to 0..7.
1952+
assert_eq!(call_ranges, [0..9, 4..7]);
1953+
}
1954+
19191955
#[test]
19201956
fn test_multiple_location_fields_combine_iterable_locations() {
19211957
let rule: Rule = rule!(

0 commit comments

Comments
 (0)