`child_with_descendant` behavior incorrect when argument is not a descendant

Author: mgsloanCreated Sep 15, 2026Updated Sep 16, 2026

AI Policy

  • I have read the AI Policy and this issue complies with it.

Problem

nvim docs for child_with_descendant specifies a:child_with_descendant(a) == nil. However, this does not hold when a has a child matching its interval - instead it returns the child.

There are also more complex cases. If you have nodes a, b, c which are nested and have the same interval, then b.child_with_descendant(a) will return c instead of None.

Steps to reproduce

https://github.com/tree-sitter/tree-sitter/compare/master...mgsloan:tree-sitter:test/child-with-descendant-self

rust
#[test]
fn test_node_child_with_descendant_same_range() {
    let mut parser = Parser::new();
    parser.set_language(&get_language("javascript")).unwrap();
    let tree = parser.parse("(a)", None).unwrap();
    let a = tree.root_node();
    let b = a.child(0).unwrap();
    let _c = b.child(0).unwrap();

    assert_eq!(a.child_with_descendant(a), None);
    assert_eq!(b.child_with_descendant(a), None);
}

Fails with

assertion `left == right` failed
  left: Some({Node expression_statement (0, 0) - (0, 3)})
 right: None

With the first assertion commented out:

assertion `left == right` failed
   left: Some({Node parenthesized_expression (0, 0) - (0, 3)})
   right: None

AI Usage: Yes, as usual I used AI. But similar to my other issues, this is not low effort. I know Tree-sitter well, wrote this issue by hand, and checked that this all makes sense.

Expected behavior

I'd expect a.child_with_descendant(a) to be None, based on nvim docs.

For b.child_with_descendant(a), None also seems like the right answer. This means doing some tree walking for the special case where self and the argument have the same byte range.

Tree-sitter version (tree-sitter --version)

1b8407d1e718f2a26e2886c03cc55622d8d1d7bd

Operating system/version

Debian