Skip to content

dead_branch_remover hoists variable out of function expressions #6732

@mischnic

Description

@mischnic

Describe the bug

The dead_branch_remover hoists a variable out of a function expression in a dead branch, and then also doesn't remove the variable even though it was a dead branch.

Input code

if (false) {
    foo(function () {
        var module = {};
        return module;
    });
}

Config

use std::collections::HashSet;

use swc::{self};
use swc_atoms::JsWord;
use swc_common::{
    chain,
    comments::SingleThreadedComments,
    errors::{DiagnosticBuilder, Emitter, Handler},
    sync::Lrc,
    FileName, Globals, Mark, SourceMap, SyntaxContext, DUMMY_SP,
};
use swc_ecma_ast::{CatchClause, Decl, Id, Ident, Module, Pat, Stmt, VarDeclKind};
use swc_ecma_codegen::{text_writer::JsWriter, Config};
use swc_ecma_parser::{lexer::Lexer, EsConfig, PResult, Parser, StringInput, Syntax, TsConfig};
use swc_ecma_preset_env::{preset_env, BrowserData, Mode, Targets, Version};
use swc_ecma_transforms::{
    compat::reserved_words::reserved_words,
    fixer,
    fixer::paren_remover,
    helpers, hygiene,
    optimization::simplify::{dead_branch_remover, expr_simplifier},
    react::{self, Runtime},
    resolver,
};
use swc_ecma_visit::{Fold, FoldWith, Visit, VisitWith};

#[derive(Debug, Clone, Default)]
pub struct ErrorBuffer(std::sync::Arc<std::sync::Mutex<Vec<swc_common::errors::Diagnostic>>>);

impl Emitter for ErrorBuffer {
    fn emit(&mut self, db: &DiagnosticBuilder) {
        self.0.lock().unwrap().push((**db).clone());
    }
}

fn main() {
    let cm = Lrc::<SourceMap>::default();

    let src = r#"
if (false) {
    foo(function () {
        var module = {};
        return module;
    });
}
"#;
    let (module, comments) = parse(src, "test.js", &cm).unwrap();

    let error_buffer = ErrorBuffer::default();
    let handler = Handler::with_emitter(true, false, Box::new(error_buffer.clone()));
    swc_common::errors::HANDLER.set(&handler, || {
        swc_common::GLOBALS.set(&Globals::new(), || {
            helpers::HELPERS.set(&helpers::Helpers::new(true), || {
                let unresolved_mark = Mark::fresh(Mark::root());
                let top_level_mark = Mark::fresh(Mark::root());
                let module =
                    module.fold_with(&mut resolver(unresolved_mark, top_level_mark, false));

                let module = module.fold_with(&mut dead_branch_remover(unresolved_mark));

                let module = module.fold_with(&mut chain!(
                    reserved_words(),
                    hygiene(),
                    fixer(Some(&comments))
                ));
                let code = emit(&module, &comments, cm);
                println!("{}", code);
            });
        });
    });
}


fn parse(
    code: &str,
    filename: &str,
    cm: &Lrc<SourceMap>,
) -> PResult<(Module, SingleThreadedComments)> {
    let source_file = cm.new_source_file(FileName::Real(filename.into()), code.into());
    let comments = SingleThreadedComments::default();

    let lexer = Lexer::new(
        Syntax::Es(EsConfig {
            jsx: true,
            ..Default::default()
        }),
        // Syntax::Typescript(TsConfig {
        //     tsx: true,
        //     ..Default::default()
        // }),
        Default::default(),
        StringInput::from(&*source_file),
        Some(&comments),
    );
    let mut parser = Parser::new_from(lexer);
    match parser.parse_module() {
        Err(err) => Err(err),
        Ok(module) => Ok((module, comments)),
    }
}

fn emit(module: &Module, comments: &SingleThreadedComments, cm: Lrc<SourceMap>) -> String {
    let mut buf = vec![];
    {
        let writer = Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None));
        let config = Config {
            minify: false,
            ..Default::default()
        };
        let mut emitter = swc_ecma_codegen::Emitter {
            cfg: config,
            comments: Some(&comments),
            cm,
            wr: writer,
        };
        emitter.emit_module(&module).unwrap();
    }

    String::from_utf8(buf).unwrap()
}

Playground link

No response

Expected behavior

Everything is removed

Actual behavior

Output is

var module;

Version

0a1e30a

Additional context

parcel-bundler/parcel#7882

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions