rust-lang/rust-analyzer · error

UseTreeLists are always nested in UseTrees

Error message

UseTreeLists are always nested in UseTrees

What it means

ast::UseTreeList::parent_use_tree() asserts that a UseTreeList (the braces part of a use item) is always nested directly inside a UseTree node. It casts the parent and panics otherwise. Like the other node_ext assertions, a panic means the structural grammar invariant was violated by a synthetic/mutated tree.

Source

Thrown at crates/syntax/src/ast/node_ext.rs:465

    pub fn parent_use_tree_list(&self) -> Option<ast::UseTreeList> {
        self.syntax().parent().and_then(ast::UseTreeList::cast)
    }

    pub fn top_use_tree(&self) -> ast::UseTree {
        let mut this = self.clone();
        while let Some(use_tree_list) = this.parent_use_tree_list() {
            this = use_tree_list.parent_use_tree();
        }
        this
    }
}

impl ast::UseTreeList {
    pub fn parent_use_tree(&self) -> ast::UseTree {
        self.syntax()
            .parent()
            .and_then(ast::UseTree::cast)
            .expect("UseTreeLists are always nested in UseTrees")
    }

    pub fn has_inner_comment(&self) -> bool {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token())
            .find_map(ast::Comment::cast)
            .is_some()
    }

    pub fn comma(&self) -> impl Iterator<Item = SyntaxToken> {
        self.syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token().filter(|it| it.kind() == T![,]))
    }

    /// Remove the unnecessary braces in current `UseTreeList`
    pub fn remove_unnecessary_braces(mut self, editor: &SyntaxEditor) {

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Fetch a fresh UseTreeList from the current tree right before calling parent_use_tree() in assists.
  2. Only call on nodes obtained by parsing real use items, not hand-built fragments.
  3. Use a defensive cast (syntax().parent().and_then(ast::UseTree::cast)) when handling synthetic nodes.
  4. If it panics on ordinary source, minimize the input and file a rust-analyzer bug.

Example fix

// before
let use_tree = use_tree_list.parent_use_tree();
// after
let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast);
if let Some(use_tree) = use_tree { /* ... */ }
Defensive patterns

Strategy: type-guard

Validate before calling

// Verify the UseTreeList is nested in a UseTree before calling parent_use_tree
fn has_use_tree_parent(list: &ast::UseTreeList) -> bool {
    list.syntax().parent().map_or(false, |p| ast::UseTree::can_cast(p.kind()))
}

Type guard

fn safe_parent_use_tree(list: &ast::UseTreeList) -> Option<ast::UseTree> {
    list.syntax().parent().and_then(ast::UseTree::cast)
}

Try / catch

let use_tree = std::panic::catch_unwind(AssertUnwindSafe(|| list.parent_use_tree()))
    .ok()
    .or_else(|| list.syntax().parent().and_then(ast::UseTree::cast));

Prevention

When it happens

Trigger: Calling parent_use_tree() (directly or via remove_unnecessary_braces) on a detached or synthetic UseTreeList whose syntax parent is not a UseTree, or after edits invalidated the node's position in the tree.

Common situations: IDE assists (remove unnecessary braces) operating on stale nodes after concurrent edits; tests constructing UseTreeList nodes without an enclosing UseTree; reparse corruption (report upstream).

Related errors


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