Skip to content

Commit 33b16d3

Browse files
committed
implicit_hasher: use a single multipart suggestion
1 parent 79783e9 commit 33b16d3

File tree

5 files changed

+181
-91
lines changed

5 files changed

+181
-91
lines changed

clippy_lints/src/implicit_hasher.rs

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::borrow::Cow;
22
use std::collections::BTreeMap;
33

4-
use rustc_errors::Diag;
4+
use rustc_errors::{Applicability, Diag};
55
use rustc_hir as hir;
66
use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
77
use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
@@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass;
1313
use rustc_span::symbol::sym;
1414
use rustc_span::Span;
1515

16-
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
16+
use clippy_utils::diagnostics::span_lint_and_then;
1717
use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt};
1818
use clippy_utils::ty::is_type_diagnostic_item;
1919

@@ -77,33 +77,32 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
7777
&generics_snip[1..generics_snip.len() - 1]
7878
};
7979

80-
multispan_sugg(
81-
diag,
82-
"consider adding a type parameter",
83-
vec![
84-
(
85-
generics_suggestion_span,
86-
format!(
87-
"<{generics_snip}{}S: ::std::hash::BuildHasher{}>",
88-
if generics_snip.is_empty() { "" } else { ", " },
89-
if vis.suggestions.is_empty() {
90-
""
91-
} else {
92-
// request users to add `Default` bound so that generic constructors can be used
93-
" + Default"
94-
},
95-
),
96-
),
97-
(
98-
target.span(),
99-
format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
80+
let mut suggestions = vec![
81+
(
82+
generics_suggestion_span,
83+
format!(
84+
"<{generics_snip}{}S: ::std::hash::BuildHasher{}>",
85+
if generics_snip.is_empty() { "" } else { ", " },
86+
if vis.suggestions.is_empty() {
87+
""
88+
} else {
89+
// request users to add `Default` bound so that generic constructors can be used
90+
" + Default"
91+
},
10092
),
101-
],
93+
),
94+
(
95+
target.span(),
96+
format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
97+
),
98+
];
99+
suggestions.extend(vis.suggestions);
100+
101+
diag.multipart_suggestion(
102+
"add a type parameter for `BuildHasher`",
103+
suggestions,
104+
Applicability::MaybeIncorrect,
102105
);
103-
104-
if !vis.suggestions.is_empty() {
105-
multispan_sugg(diag, "...and use generic constructor", vis.suggestions);
106-
}
107106
}
108107

109108
if !cx.effective_visibilities.is_exported(item.owner_id.def_id) {

tests/ui/crashes/ice-3717.stderr

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@ note: the lint level is defined here
99
|
1010
LL | #![deny(clippy::implicit_hasher)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^^
12-
help: consider adding a type parameter
12+
help: add a type parameter for `BuildHasher`
1313
|
14-
LL | pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) {
15-
| +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~
16-
help: ...and use generic constructor
14+
LL ~ pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) {
15+
LL |
16+
LL | let _ = [0u8; 0];
17+
LL ~ let _: HashSet<usize> = HashSet::default();
1718
|
18-
LL | let _: HashSet<usize> = HashSet::default();
19-
| ~~~~~~~~~~~~~~~~~~
2019

2120
error: aborting due to 1 previous error
2221

tests/ui/implicit_hasher.fixed

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//@aux-build:proc_macros.rs
2+
#![deny(clippy::implicit_hasher)]
3+
4+
#[macro_use]
5+
extern crate proc_macros;
6+
7+
use std::cmp::Eq;
8+
use std::collections::{HashMap, HashSet};
9+
use std::hash::{BuildHasher, Hash};
10+
11+
pub trait Foo<T>: Sized {
12+
fn make() -> (Self, Self);
13+
}
14+
15+
impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> {
16+
fn make() -> (Self, Self) {
17+
// OK, don't suggest to modify these
18+
let _: HashMap<i32, i32> = HashMap::new();
19+
let _: HashSet<i32> = HashSet::new();
20+
21+
(HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
22+
}
23+
}
24+
impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) {
25+
fn make() -> (Self, Self) {
26+
((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),))
27+
}
28+
}
29+
impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> {
30+
fn make() -> (Self, Self) {
31+
(HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
32+
}
33+
}
34+
35+
impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> {
36+
fn make() -> (Self, Self) {
37+
(HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
38+
}
39+
}
40+
impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> {
41+
fn make() -> (Self, Self) {
42+
(HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
43+
}
44+
}
45+
46+
impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> {
47+
fn make() -> (Self, Self) {
48+
(HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
49+
}
50+
}
51+
impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> {
52+
fn make() -> (Self, Self) {
53+
(HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
54+
}
55+
}
56+
57+
impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> {
58+
fn make() -> (Self, Self) {
59+
(HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
60+
}
61+
}
62+
impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
63+
fn make() -> (Self, Self) {
64+
(HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
65+
}
66+
}
67+
68+
pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {}
69+
70+
pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {}
71+
72+
#[inline_macros]
73+
pub mod gen {
74+
use super::*;
75+
inline! {
76+
impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
77+
fn make() -> (Self, Self) {
78+
(HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
79+
}
80+
}
81+
82+
pub fn bar(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
83+
}
84+
}
85+
86+
// When the macro is in a different file, the suggestion spans can't be combined properly
87+
// and should not cause an ICE
88+
// See #2707
89+
#[macro_use]
90+
#[path = "auxiliary/test_macro.rs"]
91+
pub mod test_macro;
92+
__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A);
93+
94+
// #4260
95+
external! {
96+
pub fn f(input: &HashMap<u32, u32>) {}
97+
}
98+
99+
// #7712
100+
pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {}
101+
102+
fn main() {}

tests/ui/implicit_hasher.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//@aux-build:proc_macros.rs
2-
//@no-rustfix
32
#![deny(clippy::implicit_hasher)]
4-
#![allow(unused)]
53

64
#[macro_use]
75
extern crate proc_macros;
@@ -67,7 +65,9 @@ impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
6765
}
6866
}
6967

70-
pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
68+
pub fn map(map: &mut HashMap<i32, i32>) {}
69+
70+
pub fn set(set: &mut HashSet<i32>) {}
7171

7272
#[inline_macros]
7373
pub mod gen {

tests/ui/implicit_hasher.stderr

Lines changed: 45 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,96 @@
11
error: impl for `HashMap` should be generalized over different hashers
2-
--> tests/ui/implicit_hasher.rs:17:35
2+
--> tests/ui/implicit_hasher.rs:15:35
33
|
44
LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
55
| ^^^^^^^^^^^^^
66
|
77
note: the lint level is defined here
8-
--> tests/ui/implicit_hasher.rs:3:9
8+
--> tests/ui/implicit_hasher.rs:2:9
99
|
1010
LL | #![deny(clippy::implicit_hasher)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^^
12-
help: consider adding a type parameter
12+
help: add a type parameter for `BuildHasher`
1313
|
14-
LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> {
15-
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
16-
help: ...and use generic constructor
14+
LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> {
15+
LL | fn make() -> (Self, Self) {
16+
...
17+
LL |
18+
LL ~ (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
1719
|
18-
LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
19-
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2020

2121
error: impl for `HashMap` should be generalized over different hashers
22-
--> tests/ui/implicit_hasher.rs:26:36
22+
--> tests/ui/implicit_hasher.rs:24:36
2323
|
2424
LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
2525
| ^^^^^^^^^^^^^
2626
|
27-
help: consider adding a type parameter
27+
help: add a type parameter for `BuildHasher`
2828
|
29-
LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) {
30-
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
31-
help: ...and use generic constructor
29+
LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) {
30+
LL | fn make() -> (Self, Self) {
31+
LL ~ ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),))
3232
|
33-
LL | ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),))
34-
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3533

3634
error: impl for `HashMap` should be generalized over different hashers
37-
--> tests/ui/implicit_hasher.rs:31:19
35+
--> tests/ui/implicit_hasher.rs:29:19
3836
|
3937
LL | impl Foo<i16> for HashMap<String, String> {
4038
| ^^^^^^^^^^^^^^^^^^^^^^^
4139
|
42-
help: consider adding a type parameter
40+
help: add a type parameter for `BuildHasher`
4341
|
44-
LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> {
45-
| +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~~~~~~~
46-
help: ...and use generic constructor
42+
LL ~ impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> {
43+
LL | fn make() -> (Self, Self) {
44+
LL ~ (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
4745
|
48-
LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
49-
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5046

5147
error: impl for `HashSet` should be generalized over different hashers
52-
--> tests/ui/implicit_hasher.rs:48:32
48+
--> tests/ui/implicit_hasher.rs:46:32
5349
|
5450
LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
5551
| ^^^^^^^^^^
5652
|
57-
help: consider adding a type parameter
53+
help: add a type parameter for `BuildHasher`
5854
|
59-
LL | impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> {
60-
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~
61-
help: ...and use generic constructor
55+
LL ~ impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> {
56+
LL | fn make() -> (Self, Self) {
57+
LL ~ (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
6258
|
63-
LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
64-
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6559

6660
error: impl for `HashSet` should be generalized over different hashers
67-
--> tests/ui/implicit_hasher.rs:53:19
61+
--> tests/ui/implicit_hasher.rs:51:19
6862
|
6963
LL | impl Foo<i16> for HashSet<String> {
7064
| ^^^^^^^^^^^^^^^
7165
|
72-
help: consider adding a type parameter
66+
help: add a type parameter for `BuildHasher`
7367
|
74-
LL | impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> {
75-
| +++++++++++++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~
76-
help: ...and use generic constructor
68+
LL ~ impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> {
69+
LL | fn make() -> (Self, Self) {
70+
LL ~ (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
7771
|
78-
LL | (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
79-
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8072

8173
error: parameter of type `HashMap` should be generalized over different hashers
82-
--> tests/ui/implicit_hasher.rs:70:23
74+
--> tests/ui/implicit_hasher.rs:68:22
8375
|
84-
LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
85-
| ^^^^^^^^^^^^^^^^^
76+
LL | pub fn map(map: &mut HashMap<i32, i32>) {}
77+
| ^^^^^^^^^^^^^^^^^
8678
|
87-
help: consider adding a type parameter
79+
help: add a type parameter for `BuildHasher`
8880
|
89-
LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32, S>, _set: &mut HashSet<i32>) {}
90-
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
81+
LL | pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {}
82+
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~
9183

9284
error: parameter of type `HashSet` should be generalized over different hashers
93-
--> tests/ui/implicit_hasher.rs:70:53
85+
--> tests/ui/implicit_hasher.rs:70:22
9486
|
95-
LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
96-
| ^^^^^^^^^^^^
87+
LL | pub fn set(set: &mut HashSet<i32>) {}
88+
| ^^^^^^^^^^^^
9789
|
98-
help: consider adding a type parameter
90+
help: add a type parameter for `BuildHasher`
9991
|
100-
LL | pub fn foo<S: ::std::hash::BuildHasher>(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32, S>) {}
101-
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
92+
LL | pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {}
93+
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~
10294

10395
error: impl for `HashMap` should be generalized over different hashers
10496
--> tests/ui/implicit_hasher.rs:76:43
@@ -107,22 +99,20 @@ LL | impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
10799
| ^^^^^^^^^^^^^
108100
|
109101
= note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info)
110-
help: consider adding a type parameter
102+
help: add a type parameter for `BuildHasher`
111103
|
112-
LL | impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
113-
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
114-
help: ...and use generic constructor
104+
LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
105+
LL | fn make() -> (Self, Self) {
106+
LL ~ (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
115107
|
116-
LL | (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
117-
| ~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
118108

119109
error: parameter of type `HashMap` should be generalized over different hashers
120110
--> tests/ui/implicit_hasher.rs:100:35
121111
|
122112
LL | pub async fn election_vote(_data: HashMap<i32, i32>) {}
123113
| ^^^^^^^^^^^^^^^^^
124114
|
125-
help: consider adding a type parameter
115+
help: add a type parameter for `BuildHasher`
126116
|
127117
LL | pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {}
128118
| +++++++++++++++++++++++++++++ ~~~~~~~~~~~~~~~~~~~~

0 commit comments

Comments
 (0)