diff --git a/Cargo.lock b/Cargo.lock
index b9767967bfba3..928ef4f06c26d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1702,6 +1702,7 @@ version = "0.123.0"
dependencies = [
"allocator-api2",
"hashbrown 0.16.1",
+ "oxc_allocator",
"oxc_ast_macros",
"oxc_data_structures",
"oxc_estree",
diff --git a/crates/oxc_allocator/Cargo.toml b/crates/oxc_allocator/Cargo.toml
index 917c3fc5911d7..0bda3e1f06aab 100644
--- a/crates/oxc_allocator/Cargo.toml
+++ b/crates/oxc_allocator/Cargo.toml
@@ -38,6 +38,10 @@ oxc_estree = { workspace = true, features = ["serialize"] }
serde = { workspace = true }
serde_json = { workspace = true }
+# Enable `testing` feature in tests and doctests.
+# Allows doctests to use `bump` module without exposing it publicly.
+oxc_allocator = { path = ".", features = ["testing"] }
+
[features]
bitset = []
fixed_size = ["from_raw_parts", "pool", "dep:oxc_ast_macros"]
@@ -46,3 +50,5 @@ pool = []
serialize = ["dep:serde", "oxc_estree/serialize"]
track_allocations = []
disable_track_allocations = []
+# Only for use in tests
+testing = []
diff --git a/crates/oxc_allocator/src/bump.rs b/crates/oxc_allocator/src/bump.rs
index 971ec12aa359d..8ba9cddadaca0 100644
--- a/crates/oxc_allocator/src/bump.rs
+++ b/crates/oxc_allocator/src/bump.rs
@@ -1,51 +1,43 @@
//! Arena allocator.
//!
-//! This module was originally derived from bumpalo v3.19.0.
+//! This module was originally copied from `bumpalo` at commit a47f6d6b7b5fee9c99a285f0de80257a0a982ef3
+//! (2 commits after 3.20.2 release). Changes have been made since.
-#![allow(
+#![expect(
clippy::borrow_as_ptr,
clippy::cast_ptr_alignment,
clippy::cast_sign_loss,
- clippy::doc_markdown,
clippy::elidable_lifetime_names,
clippy::filter_map_next,
clippy::inconsistent_struct_constructor,
clippy::inline_always,
- clippy::items_after_statements,
- clippy::manual_assert,
clippy::manual_div_ceil,
clippy::map_unwrap_or,
+ clippy::missing_errors_doc,
clippy::missing_panics_doc,
- clippy::missing_safety_doc,
- clippy::module_name_repetitions,
- clippy::must_use_candidate,
clippy::mut_from_ref,
clippy::needless_lifetimes,
- clippy::needless_pass_by_value,
clippy::ptr_as_ptr,
clippy::ptr_cast_constness,
- clippy::redundant_else,
- clippy::redundant_pub_crate,
clippy::ref_as_ptr,
clippy::semicolon_if_nothing_returned,
- clippy::similar_names,
- clippy::too_many_lines,
clippy::undocumented_unsafe_blocks,
clippy::uninlined_format_args,
clippy::unnecessary_safety_comment,
- clippy::unnecessary_wraps,
clippy::unused_self,
- missing_docs,
- rustdoc::broken_intra_doc_links,
+ clippy::single_match_else,
unsafe_op_in_unsafe_fn
)]
-
-//! Arena allocator.
+#![deny(missing_debug_implementations)]
+#![deny(missing_docs)]
#[doc(hidden)]
pub extern crate alloc as core_alloc;
+use crate::bumpalo_alloc;
+
use core::cell::Cell;
+use core::cmp::Ordering;
use core::fmt::Display;
use core::iter;
use core::marker::PhantomData;
@@ -57,7 +49,7 @@ use core_alloc::alloc::{Layout, alloc, dealloc};
use allocator_api2::alloc::{AllocError, Allocator};
-pub use crate::bumpalo_alloc::AllocErr;
+pub use bumpalo_alloc::AllocErr;
/// An error returned from [`Bump::try_alloc_try_with`].
#[derive(Clone, PartialEq, Eq, Debug)]
@@ -133,8 +125,8 @@ impl Display for AllocOrInitError {
///
/// ## Example
///
-/// ```text
-/// use bumpalo::Bump;
+/// ```
+/// # use oxc_allocator::bump::Bump;
///
/// // Create a new bump arena.
/// let bump = Bump::new();
@@ -146,7 +138,7 @@ impl Display for AllocOrInitError {
/// // Mutable references are returned from allocation.
/// let mut s = bump.alloc("bumpalo");
/// *s = "the bump allocator; and also is a buffalo";
-/// ```text
+/// ```
///
/// ## Allocation Methods Come in Many Flavors
///
@@ -187,8 +179,8 @@ impl Display for AllocOrInitError {
/// These allocation methods let you recover from out-of-memory (OOM)
/// scenarios, rather than raising a panic on OOM.
///
-/// ```text
-/// use bumpalo::Bump;
+/// ```
+/// # use oxc_allocator::bump::Bump;
///
/// let bump = Bump::new();
///
@@ -206,7 +198,7 @@ impl Display for AllocOrInitError {
/// struct MyStruct {
/// // ...
/// }
-/// ```text
+/// ```
///
/// ### Initializer Functions: The `_with` Method Suffix
///
@@ -259,8 +251,10 @@ impl Display for AllocOrInitError {
/// into this `Bump`, even though the inner reference isn't kept and the [`Err`]
/// payload is returned semantically by value:
///
-/// ```text
-/// let bump = bumpalo::Bump::new();
+/// ```rust
+/// # use oxc_allocator::bump::Bump;
+///
+/// let bump = Bump::new();
///
/// let r: Result<&mut [u8; 1000], ()> = bump.alloc_try_with(|| {
/// let _ = bump.alloc(0_u8);
@@ -268,7 +262,7 @@ impl Display for AllocOrInitError {
/// });
///
/// assert!(r.is_err());
-/// ```text
+/// ```
///
///
///
@@ -292,8 +286,10 @@ impl Display for AllocOrInitError {
///
/// #### Example
///
-/// ```text
-/// let bump = bumpalo::Bump::new();
+/// ```
+/// # use oxc_allocator::bump::Bump;
+///
+/// let bump = Bump::new();
///
/// assert_eq!(bump.allocation_limit(), None);
/// bump.set_allocation_limit(Some(0));
@@ -307,22 +303,22 @@ impl Display for AllocOrInitError {
/// bump.set_allocation_limit(None);
///
/// assert_eq!(bump.allocation_limit(), None);
-/// ```text
+/// ```
///
/// #### Warning
///
/// Because of backwards compatibility, allocations that fail
/// due to allocation limits will not present differently than
/// errors due to resource exhaustion.
-
#[derive(Debug)]
-pub struct Bump {
+pub struct Bump {
// The current chunk we are bump allocating within.
current_chunk_footer: Cell>,
allocation_limit: Cell