Repository: swc-project/swc
File: crates/swc_ecma_minifier/src/compress/optimize/iife.rs (Rust)

Code before:
                if arg.spread.is_some() {
                    break;
                }

                // Optimize
                let new = self.ignore_return_value(&mut arg.expr);

                if let Some(new) = new {
                    arg.expr = Box::new(new);
                } else {
                    // Use `0` if it's removed.
                    arg.expr = Number {
                        span: arg.expr.span(),
                        value: 0.0,
                        raw: None,
                    }
                    .into();
                }
            } else {
                break;
            }
        }

        if e.args.len() > params.len() {
            for i in (params.len()..e.args.len()).rev() {
                if let Some(arg) = e.args.get_mut(i) {
                    let new = self.ignore_return_value(&mut arg.expr);

                    if let Some(new) = new {
                        arg.expr = Box::new(new);
                    } else {
                        e.args.remove(i);
                    }
                }
            }
        }
    }

    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
    pub(super) fn inline_vars_in_node<N>(&mut self, n: &mut N, mut vars: FxHashMap<Id, Box<Expr>>)
    where
        N: for<'aa> VisitMutWith<NormalMultiReplacer<'aa>>,
    {
        trace_op!("inline: inline_vars_in_node");

        let mut v = NormalMultiReplacer::new(&mut vars, false);
        n.visit_mut_with(&mut v);
        self.changed |= v.changed;
    }

    fn may_invoke_iife(&self, call: &mut CallExpr) -> bool {

Review comment (suggestion):
`params.len()` is not a safe cutoff for removing “extra” args when the callee has a rest parameter (e.g. `(function(a,...rest){use(rest)})(1,2,3)`). Args beyond `params.len()` are still part of `rest`, so removing them changes observable behavior. Consider detecting `Pat::Rest` in `params` and skipping this optimization in that case (or only removing args starting at the rest index when you can prove the rest binding is unused).

Code after:
                if arg.spread.is_some() {
                    break;
                }

                // Optimize
                let new = self.ignore_return_value(&mut arg.expr);

                if let Some(new) = new {
                    arg.expr = Box::new(new);
                } else {
                    // Use `0` if it's removed.
                    arg.expr = Number {
                        span: arg.expr.span(),
                        value: 0.0,
                        raw: None,
                    }
                    .into();
                }
            } else {
                break;
            }
        }

        if e.args.len() > params.len() && !params.iter().any(|p| p.is_rest()) {
            for i in (params.len()..e.args.len()).rev() {
                if let Some(arg) = e.args.get_mut(i) {
                    let new = self.ignore_return_value(&mut arg.expr);

                    if let Some(new) = new {
                        arg.expr = Box::new(new);
                    } else {
                        e.args.remove(i);
                    }
                }
            }
        }
    }

    #[cfg_attr(feature = "debug", tracing::instrument(level = "debug", skip_all))]
    pub(super) fn inline_vars_in_node<N>(&mut self, n: &mut N, mut vars: FxHashMap<Id, Box<Expr>>)
    where
        N: for<'aa> VisitMutWith<NormalMultiReplacer<'aa>>,
    {
        trace_op!("inline: inline_vars_in_node");

        let mut v = NormalMultiReplacer::new(&mut vars, false);
        n.visit_mut_with(&mut v);
        self.changed |= v.changed;
    }

    fn may_invoke_iife(&self, call: &mut CallExpr) -> bool {