rust-lang/rust-analyzer · error

the nearest mapped ancestor must map its descendants

Error message

the nearest mapped ancestor must map its descendants

What it means

upmap_element walks up from a node to the nearest mapped ancestor, then calls upmap_child_element to map back down each ancestor level. The expect asserts that once an ancestor is mapped, mapping its descendants downward cannot fail. It fires when the internal invariant 'a mapped ancestor implies its descendants are mappable' is broken, i.e. the mapping table is inconsistent with the tree structure.

Source

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

        Err(MissingMapping(current))
    }

    pub(super) fn upmap_element(&self, input: &SyntaxElement) -> SyntaxElement {
        let mut current = input.clone();

        loop {
            let node = match &current {
                SyntaxElement::Node(node) => node.clone(),
                SyntaxElement::Token(token) => token.parent().unwrap(),
            };
            let Some((input_ancestor, output_ancestor)) = node.ancestors().find_map(|ancestor| {
                self.upmap_node_single(&ancestor).map(|output_ancestor| (ancestor, output_ancestor))
            }) else {
                return current;
            };
            current = self
                .upmap_child_element(&current, &input_ancestor, &output_ancestor.parent().unwrap())
                .expect("the nearest mapped ancestor must map its descendants");
        }
    }

    pub fn merge(&mut self, mut other: SyntaxMapping) {
        // Remap other's entry parents to be after the current list of entry parents
        let remap_base: u32 = self.entry_parents.len().try_into().unwrap();

        self.entry_parents.append(&mut other.entry_parents);
        self.node_mappings.extend(other.node_mappings.into_iter().map(|(node, entry)| {
            (node, MappingEntry { parent: entry.parent + remap_base, ..entry })
        }));
    }

    /// Follows the input one step along the syntax mapping tree
    fn upmap_node_single(&self, input: &SyntaxNode) -> Option<SyntaxNode> {
        let MappingEntry { parent, child_slot } = self.node_mappings.get(input)?;

        let output = self.entry_parents[*parent as usize]

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Build the mapping and query it against the same, unmutated pair of syntax trees.
  2. Verify that with_annotations/merge calls are applied in the order the edits were made; remapping of entry parents in merge assumes chronological consistency.
  3. If a mapped ancestor's child may legitimately disappear after an edit, handle that case explicitly instead of relying on the expect.
  4. Treat occurrences from standard edits as rust-analyzer bugs; minimize the fixture and report it.

Example fix

// before: assume mapped ancestors always map down
let current = self.upmap_child_element(&current, &input_ancestor, &output_ancestor.parent().unwrap())
    .expect("the nearest mapped ancestor must map its descendants");
// after: bail out gracefully
let Some(current) = self.upmap_child_element(&current, &input_ancestor, &output_ancestor.parent().unwrap()) else {
    return None; // ancestor mapping no longer valid for this tree
};
Defensive patterns

Strategy: validation

Validate before calling

fn ancestor_parent_valid(output_ancestor: &SyntaxNode) -> bool {
    output_ancestor.parent().is_some()
}

Type guard

fn mapped_parent(n: &SyntaxNode) -> Option<SyntaxNode> { n.parent() }

Try / catch

let mapped = std::panic::catch_unwind(AssertUnwindSafe(|| mapping.upmap_element(node)));
let target = mapped.ok().flatten().unwrap_or(node); // fall back to original node

Prevention

When it happens

Trigger: Calling SyntaxMapping::upmap_element (directly or via rewrite_dependent_target/with_annotations) on a tree where the mapped ancestor's parent chain was mutated — e.g. the output ancestor's parent() returned by upmap_node_single doesn't contain the expected child element at the recorded index.

Common situations: Building a SyntaxMapping incrementally with with_annotations across trees that were edited between annotations, or merging mappings from different edits so ancestor entries no longer match the target tree.

Related errors


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