diff options
| author | Quinn Sinclair <me@partiallytyped.dev> | 2023-11-19 11:33:01 +0100 |
|---|---|---|
| committer | PartiallyTyped <me@partiallytyped.dev> | 2023-11-19 11:33:01 +0100 |
| commit | 3c1e0afa58dbe1287c1e9a9ea704f9b76b6845cc (patch) | |
| tree | 832bddb91b9eb56aa83343f921e5ca6417470c1d /tests | |
| parent | 6eb935a578f7b2fcd87377bd571c4ff1dec48982 (diff) | |
| download | rust-3c1e0afa58dbe1287c1e9a9ea704f9b76b6845cc.tar.gz rust-3c1e0afa58dbe1287c1e9a9ea704f9b76b6845cc.zip | |
New Lint [`impl_hash_with_borrow_str_and_bytes`]
Implements a lint to prevent implementation of Hash, Borrow<str> and Borrow<[u8]> as it breaks Borrow<T> "semantics". According to the book, types that implement Borrow<A> and Borrow<B> must ensure equality of borrow results under Eq,Ord and Hash. > In particular Eq, Ord and Hash must be equivalent for borrowed and owned values: x.borrow() == y.borrow() should give the same result as x == y. In the same way, hash(x) == hash(x as Borrow<[u8]>) != hash(x as Borrow<str>). changelog: newlint [`impl_hash_with_borrow_str_and_bytes`]
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/ui/impl_hash_with_borrow_str_and_bytes.rs | 136 | ||||
| -rw-r--r-- | tests/ui/impl_hash_with_borrow_str_and_bytes.stderr | 41 |
2 files changed, 177 insertions, 0 deletions
diff --git a/tests/ui/impl_hash_with_borrow_str_and_bytes.rs b/tests/ui/impl_hash_with_borrow_str_and_bytes.rs new file mode 100644 index 00000000000..f6ce6153e01 --- /dev/null +++ b/tests/ui/impl_hash_with_borrow_str_and_bytes.rs @@ -0,0 +1,136 @@ +#![warn(clippy::impl_hash_borrow_with_str_and_bytes)] + +use std::borrow::Borrow; +use std::hash::{Hash, Hasher}; + +struct ExampleType { + data: String, +} + +impl Hash for ExampleType { + //~^ ERROR: can't + fn hash<H: Hasher>(&self, state: &mut H) { + self.data.hash(state); + } +} + +impl Borrow<str> for ExampleType { + fn borrow(&self) -> &str { + &self.data + } +} + +impl Borrow<[u8]> for ExampleType { + fn borrow(&self) -> &[u8] { + self.data.as_bytes() + } +} + +struct ShouldNotRaiseForHash {} +impl Hash for ShouldNotRaiseForHash { + fn hash<H: Hasher>(&self, state: &mut H) { + todo!(); + } +} + +struct ShouldNotRaiseForBorrow {} +impl Borrow<str> for ShouldNotRaiseForBorrow { + fn borrow(&self) -> &str { + todo!(); + } +} +impl Borrow<[u8]> for ShouldNotRaiseForBorrow { + fn borrow(&self) -> &[u8] { + todo!(); + } +} + +struct ShouldNotRaiseForHashBorrowStr {} +impl Hash for ShouldNotRaiseForHashBorrowStr { + fn hash<H: Hasher>(&self, state: &mut H) { + todo!(); + } +} +impl Borrow<str> for ShouldNotRaiseForHashBorrowStr { + fn borrow(&self) -> &str { + todo!(); + } +} + +struct ShouldNotRaiseForHashBorrowSlice {} +impl Hash for ShouldNotRaiseForHashBorrowSlice { + fn hash<H: Hasher>(&self, state: &mut H) { + todo!(); + } +} + +impl Borrow<[u8]> for ShouldNotRaiseForHashBorrowSlice { + fn borrow(&self) -> &[u8] { + todo!(); + } +} + +#[derive(Hash)] +//~^ ERROR: can't +struct Derived { + data: String, +} + +impl Borrow<str> for Derived { + fn borrow(&self) -> &str { + self.data.as_str() + } +} + +impl Borrow<[u8]> for Derived { + fn borrow(&self) -> &[u8] { + self.data.as_bytes() + } +} + +struct GenericExampleType<T> { + data: T, +} + +impl<T: Hash> Hash for GenericExampleType<T> { + fn hash<H: Hasher>(&self, state: &mut H) { + self.data.hash(state); + } +} + +impl Borrow<str> for GenericExampleType<String> { + fn borrow(&self) -> &str { + &self.data + } +} + +impl Borrow<[u8]> for GenericExampleType<&'static [u8]> { + fn borrow(&self) -> &[u8] { + self.data + } +} + +struct GenericExampleType2<T> { + data: T, +} + +impl Hash for GenericExampleType2<String> { + //~^ ERROR: can't + // this is correctly throwing an error for generic with concrete impl + // for all 3 types + fn hash<H: Hasher>(&self, state: &mut H) { + self.data.hash(state); + } +} + +impl Borrow<str> for GenericExampleType2<String> { + fn borrow(&self) -> &str { + &self.data + } +} + +impl Borrow<[u8]> for GenericExampleType2<String> { + fn borrow(&self) -> &[u8] { + self.data.as_bytes() + } +} diff --git a/tests/ui/impl_hash_with_borrow_str_and_bytes.stderr b/tests/ui/impl_hash_with_borrow_str_and_bytes.stderr new file mode 100644 index 00000000000..afc35ef9845 --- /dev/null +++ b/tests/ui/impl_hash_with_borrow_str_and_bytes.stderr @@ -0,0 +1,41 @@ +error: the semantics of `Borrow<T>` around `Hash` can't be satisfied when both `Borrow<str>` and `Borrow<[u8]>` are implemented + --> $DIR/impl_hash_with_borrow_str_and_bytes.rs:10:6 + | +LL | impl Hash for ExampleType { + | ^^^^ + | + = note: the `Borrow` semantics require that `Hash` must behave the same for all implementations of Borrow<T> + = note: however, the hash implementations of a string (`str`) and the bytes of a string `[u8]` do not behave the same ... + = note: ... as (`hash("abc") != hash("abc".as_bytes())` + = help: consider either removing one of the `Borrow` implementations (`Borrow<str>` or `Borrow<[u8]>`) ... + = help: ... or not implementing `Hash` for this type + = note: `-D clippy::impl-hash-borrow-with-str-and-bytes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::impl_hash_borrow_with_str_and_bytes)]` + +error: the semantics of `Borrow<T>` around `Hash` can't be satisfied when both `Borrow<str>` and `Borrow<[u8]>` are implemented + --> $DIR/impl_hash_with_borrow_str_and_bytes.rs:73:10 + | +LL | #[derive(Hash)] + | ^^^^ + | + = note: the `Borrow` semantics require that `Hash` must behave the same for all implementations of Borrow<T> + = note: however, the hash implementations of a string (`str`) and the bytes of a string `[u8]` do not behave the same ... + = note: ... as (`hash("abc") != hash("abc".as_bytes())` + = help: consider either removing one of the `Borrow` implementations (`Borrow<str>` or `Borrow<[u8]>`) ... + = help: ... or not implementing `Hash` for this type + = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: the semantics of `Borrow<T>` around `Hash` can't be satisfied when both `Borrow<str>` and `Borrow<[u8]>` are implemented + --> $DIR/impl_hash_with_borrow_str_and_bytes.rs:117:6 + | +LL | impl Hash for GenericExampleType2<String> { + | ^^^^ + | + = note: the `Borrow` semantics require that `Hash` must behave the same for all implementations of Borrow<T> + = note: however, the hash implementations of a string (`str`) and the bytes of a string `[u8]` do not behave the same ... + = note: ... as (`hash("abc") != hash("abc".as_bytes())` + = help: consider either removing one of the `Borrow` implementations (`Borrow<str>` or `Borrow<[u8]>`) ... + = help: ... or not implementing `Hash` for this type + +error: aborting due to 3 previous errors + |
