diff --git a/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs b/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs index e42070c4dda57..1f21d30f37d03 100644 --- a/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs +++ b/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs @@ -102,9 +102,8 @@ impl<'a> Traverse<'a> for NullishCoalescingOperator<'a> { // ctx.ancestor(1) is AssignmentPattern // ctx.ancestor(2) is BindingPattern; // ctx.ancestor(3) is FormalParameter - let is_parent_formal_parameter = ctx - .ancestor(3) - .is_some_and(|ancestor| matches!(ancestor, Ancestor::FormalParameterPattern(_))); + let is_parent_formal_parameter = + matches!(ctx.ancestor(3), Ancestor::FormalParameterPattern(_)); let current_scope_id = if is_parent_formal_parameter { ctx.create_child_scope_of_current(ScopeFlags::Arrow | ScopeFlags::Function) diff --git a/crates/oxc_traverse/src/context/ancestry.rs b/crates/oxc_traverse/src/context/ancestry.rs index f96203e4882b0..67790532dfc6c 100644 --- a/crates/oxc_traverse/src/context/ancestry.rs +++ b/crates/oxc_traverse/src/context/ancestry.rs @@ -59,14 +59,22 @@ impl<'a> TraverseAncestry<'a> { /// /// `level` is number of levels above. /// `ancestor(1).unwrap()` is equivalent to `parent()`. + /// + /// If `level` is out of bounds (above `Program`), returns `Ancestor::None`. #[inline] - pub fn ancestor<'t>(&'t self, level: usize) -> Option> { - self.stack.get(self.stack.len() - level).map(|&ancestor| { + pub fn ancestor<'t>(&'t self, level: usize) -> Ancestor<'a, 't> { + if level < self.stack.len() { + // SAFETY: We just checked that `level < self.stack.len()` so `self.stack.len() - level` + // cannot wrap around or be out of bounds + let ancestor = unsafe { *self.stack.get_unchecked(self.stack.len() - level) }; + // Shrink `Ancestor`'s `'t` lifetime to lifetime of `&'t self`. // SAFETY: The `Ancestor` is guaranteed valid for `'t`. It is not possible to obtain // a `&mut` ref to any AST node which this `Ancestor` gives access to during `'t`. unsafe { transmute::, Ancestor<'a, 't>>(ancestor) } - }) + } else { + Ancestor::None + } } /// Get iterator over ancestors, starting with closest ancestor diff --git a/crates/oxc_traverse/src/context/mod.rs b/crates/oxc_traverse/src/context/mod.rs index 0454a148b9f9c..6922d75e5d473 100644 --- a/crates/oxc_traverse/src/context/mod.rs +++ b/crates/oxc_traverse/src/context/mod.rs @@ -143,9 +143,11 @@ impl<'a> TraverseCtx<'a> { /// `level` is number of levels above. /// `ancestor(1).unwrap()` is equivalent to `parent()`. /// + /// If `level` is out of bounds (above `Program`), returns `Ancestor::None`. + /// /// Shortcut for `ctx.ancestry.ancestor`. #[inline] - pub fn ancestor<'t>(&'t self, level: usize) -> Option> { + pub fn ancestor<'t>(&'t self, level: usize) -> Ancestor<'a, 't> { self.ancestry.ancestor(level) } diff --git a/crates/oxc_traverse/src/lib.rs b/crates/oxc_traverse/src/lib.rs index 5f2b4dafd514a..72f1f761918ba 100644 --- a/crates/oxc_traverse/src/lib.rs +++ b/crates/oxc_traverse/src/lib.rs @@ -134,7 +134,7 @@ mod compile_fail_tests; /// } /// /// // Read grandparent -/// if let Some(Ancestor::ExpressionStatementExpression(stmt_ref)) = ctx.ancestor(2) { +/// if let Ancestor::ExpressionStatementExpression(stmt_ref) = ctx.ancestor(2) { /// // This is legal /// println!("expression stmt's span: {:?}", stmt_ref.span()); ///