swc-project/swc · error

illegal conversion: Cannot convert {:?} to ClassBodyEl

Error message

illegal conversion: Cannot convert {:?} to ClassBodyEl

What it means

swc_estree_compat converts the SWC AST into a Babel/ESTree-shaped AST (swc_estree_ast). Babel's ClassBody element union has no node for an empty class member, so babelify of ClassMember::Empty — what a stray `;` inside a class body parses to — panics with "illegal conversion: Cannot convert ... to ClassBodyEl" (class.rs:60).

Source

Thrown at crates/swc_estree_compat/src/babelify/class.rs:60

            ),
            id: Default::default(),
            mixins: Default::default(),
        }
    }
}

impl Babelify for ClassMember {
    type Output = ClassBodyEl;

    fn babelify(self, ctx: &Context) -> Self::Output {
        match self {
            ClassMember::Constructor(c) => ClassBodyEl::Method(c.babelify(ctx)),
            ClassMember::Method(m) => ClassBodyEl::Method(m.babelify(ctx)),
            ClassMember::PrivateMethod(m) => ClassBodyEl::PrivateMethod(m.babelify(ctx)),
            ClassMember::ClassProp(p) => ClassBodyEl::Prop(p.babelify(ctx)),
            ClassMember::PrivateProp(p) => ClassBodyEl::PrivateProp(p.babelify(ctx)),
            ClassMember::TsIndexSignature(s) => ClassBodyEl::TSIndex(s.babelify(ctx)),
            ClassMember::Empty(_) => panic!(
                "illegal conversion: Cannot convert {:?} to ClassBodyEl",
                &self
            ),
            ClassMember::StaticBlock(s) => ClassBodyEl::StaticBlock(s.babelify(ctx)),
            ClassMember::AutoAccessor(..) => todo!("auto accessor"),
            #[cfg(swc_ast_unknown)]
            _ => panic!("unable to access unknown nodes"),
        }
    }
}

impl Babelify for ClassProp {
    type Output = ClassProperty;

    fn babelify(self, ctx: &Context) -> Self::Output {
        let computed = Some(self.key.is_computed());

        ClassProperty {

View on GitHub (pinned to 5176682b65)

Solutions

  1. Remove stray semicolons from class bodies in the input source.
  2. Pre-visit the AST and strip ClassMember::Empty nodes before babelify — they are semantically void.
  3. Patch swc_estree_compat to skip Empty members instead of panicking, and report upstream.

Example fix

// before
class A {
  ;
  method() {}
}

// after
class A {
  method() {}
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Strip semantically-void empty class members before babelify.
fn drop_empty_class_members(program: &mut Program) {
    struct Clean;
    impl VisitMut for Clean {
        fn visit_mut_class_members(&mut self, n: &mut Vec<ClassMember>) {
            n.retain(|m| !matches!(m, ClassMember::Empty(_)));
            n.visit_mut_children_with(self);
        }
    }
    program.visit_mut_with(&mut Clean);
}

Type guard

fn has_empty_class_member(program: &Program) -> bool {
    struct Scan(bool);
    impl Visit for Scan {
        fn visit_class_member(&mut self, m: &ClassMember) {
            if matches!(m, ClassMember::Empty(_)) { self.0 = true; }
            m.visit_children_with(self);
        }
    }
    let mut s = Scan(false);
    program.visit_with(&mut s);
    s.0
}

Prevention

When it happens

Trigger: Calling the crate's Babelify conversion (or any tool wrapping swc_estree_compat) on a program containing `class A { ; }` or a trailing semicolon after the last member such as `class A { m() {} ; }`.

Common situations: Machine-generated code with defensive semicolons; formatters leaving `;` after members; converting an already-parsed SWC AST that kept Empty members instead of dropping them.

Related errors


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