Skip to content

Commit e009073

Browse files
committed
Fix FP when using raw pointers as hashed keys
1 parent a64b769 commit e009073

File tree

3 files changed

+40
-15
lines changed

3 files changed

+40
-15
lines changed

clippy_lints/src/mut_key.rs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,26 +103,39 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::
103103
fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
104104
let ty = ty.peel_refs();
105105
if let Adt(def, substs) = ty.kind() {
106-
if [sym::hashmap_type, sym::BTreeMap, sym::hashset_type, sym::BTreeMap]
106+
let is_map_type = [sym::hashmap_type, sym::BTreeMap, sym::hashset_type, sym::BTreeMap]
107107
.iter()
108-
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did))
109-
&& is_mutable_type(cx, substs.type_at(0), span)
110-
{
108+
.any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did));
109+
if is_map_type && is_mutable_type(cx, substs.type_at(0), span, true) {
111110
span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
112111
}
113112
}
114113
}
115114

116-
fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
115+
fn is_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, is_top_level_type: bool) -> bool {
117116
match *ty.kind() {
118-
RawPtr(TypeAndMut { ty: inner_ty, mutbl }) | Ref(_, inner_ty, mutbl) => {
119-
mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span)
117+
RawPtr(TypeAndMut { ty: inner_ty, mutbl }) => {
118+
if is_top_level_type {
119+
// Raw pointers are hashed by the address they point to, not what is pointed to.
120+
// Therefore, using a raw pointer to any type as the top-level key type is OK.
121+
// Using raw pointers _in_ the key type is not, because the wrapper type could
122+
// provide a custom `impl` for `Hash` (which could deref the raw pointer).
123+
//
124+
// see:
125+
// - clippy issue: https://github.com/rust-lang/rust-clippy/issues/6745
126+
// - std code: https://github.com/rust-lang/rust/blob/1.54.0/library/core/src/hash/mod.rs#L717-L736
127+
false
128+
} else {
129+
mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span, false)
130+
}
120131
},
121-
Slice(inner_ty) => is_mutable_type(cx, inner_ty, span),
132+
Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || is_mutable_type(cx, inner_ty, span, false),
133+
Slice(inner_ty) => is_mutable_type(cx, inner_ty, span, false),
122134
Array(inner_ty, size) => {
123-
size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) && is_mutable_type(cx, inner_ty, span)
135+
size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0)
136+
&& is_mutable_type(cx, inner_ty, span, false)
124137
},
125-
Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span)),
138+
Tuple(..) => ty.tuple_fields().any(|ty| is_mutable_type(cx, ty, span, false)),
126139
Adt(..) => {
127140
!ty.has_escaping_bound_vars()
128141
&& cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()

tests/ui/mut_key.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::cell::Cell;
12
use std::collections::{HashMap, HashSet};
23
use std::hash::{Hash, Hasher};
34
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
@@ -31,11 +32,19 @@ fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<K
3132

3233
fn this_is_ok(_m: &mut HashMap<usize, Key>) {}
3334

35+
// Raw pointers are hashed by the address they point to, so it doesn't matter if they point to a
36+
// type with interior mutability. See:
37+
// - clippy issue: https://github.com/rust-lang/rust-clippy/issues/6745
38+
// - std lib: https://github.com/rust-lang/rust/blob/1.54.0/library/core/src/hash/mod.rs#L717-L736
39+
// So these are OK:
40+
fn raw_ptr_is_ok(_m: &mut HashMap<*const Key, ()>) {}
41+
fn raw_mut_ptr_is_ok(_m: &mut HashMap<*mut Key, ()>) {}
42+
3443
#[allow(unused)]
3544
trait Trait {
3645
type AssociatedType;
3746

38-
fn trait_fn(&self, set: std::collections::HashSet<Self::AssociatedType>);
47+
fn trait_fn(&self, set: HashSet<Self::AssociatedType>);
3948
}
4049

4150
fn generics_are_ok_too<K>(_m: &mut HashSet<K>) {
@@ -52,4 +61,7 @@ fn main() {
5261
tuples::<Key>(&mut HashMap::new());
5362
tuples::<()>(&mut HashMap::new());
5463
tuples_bad::<()>(&mut HashMap::new());
64+
65+
raw_ptr_is_ok(&mut HashMap::new());
66+
raw_mut_ptr_is_ok(&mut HashMap::new());
5567
}

tests/ui/mut_key.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
error: mutable key type
2-
--> $DIR/mut_key.rs:27:32
2+
--> $DIR/mut_key.rs:28:32
33
|
44
LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: `-D clippy::mutable-key-type` implied by `-D warnings`
88

99
error: mutable key type
10-
--> $DIR/mut_key.rs:27:72
10+
--> $DIR/mut_key.rs:28:72
1111
|
1212
LL | fn should_not_take_this_arg(m: &mut HashMap<Key, usize>, _n: usize) -> HashSet<Key> {
1313
| ^^^^^^^^^^^^
1414

1515
error: mutable key type
16-
--> $DIR/mut_key.rs:28:5
16+
--> $DIR/mut_key.rs:29:5
1717
|
1818
LL | let _other: HashMap<Key, bool> = HashMap::new();
1919
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2020

2121
error: mutable key type
22-
--> $DIR/mut_key.rs:47:22
22+
--> $DIR/mut_key.rs:56:22
2323
|
2424
LL | fn tuples_bad<U>(_m: &mut HashMap<(Key, U), bool>) {}
2525
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)