Auto merge of #11781 - partiallytyped:11710, r=xFrednet
Verify Borrow<T> semantics for types that implement Hash, Borrow<str> and Borrow<[u8]>. Fixes #11710 The essence of the issue is that types that implement Borrow<T> provide a facet or a representation of the underlying type. Under these semantics `hash(a) == hash(a.borrow())`. This is a problem when a type implements `Borrow<str>`, `Borrow<[u8]>` and Hash, it is expected that the hash of all three types is identical. The problem is that the hash of [u8] is not the same as that of a String, even when the byte reference ([u8]) is derived from `.as_bytes()` - [x] Followed [lint naming conventions][lint_naming] - [x] Added passing UI tests (including committed `.stderr` file) - [x] `cargo test` passes locally - [x] Executed `cargo dev update_lints` - [x] Added lint documentation - [x] Run `cargo dev fmt` --- - [x] Explanation of the issue in the code - [x] Tests reproducing the issue - [x] Lint rule and emission --- changelog: New lint: [`impl_hash_borrow_with_str_and_bytes`] [#11781](https://github.com/rust-lang/rust-clippy/pull/11781)
This commit is contained in:
commit
9c3a365fd2
6 changed files with 287 additions and 0 deletions
136
tests/ui/impl_hash_with_borrow_str_and_bytes.rs
Normal file
136
tests/ui/impl_hash_with_borrow_str_and_bytes.rs
Normal file
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
41
tests/ui/impl_hash_with_borrow_str_and_bytes.stderr
Normal file
41
tests/ui/impl_hash_with_borrow_str_and_bytes.stderr
Normal file
|
|
@ -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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue