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 generator pass also rewrites for-of loops that appear inside converted functions: it extracts the loop head to emit the assignment `head = _key`. It handles VarDecl and Pat heads but panics on ForHead::UsingDecl because using-declarations (`for (using x of xs)`) must be lowered by swc_ecma_transforms_proposal::explicit_resource_management before generator conversion.

Source

Thrown at crates/swc_ecma_compat_es2015/src/generator.rs:1918

            // Assign to user's variable
            let variable = match node.left {
                ForHead::VarDecl(initializer) => {
                    for variable in initializer.decls.iter() {
                        self.hoist_variable_declaration(&Ident::from(
                            variable.name.as_ident().unwrap(),
                        ));
                    }

                    initializer.decls[0].name.clone()
                }
                ForHead::Pat(mut initializer) => {
                    initializer.visit_mut_with(self);
                    *initializer
                }

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

                #[cfg(swc_ast_unknown)]
                _ => panic!("unable to access unknown nodes"),
            };
            self.emit_assignment(variable.try_into().unwrap(), Box::new(key.into()), None);
            self.transform_and_emit_embedded_stmt(*node.body);

            self.mark_label(increment_label);
            self.emit_stmt(
                ExprStmt {
                    span: DUMMY_SP,
                    expr: UpdateExpr {
                        span: DUMMY_SP,
                        prefix: false,
                        op: op!("++"),
                        arg: Box::new(keys_index.clone().into()),
                    }

View on GitHub (pinned to 5176682b65)

Solutions

  1. Put explicit_resource_management() before generator()/for_of() in your chain.
  2. Use preset_env/CompatEnv (correct ordering guaranteed).
  3. Unify swc versions via a single swc_core release.
  4. Avoid `using` in for-of heads until wired.

Example fix

// before
let pass = Chain::new(swc_ecma_compat_es2015::generator());
// after
let pass = Chain::new(
    swc_ecma_transforms_proposal::explicit_resource_management(),
    swc_ecma_compat_es2015::generator(),
);
Defensive patterns

Strategy: validation

Validate before calling

// Reuse the ForHead scan before generator conversion
use swc_ecma_visit::{Visit, VisitWith};
struct UsingHead(bool); impl Visit for UsingHead {
    fn visit_for_head(&mut self, h: &ForHead) {
        self.0 |= matches!(h, ForHead::UsingDecl(_)); h.visit_children_with(self);
    }
}
// if UsingHead found anything, prepend explicit_resource_management()

Prevention

When it happens

Trigger: A for-of loop with a `using` head inside a generator or async function, compiled by a chain that includes es2015::generator but not explicit_resource_management.

Common situations: TS 5.2+ `using` syntax compiled to ES5 through custom swc_core pipelines; bundler plugins assembling compat passes by hand; swc_core version mismatch omitting the proposal pass.

Related errors


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