Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dead_branch_remover hoists variable out of function expressions #6732

Closed
mischnic opened this issue Jan 1, 2023 · 1 comment · Fixed by #6738
Closed

dead_branch_remover hoists variable out of function expressions #6732

mischnic opened this issue Jan 1, 2023 · 1 comment · Fixed by #6738
Assignees
Labels
Milestone

Comments

@mischnic
Copy link
Contributor

mischnic commented Jan 1, 2023

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

@swc-bot
Copy link
Collaborator

swc-bot commented Feb 4, 2023

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@swc-project swc-project locked as resolved and limited conversation to collaborators Feb 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Development

Successfully merging a pull request may close this issue.

3 participants