swc-project/swc · error

method of Fold / VisitMut must accept two parameters

Error message

method of Fold / VisitMut must accept two parameters

What it means

When `#[fast_path]` patches each method of a `Fold`/`VisitMut` impl, `patch_method` takes the last entry of `m.sig.inputs` and expects it to exist. `inputs.last()` is None only when the method signature has zero parameters, so the `expect("method of Fold / VisitMut must accept two parameters")` panics during macro expansion for any associated function in the impl that declares no arguments at all. The following match also asserts the last argument is `FnArg::Typed`, not `self`.

Source

Thrown at crates/swc_ecma_transforms_macros/src/fast.rs:92

                    fn #name(&mut self, node: &mut #ty) {
                        node.visit_mut_children_with(self)
                    }
                ),
            };

            items.push(method);
        }

        items
    }

    /// Add fast path to a method
    fn patch_method(&self, mut m: ImplItemFn) -> ImplItemFn {
        let ty_arg = m
            .sig
            .inputs
            .last()
            .expect("method of Fold / VisitMut must accept two parameters");
        let ty_arg = match ty_arg {
            FnArg::Receiver(_) => unreachable!(),
            FnArg::Typed(ty) => ty,
        };
        if m.sig.ident == "visit_mut_ident" || m.sig.ident == "fold_ident" {
            return m;
        }
        if m.block.stmts.is_empty() {
            return m;
        }

        let arg = match &*ty_arg.pat {
            Pat::Ident(i) => &i.ident,
            _ => unimplemented!(
                "Fast-path injection for Fold / VisitMut where pattern is not an ident"
            ),
        };

View on GitHub (pinned to 5176682b65)

Solutions

  1. Give every fn in the `#[fast_path]`-annotated impl the visitor shape: a receiver plus the node parameter, e.g. `fn visit_mut_expr(&mut self, n: &mut Expr)`.
  2. Move zero-argument helpers (constructors, config fns) out of the annotated impl block into a separate `impl` or free functions.
  3. Methods named `visit_mut_ident`/`fold_ident` and empty-bodied methods are skipped automatically; helpers cannot be marked otherwise.

Example fix

// before
#[fast_path(ArrowVisitor)]
impl Fold for ArrowPass {
    fn config() -> Config { Config::default() } // zero params -> panic
}

// after
impl ArrowPass {
    fn config() -> Config { Config::default() } // moved out
}
#[fast_path(ArrowVisitor)]
impl Fold for ArrowPass { /* visitor methods only */ }
Defensive patterns

Strategy: validation

Validate before calling

// Every fn inside a #[fast_path] impl must have >= 1 non-self parameter.
// Check signatures before adding helpers:
fn has_visitor_shape(sig: &dyn std::any::Any) -> bool { false } // placeholder
// Practical rule (manual): keep only methods like
//   fn visit_mut_expr(&mut self, n: &mut Expr)
//   fn fold_expr(&mut self, e: Expr) -> Expr
// and move zero-arg helpers to a separate impl block.

Prevention

When it happens

Trigger: Adding a helper item inside the annotated impl such as `fn make() -> Self { ... }` or `fn default_config() -> Config { ... }` with no parameters and no `self`; the macro blindly patches every `ImplItem::Fn` in the block.

Common situations: Transform authors adding constructors or utility functions to an impl block that carries `#[fast_path]`. Note the macro only needs visitor-shaped methods like `fn fold_expr(&mut self, e: Expr) -> Expr`.

Related errors


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