diff --git a/crates/oxc_traverse/src/context/ancestry.rs b/crates/oxc_traverse/src/context/ancestry.rs index 1ddaa2a7f4e743..7b86292e076b97 100644 --- a/crates/oxc_traverse/src/context/ancestry.rs +++ b/crates/oxc_traverse/src/context/ancestry.rs @@ -45,6 +45,7 @@ impl<'a> TraverseAncestry<'a> { /// Get parent of current node. #[inline] pub fn parent<'t>(&'t self) -> Ancestor<'a, 't> { + debug_assert!(!self.stack.is_empty()); // SAFETY: Stack contains 1 entry initially. Entries are pushed as traverse down the AST, // and popped as go back up. So even when visiting `Program`, the initial entry is in the stack. let ancestor = unsafe { *self.stack.last().unwrap_unchecked() }; @@ -117,6 +118,7 @@ impl<'a> TraverseAncestry<'a> { /// This method must not be public outside this crate, or consumer could break safety invariants. #[inline] pub(crate) unsafe fn pop_stack(&mut self) { + debug_assert!(!self.stack.is_empty()); self.stack.pop().unwrap_unchecked(); } @@ -143,6 +145,7 @@ impl<'a> TraverseAncestry<'a> { #[inline] #[allow(unsafe_code, clippy::ptr_as_ptr, clippy::ref_as_ptr)] pub(crate) unsafe fn retag_stack(&mut self, ty: AncestorType) { + debug_assert!(!self.stack.is_empty()); *(self.stack.last_mut().unwrap_unchecked() as *mut _ as *mut AncestorType) = ty; } }