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
0a1e30a4f850949ce2ec5b7e95f53cb1a3d4b771
Additional context
https://github.com/parcel-bundler/parcel/issues/7882
C-bug