feat(str)!: Str and Ident methods take &GetAllocator (#23781)

Continuation of #23767, #23756, and #23755.

Make all methods that construct `Str`s and `Ident`s take `&A where A: GetAllocator<'a>` instead of `&'a Allocator`.
This commit is contained in:
overlookmotel committed 2026-06-25 11:59:59 +00:00
1 parent 838fd638a9
commit 88f4455c72
7 files changed
+153 -22

No files matched your search

+4 -4
View File
@@ -100,7 +100,7 @@ impl<'a> AstBuilder<'a> {
/// Allocate an [`Ident`] from an array of string slices.
#[inline]
pub fn ident_from_strs_array<const N: usize>(self, strings: [&str; N]) -> Ident<'a> {
Ident::from_strs_array_in(strings, self.allocator)
Ident::from_strs_array_in(strings, &self)
}
/// Convert a [`Cow<'a, str>`] to an [`Ident<'a>`].
@@ -111,7 +111,7 @@ impl<'a> AstBuilder<'a> {
/// If the `Cow` is owned, allocates the string into arena to generate a new `Ident`.
#[inline]
pub fn ident_from_cow(self, value: &Cow<'a, str>) -> Ident<'a> {
Ident::from_cow_in(value, self.allocator)
Ident::from_cow_in(value, &self)
}
/// Allocate a [`Str`] from a string slice.
@@ -123,7 +123,7 @@ impl<'a> AstBuilder<'a> {
/// Allocate a [`Str`] from an array of string slices.
#[inline]
pub fn str_from_strs_array<const N: usize>(self, strings: [&str; N]) -> Str<'a> {
Str::from_strs_array_in(strings, self.allocator)
Str::from_strs_array_in(strings, &self)
}
/// Convert a [`Cow<'a, str>`] to a [`Str<'a>`].
@@ -134,7 +134,7 @@ impl<'a> AstBuilder<'a> {
/// If the `Cow` is owned, allocates the string into arena to generate a new `Str`.
#[inline]
pub fn str_from_cow(self, value: &Cow<'a, str>) -> Str<'a> {
Str::from_cow_in(value, self.allocator)
Str::from_cow_in(value, &self)
}
/// `0`
@@ -1624,9 +1624,10 @@ mod fix {
let mut codegen = fixer.codegen();
let alloc = Allocator::default();
let ast_builder = AstBuilder::new(&alloc);
let alloc = &alloc;
let ast_builder = AstBuilder::new(alloc);
let mut vec = deps.elements.clone_in(&alloc);
let mut vec = deps.elements.clone_in(alloc);
for name in names {
vec.push(
@@ -286,7 +286,7 @@ impl<'a> UidGenerator<'a> {
let digits = buffer.format(uid_name.postfix);
if uid_name.underscore_count == 1 {
Ident::from_strs_array_in(["_", base, digits], self.allocator)
Ident::from_strs_array_in(["_", base, digits], &self.allocator)
} else {
let mut uid = ArenaStringBuilder::with_capacity_in(
uid_name.underscore_count as usize + base.len() + digits.len(),
@@ -298,7 +298,7 @@ impl<'a> UidGenerator<'a> {
Ident::from(uid)
}
} else {
let uid = Ident::from_strs_array_in(["_", base], self.allocator);
let uid = Ident::from_strs_array_in(["_", base], &self.allocator);
// SAFETY: String starts with `_`, so trimming off that byte leaves a valid UTF-8 string
let base = unsafe { uid.as_str().get_unchecked(1..) };
self.names.insert(base, UidName { underscore_count: 1, postfix: 1 });
+1 -1
View File
@@ -403,7 +403,7 @@ pub fn parse_big_int<'a>(
let radix = match kind {
// Skip parsing with `BigInt` - it's already in decimal form, and underscores are removed
Kind::Decimal => return Str::from_cow_in(&s, allocator),
Kind::Decimal => return Str::from_cow_in(&s, &allocator),
Kind::Binary => 2,
Kind::Octal => 8,
Kind::Hex => 16,
+70 -5
View File
@@ -217,11 +217,11 @@ impl<'a> Ident<'a> {
// are statically known. See `Allocator::alloc_concat_strs_array`.
#[expect(clippy::inline_always)]
#[inline(always)]
pub fn from_strs_array_in<const N: usize>(
pub fn from_strs_array_in<const N: usize, A: GetAllocator<'a>>(
strings: [&str; N],
allocator: &'a Allocator,
allocator: &A,
) -> Ident<'a> {
Self::from(allocator.alloc_concat_strs_array(strings))
Self::from(allocator.allocator().alloc_concat_strs_array(strings))
}
/// Convert a [`Cow<'a, str>`] to an [`Ident<'a>`].
@@ -231,10 +231,10 @@ impl<'a> Ident<'a> {
///
/// If the `Cow` is owned, allocates the string into arena to generate a new `Ident`.
#[inline]
pub fn from_cow_in(value: &Cow<'a, str>, allocator: &'a Allocator) -> Ident<'a> {
pub fn from_cow_in<A: GetAllocator<'a>>(value: &Cow<'a, str>, allocator: &A) -> Ident<'a> {
match value {
Cow::Borrowed(s) => Ident::from(*s),
Cow::Owned(s) => Ident::from_in(s, allocator),
Cow::Owned(s) => Ident::from_str_in(s, allocator),
}
}
}
@@ -738,6 +738,71 @@ mod test {
assert_eq!(ident, Ident::from("hello"));
}
#[test]
#[expect(clippy::items_after_statements)]
fn ident_from_strs_array_in() {
let allocator = Allocator::new();
let allocator = &allocator;
// Pass an actual `Allocator`
let ident = Ident::from_strs_array_in(["hello", " ", "world", "!"], &allocator);
assert_eq!(ident.as_str(), "hello world!");
assert_eq!(ident, Ident::from("hello world!"));
// Pass a struct which implements `GetAllocator`
struct Wrapper<'a>(&'a Allocator);
impl<'a> GetAllocator<'a> for Wrapper<'a> {
fn allocator(&self) -> &'a Allocator {
self.0
}
}
let wrapper = Wrapper(allocator);
let ident = Ident::from_strs_array_in(["foo", "_", "bar"], &wrapper);
assert_eq!(ident.as_str(), "foo_bar");
assert_eq!(ident, Ident::from("foo_bar"));
}
#[test]
#[expect(clippy::items_after_statements)]
fn ident_from_cow_in() {
let allocator = Allocator::new();
let allocator = &allocator;
// `Cow::Borrowed` references the same string, without allocating in arena
let borrowed = "world";
let used_before = allocator.used_bytes();
let ident = Ident::from_cow_in(&Cow::Borrowed(borrowed), &allocator);
assert_eq!(ident.as_str(), "world");
assert_eq!(ident, Ident::from("world"));
assert_eq!(ident.as_str().as_ptr(), borrowed.as_ptr());
assert_eq!(allocator.used_bytes(), used_before);
// `Cow::Owned` allocates a new string in arena
let owned = "owned".to_string();
let owned_ptr = owned.as_ptr();
let ident = Ident::from_cow_in(&Cow::Owned(owned), &allocator);
assert_eq!(ident.as_str(), "owned");
assert_eq!(ident, Ident::from("owned"));
assert_ne!(ident.as_str().as_ptr(), owned_ptr);
assert!(allocator.used_bytes() > used_before);
// Pass a struct which implements `GetAllocator`
struct Wrapper<'a>(&'a Allocator);
impl<'a> GetAllocator<'a> for Wrapper<'a> {
fn allocator(&self) -> &'a Allocator {
self.0
}
}
let wrapper = Wrapper(allocator);
let ident = Ident::from_cow_in(&Cow::Borrowed("hello"), &wrapper);
assert_eq!(ident.as_str(), "hello");
assert_eq!(ident, Ident::from("hello"));
}
#[test]
fn arena_ident_hashmap() {
let allocator = Allocator::new();
+71 -6
View File
@@ -84,18 +84,18 @@ impl<'a> Str<'a> {
/// use oxc_str::Str;
///
/// let allocator = Allocator::new();
/// let s = Str::from_strs_array_in(["hello", " ", "world", "!"], &allocator);
/// let s = Str::from_strs_array_in(["hello", " ", "world", "!"], &&allocator);
/// assert_eq!(s.as_str(), "hello world!");
/// ```
// `#[inline(always)]` because want compiler to be able to optimize where some of `strings`
// are statically known. See `Allocator::alloc_concat_strs_array`.
#[expect(clippy::inline_always)]
#[inline(always)]
pub fn from_strs_array_in<const N: usize>(
pub fn from_strs_array_in<const N: usize, A: GetAllocator<'a>>(
strings: [&str; N],
allocator: &'a Allocator,
allocator: &A,
) -> Str<'a> {
Self::from(allocator.alloc_concat_strs_array(strings))
Self::from(allocator.allocator().alloc_concat_strs_array(strings))
}
/// Convert a [`Cow<'a, str>`] to a [`Str<'a>`].
@@ -105,10 +105,10 @@ impl<'a> Str<'a> {
///
/// If the `Cow` is owned, allocates the string into arena to generate a new `Str`.
#[inline]
pub fn from_cow_in(value: &Cow<'a, str>, allocator: &'a Allocator) -> Str<'a> {
pub fn from_cow_in<A: GetAllocator<'a>>(value: &Cow<'a, str>, allocator: &A) -> Str<'a> {
match value {
Cow::Borrowed(s) => Str::from(*s),
Cow::Owned(s) => Str::from_in(s, allocator),
Cow::Owned(s) => Str::from_str_in(s, allocator),
}
}
}
@@ -368,4 +368,69 @@ mod test {
assert_eq!(s.as_str(), "hello");
assert_eq!(s, Str::from("hello"));
}
#[test]
#[expect(clippy::items_after_statements)]
fn str_from_strs_array_in() {
let allocator = Allocator::new();
let allocator = &allocator;
// Pass an actual `Allocator`
let s = Str::from_strs_array_in(["hello", " ", "world", "!"], &allocator);
assert_eq!(s.as_str(), "hello world!");
assert_eq!(s, Str::from("hello world!"));
// Pass a struct which implements `GetAllocator`
struct Wrapper<'a>(&'a Allocator);
impl<'a> GetAllocator<'a> for Wrapper<'a> {
fn allocator(&self) -> &'a Allocator {
self.0
}
}
let wrapper = Wrapper(allocator);
let s = Str::from_strs_array_in(["foo", "_", "bar"], &wrapper);
assert_eq!(s.as_str(), "foo_bar");
assert_eq!(s, Str::from("foo_bar"));
}
#[test]
#[expect(clippy::items_after_statements)]
fn str_from_cow_in() {
let allocator = Allocator::new();
let allocator = &allocator;
// `Cow::Borrowed` references the same string, without allocating in arena
let borrowed = "world";
let used_before = allocator.used_bytes();
let s = Str::from_cow_in(&Cow::Borrowed(borrowed), &allocator);
assert_eq!(s.as_str(), "world");
assert_eq!(s, Str::from("world"));
assert_eq!(s.as_str().as_ptr(), borrowed.as_ptr());
assert_eq!(allocator.used_bytes(), used_before);
// `Cow::Owned` allocates a new string in arena
let owned = "owned".to_string();
let owned_ptr = owned.as_ptr();
let s = Str::from_cow_in(&Cow::Owned(owned), &allocator);
assert_eq!(s.as_str(), "owned");
assert_eq!(s, Str::from("owned"));
assert_ne!(s.as_str().as_ptr(), owned_ptr);
assert!(allocator.used_bytes() > used_before);
// Pass a struct which implements `GetAllocator`
struct Wrapper<'a>(&'a Allocator);
impl<'a> GetAllocator<'a> for Wrapper<'a> {
fn allocator(&self) -> &'a Allocator {
self.0
}
}
let wrapper = Wrapper(allocator);
let s = Str::from_cow_in(&Cow::Borrowed("hello"), &wrapper);
assert_eq!(s.as_str(), "hello");
assert_eq!(s, Str::from("hello"));
}
}
+2 -2
View File
@@ -286,7 +286,7 @@ impl<'a> UidGenerator<'a> {
let digits = buffer.format(uid_name.postfix);
if uid_name.underscore_count == 1 {
Ident::from_strs_array_in(["_", base, digits], self.allocator)
Ident::from_strs_array_in(["_", base, digits], &self.allocator)
} else {
let mut uid = ArenaStringBuilder::with_capacity_in(
uid_name.underscore_count as usize + base.len() + digits.len(),
@@ -298,7 +298,7 @@ impl<'a> UidGenerator<'a> {
Ident::from(uid)
}
} else {
let uid = Ident::from_strs_array_in(["_", base], self.allocator);
let uid = Ident::from_strs_array_in(["_", base], &self.allocator);
// SAFETY: String starts with `_`, so trimming off that byte leaves a valid UTF-8 string
let base = unsafe { uid.as_str().get_unchecked(1..) };
self.names.insert(base, UidName { underscore_count: 1, postfix: 1 });