Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion crates/perry-hir/src/destructuring/var_decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,24 @@ pub(crate) fn lower_var_decl_with_destructuring(
ctx.async_generator_func_names.insert(name.clone());
}
}
let id = if let Some(pid) = pre_id {
// Annex B B.3.4: a `var <name> = init` whose name shadows a live
// `catch (<name>)` parameter assigns to the catch binding, so the
// function-scoped hoisted `var` keeps its pre-catch value. Target the
// catch parameter (innermost `lookup_local`) instead of reusing the
// hoisted id. Only a simple `var x = init` (a plain ident declarator
// with an initializer); a bare `var x;` re-declaration is inert and a
// destructuring pattern never names the catch param directly.
let shadows_catch_param = is_var_decl
&& init.is_some()
&& matches!(&decl.name, ast::Pat::Ident(_))
&& ctx
.catch_param_scopes
.iter()
.any(|scope| scope.contains(&name));
let id = if shadows_catch_param {
ctx.lookup_local(&name)
.unwrap_or_else(|| ctx.define_local(name.clone(), ty.clone()))
} else if let Some(pid) = pre_id {
pid
} else if ctx.scope_depth == 0
&& ctx.inside_block_scope == 0
Expand Down
1 change: 1 addition & 0 deletions crates/perry-hir/src/lower/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ impl LoweringContext {
unresolved_ident_as_global: false,
with_env_stack: Vec::new(),
var_hoisted_ids: HashSet::new(),
catch_param_scopes: Vec::new(),
annexb_block_fn_var_ids: HashMap::new(),
annexb_block_fn_names_all: HashSet::new(),
lexical_forward_decls: HashMap::new(),
Expand Down
9 changes: 9 additions & 0 deletions crates/perry-hir/src/lower/lowering_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,15 @@ pub struct LoweringContext {
/// continue to lexically shadow the object environment.
pub(crate) with_env_stack: Vec<WithEnvFrame>,
pub(crate) var_hoisted_ids: HashSet<LocalId>,
/// Names bound by an enclosing `catch (e)` parameter that is currently in
/// scope (a stack, innermost last). Annex B B.3.4: a `var e = init;` whose
/// name collides with a live catch parameter assigns to that *catch
/// parameter* binding, not the function-scoped hoisted `var` — so the outer
/// `var e` keeps its pre-catch value (test262 `annexB/language/statements/
/// try/catch-redeclared-var-statement`). `lower_var_decl_with_destructuring`
/// consults this to target the shadowing catch binding via `lookup_local`
/// instead of reusing the hoisted id. Pushed/popped around the catch body.
pub(crate) catch_param_scopes: Vec<HashSet<String>>,
/// Annex B B.3.3 (#5297): for the function/program scope currently being
/// lowered, maps each name declared by a *block-nested* `function f(){}`
/// (legacy sloppy-mode block-level function declaration) to the enclosing-
Expand Down
15 changes: 15 additions & 0 deletions crates/perry-hir/src/lower/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1570,7 +1570,22 @@ pub(crate) fn lower_stmt(
None
};

// Annex B B.3.4: track the (simple-ident) catch parameter name so
// a `var <name> = init` inside the catch body targets the catch
// binding, not the function-scoped hoisted `var`.
let mut pushed_catch_scope = false;
if let Some((_, ref pname)) = param {
if matches!(catch_clause.param, Some(ast::Pat::Ident(_))) {
let mut set = std::collections::HashSet::new();
set.insert(pname.clone());
ctx.catch_param_scopes.push(set);
pushed_catch_scope = true;
}
}
let mut catch_body = lower_block_stmt(ctx, &catch_clause.body)?;
if pushed_catch_scope {
ctx.catch_param_scopes.pop();
}
for (i, stmt) in binding_stmts.into_iter().enumerate() {
catch_body.insert(i, stmt);
}
Expand Down
Loading