about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
authorQuinn Sinclair <me@partiallytyped.dev>2023-11-19 11:33:01 +0100
committerPartiallyTyped <me@partiallytyped.dev>2023-11-19 11:33:01 +0100
commit3c1e0afa58dbe1287c1e9a9ea704f9b76b6845cc (patch)
tree832bddb91b9eb56aa83343f921e5ca6417470c1d /tests
parent6eb935a578f7b2fcd87377bd571c4ff1dec48982 (diff)
downloadrust-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.rs136
-rw-r--r--tests/ui/impl_hash_with_borrow_str_and_bytes.stderr41
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
+