Skip to content

Commit 1504461

Browse files
committed
add new lint: rest_when_destructuring_struct
1 parent 6bb0c97 commit 1504461

File tree

6 files changed

+116
-0
lines changed

6 files changed

+116
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6238,6 +6238,7 @@ Released 2018-09-13
62386238
[`repr_packed_without_abi`]: https://rust-lang.github.io/rust-clippy/master/index.html#repr_packed_without_abi
62396239
[`reserve_after_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#reserve_after_initialization
62406240
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
6241+
[`rest_when_destructuring_struct`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_when_destructuring_struct
62416242
[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
62426243
[`result_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_filter_map
62436244
[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
655655
crate::regex::TRIVIAL_REGEX_INFO,
656656
crate::repeat_vec_with_capacity::REPEAT_VEC_WITH_CAPACITY_INFO,
657657
crate::reserve_after_initialization::RESERVE_AFTER_INITIALIZATION_INFO,
658+
crate::rest_when_destructuring_struct::REST_WHEN_DESTRUCTURING_STRUCT_INFO,
658659
crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO,
659660
crate::returns::LET_AND_RETURN_INFO,
660661
crate::returns::NEEDLESS_RETURN_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ mod reference;
329329
mod regex;
330330
mod repeat_vec_with_capacity;
331331
mod reserve_after_initialization;
332+
mod rest_when_destructuring_struct;
332333
mod return_self_not_must_use;
333334
mod returns;
334335
mod same_name_method;
@@ -951,5 +952,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
951952
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
952953
store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
953954
store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny));
955+
store.register_early_pass(|| Box::new(rest_when_destructuring_struct::RestWhenDestructuringStruct));
954956
// add lints here, do not remove this comment, it's used in `new_lint`
955957
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use clippy_utils::diagnostics::span_lint_and_help;
2+
use rustc_ast::ast::{Pat, PatFieldsRest, PatKind};
3+
use rustc_lint::{EarlyContext, EarlyLintPass};
4+
use rustc_session::declare_lint_pass;
5+
6+
declare_clippy_lint! {
7+
/// ### What it does
8+
/// Disallows the use of rest patterns when destructuring structs.
9+
///
10+
/// ### Why is this bad?
11+
/// It might lead to unhandled fields when the struct changes.
12+
///
13+
/// ### Example
14+
/// ```no_run
15+
/// struct S {
16+
/// a: u8,
17+
/// b: u8,
18+
/// c: u8,
19+
/// }
20+
///
21+
/// let s = S { a: 1, b: 2, c: 3 };
22+
///
23+
/// let S { a, b, .. } = s;
24+
/// ```
25+
/// Use instead:
26+
/// ```no_run
27+
/// struct S {
28+
/// a: u8,
29+
/// b: u8,
30+
/// c: u8,
31+
/// }
32+
///
33+
/// let s = S { a: 1, b: 2, c: 3 };
34+
///
35+
/// let S { a, b, c: _ } = s;
36+
/// ```
37+
#[clippy::version = "1.89.0"]
38+
pub REST_WHEN_DESTRUCTURING_STRUCT,
39+
nursery,
40+
"rest (..) in destructuring expression"
41+
}
42+
declare_lint_pass!(RestWhenDestructuringStruct => [REST_WHEN_DESTRUCTURING_STRUCT]);
43+
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+
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
48+
span_lint_and_help(
49+
cx,
50+
REST_WHEN_DESTRUCTURING_STRUCT,
51+
pat.span,
52+
"struct destructuring with rest (..)",
53+
None,
54+
"consider explicitly ignoring remaining fields with wildcard patterns (x: _)",
55+
);
56+
}
57+
}
58+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
}
15+
16+
fn main() {
17+
let s = S { a: 1, b: 2, c: 3 };
18+
19+
let S { a, b, .. } = s;
20+
//~^ rest_when_destructuring_struct
21+
22+
let e = E::A { a1: 1, a2: 2 };
23+
24+
match e {
25+
E::A { a1, a2 } => (),
26+
E::B { .. } => (),
27+
//~^ rest_when_destructuring_struct
28+
}
29+
30+
match e {
31+
E::A { a1: _, a2: _ } => (),
32+
E::B { b1, b2: _ } => (),
33+
}
34+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: struct destructuring with rest (..)
2+
--> tests/ui/rest_when_destructuring_struct.rs:19:9
3+
|
4+
LL | let S { a, b, .. } = s;
5+
| ^^^^^^^^^^^^^^
6+
|
7+
= help: consider explicitly ignoring remaining fields with wildcard patterns (x: _)
8+
= note: `-D clippy::rest-when-destructuring-struct` implied by `-D warnings`
9+
= help: to override `-D warnings` add `#[allow(clippy::rest_when_destructuring_struct)]`
10+
11+
error: struct destructuring with rest (..)
12+
--> tests/ui/rest_when_destructuring_struct.rs:26:9
13+
|
14+
LL | E::B { .. } => (),
15+
| ^^^^^^^^^^^
16+
|
17+
= help: consider explicitly ignoring remaining fields with wildcard patterns (x: _)
18+
19+
error: aborting due to 2 previous errors
20+

0 commit comments

Comments
 (0)