diff --git a/.config/nextest.toml b/.config/nextest.toml index 388a4c3a..5c96ca4f 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -1,3 +1,3 @@ [profile.default-miri] # proptests are too slow to be run through miri -default-filter = "not test(proptest)" +default-filter = "not test(proptest) and not binary_id(iddqd::ui)" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15fc1b9a..f2717fc3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,8 +49,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - # 1.85 is the MSRV - rust-version: ["1.85", "stable"] + rust-version: ["stable"] partition: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] fail-fast: false env: @@ -72,7 +71,40 @@ jobs: run: just powerset --partition ${{ matrix.partition }}/10 nextest run - name: Doctests run: just powerset --partition ${{ matrix.partition }}/10 test --doc - + + build-and-test-msrv: + name: Build and test (MSRV) + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + # 1.85 is the MSRV + rust-version: ["1.85"] + partition: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"] + fail-fast: false + env: + RUSTFLAGS: -D warnings + steps: + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust-version }} + - uses: Swatinem/rust-cache@98c8021b550208e191a6a3145459bfc9fb29c4c0 # v2.8.0 + with: + key: partition-${{ matrix.partition }}-msrv + - uses: taiki-e/install-action@cargo-hack + - uses: taiki-e/install-action@just + - uses: taiki-e/install-action@nextest + - name: Build + run: just powerset --partition ${{ matrix.partition }}/10 build + # We don't run UI tests on the MSRV since compiler output varies by + # version. + - name: Run tests (excluding UI tests) + run: just powerset --partition ${{ matrix.partition }}/10 nextest run -E 'not binary_id(iddqd::ui)' + - name: Doctests + run: just powerset --partition ${{ matrix.partition }}/10 test --doc + + build-no-std: name: Build on no-std runs-on: ${{ matrix.os }} diff --git a/Cargo.lock b/Cargo.lock index 74fa071a..752b7e3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -299,6 +299,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "half" version = "2.6.0" @@ -335,6 +341,12 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "heck" version = "0.5.0" @@ -370,6 +382,7 @@ dependencies = [ "serde_core", "serde_json", "test-strategy", + "trybuild", ] [[package]] @@ -418,6 +431,16 @@ dependencies = [ "serde", ] +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + [[package]] name = "itertools" version = "0.13.0" @@ -763,7 +786,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "dyn-clone", - "indexmap", + "indexmap 1.9.3", "schemars_derive", "serde", "serde_json", @@ -792,9 +815,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.223" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a505d71960adde88e293da5cb5eda57093379f64e61cf77bf0e6a63af07a7bac" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", @@ -802,18 +825,18 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.223" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20f57cbd357666aa7b3ac84a90b4ea328f1d4ddb6772b430caa5d9e1309bb9e9" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.223" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d428d07faf17e306e699ec1e91996e5a165ba5d6bce5b5155173e91a8a01a56" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -844,6 +867,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + [[package]] name = "serde_tokenstream" version = "0.2.2" @@ -902,6 +934,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "target-triple" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b" + [[package]] name = "tempfile" version = "3.20.0" @@ -915,6 +953,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "test-strategy" version = "0.4.3" @@ -958,6 +1005,60 @@ dependencies = [ "serde_json", ] +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "trybuild" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c635f0191bd3a2941013e5062667100969f8c4e9cd787c14f977265d73616e" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", +] + [[package]] name = "typify" version = "0.4.2" @@ -1207,6 +1308,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/Cargo.toml b/Cargo.toml index bc4dea16..f28fa91e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ serde = "1.0.223" serde_core = "1.0.223" serde_json = "1.0.145" test-strategy = "0.4.3" +trybuild = "1.0.116" typify = "0.4.2" [profile.dev] diff --git a/crates/iddqd/Cargo.toml b/crates/iddqd/Cargo.toml index 5edc7a2c..39a85faa 100644 --- a/crates/iddqd/Cargo.toml +++ b/crates/iddqd/Cargo.toml @@ -40,6 +40,7 @@ iddqd-test-utils.workspace = true proptest.workspace = true serde.workspace = true test-strategy.workspace = true +trybuild.workspace = true [features] allocator-api2 = ["iddqd-test-utils/allocator-api2"] diff --git a/crates/iddqd/src/bi_hash_map/imp.rs b/crates/iddqd/src/bi_hash_map/imp.rs index 86e8c7b6..54c5f2c0 100644 --- a/crates/iddqd/src/bi_hash_map/imp.rs +++ b/crates/iddqd/src/bi_hash_map/imp.rs @@ -1926,7 +1926,7 @@ impl BiHashMap { /// ``` pub fn retain<'a, F>(&'a mut self, mut f: F) where - F: FnMut(RefMut<'a, T, S>) -> bool, + F: for<'b> FnMut(RefMut<'b, T, S>) -> bool, { let hash_state = self.tables.state.clone(); let (_, mut dormant_items) = DormantMutRef::new(&mut self.items); diff --git a/crates/iddqd/src/id_hash_map/imp.rs b/crates/iddqd/src/id_hash_map/imp.rs index 79fa35dc..57a2e871 100644 --- a/crates/iddqd/src/id_hash_map/imp.rs +++ b/crates/iddqd/src/id_hash_map/imp.rs @@ -1306,7 +1306,7 @@ impl IdHashMap { /// ``` pub fn retain<'a, F>(&'a mut self, mut f: F) where - F: FnMut(RefMut<'a, T, S>) -> bool, + F: for<'b> FnMut(RefMut<'b, T, S>) -> bool, { let hash_state = self.tables.state.clone(); let (_, mut dormant_items) = DormantMutRef::new(&mut self.items); diff --git a/crates/iddqd/src/id_ord_map/imp.rs b/crates/iddqd/src/id_ord_map/imp.rs index ed726ec4..0d9cad6b 100644 --- a/crates/iddqd/src/id_ord_map/imp.rs +++ b/crates/iddqd/src/id_ord_map/imp.rs @@ -1307,7 +1307,7 @@ impl IdOrdMap { /// ``` pub fn retain<'a, F>(&'a mut self, mut f: F) where - F: FnMut(RefMut<'a, T>) -> bool, + F: for<'b> FnMut(RefMut<'b, T>) -> bool, T::Key<'a>: Hash, { let hash_state = self.tables.state().clone(); diff --git a/crates/iddqd/src/tri_hash_map/imp.rs b/crates/iddqd/src/tri_hash_map/imp.rs index cd462cbc..517aea4b 100644 --- a/crates/iddqd/src/tri_hash_map/imp.rs +++ b/crates/iddqd/src/tri_hash_map/imp.rs @@ -2584,7 +2584,7 @@ impl TriHashMap { /// ``` pub fn retain<'a, F>(&'a mut self, mut f: F) where - F: FnMut(RefMut<'a, T, S>) -> bool, + F: for<'b> FnMut(RefMut<'b, T, S>) -> bool, { let hash_state = self.tables.state.clone(); let (_, mut dormant_items) = DormantMutRef::new(&mut self.items); diff --git a/crates/iddqd/tests/integration/bi_hash_map.rs b/crates/iddqd/tests/integration/bi_hash_map.rs index 3adf3cc0..55287da8 100644 --- a/crates/iddqd/tests/integration/bi_hash_map.rs +++ b/crates/iddqd/tests/integration/bi_hash_map.rs @@ -11,7 +11,10 @@ use iddqd_test_utils::{ }, }; use proptest::prelude::*; -use std::{borrow::Cow, path::Path}; +use std::{ + borrow::Cow, + path::{Path, PathBuf}, +}; use test_strategy::{Arbitrary, proptest}; #[derive(Clone, Debug)] @@ -910,6 +913,36 @@ fn borrowed_item() { } } +#[test] +fn borrowed_item_retain_non_static() { + let foo_key = String::from("foo"); + let bar_key = String::from("bar"); + let foo_bytes = b"foo".to_vec(); + let bar_bytes = b"bar".to_vec(); + let foo_path = PathBuf::from("foo"); + let bar_path = PathBuf::from("bar"); + + let mut map = BiHashMap::, HashBuilder, Alloc>::default(); + map.insert_unique(BorrowedItem { + key1: foo_key.as_str(), + key2: Cow::Borrowed(foo_bytes.as_slice()), + key3: foo_path.as_path(), + }) + .unwrap(); + map.insert_unique(BorrowedItem { + key1: bar_key.as_str(), + key2: Cow::Borrowed(bar_bytes.as_slice()), + key3: bar_path.as_path(), + }) + .unwrap(); + + map.retain(|item| item.key1 == foo_key.as_str()); + + assert_eq!(map.len(), 1); + assert!(map.get1(foo_key.as_str()).is_some()); + assert!(map.get1(bar_key.as_str()).is_none()); +} + #[test] fn test_retain_all() { let mut map = BiHashMap::::make_new(); diff --git a/crates/iddqd/tests/integration/id_hash_map.rs b/crates/iddqd/tests/integration/id_hash_map.rs index 275caa41..27cb20f2 100644 --- a/crates/iddqd/tests/integration/id_hash_map.rs +++ b/crates/iddqd/tests/integration/id_hash_map.rs @@ -11,7 +11,10 @@ use iddqd_test_utils::{ }, }; use proptest::prelude::*; -use std::{borrow::Cow, path::Path}; +use std::{ + borrow::Cow, + path::{Path, PathBuf}, +}; use test_strategy::{Arbitrary, proptest}; #[derive(Clone, Debug)] @@ -619,6 +622,36 @@ fn borrowed_item() { } } +#[test] +fn borrowed_item_retain_non_static() { + let foo_key = String::from("foo"); + let bar_key = String::from("bar"); + let foo_bytes = b"foo".to_vec(); + let bar_bytes = b"bar".to_vec(); + let foo_path = PathBuf::from("foo"); + let bar_path = PathBuf::from("bar"); + + let mut map = IdHashMap::, HashBuilder, Alloc>::default(); + map.insert_unique(BorrowedItem { + key1: foo_key.as_str(), + key2: Cow::Borrowed(foo_bytes.as_slice()), + key3: foo_path.as_path(), + }) + .unwrap(); + map.insert_unique(BorrowedItem { + key1: bar_key.as_str(), + key2: Cow::Borrowed(bar_bytes.as_slice()), + key3: bar_path.as_path(), + }) + .unwrap(); + + map.retain(|item| item.key1 == foo_key.as_str()); + + assert_eq!(map.len(), 1); + assert!(map.get(foo_key.as_str()).is_some()); + assert!(map.get(bar_key.as_str()).is_none()); +} + #[test] fn test_retain_all() { let mut map = IdHashMap::::make_new(); diff --git a/crates/iddqd/tests/integration/id_ord_map.rs b/crates/iddqd/tests/integration/id_ord_map.rs index aaff92b9..3d8548ff 100644 --- a/crates/iddqd/tests/integration/id_ord_map.rs +++ b/crates/iddqd/tests/integration/id_ord_map.rs @@ -13,7 +13,10 @@ use iddqd_test_utils::{ unwind::catch_panic, }; use proptest::prelude::*; -use std::{borrow::Cow, path::Path}; +use std::{ + borrow::Cow, + path::{Path, PathBuf}, +}; use test_strategy::{Arbitrary, proptest}; #[test] @@ -970,6 +973,36 @@ fn borrowed_item() { entry_api_tests(&mut map); } +#[test] +fn borrowed_item_retain_non_static() { + let foo_key = String::from("foo"); + let bar_key = String::from("bar"); + let foo_bytes = b"foo".to_vec(); + let bar_bytes = b"bar".to_vec(); + let foo_path = PathBuf::from("foo"); + let bar_path = PathBuf::from("bar"); + + let mut map = IdOrdMap::>::default(); + map.insert_unique(BorrowedItem { + key1: foo_key.as_str(), + key2: Cow::Borrowed(foo_bytes.as_slice()), + key3: foo_path.as_path(), + }) + .unwrap(); + map.insert_unique(BorrowedItem { + key1: bar_key.as_str(), + key2: Cow::Borrowed(bar_bytes.as_slice()), + key3: bar_path.as_path(), + }) + .unwrap(); + + map.retain(|item| item.key1 == foo_key.as_str()); + + assert_eq!(map.len(), 1); + assert!(map.get(foo_key.as_str()).is_some()); + assert!(map.get(bar_key.as_str()).is_none()); +} + mod macro_tests { use super::*; diff --git a/crates/iddqd/tests/integration/tri_hash_map.rs b/crates/iddqd/tests/integration/tri_hash_map.rs index 7f5d2a0a..ff1077e5 100644 --- a/crates/iddqd/tests/integration/tri_hash_map.rs +++ b/crates/iddqd/tests/integration/tri_hash_map.rs @@ -12,7 +12,10 @@ use iddqd_test_utils::{ }, }; use proptest::prelude::*; -use std::{borrow::Cow, path::Path}; +use std::{ + borrow::Cow, + path::{Path, PathBuf}, +}; use test_strategy::{Arbitrary, proptest}; #[derive(Clone, Debug)] @@ -762,6 +765,36 @@ fn borrowed_item() { } } +#[test] +fn borrowed_item_retain_non_static() { + let foo_key = String::from("foo"); + let bar_key = String::from("bar"); + let foo_bytes = b"foo".to_vec(); + let bar_bytes = b"bar".to_vec(); + let foo_path = PathBuf::from("foo"); + let bar_path = PathBuf::from("bar"); + + let mut map = TriHashMap::, HashBuilder, Alloc>::default(); + map.insert_unique(BorrowedItem { + key1: foo_key.as_str(), + key2: Cow::Borrowed(foo_bytes.as_slice()), + key3: foo_path.as_path(), + }) + .unwrap(); + map.insert_unique(BorrowedItem { + key1: bar_key.as_str(), + key2: Cow::Borrowed(bar_bytes.as_slice()), + key3: bar_path.as_path(), + }) + .unwrap(); + + map.retain(|item| item.key1 == foo_key.as_str()); + + assert_eq!(map.len(), 1); + assert!(map.get1(foo_key.as_str()).is_some()); + assert!(map.get1(bar_key.as_str()).is_none()); +} + #[test] fn test_retain_all() { let mut map = TriHashMap::::make_new(); diff --git a/crates/iddqd/tests/ui.rs b/crates/iddqd/tests/ui.rs new file mode 100644 index 00000000..019056fb --- /dev/null +++ b/crates/iddqd/tests/ui.rs @@ -0,0 +1,8 @@ +//! UI tests. + +#[cfg(all(feature = "std", feature = "default-hasher"))] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/invalid/*.rs"); +} diff --git a/crates/iddqd/tests/ui/invalid/bi_hash_retain_stash.rs b/crates/iddqd/tests/ui/invalid/bi_hash_retain_stash.rs new file mode 100644 index 00000000..ffa1b78d --- /dev/null +++ b/crates/iddqd/tests/ui/invalid/bi_hash_retain_stash.rs @@ -0,0 +1,35 @@ +use iddqd::{BiHashItem, BiHashMap, bi_upcast}; + +#[derive(Debug)] +struct Item { + id: u32, + key2: u32, +} + +impl BiHashItem for Item { + type K1<'a> = u32; + type K2<'a> = u32; + + fn key1(&self) -> Self::K1<'_> { + self.id + } + + fn key2(&self) -> Self::K2<'_> { + self.key2 + } + + bi_upcast!(); +} + +fn main() { + let mut map = BiHashMap::::new(); + map.insert_unique(Item { id: 0, key2: 10 }).unwrap(); + + let mut stashed = Vec::new(); + map.retain(|item| { + stashed.push(item); + false + }); + + stashed[0].id = 1; +} diff --git a/crates/iddqd/tests/ui/invalid/bi_hash_retain_stash.stderr b/crates/iddqd/tests/ui/invalid/bi_hash_retain_stash.stderr new file mode 100644 index 00000000..5589ad83 --- /dev/null +++ b/crates/iddqd/tests/ui/invalid/bi_hash_retain_stash.stderr @@ -0,0 +1,13 @@ +error[E0521]: borrowed data escapes outside of closure + --> tests/ui/invalid/bi_hash_retain_stash.rs:30:9 + | +28 | let mut stashed = Vec::new(); + | ----------- `stashed` declared here, outside of the closure body +29 | map.retain(|item| { + | ---- `item` is a reference that is only valid in the closure body +30 | stashed.push(item); + | ^^^^^^^^^^^^^^^^^^ `item` escapes the closure body here + | + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance diff --git a/crates/iddqd/tests/ui/invalid/id_hash_retain_stash.rs b/crates/iddqd/tests/ui/invalid/id_hash_retain_stash.rs new file mode 100644 index 00000000..b11b00ae --- /dev/null +++ b/crates/iddqd/tests/ui/invalid/id_hash_retain_stash.rs @@ -0,0 +1,29 @@ +use iddqd::{IdHashItem, IdHashMap, id_upcast}; + +#[derive(Debug)] +struct Item { + id: u32, +} + +impl IdHashItem for Item { + type Key<'a> = u32; + + fn key(&self) -> Self::Key<'_> { + self.id + } + + id_upcast!(); +} + +fn main() { + let mut map = IdHashMap::::new(); + map.insert_unique(Item { id: 0 }).unwrap(); + + let mut stashed = Vec::new(); + map.retain(|item| { + stashed.push(item); + false + }); + + stashed[0].id = 1; +} diff --git a/crates/iddqd/tests/ui/invalid/id_hash_retain_stash.stderr b/crates/iddqd/tests/ui/invalid/id_hash_retain_stash.stderr new file mode 100644 index 00000000..8a640b3e --- /dev/null +++ b/crates/iddqd/tests/ui/invalid/id_hash_retain_stash.stderr @@ -0,0 +1,13 @@ +error[E0521]: borrowed data escapes outside of closure + --> tests/ui/invalid/id_hash_retain_stash.rs:24:9 + | +22 | let mut stashed = Vec::new(); + | ----------- `stashed` declared here, outside of the closure body +23 | map.retain(|item| { + | ---- `item` is a reference that is only valid in the closure body +24 | stashed.push(item); + | ^^^^^^^^^^^^^^^^^^ `item` escapes the closure body here + | + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance diff --git a/crates/iddqd/tests/ui/invalid/id_ord_retain_stash.rs b/crates/iddqd/tests/ui/invalid/id_ord_retain_stash.rs new file mode 100644 index 00000000..f7ad2d62 --- /dev/null +++ b/crates/iddqd/tests/ui/invalid/id_ord_retain_stash.rs @@ -0,0 +1,29 @@ +use iddqd::{IdOrdItem, IdOrdMap, id_upcast}; + +#[derive(Debug)] +struct Item { + id: u32, +} + +impl IdOrdItem for Item { + type Key<'a> = u32; + + fn key(&self) -> Self::Key<'_> { + self.id + } + + id_upcast!(); +} + +fn main() { + let mut map = IdOrdMap::::new(); + map.insert_unique(Item { id: 0 }).unwrap(); + + let mut stashed = Vec::new(); + map.retain(|item| { + stashed.push(item); + false + }); + + stashed[0].id = 1; +} diff --git a/crates/iddqd/tests/ui/invalid/id_ord_retain_stash.stderr b/crates/iddqd/tests/ui/invalid/id_ord_retain_stash.stderr new file mode 100644 index 00000000..96e45513 --- /dev/null +++ b/crates/iddqd/tests/ui/invalid/id_ord_retain_stash.stderr @@ -0,0 +1,13 @@ +error[E0521]: borrowed data escapes outside of closure + --> tests/ui/invalid/id_ord_retain_stash.rs:24:9 + | +22 | let mut stashed = Vec::new(); + | ----------- `stashed` declared here, outside of the closure body +23 | map.retain(|item| { + | ---- `item` is a reference that is only valid in the closure body +24 | stashed.push(item); + | ^^^^^^^^^^^^^^^^^^ `item` escapes the closure body here + | + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance diff --git a/crates/iddqd/tests/ui/invalid/tri_hash_retain_stash.rs b/crates/iddqd/tests/ui/invalid/tri_hash_retain_stash.rs new file mode 100644 index 00000000..faa96c00 --- /dev/null +++ b/crates/iddqd/tests/ui/invalid/tri_hash_retain_stash.rs @@ -0,0 +1,46 @@ +use iddqd::{TriHashItem, TriHashMap, tri_upcast}; + +#[derive(Debug)] +struct Item { + id: u32, + key2: u32, + key3: u32, +} + +impl TriHashItem for Item { + type K1<'a> = u32; + type K2<'a> = u32; + type K3<'a> = u32; + + fn key1(&self) -> Self::K1<'_> { + self.id + } + + fn key2(&self) -> Self::K2<'_> { + self.key2 + } + + fn key3(&self) -> Self::K3<'_> { + self.key3 + } + + tri_upcast!(); +} + +fn main() { + let mut map = TriHashMap::::new(); + map.insert_unique(Item { + id: 0, + key2: 10, + key3: 20, + }) + .unwrap(); + + let mut stashed = Vec::new(); + map.retain(|item| { + stashed.push(item); + false + }); + + stashed[0].id = 1; +} diff --git a/crates/iddqd/tests/ui/invalid/tri_hash_retain_stash.stderr b/crates/iddqd/tests/ui/invalid/tri_hash_retain_stash.stderr new file mode 100644 index 00000000..188dfafe --- /dev/null +++ b/crates/iddqd/tests/ui/invalid/tri_hash_retain_stash.stderr @@ -0,0 +1,13 @@ +error[E0521]: borrowed data escapes outside of closure + --> tests/ui/invalid/tri_hash_retain_stash.rs:41:9 + | +39 | let mut stashed = Vec::new(); + | ----------- `stashed` declared here, outside of the closure body +40 | map.retain(|item| { + | ---- `item` is a reference that is only valid in the closure body +41 | stashed.push(item); + | ^^^^^^^^^^^^^^^^^^ `item` escapes the closure body here + | + = note: requirement occurs because of a mutable reference to `Vec>` + = note: mutable references are invariant over their type parameter + = help: see for more information about variance