Skip to content

Commit ff64396

Browse files
committed
Recurse into hir types
1 parent 6832bf1 commit ff64396

File tree

3 files changed

+71
-12
lines changed

3 files changed

+71
-12
lines changed

clippy_lints/src/zero_sized_map_values.rs

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,31 @@ use rustc_typeck::hir_ty_to_ty;
1010
use crate::utils::{match_type, paths, span_lint_and_help};
1111

1212
declare_clippy_lint! {
13-
/// **What it does:**
13+
/// **What it does:** Checks for maps with zero-sized value types anywhere in the code.
1414
///
15-
/// **Why is this bad?**
15+
/// **Why is this bad?** Since there is only a single value for a zero-sized type, a map
16+
/// containing zero sized values is effectively a set. Using a set in that case improves
17+
/// readability and communicates intent more clearly.
1618
///
17-
/// **Known problems:** None.
19+
/// **Known problems:** A zero-sized type cannot be recovered later if it contains private
20+
/// fields.
1821
///
1922
/// **Example:**
2023
///
2124
/// ```rust
22-
/// // example code where clippy issues a warning
25+
/// fn unique_words(text: &str) -> HashMap<&str, ()> {
26+
/// todo!();
27+
/// }
2328
/// ```
2429
/// Use instead:
2530
/// ```rust
26-
/// // example code which does not raise clippy warning
31+
/// fn unique_words(text: &str) -> HashSet<&str> {
32+
/// todo!();
33+
/// }
2734
/// ```
2835
pub ZERO_SIZED_MAP_VALUES,
2936
nursery,
30-
"default lint description"
37+
"usage of map with zero-sized value type"
3138
}
3239

3340
declare_lint_pass!(ZeroSizedMapValues => [ZERO_SIZED_MAP_VALUES]);
@@ -59,7 +66,49 @@ impl ZeroSizedMapValues {
5966
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
6067
self.check_ty(cx, ty, hir_ty.span);
6168

62-
// TODO recurse on qpath
69+
match hir_ty.kind {
70+
TyKind::Path(ref qpath) => match *qpath {
71+
QPath::Resolved(ty, ref p) => {
72+
if let Some(ty) = ty {
73+
self.check_hir_ty(cx, ty);
74+
}
75+
76+
for ty in p.segments.iter().flat_map(|seg| {
77+
seg.args
78+
.as_ref()
79+
.map_or_else(|| [].iter(), |params| params.args.iter())
80+
.filter_map(|arg| match arg {
81+
GenericArg::Type(ty) => Some(ty),
82+
_ => None,
83+
})
84+
}) {
85+
self.check_hir_ty(cx, ty);
86+
}
87+
},
88+
QPath::TypeRelative(ty, ref seg) => {
89+
self.check_hir_ty(cx, ty);
90+
91+
if let Some(ref params) = seg.args {
92+
for ty in params.args.iter().filter_map(|arg| match arg {
93+
GenericArg::Type(ty) => Some(ty),
94+
_ => None,
95+
}) {
96+
self.check_hir_ty(cx, ty);
97+
}
98+
}
99+
},
100+
QPath::LangItem(..) => {},
101+
},
102+
TyKind::Slice(ref ty) | TyKind::Array(ref ty, _) | TyKind::Ptr(MutTy { ref ty, .. }) => {
103+
self.check_hir_ty(cx, ty);
104+
},
105+
TyKind::Tup(tys) => {
106+
for ty in tys {
107+
self.check_hir_ty(cx, ty);
108+
}
109+
},
110+
_ => {},
111+
}
63112
}
64113

65114
fn check_ty<'tcx>(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) {

tests/ui/zero_sized_map_values.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::collections::HashMap;
44
struct Test {
55
ok: HashMap<String, usize>,
66
not_ok: HashMap<String, ()>,
7+
8+
also_not_ok: Vec<HashMap<usize, ()>>,
79
}
810

911
fn test(map: HashMap<String, ()>, key: &str) -> HashMap<String, ()> {

tests/ui/zero_sized_map_values.stderr

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,44 @@ LL | not_ok: HashMap<String, ()>,
88
= help: consider using a set instead
99

1010
error: map with zero-sized value type
11-
--> $DIR/zero_sized_map_values.rs:9:14
11+
--> $DIR/zero_sized_map_values.rs:8:22
12+
|
13+
LL | also_not_ok: Vec<HashMap<usize, ()>>,
14+
| ^^^^^^^^^^^^^^^^^^
15+
|
16+
= help: consider using a set instead
17+
18+
error: map with zero-sized value type
19+
--> $DIR/zero_sized_map_values.rs:11:14
1220
|
1321
LL | fn test(map: HashMap<String, ()>, key: &str) -> HashMap<String, ()> {
1422
| ^^^^^^^^^^^^^^^^^^^
1523
|
1624
= help: consider using a set instead
1725

1826
error: map with zero-sized value type
19-
--> $DIR/zero_sized_map_values.rs:9:49
27+
--> $DIR/zero_sized_map_values.rs:11:49
2028
|
2129
LL | fn test(map: HashMap<String, ()>, key: &str) -> HashMap<String, ()> {
2230
| ^^^^^^^^^^^^^^^^^^^
2331
|
2432
= help: consider using a set instead
2533

2634
error: map with zero-sized value type
27-
--> $DIR/zero_sized_map_values.rs:19:9
35+
--> $DIR/zero_sized_map_values.rs:21:9
2836
|
2937
LL | let _: HashMap<String, ()> = HashMap::new();
3038
| ^
3139
|
3240
= help: consider using a set instead
3341

3442
error: map with zero-sized value type
35-
--> $DIR/zero_sized_map_values.rs:22:9
43+
--> $DIR/zero_sized_map_values.rs:24:9
3644
|
3745
LL | let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect();
3846
| ^
3947
|
4048
= help: consider using a set instead
4149

42-
error: aborting due to 5 previous errors
50+
error: aborting due to 6 previous errors
4351

0 commit comments

Comments
 (0)