swc-project/swc · error

illegal conversion: Cannot convert {:?} to BinaryExprOp

Error message

illegal conversion: Cannot convert {:?} to BinaryExprOp

What it means

SWC unifies all binary and logical operators into one BinaryOp enum, but Babel splits them across two node types: BinaryExpression (arithmetic/comparison/bitwise/in/instanceof) and LogicalExpression (&&, ||, ??). BinaryOpOutput carries either a BinOp or a LogicOp, and this From impl converts it to BinaryExprOp, panicking if the value holds the logical kind. The panic means a BinExpr node was built whose operator is logical — a shape the SWC parser never produces, so it originates from programmatic AST construction or a transform.

Source

Thrown at crates/swc_estree_compat/src/babelify/operators.rs:17

use serde::{Deserialize, Serialize};
use swc_ecma_ast::{AssignOp, BinaryOp, UnaryOp, UpdateOp};
use swc_estree_ast::{BinaryExprOp, LogicalExprOp, UnaryExprOp, UpdateExprOp};

use crate::babelify::{Babelify, Context};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BinaryOpOutput {
    BinOp(BinaryExprOp),
    LogicOp(LogicalExprOp),
}

impl From<BinaryOpOutput> for BinaryExprOp {
    fn from(o: BinaryOpOutput) -> Self {
        match o {
            BinaryOpOutput::BinOp(op) => op,
            BinaryOpOutput::LogicOp(_) => panic!(
                "illegal conversion: Cannot convert {:?} to BinaryExprOp",
                &o
            ),
        }
    }
}

impl From<BinaryOpOutput> for LogicalExprOp {
    fn from(o: BinaryOpOutput) -> Self {
        match o {
            BinaryOpOutput::LogicOp(op) => op,
            BinaryOpOutput::BinOp(_) => panic!(
                "illegal conversion: Cannot convert {:?} to LogicalExprOp",
                &o
            ),
        }
    }
}

View on GitHub (pinned to 5176682b65)

Solutions

  1. Use LogicalExpr (not BinExpr) when the operator is &&, ||, or ??
  2. If a transform rewrites operators, have it swap the node type (BinExpr <-> LogicalExpr) together with the op
  3. Validate before babelify: every BinExpr op must be non-logical, every LogicalExpr op must be logical
  4. Fall back to catch_unwind around babelify to surface a clear error

Example fix

// before
BinExpr {
    op: BinaryOp::LogicalOr,
    left,
    right,
}

// after
LogicalExpr {
    op: BinaryOp::LogicalOr,
    left,
    right,
}
Defensive patterns

Strategy: type-guard

Validate before calling

fn bin_exprs_consistent(program: &Program) -> Result<(), String> {
    for e in exprs(program) {
        if let Expr::Bin(b) = e {
            if matches!(b.op, BinaryOp::LogicalAnd | BinaryOp::LogicalOr | BinaryOp::NullishCoalescing) {
                return Err(format!("BinExpr with logical op {:?} at {:?}", b.op, b.span));
            }
        }
    }
    Ok(())
}

Type guard

fn op_is_binary(o: BinaryOp) -> bool {
    !matches!(o, BinaryOp::LogicalAnd | BinaryOp::LogicalOr | BinaryOp::NullishCoalescing)
}

Try / catch

let out = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| program.babelify(&ctx)))
    .map_err(|_| anyhow::anyhow!("illegal conversion: logical operator used in BinaryExpression"))?;

Prevention

When it happens

Trigger: Constructing a swc_ecma_ast BinExpr with op set to LogicalAnd/LogicalOr/NullishCoalescing (or mutating a BinExpr's op to a logical one) and babelifying it — the `.into()` on the babelified op hits the mismatch.

Common situations: Codemods and transforms that flip or swap operators in place; AST-building helpers/macros (e.g. swc_ecma_quote-style constructors) choosing BinExpr for `a || b`; deserializing hand-made ASTs from other tools.

Related errors


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