rust-lang/rust · error
cloning statement `NodeId`s is prohibited by default, the vi
Error message
cloning statement `NodeId`s is prohibited by default, the visitor should implement custom statement visiting
What it means
This panic in `walk_flat_map_stmt` fires when a `MutVisitor`'s `flat_map_stmt` expands a single statement into *multiple* statements (more than one element in the returned `SmallVec`). The default `NodeId`-cloning behavior would assign the same `NodeId` to all resulting statements, which corrupts node identity; the visitor must instead override statement visiting to assign fresh ids.
Source
Thrown at compiler/rustc_ast/src/mut_visit.rs:356
pub fn walk_filter_map_expr<T: MutVisitor>(vis: &mut T, mut e: Box<Expr>) -> Option<Box<Expr>> {
vis.visit_expr(&mut e);
Some(e)
}
pub fn walk_flat_map_stmt<T: MutVisitor>(
vis: &mut T,
Stmt { kind, span, mut id }: Stmt,
) -> SmallVec<[Stmt; 1]> {
vis.visit_id(&mut id);
let mut stmts: SmallVec<[Stmt; 1]> = walk_flat_map_stmt_kind(vis, kind)
.into_iter()
.map(|kind| Stmt { id, kind, span })
.collect();
match &mut stmts[..] {
[] => {}
[stmt] => vis.visit_span(&mut stmt.span),
_ => panic!(
"cloning statement `NodeId`s is prohibited by default, \
the visitor should implement custom statement visiting"
),
}
stmts
}
pub fn walk_flat_map_stmt_kind<T: MutVisitor>(
vis: &mut T,
kind: StmtKind,
) -> SmallVec<[StmtKind; 1]> {
match kind {
StmtKind::Let(mut local) => smallvec![StmtKind::Let({
vis.visit_local(&mut local);
local
})],
StmtKind::Item(item) => vis.flat_map_item(item).into_iter().map(StmtKind::Item).collect(),
StmtKind::Expr(expr) => vis.filter_map_expr(expr).into_iter().map(StmtKind::Expr).collect(),View on GitHub (pinned to 22057b88b0)
Solutions
- Override `flat_map_stmt` (or `visit_stmt`) on your `MutVisitor` to assign distinct `NodeId`s to each output statement (e.g., via `NodeId` reservation / `rustc_session`'s id allocator).
- Avoid expanding a single statement into multiple in the default walk — restructure so the visitor emits one statement per input, or pre-expands outside the flat-map.
- Reproduce with `RUST_BACKTRACE=1` to confirm which `flat_map_*` is multiplying statements.
Example fix
// before
impl<'a> MutVisitor for MyVis { /* no flat_map_stmt override; default walk expands */ }
// after
impl<'a> MutVisitor for MyVis {
fn flat_map_stmt(&mut self, mut st: Stmt) -> SmallVec<[Stmt; 1]> {
// assign fresh ids to each expanded statement
...
}
} Defensive patterns
Strategy: validation
Validate before calling
// The default flat_map visitor clones NodeId when one stmt expands to many.
// Override statement visiting in your MutVisitor:
impl MutVisitor for MyVisitor {
fn visit_stmt(&mut self, stmt: &mut Stmt) {
// assign fresh Node_id, or fold flat_map yourself
}
} Prevention
- Implement `visit_stmt` (or `flat_map_stmt`) on your `MutVisitor` whenever an AST rewrite can split one statement into several — that is the only sanctioned way to handle NodeId cloning.
- Do not rely on `walk_flat_map_stmt_kind` for transformations that change the statement count.
- If a single stmt must yield >1 stmt, mint new `NodeId`s explicitly rather than reusing the source id.
When it happens
Trigger: A `MutVisitor` implementation whose default `walk_flat_map_stmt` returns more than one `Stmt` (via `flat_map_stmt_kind` producing multiple `StmtKind`s). This happens when a visitor's `flat_map_item` / `filter_map_expr` (called from `walk_flat_map_stmt_kind`) expands a single `Item`/`Expr` statement into several — e.g., desugaring one stmt into many, or macro-like rewrites.
Common situations: Custom lints, AST rewriters, or desugaring passes in rustc that expand statements but forget to implement the matching `visit_stmt` / `flat_map_stmt` override. Refactors that add expansion behavior to an existing visitor. Hit most often when porting `MutVisitor`-based transformations.
Related errors
- empty prefix in a simple import
- attribute is missing tokens: {self:?}
- Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}
- if we had an explicit self, we wouldn't be here
- `UseTree::ident` can only be used on a simple import
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/9da4f4357138331b.json.
Report an issue: GitHub.