rust-lang/rust-analyzer · error

equivalent ancestor node should be present in target tree

Error message

equivalent ancestor node should be present in target tree

What it means

This panic occurs in rust-analyzer's syntax editor during upmap_child (syntax tree node mapping between original and edited trees). After walking down the target tree by child indices, it asserts that the child at that index exists AND is a node; the .expect fires when the index is out of bounds or the child is a token, meaning the equivalent ancestor structure assumed by the mapping no longer exists in the target tree.

Source

Thrown at crates/syntax/src/syntax_editor/mapping.rs:96

        // Progressively up-map the input ancestor until we get to the output ancestor
        let to_output_ancestor = if input_ancestor != output_ancestor {
            self.upmap_to_ancestor(input_ancestor, output_ancestor)?
        } else {
            vec![]
        };

        let to_map_down =
            to_output_ancestor.into_iter().rev().chain(to_first_upmap.into_iter().rev());

        let mut target = output_ancestor.clone();

        for index in to_map_down {
            target = target
                .children_with_tokens()
                .nth(index)
                .and_then(|it| it.into_node())
                .expect("equivalent ancestor node should be present in target tree");
        }

        debug_assert_eq!(child.kind(), target.kind());

        Ok(target)
    }

    fn upmap_to_ancestor(
        &self,
        input_ancestor: &SyntaxNode,
        output_ancestor: &SyntaxNode,
    ) -> Result<Vec<usize>, MissingMapping> {
        let mut current =
            self.upmap_node_single(input_ancestor).unwrap_or_else(|| input_ancestor.clone());
        let mut upmap_chain = vec![current.index()];

        loop {
            let Some(parent) = current.parent() else { break };

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Ensure the mapping and the target tree come from the same syntax revision; rebuild the mapping after any tree mutation.
  2. Check that indices recorded during mapping (to_map_down) are still valid for the current target node's children_with_tokens().
  3. If a mapped position may be a token, use .and_then(|it| it.into_node()) handling explicitly instead of relying on the expect.
  4. Reproduce with a minimal fixture and file an issue if this fires from a plain edit/assist — it indicates a mapping bug in rust-analyzer itself.

Example fix

// before: index assumed to point at a node
let target = target.children_with_tokens().nth(index).and_then(|it| it.into_node())
    .expect("equivalent ancestor node should be present in target tree");
// after: defensive fallback in caller-side transform
let Some(target) = target.children_with_tokens().nth(index).and_then(|it| it.into_node()) else {
    return Err(SyntaxEditorError::MappingFailed);
};
Defensive patterns

Strategy: validation

Validate before calling

fn mapped_child_is_node(target: &SyntaxNode, index: usize) -> bool {
    target
        .children_with_tokens()
        .nth(index)
        .map_or(false, |it| it.into_node().is_some())
}

Type guard

fn as_node(el: SyntaxElement) -> Option<SyntaxNode> { el.into_node() }

Try / catch

// expect! panics rather than returning Result; wrap custom transforms
let result = std::panic::catch_unwind(AssertUnwindSafe(|| editor.map_range(range)));
match result { Ok(mapped) => mapped, Err(_) => fallback_range }

Prevention

When it happens

Trigger: Calling SyntaxMapping::upmap_child (directly or via rewrite_dependent_target/upmap_child_element) with an index into children_with_tokens() that points past the end of the target node's children, or at a token rather than a node — i.e. the source and target trees have diverged structurally at that position.

Common situations: Writing a syntax-editor transform whose recorded child indices don't match the tree actually being edited (e.g. applying a mapping built for one syntax revision to another tree, or custom tree mutations that insert/remove children before the mapped index).

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/930f213b07fa1b51. Report an issue: GitHub.