feat(str): add Str::from_str_in and Ident::from_str_in methods (#23767)

Part of #23043.

Add 2 new methods:

- `Str::from_str_in`
- `Ident::from_str_in`

Both take a `&str`, allocate it in arena, and return a `Str<'a>` / `Ident<'a>`.

Both take a `&A where A: GetAllocator`, so can be passed any type which implements `GetAllocator` e.g. `ParserImpl`, `TraverseCtx` (same as `Box::new_in`, `Vec::new_in` etc).
This commit is contained in:
overlookmotel committed 2026-06-24 17:40:35 +00:00
1 parent 7537c582e0
commit 2580eda2a7
2 files changed
+74 -2

No files matched your search

+34 -1
View File
@@ -11,7 +11,8 @@ use std::{
};
use oxc_allocator::{
Allocator, ArenaStringBuilder, CloneIn, Dummy, FromIn, IdentBuildHasher, ident_hash,
Allocator, ArenaStringBuilder, CloneIn, Dummy, FromIn, GetAllocator, IdentBuildHasher,
ident_hash,
};
#[cfg(feature = "serialize")]
use oxc_estree::{ESTree, JsonSafeString, Serializer as ESTreeSerializer};
@@ -129,6 +130,12 @@ pub const fn new_const_ident(s: &str) -> Ident<'_> {
}
impl<'a> Ident<'a> {
/// Allocate provided `&str` into arena, and return an [`Ident<'a>`].
#[inline]
pub fn from_str_in<A: GetAllocator<'a>>(s: &str, allocator: &A) -> Self {
new_const_ident(allocator.allocator().alloc_str(s))
}
/// Create an [`Ident`] from raw components.
///
/// # SAFETY
@@ -705,6 +712,32 @@ mod test {
assert_eq!(IDENT.as_str(), "hello");
}
#[test]
#[expect(clippy::items_after_statements)]
fn ident_from_str_in() {
let allocator = Allocator::new();
let allocator: &Allocator = &allocator;
// Pass an actual `Allocator`
let ident = Ident::from_str_in("world", &allocator);
assert_eq!(ident.as_str(), "world");
assert_eq!(ident, Ident::from("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_str_in("hello", &wrapper);
assert_eq!(ident.as_str(), "hello");
assert_eq!(ident, Ident::from("hello"));
}
#[test]
fn arena_ident_hashmap() {
let allocator = Allocator::new();
+40 -1
View File
@@ -4,7 +4,7 @@ use std::{
ops::Deref,
};
use oxc_allocator::{Allocator, ArenaStringBuilder, CloneIn, Dummy, FromIn};
use oxc_allocator::{Allocator, ArenaStringBuilder, CloneIn, Dummy, FromIn, GetAllocator};
#[cfg(feature = "serialize")]
use oxc_estree::{ESTree, Serializer as ESTreeSerializer};
#[cfg(feature = "serialize")]
@@ -36,6 +36,12 @@ impl Str<'static> {
}
impl<'a> Str<'a> {
/// Allocate provided `&str` into arena, and return a [`Str<'a>`].
#[inline]
pub fn from_str_in<A: GetAllocator<'a>>(s: &str, allocator: &A) -> Self {
Self(allocator.allocator().alloc_str(s))
}
/// Borrow a string slice.
#[expect(clippy::inline_always)]
#[inline(always)] // Because this is a no-op
@@ -330,3 +336,36 @@ macro_rules! format_str {
Str::from(s)
}}
}
#[cfg(test)]
mod test {
use oxc_allocator::Allocator;
use super::*;
#[test]
#[expect(clippy::items_after_statements)]
fn str_from_str_in() {
let allocator = Allocator::new();
let allocator = &allocator;
// Pass an actual `Allocator`
let s = Str::from_str_in("world", &allocator);
assert_eq!(s.as_str(), "world");
assert_eq!(s, Str::from("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_str_in("hello", &wrapper);
assert_eq!(s.as_str(), "hello");
assert_eq!(s, Str::from("hello"));
}
}