Skip to content

Commit d924014

Browse files
committed
add suggestions to rest_when_destructuring_struct
1 parent 4cdbec3 commit d924014

File tree

5 files changed

+139
-21
lines changed

5 files changed

+139
-21
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,6 +952,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
952952
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
953953
store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
954954
store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny));
955-
store.register_early_pass(|| Box::new(rest_when_destructuring_struct::RestWhenDestructuringStruct));
955+
store.register_late_pass(|_| Box::new(rest_when_destructuring_struct::RestWhenDestructuringStruct));
956956
// add lints here, do not remove this comment, it's used in `new_lint`
957957
}

clippy_lints/src/rest_when_destructuring_struct.rs

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
use clippy_utils::diagnostics::span_lint_and_help;
2-
use rustc_ast::ast::{Pat, PatFieldsRest, PatKind};
3-
use rustc_lint::{EarlyContext, EarlyLintPass};
1+
use crate::rustc_lint::LintContext as _;
2+
use clippy_utils::diagnostics::span_lint_and_then;
3+
use itertools::Itertools;
4+
use rustc_lint::LateLintPass;
5+
use rustc_middle::ty;
46
use rustc_session::declare_lint_pass;
57

68
declare_clippy_lint! {
@@ -41,17 +43,57 @@ declare_clippy_lint! {
4143
}
4244
declare_lint_pass!(RestWhenDestructuringStruct => [REST_WHEN_DESTRUCTURING_STRUCT]);
4345

44-
impl EarlyLintPass for RestWhenDestructuringStruct {
45-
fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
46-
if let PatKind::Struct(_, _, _, PatFieldsRest::Rest) = pat.kind {
47-
span_lint_and_help(
48-
cx,
49-
REST_WHEN_DESTRUCTURING_STRUCT,
50-
pat.span,
51-
"struct destructuring with rest (..)",
52-
None,
53-
"consider explicitly ignoring remaining fields with wildcard patterns (x: _)",
54-
);
46+
impl<'tcx> LateLintPass<'tcx> for RestWhenDestructuringStruct {
47+
fn check_pat(&mut self, cx: &rustc_lint::LateContext<'tcx>, pat: &'tcx rustc_hir::Pat<'tcx>) {
48+
if pat.span.in_external_macro(cx.tcx.sess.source_map()) {
49+
return;
50+
}
51+
52+
if let rustc_hir::PatKind::Struct(path, fields, true) = pat.kind {
53+
let qty = cx.typeck_results().qpath_res(&path, pat.hir_id);
54+
let ty = cx.typeck_results().pat_ty(pat);
55+
if let ty::Adt(a, _) = ty.kind() {
56+
let vid = a.variant_index_with_id(qty.def_id());
57+
let mut rest_fields = a.variants()[vid]
58+
.fields
59+
.iter()
60+
.map(|field| field.ident(cx.tcx))
61+
.filter(|pf| !fields.iter().any(|x| x.ident == *pf))
62+
.map(|x| format!("{x}: _"));
63+
let fmt_fields = rest_fields.join(", ");
64+
65+
let sm = cx.sess().source_map();
66+
67+
// It is not possible to get the span of the et cetera symbol at HIR level
68+
// so we have to get it in a bit of a roundabout way:
69+
70+
// Find the end of the last field if any.
71+
let last_field = fields.iter().last().map(|x| x.span.shrink_to_hi());
72+
// If no last field take the whole pattern.
73+
let last_field = last_field.unwrap_or(pat.span.shrink_to_lo());
74+
// Create a new span starting and ending just before the first .
75+
let before_dot = sm.span_extend_to_next_char(last_field, '.', true).shrink_to_hi();
76+
// Extend the span to the end of the line
77+
let rest_of_line = sm.span_extend_to_next_char(before_dot, '\n', true);
78+
// Shrink the span so it only contains dots
79+
let dotdot = sm.span_take_while(rest_of_line, |x| *x == '.');
80+
81+
span_lint_and_then(
82+
cx,
83+
REST_WHEN_DESTRUCTURING_STRUCT,
84+
pat.span,
85+
"struct destructuring with rest (..)",
86+
|diag| {
87+
// println!("{:?}", pat);
88+
diag.span_suggestion_verbose(
89+
dotdot,
90+
"consider explicitly ignoring remaining fields with wildcard patterns (x: _)",
91+
fmt_fields,
92+
rustc_errors::Applicability::MachineApplicable,
93+
);
94+
},
95+
);
96+
}
5597
}
5698
}
5799
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#![warn(clippy::rest_when_destructuring_struct)]
2+
#![allow(dead_code)]
3+
#![allow(unused_variables)]
4+
5+
struct S {
6+
a: u8,
7+
b: u8,
8+
c: u8,
9+
}
10+
11+
enum E {
12+
A { a1: u8, a2: u8 },
13+
B { b1: u8, b2: u8 },
14+
C {},
15+
}
16+
17+
fn main() {
18+
let s = S { a: 1, b: 2, c: 3 };
19+
20+
let S { a, b, c: _ } = s;
21+
//~^ rest_when_destructuring_struct
22+
23+
let e = E::A { a1: 1, a2: 2 };
24+
25+
match e {
26+
E::A { a1, a2 } => (),
27+
E::B { b1: _, b2: _ } => (),
28+
//~^ rest_when_destructuring_struct
29+
E::C { } => (),
30+
//~^ rest_when_destructuring_struct
31+
}
32+
33+
match e {
34+
E::A { a1: _, a2: _ } => (),
35+
E::B { b1: _, b2: _ } => (),
36+
//~^ rest_when_destructuring_struct
37+
E::C {} => (),
38+
}
39+
}

tests/ui/rest_when_destructuring_struct.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ struct S {
1111
enum E {
1212
A { a1: u8, a2: u8 },
1313
B { b1: u8, b2: u8 },
14+
C {},
1415
}
1516

1617
fn main() {
@@ -25,10 +26,14 @@ fn main() {
2526
E::A { a1, a2 } => (),
2627
E::B { .. } => (),
2728
//~^ rest_when_destructuring_struct
29+
E::C { .. } => (),
30+
//~^ rest_when_destructuring_struct
2831
}
2932

3033
match e {
3134
E::A { a1: _, a2: _ } => (),
32-
E::B { b1, b2: _ } => (),
35+
E::B { b1: _, .. } => (),
36+
//~^ rest_when_destructuring_struct
37+
E::C {} => (),
3338
}
3439
}
Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,52 @@
11
error: struct destructuring with rest (..)
2-
--> tests/ui/rest_when_destructuring_struct.rs:19:9
2+
--> tests/ui/rest_when_destructuring_struct.rs:20:9
33
|
44
LL | let S { a, b, .. } = s;
55
| ^^^^^^^^^^^^^^
66
|
7-
= help: consider explicitly ignoring remaining fields with wildcard patterns (x: _)
87
= note: `-D clippy::rest-when-destructuring-struct` implied by `-D warnings`
98
= help: to override `-D warnings` add `#[allow(clippy::rest_when_destructuring_struct)]`
9+
help: consider explicitly ignoring remaining fields with wildcard patterns (x: _)
10+
|
11+
LL - let S { a, b, .. } = s;
12+
LL + let S { a, b, c: _ } = s;
13+
|
1014

1115
error: struct destructuring with rest (..)
12-
--> tests/ui/rest_when_destructuring_struct.rs:26:9
16+
--> tests/ui/rest_when_destructuring_struct.rs:27:9
1317
|
1418
LL | E::B { .. } => (),
1519
| ^^^^^^^^^^^
1620
|
17-
= help: consider explicitly ignoring remaining fields with wildcard patterns (x: _)
21+
help: consider explicitly ignoring remaining fields with wildcard patterns (x: _)
22+
|
23+
LL - E::B { .. } => (),
24+
LL + E::B { b1: _, b2: _ } => (),
25+
|
26+
27+
error: struct destructuring with rest (..)
28+
--> tests/ui/rest_when_destructuring_struct.rs:29:9
29+
|
30+
LL | E::C { .. } => (),
31+
| ^^^^^^^^^^^
32+
|
33+
help: consider explicitly ignoring remaining fields with wildcard patterns (x: _)
34+
|
35+
LL - E::C { .. } => (),
36+
LL + E::C { } => (),
37+
|
38+
39+
error: struct destructuring with rest (..)
40+
--> tests/ui/rest_when_destructuring_struct.rs:35:9
41+
|
42+
LL | E::B { b1: _, .. } => (),
43+
| ^^^^^^^^^^^^^^^^^^
44+
|
45+
help: consider explicitly ignoring remaining fields with wildcard patterns (x: _)
46+
|
47+
LL - E::B { b1: _, .. } => (),
48+
LL + E::B { b1: _, b2: _ } => (),
49+
|
1850

19-
error: aborting due to 2 previous errors
51+
error: aborting due to 4 previous errors
2052

0 commit comments

Comments
 (0)