rust-lang/rust-analyzer · error

invalid delimiter `{delimiter:?}`

Error message

invalid delimiter `{delimiter:?}`

What it means

`make::token_tree(delimiter, tt)` builds an `ast::TokenTree` by rendering `tt!(...)` with the given delimiter. It only accepts the three group-opening punct kinds `T!['(']`, `T!['[']`, `T!['{']`; any other `SyntaxKind` hits the `_` arm and panics with `invalid delimiter`. The FileChange/fmt/set_roots mention in the error metadata refers to unrelated same-workspace symbols and is not the throwing item.

Source

Thrown at crates/syntax/src/ast/make.rs:1363

    inner: impl IntoIterator<Item = ast::Meta>,
) -> ast::CfgAttrMeta {
    let inner = inner.into_iter().join(", ");
    ast_from_text(&format!("#![cfg_attr({predicate}, {inner})]"))
}

pub fn cfg_flag(flag: &str) -> ast::CfgPredicate {
    ast_from_text(&format!("#![cfg({flag})]"))
}

pub fn token_tree(
    delimiter: SyntaxKind,
    tt: impl IntoIterator<Item = NodeOrToken<ast::TokenTree, SyntaxToken>>,
) -> ast::TokenTree {
    let (l_delimiter, r_delimiter) = match delimiter {
        T!['('] => ('(', ')'),
        T!['['] => ('[', ']'),
        T!['{'] => ('{', '}'),
        _ => panic!("invalid delimiter `{delimiter:?}`"),
    };
    let tt = tt.into_iter().join("");

    ast_from_text(&format!("tt!{l_delimiter}{tt}{r_delimiter}"))
}

pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
    expr_from_text(&format!("while let {pattern} = {expr} {{}}"))
}

#[track_caller]
fn expr_from_text<E: Into<ast::Expr> + AstNode>(text: &str) -> E {
    expr_from_text_with_edition(text, Edition::CURRENT)
}

#[track_caller]
fn expr_from_text_with_edition<E: Into<ast::Expr> + AstNode>(text: &str, edition: Edition) -> E {
    let parse = ast::Expr::parse(text, edition);

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Pass exactly one of `T!['(']`, `T!['[']`, `T!['{']` (punctuation SyntaxKinds, not `DelimiterKind` values).
  2. Map `tt::DelimiterKind` explicitly: Parenthesis→`T!['(']`, Brace→`T!['{']`, Bracket→`T!['[']`, and handle/skip `Invisible` before calling.
  3. Validate the kind with a match before calling and fall back to a default delimiter for unexpected values.

Example fix

// before
make::token_tree(T![')'], items); // panics: invalid delimiter
// after
make::token_tree(T!['('], items);
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_delimiter(kind: syntax::SyntaxKind) -> bool {
    use syntax::SyntaxKind::*;
    matches!(kind, T!['('] | T!['['] | T!['{'])
}
// if !is_valid_delimiter(kind) { return Err(...) }

Type guard

fn to_delimiters(kind: syntax::SyntaxKind) -> Option<(char, char)> {
    use syntax::SyntaxKind as K;
    match kind {
        K::L_PAREN => Some(('(', ')')),
        K::L_BRACK => Some(('[', ']')),
        K::L_CURLY => Some(('{', '}')),
        _ => None,
    }
}

Prevention

When it happens

Trigger: Calling `make::token_tree` with a `SyntaxKind` other than `T!['(']`, `T!['[']`, `T!['{']` — e.g. a closing-kind like `T![')']`, an arbitrary token kind, or a kind derived from a `tt::DelimiterKind` without handling `Invisible`.

Common situations: Mapping a `tt::DelimiterKind`/token kind to a make-delimiter with an incomplete match; delimiters read from token streams where `Invisible` maps to no punct kind; passing a closing delimiter kind by mistake.

Related errors


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