Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 13 additions & 36 deletions crates/oxc_semantic/src/multi_index_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,44 +260,21 @@ macro_rules! multi_index_vec {
{
fn clone(&self) -> Self {
let len = self.len as usize;
if len == 0 {
return Self::new();
}
// Clone with exact capacity (no excess).
let layout = Self::compute_layout(len)
.expect("len > 0 so layout must exist");
// SAFETY: We allocate a fresh buffer and copy `len` elements per field.
// All source pointers are valid for `len` reads, and destination pointers
// are valid for `len` writes.
unsafe {
let new_base = ::std::alloc::alloc(layout);
if new_base.is_null() {
::std::alloc::handle_alloc_error(layout);
let mut result = Self::new();
if len > 0 {
result.reserve(len);
for i in 0..len {
// SAFETY: `i < len`, and `len <= max_len = Idx::MAX + 1`,
// so `i <= Idx::MAX`.
let id = unsafe { <$idx as ::oxc_index::Idx>::from_usize_unchecked(i) };
// `push` handles destination initialization and len updates,
// so this remains panic-safe if a field clone panics.
result.push(
$( self.$fname(id).clone() ),*
);
}
let mut result = Self {
base: ::core::ptr::NonNull::new_unchecked(new_base),
$( $fname: ::core::ptr::NonNull::dangling(), )*
len: self.len,
cap: self.len, // exact capacity
};
result.set_pointers(new_base, len);
$(
if ::core::mem::needs_drop::<$fty>() {
for i in 0..len {
result.$fname.as_ptr().add(i).write(
(*self.$fname.as_ptr().add(i)).clone(),
);
}
} else {
::core::ptr::copy_nonoverlapping(
self.$fname.as_ptr(),
result.$fname.as_ptr(),
len,
);
}
)*
result
}
result
}
}

Expand Down
3 changes: 3 additions & 0 deletions crates/oxc_semantic/tests/integration/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ impl<'a> SemanticTester<'a> {
}

#[cfg(not(feature = "cfg"))]
#[expect(clippy::unused_self, reason = "keep method signature consistent across cfg modes")]
pub fn basic_blocks_count(&self) -> usize {
0
}
Expand All @@ -214,6 +215,7 @@ impl<'a> SemanticTester<'a> {
}

#[cfg(not(feature = "cfg"))]
#[expect(clippy::unused_self, reason = "keep method signature consistent across cfg modes")]
pub fn basic_blocks_printed(&self) -> String {
String::default()
}
Expand All @@ -225,6 +227,7 @@ impl<'a> SemanticTester<'a> {
}

#[cfg(not(feature = "cfg"))]
#[expect(clippy::unused_self, reason = "keep method signature consistent across cfg modes")]
pub fn cfg_dot_diagram(&self) -> String {
String::default()
}
Expand Down
Loading