swc-project/swc · error

add_effects for class expression

Error message

add_effects for class expression

What it means

ExprExt::extract_side_effects_to in swc_ecma_utils recursively splits an expression into its side-effecting subexpressions (used by transforms that reorder or eliminate expressions). It handles most expression kinds but class expressions (Expr::Class, i.e. an anonymous `class {}` used as a value) hit `unimplemented!("add_effects for class expression")` because nobody contributed that arm.

Solutions

  1. Guard the call site: skip or specially handle `matches!(expr, Expr::Class(_))` before calling extract_side_effects_to
  2. Rewrite the input so the class expression is hoisted into a variable (`const C = class {};`) before the transform runs
  3. Upgrade swc_ecma_utils, or if you own the fork, implement the Expr::Class arm (push the class expr itself or its computed keys/superclass)

Example fix

// before
let mut effects = Vec::new();
expr.extract_side_effects_to(&mut effects);

// after
let mut effects = Vec::new();
if matches!(&*expr, Expr::Class(_)) {
    effects.push(expr); // preserve the whole class expression
} else {
    expr.extract_side_effects_to(&mut effects);
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Rust: pre-walk the expression and bail out early
fn contains_class_expr(e: &swc_ecma_ast::Expr) -> bool {
    if matches!(e, swc_ecma_ast::Expr::Class(_)) { return true; }
    // also check immediate children relevant to your pass
    false
}

Type guard

fn is_class_expr(e: &swc_ecma_ast::Expr) -> bool {
    matches!(e, swc_ecma_ast::Expr::Class(_))
}

Try / catch

// Wrap library calls in catch_unwind when feeding third-party code through
// transform pipelines that use extract_side_effects_to
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    run_transform(program)
}));
if result.is_err() { /* log node span, skip this module, fail loudly */ }

Prevention

When it happens

Trigger: A transform or utility that calls extract_side_effects_to on an expression tree containing an anonymous class expression in value position, e.g. `x = (class { ... }, y)` or an inlined IIFE result where a class expression remains.

Common situations: Custom passes built on ExprExt that feed arbitrary user code through extract_side_effects_to; optimization/inlining scenarios where a class expression survives to the extraction step.

Related errors


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

Appendix: source

Thrown at crates/swc_ecma_utils/src/lib.rs:2021

                    self.extract_side_effects_to(v, *e.expr);

                    v
                });
            }

            Expr::TaggedTpl(TaggedTpl { tag, tpl, .. }) => {
                self.extract_side_effects_to(to, *tag);

                tpl.exprs
                    .into_iter()
                    .for_each(|e| self.extract_side_effects_to(to, *e));
            }
            Expr::Tpl(Tpl { exprs, .. }) => {
                exprs
                    .into_iter()
                    .for_each(|e| self.extract_side_effects_to(to, *e));
            }
            Expr::Class(ClassExpr { .. }) => unimplemented!("add_effects for class expression"),

            Expr::JSXMember(..)
            | Expr::JSXNamespacedName(..)
            | Expr::JSXEmpty(..)
            | Expr::JSXElement(..)
            | Expr::JSXFragment(..) => to.push(Box::new(expr)),

            Expr::TsTypeAssertion(TsTypeAssertion { expr, .. })
            | Expr::TsNonNull(TsNonNullExpr { expr, .. })
            | Expr::TsAs(TsAsExpr { expr, .. })
            | Expr::TsConstAssertion(TsConstAssertion { expr, .. })
            | Expr::TsInstantiation(TsInstantiation { expr, .. })
            | Expr::TsSatisfies(TsSatisfiesExpr { expr, .. }) => {
                self.extract_side_effects_to(to, *expr)
            }
            Expr::OptChain(..) => to.push(Box::new(expr)),

            Expr::Invalid(..) => unreachable!(),

View on GitHub (pinned to 5176682b65)