swc-project/swc · error

using declaration must be removed by previous pass

Error message

using declaration must be removed by previous pass

What it means

The es2015 destructuring pass rewrites for-of/for-in loop heads. `for (using x of y)` (explicit resource management) must already be lowered to plain declarations by swc_ecma_transforms_proposal::explicit_resource_management; when destructuring encounters ForHead::UsingDecl it panics because it has no lowering for it. The pass ordering contract was violated.

Source

Thrown at crates/swc_ecma_compat_es2015/src/destructuring.rs:124

                        return;
                    }
                    _ => {
                        let left_ident = make_ref_ident_for_for_stmt();
                        let left = ForHead::Pat(left_ident.clone().into());
                        // Unpack variables
                        let stmt = AssignExpr {
                            span: DUMMY_SP,
                            left: pat.take().try_into().unwrap(),
                            op: op!("="),
                            right: Box::new(left_ident.into()),
                        }
                        .into_stmt();
                        (left, stmt)
                    }
                },

                ForHead::UsingDecl(..) => {
                    unreachable!("using declaration must be removed by previous pass")
                }

                #[cfg(swc_ast_unknown)]
                _ => panic!("unable to access unknown nodes"),
            };

            for_stmt.left = left;

            for_stmt.body = Box::new(Stmt::Block(match &mut *for_stmt.body {
                Stmt::Block(BlockStmt { span, stmts, ctxt }) => BlockStmt {
                    span: *span,
                    stmts: iter::once(stmt).chain(stmts.take()).collect(),
                    ctxt: *ctxt,
                },
                body => BlockStmt {
                    stmts: vec![stmt, body.take()],
                    ..Default::default()
                },

View on GitHub (pinned to 5176682b65)

Solutions

  1. Use the full compat preset (swc core build/CompatibleWith targets) so the explicit-resource-management lowering runs before destructuring
  2. In a manual chain, insert swc_ecma_transforms_proposal::explicit_resource_management ahead of es2015 destructuring
  3. Avoid `using` declarations in for-of heads when you control the pass list, or pre-transpile them

Example fix

// before (custom chain, missing the lowering pass)
let passes = vec![destructuring()];

// after
let passes = vec![
  Box::new(swc_ecma_transforms_proposal::explicit_resource_management()),
  destructuring(),
];
Defensive patterns

Strategy: validation

Validate before calling

// Detect `using` declarations before running a partial es2015 chain
let uses_using = regex::Regex::new(
    r#"(?:^|;|\{)\s*using\s+[A-Za-z_$]|for\s*\(?:?\s*(?:await\s+)?using\s+"#,
).unwrap();
if uses_using.is_match(&source) {
    return Err("explicit_resource_management pass must run before destructuring".into());
}

Try / catch

if let Err(p) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| destructuring(&mut program))) {
    if panic_message(&p).contains("using declaration") {
        // fix chain: insert explicit_resource_management() before destructuring and retry
    } else { std::panic::resume_unwind(p); }
}

Prevention

When it happens

Trigger: Running swc_ecma_compat_es2015::destructuring directly (or a hand-assembled chain missing the explicit-resource-management pass) on code containing `for (using res of iterable)` or `for await (using res of asyncIterable)`.

Common situations: Enabling the explicitResourceManagement parser feature but assembling compat passes manually; targeting ES2015 with a custom pass list after swc added `using` support; old pipelines replayed against newer sources.

Related errors


AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17). Data as JSON: /api/errors/d74727923f4af5aa. Report an issue: GitHub.