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 for-of lowering (swc_ecma_compat_es2015::for_of, array-iteration strategy) rewrites `for (... of ...)` heads into index-based ForStmt loops. It only knows how to emit the element assignment for VarDecl and Pat heads; ForHead::UsingDecl (`for (using x of xs)`, the explicit-resource-management proposal) must already have been rewritten by swc_ecma_transforms_proposal::explicit_resource_management, so seeing it is an invariant violation and panics.

Source

Thrown at crates/swc_ecma_compat_es2015/src/for_of.rs:184

                            ..Default::default()
                        }
                        .into(),
                    )
                }

                ForHead::Pat(pat) => prepend_stmt(
                    &mut body.stmts,
                    AssignExpr {
                        span: DUMMY_SP,
                        left: pat.try_into().unwrap(),
                        op: op!("="),
                        right: arr.computed_member(i).into(),
                    }
                    .into_stmt(),
                ),

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

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

            let stmt = ForStmt {
                span,
                init: Some(
                    VarDecl {
                        span: DUMMY_SP,
                        kind: VarDeclKind::Let,
                        declare: false,
                        decls,
                        ..Default::default()
                    }
                    .into(),
                ),

View on GitHub (pinned to 5176682b65)

Solutions

  1. Run swc_ecma_transforms_proposal::explicit_resource_management() before any swc_ecma_compat_es2015 for-of/generator pass in your chain.
  2. Use the standard preset_env/CompatEnv pipeline, which schedules the using-declaration lowering before compat passes.
  3. Align all swc crates on a single swc_core version.
  4. As a stopgap, avoid `using` bindings in for-of heads until your pipeline lowers them.

Example fix

// before: for-of lowering panics on `for (using res of resources)`
let pass = Chain::new(swc_ecma_compat_es2015::for_of());

// after: lower using-declarations first
let pass = Chain::new(
    swc_ecma_transforms_proposal::explicit_resource_management(),
    swc_ecma_compat_es2015::for_of(),
);
Defensive patterns

Strategy: validation

Validate before calling

use swc_ecma_ast::{ForHead, Program}; use swc_ecma_visit::{Visit, VisitWith};
struct UsingInForOf; impl Visit for UsingInForOf {
    fn visit_for_of_stmt(&mut self, n: &ForOfStmt) {
        if let ForHead::UsingDecl(_) = n.left {
            panic!("explicit_resource_management pass must run before compat es2015");
        }
        n.visit_children_with(self);
    }
}
// program.visit_with(&mut UsingInForOf);

Try / catch

let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| for_of_pass.apply(cm, &mut program)));
r.map_err(|p| map_payload_to_config_error(p, "run explicit_resource_management first"))?;

Prevention

When it happens

Trigger: Compiling `for (using x of xs) { ... }` (or `for await (using x of xs)`) through es2015 for-of lowering when the explicit_resource_management pass has not run first — typically a hand-built compat chain, or a driver that enables `using` parsing without wiring its lowering pass.

Common situations: Adopting TypeScript 5.2+ `using` declarations while targeting ES5/ES2015 via custom swc integrations; codemods/bundlers embedding swc_core that assemble compat passes manually; swc_core version mismatch dropping the proposal pass.

Related errors


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