docs: function collapse(statements, compressor) {
                if (scope.pinned()) return statements;
                var args;
                var candidates = [];
                var stat_index = statements.length;
                var scanner = new TreeTransformer(function(node, descend) {
                    if (abort) return node;
                    // Skip nodes before `candidate` as quickly as possible
                    if (!hit) {
                        if (node !== hit_stack[hit_index]) return node;
                        hit_index++;
                        if (hit_index < hit_stack.length) return handle_custom_scan_order(node);
                        hit = true;
                        stop_after = find_stop(node, 0);
                        if (stop_after === node) abort = true;
                        return node;
                    }
                    // Stop immediately if these node types are encountered
                    var parent = scanner.parent();
                    if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left)
                        || node instanceof AST_Await
                        || node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
                        || node instanceof AST_Debugger
                        || node instanceof AST_Destructuring
                        || node instanceof AST_Expansion
                           && node.expression instanceof AST_Symbol
                           && node.expression.definition().references.length > 1
                        || node instanceof AST_IterationStatement && !(node instanceof AST_For)
                        || node instanceof AST_LoopControl
                        || node instanceof AST_Try
                        || node instanceof AST_With
                        || node instanceof AST_Yield
                        || parent instanceof AST_For && node !== parent.init
                        || !replace_all
                            && (node instanceof AST_SymbolRef && !node.is_declared(compressor))) {
                        abort = true;
                        return node;
                    }
                    // Stop only if candidate is found within conditional branches
                    if (!stop_if_hit && (!lhs_local || !replace_all)
                        && (parent instanceof AST_Binary && lazy_op(parent.operator) && parent.left !== node
                            || parent instanceof AST_Conditional && parent.condition !== node
                            || parent instanceof AST_If && parent.condition !== node)) {
                        stop_if_hit = parent;
                    }
                    // Replace variable with assignment when found
                    if (can_replace
                        && !(node instanceof AST_SymbolDeclaration)
                        && lhs.equivalent_to(node)) {
                        if (stop_if_hit) {
                            abort = true;
                            return node;
                        }
                        if (is_lhs(node, parent)) {
                            if (value_def) replaced++;
                            return node;
                        }
                        CHANGED = abort = true;
                        replaced++;
                        compressor.info("Collapsing {name} [{file}:{line},{col}]", {
                            name: node.print_to_string(),
                            file: node.start.file,
                            line: node.start.line,
                            col: node.start.col
                        });
                        if (candidate instanceof AST_UnaryPostfix) {
                            return make_node(AST_UnaryPrefix, candidate, candidate);
                        }
                        if (candidate instanceof AST_VarDef) {
                            if (value_def) {
                                abort = false;
                                return node;
                            }
                            var def = candidate.name.definition();
                            var value = candidate.value;
                            if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
                                def.replaced++;
                                if (funarg && is_identifier_atom(value)) {
                                    return value.transform(compressor);
                                } else {
                                    return maintain_this_binding(parent, node, value);
                                }
                            }
                            return make_node(AST_Assign, candidate, {
                                operator: "=",
                                left: make_node(AST_SymbolRef, candidate.name, candidate.name),
                                right: value
                            });
                        }
                        candidate.write_only = false;
                        return candidate;
                    }
                    // These node types have child nodes that execute sequentially,
                    // but are otherwise not safe to scan into or beyond them.
                    var sym;
                    if (node instanceof AST_Call
                        || node instanceof AST_Exit
                            && (side_effects || lhs instanceof AST_PropAccess || may_modify(lhs))
                        || node instanceof AST_PropAccess
                            && (side_effects || node.expression.may_throw_on_access(compressor))
                        || node instanceof AST_SymbolRef
                            && (lvalues[node.name] || side_effects && may_modify(node))
                        || node instanceof AST_VarDef && node.value
                            && (node.name.name in lvalues || side_effects && may_modify(node.name))
                        || (sym = is_lhs(node.left, node))
                            && (sym instanceof AST_PropAccess || sym.name in lvalues)
                        || may_throw
                            && (in_try ? node.has_side_effects(compressor) : side_effects_external(node))) {
                        stop_after = node;
                        if (node instanceof AST_Scope) abort = true;
                    }
                    return handle_custom_scan_order(node);
                }, function(node) {
                    if (abort) return;
                    if (stop_after === node) abort = true;
                    if (stop_if_hit === node) stop_if_hit = null;
                });
                var multi_replacer = new TreeTransformer(function(node) {
                    if (abort) return node;
                    // Skip nodes before `candidate` as quickly as possible
                    if (!hit) {
                        if (node !== hit_stack[hit_index]) return node;
                        hit_index++;
                        if (hit_index < hit_stack.length) return;
                        hit = true;
                        return node;
                    }
                    // Replace variable when found
                    if (node instanceof AST_SymbolRef
                        && node.name == def.name) {
                        if (!--replaced) abort = true;
                        if (is_lhs(node, multi_replacer.parent())) return node;
                        def.replaced++;
                        value_def.replaced--;
                        return candidate.value;
                    }
                    // Skip (non-executed) functions and (leading) default case in switch statements
                    if (node instanceof AST_Default || node instanceof AST_Scope) return node;
                });
                while (--stat_index >= 0) {
                    // Treat parameters as collapsible in IIFE, i.e.
                    //   function(a, b){ ... }(x());
                    // would be translated into equivalent assignments:
                    //   var a = x(), b = undefined;
                    if (stat_index == 0 && compressor.option("unused")) extract_args();
                    // Find collapsible assignments
                    var hit_stack = [];
                    extract_candidates(statements[stat_index]);
                    while (candidates.length > 0) {
                        hit_stack = candidates.pop();
                        var hit_index = 0;
                        var candidate = hit_stack[hit_stack.length - 1];
                        var value_def = null;
                        var stop_after = null;
                        var stop_if_hit = null;
                        var lhs = get_lhs(candidate);
                        if (!lhs || is_lhs_read_only(lhs) || lhs.has_side_effects(compressor)) continue;
                        // Locate symbols which may execute code outside of scanning range
                        var lvalues = get_lvalues(candidate);
                        var lhs_local = is_lhs_local(lhs);
                        if (lhs instanceof AST_SymbolRef) lvalues[lhs.name] = false;
                        var side_effects = value_has_side_effects(candidate);
                        var replace_all = replace_all_symbols();
                        var may_throw = candidate.may_throw(compressor);
                        var funarg = candidate.name instanceof AST_SymbolFunarg;
                        var hit = funarg;
                        var abort = false, replaced = 0, can_replace = !args || !hit;
                        if (!can_replace) {
                            for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; !abort && j < args.length; j++) {
                                args[j].transform(scanner);
                            }
                            can_replace = true;
                        }
                        for (var i = stat_index; !abort && i < statements.length; i++) {
                            statements[i].transform(scanner);
                        }
                        if (value_def) {
                            var def = candidate.name.definition();
                            if (abort && def.references.length - def.replaced > replaced) replaced = false;
                            else {
                                abort = false;
                                hit_index = 0;
                                hit = funarg;
                                for (var i = stat_index; !abort && i < statements.length; i++) {
                                    statements[i].transform(multi_replacer);
                                }
                                value_def.single_use = false;
                            }
                        }
                        if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
                    }
                }
  
                function handle_custom_scan_order(node) {
                    // Skip (non-executed) functions
                    if (node instanceof AST_Scope) return node;
                    // Scan case expressions first in a switch statement
                    if (node instanceof AST_Switch) {
                        node.expression = node.expression.transform(scanner);
                        for (var i = 0, len = node.body.length; !abort && i < len; i++) {
                            var branch = node.body[i];
                            if (branch instanceof AST_Case) {
                                if (!hit) {
                                    if (branch !== hit_stack[hit_index]) continue;
                                    hit_index++;
                                }
                                branch.expression = branch.expression.transform(scanner);
                                if (!replace_all) break;
                            }
                        }
                        abort = true;
                        return node;
                    }
                }
  
                function has_overlapping_symbol(fn, arg, fn_strict) {
                    var found = false, scan_this = !(fn instanceof AST_Arrow);
                    arg.walk(new TreeWalker(function(node, descend) {
                        if (found) return true;
                        if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
                            var s = node.definition().scope;
                            if (s !== scope) while (s = s.parent_scope) {
                                if (s === scope) return true;
                            }
                            return found = true;
                        }
                        if ((fn_strict || scan_this) && node instanceof AST_This) {
                            return found = true;
                        }
                        if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
                            var prev = scan_this;
                            scan_this = false;
                            descend();
                            scan_this = prev;
                            return true;
                        }
                    }));
                    return found;
                }
  
                function extract_args() {
                    var iife, fn = compressor.self();
                    if (is_func_expr(fn)
                        && !fn.name
                        && !fn.uses_arguments
                        && !fn.pinned()
                        && (iife = compressor.parent()) instanceof AST_Call
                        && iife.expression === fn
                        && all(iife.args, function(arg) {
                            return !(arg instanceof AST_Expansion);
                        })) {
                        var fn_strict = compressor.has_directive("use strict");
                        if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
                        var len = fn.argnames.length;
                        args = iife.args.slice(len);
                        var names = Object.create(null);
                        for (var i = len; --i >= 0;) {
                            var sym = fn.argnames[i];
                            var arg = iife.args[i];
                            args.unshift(make_node(AST_VarDef, sym, {
                                name: sym,
                                value: arg
                            }));
                            if (sym.name in names) continue;
                            names[sym.name] = true;
                            if (sym instanceof AST_Expansion) {
                                var elements = iife.args.slice(i);
                                if (all(elements, function(arg) {
                                    return !has_overlapping_symbol(fn, arg, fn_strict);
                                })) {
                                    candidates.unshift([ make_node(AST_VarDef, sym, {
                                        name: sym.expression,
                                        value: make_node(AST_Array, iife, {
                                            elements: elements
                                        })
                                    }) ]);
                                }
                            } else {
                                if (!arg) {
                                    arg = make_node(AST_Undefined, sym).transform(compressor);
                                } else if (arg instanceof AST_Lambda && arg.pinned()
                                    || has_overlapping_symbol(fn, arg, fn_strict)) {
                                    arg = null;
                                }
                                if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
                                    name: sym,
                                    value: arg
                                }) ]);
                            }
                        }
                    }
                }
  
                function extract_candidates(expr) {
                    hit_stack.push(expr);
                    if (expr instanceof AST_Assign) {
                        if (!expr.left.has_side_effects(compressor)) {
                            candidates.push(hit_stack.slice());
                        }
                        extract_candidates(expr.right);
                    } else if (expr instanceof AST_Binary) {
                        extract_candidates(expr.left);
                        extract_candidates(expr.right);
                    } else if (expr instanceof AST_Call) {
                        extract_candidates(expr.expression);
                        expr.args.forEach(extract_candidates);
                    } else if (expr instanceof AST_Case) {
                        extract_candidates(expr.expression);
                    } else if (expr instanceof AST_Conditional) {
                        extract_candidates(expr.condition);
                        extract_candidates(expr.consequent);
                        extract_candidates(expr.alternative);
                    } else if (expr instanceof AST_Definitions
                        && (compressor.option("unused") || !(expr instanceof AST_Const))) {
                        var len = expr.definitions.length;
                        // limit number of trailing variable definitions for consideration
                        var i = len - 200;
                        if (i < 0) i = 0;
                        for (; i < len; i++) {
                            extract_candidates(expr.definitions[i]);
                        }
                    } else if (expr instanceof AST_DWLoop) {
                        extract_candidates(expr.condition);
                        if (!(expr.body instanceof AST_Block)) {
                            extract_candidates(expr.body);
                        }
                    } else if (expr instanceof AST_Exit) {
                        if (expr.value) extract_candidates(expr.value);
                    } else if (expr instanceof AST_For) {
                        if (expr.init) extract_candidates(expr.init);
                        if (expr.condition) extract_candidates(expr.condition);
                        if (expr.step) extract_candidates(expr.step);
                        if (!(expr.body instanceof AST_Block)) {
                            extract_candidates(expr.body);
                        }
                    } else if (expr instanceof AST_ForIn) {
                        extract_candidates(expr.object);
                        if (!(expr.body instanceof AST_Block)) {
                            extract_candidates(expr.body);
                        }
                    } else if (expr instanceof AST_If) {
                        extract_candidates(expr.condition);
                        if (!(expr.body instanceof AST_Block)) {
                            extract_candidates(expr.body);
                        }
                        if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
                            extract_candidates(expr.alternative);
                        }
                    } else if (expr instanceof AST_Sequence) {
                        expr.expressions.forEach(extract_candidates);
                    } else if (expr instanceof AST_SimpleStatement) {
                        extract_candidates(expr.body);
                    } else if (expr instanceof AST_Switch) {
                        extract_candidates(expr.expression);
                        expr.body.forEach(extract_candidates);
                    } else if (expr instanceof AST_Unary) {
                        if (expr.operator == "++" || expr.operator == "--") {
                            candidates.push(hit_stack.slice());
                        }
                    } else if (expr instanceof AST_VarDef) {
                        if (expr.value) {
                            candidates.push(hit_stack.slice());
                            extract_candidates(expr.value);
                        }
                    }
                    hit_stack.pop();
                }
  
                function find_stop(node, level, write_only) {
                    var parent = scanner.parent(level);
                    if (parent instanceof AST_Assign) {
                        if (write_only
                            && !(parent.left instanceof AST_PropAccess
                                || parent.left.name in lvalues)) {
                            return find_stop(parent, level + 1, write_only);
                        }
                        return node;
                    }
                    if (parent instanceof AST_Binary) {
                        if (write_only && (!lazy_op(parent.operator) || parent.left === node)) {
                            return find_stop(parent, level + 1, write_only);
                        }
                        return node;
                    }
                    if (parent instanceof AST_Call) return node;
                    if (parent instanceof AST_Case) return node;
                    if (parent instanceof AST_Conditional) {
                        if (write_only && parent.condition === node) {
                            return find_stop(parent, level + 1, write_only);
                        }
                        return node;
                    }
                    if (parent instanceof AST_Definitions) {
                        return find_stop(parent, level + 1, true);
                    }
                    if (parent instanceof AST_Exit) {
                        return write_only ? find_stop(parent, level + 1, write_only) : node;
                    }
                    if (parent instanceof AST_If) {
                        if (write_only && parent.condition === node) {
                            return find_stop(parent, level + 1, write_only);
                        }
                        return node;
                    }
                    if (parent instanceof AST_IterationStatement) return node;
                    if (parent instanceof AST_Sequence) {
                        return find_stop(parent, level + 1, parent.tail_node() !== node);
                    }
                    if (parent instanceof AST_SimpleStatement) {
                        return find_stop(parent, level + 1, true);
                    }
                    if (parent instanceof AST_Switch) return node;
                    if (parent instanceof AST_VarDef) return node;
                    return null;
                }
  
                function mangleable_var(var_def) {
                    var value = var_def.value;
                    if (!(value instanceof AST_SymbolRef)) return;
                    if (value.name == "arguments") return;
                    var def = value.definition();
                    if (def.undeclared) return;
                    return value_def = def;
                }
  
                function get_lhs(expr) {
                    if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
                        var def = expr.name.definition();
                        if (!member(expr.name, def.orig)) return;
                        var referenced = def.references.length - def.replaced;
                        if (!referenced) return;
                        var declared = def.orig.length - def.eliminated;
                        if (declared > 1 && !(expr.name instanceof AST_SymbolFunarg)
                            || (referenced > 1 ? mangleable_var(expr) : !compressor.exposed(def))) {
                            return make_node(AST_SymbolRef, expr.name, expr.name);
                        }
                    } else {
                        var lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
                        return !is_ref_of(lhs, AST_SymbolConst) && lhs;
                    }
                }
  
                function get_rvalue(expr) {
                    return expr[expr instanceof AST_Assign ? "right" : "value"];
                }
  
                function get_lvalues(expr) {
                    var lvalues = Object.create(null);
                    if (expr instanceof AST_Unary) return lvalues;
                    var tw = new TreeWalker(function(node, descend) {
                        var sym = node;
                        while (sym instanceof AST_PropAccess) sym = sym.expression;
                        if (sym instanceof AST_SymbolRef || sym instanceof AST_This) {
                            lvalues[sym.name] = lvalues[sym.name] || is_modified(compressor, tw, node, node, 0);
                        }
                    });
                    get_rvalue(expr).walk(tw);
                    return lvalues;
                }
  
                function remove_candidate(expr) {
                    if (expr.name instanceof AST_SymbolFunarg) {
                        var iife = compressor.parent(), argnames = compressor.self().argnames;
                        var index = argnames.indexOf(expr.name);
                        if (index < 0) {
                            iife.args.length = Math.min(iife.args.length, argnames.length - 1);
                        } else {
                            var args = iife.args;
                            if (args[index]) args[index] = make_node(AST_Number, args[index], {
                                value: 0
                            });
                        }
                        return true;
                    }
                    var found = false;
                    return statements[stat_index].transform(new TreeTransformer(function(node, descend, in_list) {
                        if (found) return node;
                        if (node === expr || node.body === expr) {
                            found = true;
                            if (node instanceof AST_VarDef) {
                                node.value = null;
                                return node;
                            }
                            return in_list ? MAP.skip : null;
                        }
                    }, function(node) {
                        if (node instanceof AST_Sequence) switch (node.expressions.length) {
                          case 0: return null;
                          case 1: return node.expressions[0];
                        }
                    }));
                }
  
                function is_lhs_local(lhs) {
                    while (lhs instanceof AST_PropAccess) lhs = lhs.expression;
                    return lhs instanceof AST_SymbolRef
                        && lhs.definition().scope === scope
                        && !(in_loop
                            && (lhs.name in lvalues
                                || candidate instanceof AST_Unary
                                || candidate instanceof AST_Assign && candidate.operator != "="));
                }
  
                function value_has_side_effects(expr) {
                    if (expr instanceof AST_Unary) return false;
                    return get_rvalue(expr).has_side_effects(compressor);
                }
  
                function replace_all_symbols() {
                    if (side_effects) return false;
                    if (value_def) return true;
                    if (lhs instanceof AST_SymbolRef) {
                        var def = lhs.definition();
                        if (def.references.length - def.replaced == (candidate instanceof AST_VarDef ? 1 : 2)) {
                            return true;
                        }
                    }
                    return false;
                }
  
                function may_modify(sym) {
                    if (!sym.definition) return true; // AST_Destructuring
                    var def = sym.definition();
                    if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
                    if (def.scope.get_defun_scope() !== scope) return true;
                    return !all(def.references, function(ref) {
                        var s = ref.scope.get_defun_scope();
                        // "block" scope within AST_Catch
                        if (s.TYPE == "Scope") s = s.parent_scope;
                        return s === scope;
                    });
                }
  
                function side_effects_external(node, lhs) {
                    if (node instanceof AST_Assign) return side_effects_external(node.left, true);
                    if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
                    if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
                    if (lhs) {
                        if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
                        if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
                        if (node instanceof AST_SymbolRef) return node.definition().scope !== scope;
                    }
                    return false;
                }
            }