Skip to content

Commit eb39587

Browse files
committed
Auto merge of #5258 - ThibsG:UselessBindingInStruct638, r=flip1995
Add lint for .. use in fully binded struct This PR adds the lint `match-wild-in-fully-binded-struct` to prevent the use of the `..` pattern when all fields of the struct are already binded. Fixes: #638 changelog: Add [`rest_pat_in_fully_bound_structs`] lint to warn against the use of `..` in fully binded struct
2 parents d74229b + 6526b2b commit eb39587

File tree

7 files changed

+124
-5
lines changed

7 files changed

+124
-5
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,7 @@ Released 2018-09-13
13211321
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
13221322
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
13231323
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
1324+
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
13241325
[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
13251326
[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
13261327
[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
77

8-
[There are 358 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
8+
[There are 359 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
99

1010
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1111

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
610610
&matches::MATCH_REF_PATS,
611611
&matches::MATCH_SINGLE_BINDING,
612612
&matches::MATCH_WILD_ERR_ARM,
613+
&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
613614
&matches::SINGLE_MATCH,
614615
&matches::SINGLE_MATCH_ELSE,
615616
&matches::WILDCARD_ENUM_MATCH_ARM,
@@ -1026,6 +1027,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10261027
LintId::of(&integer_division::INTEGER_DIVISION),
10271028
LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE),
10281029
LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION),
1030+
LintId::of(&matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
10291031
LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM),
10301032
LintId::of(&mem_forget::MEM_FORGET),
10311033
LintId::of(&methods::CLONE_ON_REF_PTR),

clippy_lints/src/matches.rs

+55-3
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ use rustc_ast::ast::LitKind;
1414
use rustc_errors::Applicability;
1515
use rustc_hir::def::CtorKind;
1616
use rustc_hir::{
17-
print, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, PatKind, QPath,
18-
RangeEnd,
17+
print, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Pat, PatKind,
18+
QPath, RangeEnd,
1919
};
2020
use rustc_lint::{LateContext, LateLintPass, LintContext};
2121
use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -311,6 +311,36 @@ declare_clippy_lint! {
311311
"a match with a single binding instead of using `let` statement"
312312
}
313313

314+
declare_clippy_lint! {
315+
/// **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
316+
///
317+
/// **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after
318+
/// matching all enum variants explicitly.
319+
///
320+
/// **Known problems:** None.
321+
///
322+
/// **Example:**
323+
/// ```rust
324+
/// # struct A { a: i32 }
325+
/// let a = A { a: 5 };
326+
///
327+
/// // Bad
328+
/// match a {
329+
/// A { a: 5, .. } => {},
330+
/// _ => {},
331+
/// }
332+
///
333+
/// // Good
334+
/// match a {
335+
/// A { a: 5 } => {},
336+
/// _ => {},
337+
/// }
338+
/// ```
339+
pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
340+
restriction,
341+
"a match on a struct that binds all fields but still uses the wildcard pattern"
342+
}
343+
314344
#[derive(Default)]
315345
pub struct Matches {
316346
infallible_destructuring_match_linted: bool,
@@ -327,7 +357,8 @@ impl_lint_pass!(Matches => [
327357
WILDCARD_ENUM_MATCH_ARM,
328358
WILDCARD_IN_OR_PATTERNS,
329359
MATCH_SINGLE_BINDING,
330-
INFALLIBLE_DESTRUCTURING_MATCH
360+
INFALLIBLE_DESTRUCTURING_MATCH,
361+
REST_PAT_IN_FULLY_BOUND_STRUCTS
331362
]);
332363

333364
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
@@ -388,6 +419,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
388419
}
389420
}
390421
}
422+
423+
fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat<'_>) {
424+
if_chain! {
425+
if let PatKind::Struct(ref qpath, fields, true) = pat.kind;
426+
if let QPath::Resolved(_, ref path) = qpath;
427+
let def_id = path.res.def_id();
428+
let ty = cx.tcx.type_of(def_id);
429+
if let ty::Adt(def, _) = ty.kind;
430+
if fields.len() == def.non_enum_variant().fields.len();
431+
432+
then {
433+
span_lint_and_help(
434+
cx,
435+
REST_PAT_IN_FULLY_BOUND_STRUCTS,
436+
pat.span,
437+
"unnecessary use of `..` pattern in struct binding. All fields were already bound",
438+
"consider removing `..` from this binding",
439+
);
440+
}
441+
}
442+
}
391443
}
392444

393445
#[rustfmt::skip]

src/lintlist/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 358] = [
9+
pub const ALL_LINTS: [Lint; 359] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -1785,6 +1785,13 @@ pub const ALL_LINTS: [Lint; 358] = [
17851785
deprecation: None,
17861786
module: "replace_consts",
17871787
},
1788+
Lint {
1789+
name: "rest_pat_in_fully_bound_structs",
1790+
group: "restriction",
1791+
desc: "a match on a struct that binds all fields but still uses the wildcard pattern",
1792+
deprecation: None,
1793+
module: "matches",
1794+
},
17881795
Lint {
17891796
name: "result_expect_used",
17901797
group: "restriction",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![warn(clippy::rest_pat_in_fully_bound_structs)]
2+
3+
struct A {
4+
a: i32,
5+
b: i64,
6+
c: &'static str,
7+
}
8+
9+
fn main() {
10+
let a_struct = A { a: 5, b: 42, c: "A" };
11+
12+
match a_struct {
13+
A { a: 5, b: 42, c: "", .. } => {}, // Lint
14+
A { a: 0, b: 0, c: "", .. } => {}, // Lint
15+
_ => {},
16+
}
17+
18+
match a_struct {
19+
A { a: 5, b: 42, .. } => {},
20+
A { a: 0, b: 0, c: "", .. } => {}, // Lint
21+
_ => {},
22+
}
23+
24+
// No lint
25+
match a_struct {
26+
A { a: 5, .. } => {},
27+
A { a: 0, b: 0, .. } => {},
28+
_ => {},
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error: unnecessary use of `..` pattern in struct binding. All fields were already bound
2+
--> $DIR/rest_pat_in_fully_bound_structs.rs:13:9
3+
|
4+
LL | A { a: 5, b: 42, c: "", .. } => {}, // Lint
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings`
8+
= help: consider removing `..` from this binding
9+
10+
error: unnecessary use of `..` pattern in struct binding. All fields were already bound
11+
--> $DIR/rest_pat_in_fully_bound_structs.rs:14:9
12+
|
13+
LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
15+
|
16+
= help: consider removing `..` from this binding
17+
18+
error: unnecessary use of `..` pattern in struct binding. All fields were already bound
19+
--> $DIR/rest_pat_in_fully_bound_structs.rs:20:9
20+
|
21+
LL | A { a: 0, b: 0, c: "", .. } => {}, // Lint
22+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
23+
|
24+
= help: consider removing `..` from this binding
25+
26+
error: aborting due to 3 previous errors
27+

0 commit comments

Comments
 (0)