Skip to content
Open
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
3 changes: 2 additions & 1 deletion src/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ impl<K, V> Core<K, V> {

/// Insert a key-value pair in `entries` at a particular index,
/// *without* checking whether it already exists.
#[track_caller]
pub(crate) fn shift_insert_unique(
&mut self,
index: usize,
Expand All @@ -528,7 +529,7 @@ impl<K, V> Core<K, V> {
value: V,
) -> &mut Bucket<K, V> {
let end = self.indices.len();
assert!(index <= end);
assert_index_le(index, end);
// Increment others first so we don't have duplicate indices.
self.increment_indices(index, end);
let entries = &*self.entries;
Expand Down
23 changes: 23 additions & 0 deletions src/map/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,29 @@ fn get_range_mut() {
assert_eq!(slice, &mut [(3, 31), (4, 41)]);
}

#[test]
#[should_panic = "index out of bounds: the len is 1 but the index is 5. Expected index <= len"]
fn vacant_entry_shift_insert_oob() {
let mut map: IndexMap<u32, u32> = IndexMap::new();
map.insert(0, 0);
let Entry::Vacant(entry) = map.entry(1) else {
unreachable!()
};
entry.shift_insert(5, 10);
}

#[test]
fn vacant_entry_shift_insert_at_len() {
let mut map: IndexMap<u32, u32> = IndexMap::new();
map.insert(0, 0);
let len = map.len();
let Entry::Vacant(entry) = map.entry(1) else {
unreachable!()
};
entry.shift_insert(len, 10);
assert_eq!(map.get_index(1), Some((&1, &10)));
}

#[test]
#[should_panic = "index out of bounds"]
fn shift_insert_oob() {
Expand Down
33 changes: 33 additions & 0 deletions tests/track_caller.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! Panics from the index-taking `IndexMap` entry points must name the caller.
//!
//! This is its own test binary because it replaces the global panic hook.

use indexmap::IndexMap;
use indexmap::map::Entry;

#[test]
fn vacant_entry_shift_insert_oob_reports_the_caller() {
let file = std::sync::Arc::new(std::sync::Mutex::new(String::new()));
let hook_file = std::sync::Arc::clone(&file);
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
if let Some(loc) = info.location() {
*hook_file.lock().unwrap() = String::from(loc.file());
}
}));
let caught = std::panic::catch_unwind(|| {
let mut map: IndexMap<u32, u32> = IndexMap::new();
map.insert(0, 0);
let Entry::Vacant(entry) = map.entry(1) else {
unreachable!()
};
entry.shift_insert(5, 10);
});
std::panic::set_hook(prev);
assert!(caught.is_err());
let file = file.lock().unwrap().clone();
assert!(
file.ends_with("track_caller.rs"),
"panic reported at {file}"
);
}