Skip to content

Commit b52ea0e

Browse files
committed
handle non local non-exhaustive structs as well as private fields
1 parent ab43c13 commit b52ea0e

File tree

5 files changed

+74
-8
lines changed

5 files changed

+74
-8
lines changed

clippy_lints/src/rest_when_destructuring_struct.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clippy_utils::is_from_proc_macro;
33
use itertools::Itertools;
44
use rustc_abi::VariantIdx;
55
use rustc_lint::LateLintPass;
6-
use rustc_middle::ty;
6+
use rustc_middle::ty::{self, Visibility};
77
use rustc_session::declare_lint_pass;
88

99
declare_clippy_lint! {
@@ -56,13 +56,33 @@ impl<'tcx> LateLintPass<'tcx> for RestWhenDestructuringStruct {
5656
let vid = qty
5757
.opt_def_id()
5858
.map_or(VariantIdx::ZERO, |x| a.variant_index_with_id(x));
59+
60+
let leave_dotdot = a.variants()[vid].field_list_has_applicable_non_exhaustive();
61+
5962
let mut rest_fields = a.variants()[vid]
6063
.fields
6164
.iter()
65+
.filter(|f| {
66+
if a.did().is_local() {
67+
true
68+
} else {
69+
matches!(f.vis, Visibility::Public)
70+
}
71+
})
6272
.map(|field| field.ident(cx.tcx))
6373
.filter(|pf| !fields.iter().any(|x| x.ident == *pf))
6474
.map(|x| format!("{x}: _"));
65-
let fmt_fields = rest_fields.join(", ");
75+
76+
let mut fmt_fields = rest_fields.join(", ");
77+
78+
if fmt_fields.is_empty() && leave_dotdot {
79+
// The struct is non_exhaustive, from a non-local crate and all public fields are explicitly named.
80+
return;
81+
}
82+
83+
if leave_dotdot {
84+
fmt_fields.push_str(", ..");
85+
}
6686

6787
span_lint_and_then(
6888
cx,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#[non_exhaustive]
2+
#[derive(Default)]
3+
pub struct NonExhaustiveStruct {
4+
pub field1: i32,
5+
pub field2: i32,
6+
_private: i32,
7+
}

tests/ui/rest_when_destructuring_struct.fixed

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
//@aux-build:proc_macros.rs
2+
//@aux-build:non-exhaustive-struct.rs
23
#![warn(clippy::rest_when_destructuring_struct)]
34
#![allow(dead_code)]
45
#![allow(unused_variables)]
56

67
extern crate proc_macros;
78

9+
extern crate non_exhaustive_struct;
10+
11+
use non_exhaustive_struct::NonExhaustiveStruct;
12+
813
struct S {
914
a: u8,
1015
b: u8,
@@ -53,4 +58,13 @@ fn main() {
5358
let s2 = S { a: 1, b: 2, c: 3 };
5459
let S { a, b, .. } = s2;
5560
}
61+
62+
let ne = NonExhaustiveStruct::default();
63+
let NonExhaustiveStruct { field1: _, field2: _, .. } = ne;
64+
//~^ rest_when_destructuring_struct
65+
66+
let ne = NonExhaustiveStruct::default();
67+
let NonExhaustiveStruct {
68+
field1: _, field2: _, ..
69+
} = ne;
5670
}

tests/ui/rest_when_destructuring_struct.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
//@aux-build:proc_macros.rs
2+
//@aux-build:non-exhaustive-struct.rs
23
#![warn(clippy::rest_when_destructuring_struct)]
34
#![allow(dead_code)]
45
#![allow(unused_variables)]
56

67
extern crate proc_macros;
78

9+
extern crate non_exhaustive_struct;
10+
11+
use non_exhaustive_struct::NonExhaustiveStruct;
12+
813
struct S {
914
a: u8,
1015
b: u8,
@@ -53,4 +58,13 @@ fn main() {
5358
let s2 = S { a: 1, b: 2, c: 3 };
5459
let S { a, b, .. } = s2;
5560
}
61+
62+
let ne = NonExhaustiveStruct::default();
63+
let NonExhaustiveStruct { field1: _, .. } = ne;
64+
//~^ rest_when_destructuring_struct
65+
66+
let ne = NonExhaustiveStruct::default();
67+
let NonExhaustiveStruct {
68+
field1: _, field2: _, ..
69+
} = ne;
5670
}

tests/ui/rest_when_destructuring_struct.stderr

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: struct destructuring with rest (..)
2-
--> tests/ui/rest_when_destructuring_struct.rs:23:9
2+
--> tests/ui/rest_when_destructuring_struct.rs:28:9
33
|
44
LL | let S { a, b, .. } = s;
55
| ^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL + let S { a, b, c: _ } = s;
1313
|
1414

1515
error: struct destructuring with rest (..)
16-
--> tests/ui/rest_when_destructuring_struct.rs:26:9
16+
--> tests/ui/rest_when_destructuring_struct.rs:31:9
1717
|
1818
LL | let S { a, b, c, .. } = s;
1919
| ^^^^^^^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL + let S { a, b, c, } = s;
2525
|
2626

2727
error: struct destructuring with rest (..)
28-
--> tests/ui/rest_when_destructuring_struct.rs:33:9
28+
--> tests/ui/rest_when_destructuring_struct.rs:38:9
2929
|
3030
LL | E::B { .. } => (),
3131
| ^^^^^^^^^^^
@@ -37,7 +37,7 @@ LL + E::B { b1: _, b2: _ } => (),
3737
|
3838

3939
error: struct destructuring with rest (..)
40-
--> tests/ui/rest_when_destructuring_struct.rs:35:9
40+
--> tests/ui/rest_when_destructuring_struct.rs:40:9
4141
|
4242
LL | E::C { .. } => (),
4343
| ^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL + E::C { } => (),
4949
|
5050

5151
error: struct destructuring with rest (..)
52-
--> tests/ui/rest_when_destructuring_struct.rs:41:9
52+
--> tests/ui/rest_when_destructuring_struct.rs:46:9
5353
|
5454
LL | E::B { b1: _, .. } => (),
5555
| ^^^^^^^^^^^^^^^^^^
@@ -60,5 +60,16 @@ LL - E::B { b1: _, .. } => (),
6060
LL + E::B { b1: _, b2: _ } => (),
6161
|
6262

63-
error: aborting due to 5 previous errors
63+
error: struct destructuring with rest (..)
64+
--> tests/ui/rest_when_destructuring_struct.rs:63:9
65+
|
66+
LL | let NonExhaustiveStruct { field1: _, .. } = ne;
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
68+
|
69+
help: consider explicitly ignoring remaining fields with wildcard patterns (x: _)
70+
|
71+
LL | let NonExhaustiveStruct { field1: _, field2: _, .. } = ne;
72+
| ++++++++++
73+
74+
error: aborting due to 6 previous errors
6475

0 commit comments

Comments
 (0)