Skip to content

Commit d5ae91d

Browse files
committed
Add new lint unneeded_struct_pattern
1 parent f328623 commit d5ae91d

10 files changed

+377
-2
lines changed

Diff for: CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6046,6 +6046,7 @@ Released 2018-09-13
60466046
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
60476047
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
60486048
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
6049+
[`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern
60496050
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
60506051
[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
60516052
[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable

Diff for: 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 over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
8+
[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
99

1010
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
1111
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.

Diff for: book/src/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
A collection of lints to catch common mistakes and improve your
77
[Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
Lints are divided into categories, each with a default [lint
1212
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how

Diff for: clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
744744
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
745745
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
746746
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
747+
crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO,
747748
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
748749
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
749750
crate::unused_async::UNUSED_ASYNC_INFO,

Diff for: clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ mod unnecessary_owned_empty_strings;
372372
mod unnecessary_self_imports;
373373
mod unnecessary_struct_initialization;
374374
mod unnecessary_wraps;
375+
mod unneeded_struct_pattern;
375376
mod unnested_or_patterns;
376377
mod unsafe_removed_from_name;
377378
mod unused_async;
@@ -951,5 +952,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
951952
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
952953
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
953954
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
955+
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
954956
// add lints here, do not remove this comment, it's used in `new_lint`
955957
}

Diff for: clippy_lints/src/unneeded_struct_pattern.rs

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use rustc_errors::Applicability;
3+
use rustc_hir::def::{DefKind, Res};
4+
use rustc_hir::{Pat, PatKind, QPath};
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_session::declare_lint_pass;
7+
8+
declare_clippy_lint! {
9+
/// ### What it does
10+
/// Checks for struct patterns that match against unit variant.
11+
///
12+
/// ### Why is this bad?
13+
/// Struct pattern `{ }` or `{ .. }` is not needed for unit variant.
14+
///
15+
/// ### Example
16+
/// ```no_run
17+
/// match Some(42) {
18+
/// Some(v) => v,
19+
/// None { .. } => 0,
20+
/// };
21+
/// // Or
22+
/// match Some(42) {
23+
/// Some(v) => v,
24+
/// None { } => 0,
25+
/// };
26+
/// ```
27+
/// Use instead:
28+
/// ```no_run
29+
/// match Some(42) {
30+
/// Some(v) => v,
31+
/// None => 0,
32+
/// };
33+
/// ```
34+
#[clippy::version = "1.83.0"]
35+
pub UNNEEDED_STRUCT_PATTERN,
36+
nursery,
37+
"using struct pattern to match against unit variant"
38+
}
39+
40+
declare_lint_pass!(UnneededStructPattern => [UNNEEDED_STRUCT_PATTERN]);
41+
42+
impl LateLintPass<'_> for UnneededStructPattern {
43+
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
44+
if !pat.span.from_expansion()
45+
&& let PatKind::Struct(path, [], _) = &pat.kind
46+
&& let QPath::Resolved(_, path) = path
47+
&& let Res::Def(DefKind::Variant, did) = path.res
48+
{
49+
let enum_did = cx.tcx.parent(did);
50+
let variant = cx.tcx.adt_def(enum_did).variant_with_id(did);
51+
52+
let has_fields_brackets = !(variant.ctor.is_some() && variant.fields.is_empty());
53+
let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive();
54+
if has_fields_brackets || non_exhaustive_activated {
55+
return;
56+
}
57+
58+
if let Some(brackets_span) = pat.span.trim_start(path.span) {
59+
span_lint_and_sugg(
60+
cx,
61+
UNNEEDED_STRUCT_PATTERN,
62+
brackets_span,
63+
"struct pattern is not needed for a unit variant",
64+
"remove the struct pattern",
65+
String::new(),
66+
Applicability::MachineApplicable,
67+
);
68+
}
69+
}
70+
}
71+
}

Diff for: tests/ui/auxiliary/non-exhaustive-enum.rs

+21
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,24 @@ pub enum ErrorKind {
66
#[doc(hidden)]
77
Uncategorized,
88
}
9+
10+
#[non_exhaustive]
11+
pub enum ExtNonExhaustiveEnum {
12+
Unit,
13+
Tuple(i32),
14+
Struct { field: i32 },
15+
}
16+
17+
pub enum ExtNonExhaustiveVariant {
18+
ExhaustiveUnit,
19+
#[non_exhaustive]
20+
Unit,
21+
#[non_exhaustive]
22+
Tuple(i32),
23+
#[non_exhaustive]
24+
StructNoField {},
25+
#[non_exhaustive]
26+
Struct {
27+
field: i32,
28+
},
29+
}

Diff for: tests/ui/unneeded_struct_pattern.fixed

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//@aux-build:non-exhaustive-enum.rs
2+
#![allow(clippy::manual_unwrap_or_default, clippy::manual_unwrap_or)]
3+
#![warn(clippy::unneeded_struct_pattern)]
4+
5+
extern crate non_exhaustive_enum;
6+
use non_exhaustive_enum::*;
7+
8+
fn main() {
9+
match Some(114514) {
10+
Some(v) => v,
11+
None => 0,
12+
};
13+
14+
match Some(1919810) {
15+
Some(v) => v,
16+
None => 0,
17+
};
18+
19+
match Some(123456) {
20+
Some(v) => v,
21+
None => 0,
22+
};
23+
24+
match Some(Some(123456)) {
25+
Some(Some(v)) => v,
26+
Some(None) => 0,
27+
None => 0,
28+
};
29+
30+
enum Custom {
31+
HasFields {
32+
field: i32,
33+
},
34+
HasBracketsNoFields {},
35+
NoBrackets,
36+
#[non_exhaustive]
37+
NoBracketsNonExhaustive,
38+
Init,
39+
};
40+
41+
match Custom::Init {
42+
Custom::HasFields { field: value } => value, // Should be ignored
43+
Custom::HasBracketsNoFields {} => 0, // Should be ignored
44+
Custom::NoBrackets => 0, // Should be fixed
45+
Custom::NoBracketsNonExhaustive => 0, // Should be fixed
46+
_ => 0,
47+
};
48+
49+
match Custom::Init {
50+
Custom::HasFields { field: value } => value, // Should be ignored
51+
Custom::HasBracketsNoFields { .. } => 0, // Should be ignored
52+
Custom::NoBrackets => 0, // Should be fixed
53+
Custom::NoBracketsNonExhaustive => 0, // Should be fixed
54+
_ => 0,
55+
};
56+
57+
match Custom::Init {
58+
Custom::NoBrackets if true => 0, // Should be fixed
59+
_ => 0,
60+
};
61+
62+
match Custom::Init {
63+
Custom::NoBrackets | Custom::NoBracketsNonExhaustive => 0, // Should be fixed
64+
_ => 0,
65+
};
66+
}
67+
68+
fn external_crate() {
69+
use ExtNonExhaustiveVariant::*;
70+
71+
match ExhaustiveUnit {
72+
// Expected
73+
ExhaustiveUnit => 0,
74+
_ => 0,
75+
};
76+
77+
match ExhaustiveUnit {
78+
// Exhaustive variant, should be fixed
79+
ExhaustiveUnit => 0,
80+
_ => 0,
81+
};
82+
83+
match ExhaustiveUnit {
84+
// Exhaustive variant, should be fixed
85+
ExhaustiveUnit => 0,
86+
_ => 0,
87+
};
88+
89+
match ExhaustiveUnit {
90+
ExhaustiveUnit => 0,
91+
// vvvvv Non-exhaustive variants, should all be ignored
92+
Unit { .. } => 0,
93+
Tuple { 0: field, .. } => field,
94+
StructNoField { .. } => 0,
95+
Struct { field, .. } => field,
96+
_ => 0,
97+
};
98+
}

Diff for: tests/ui/unneeded_struct_pattern.rs

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//@aux-build:non-exhaustive-enum.rs
2+
#![allow(clippy::manual_unwrap_or_default, clippy::manual_unwrap_or)]
3+
#![warn(clippy::unneeded_struct_pattern)]
4+
5+
extern crate non_exhaustive_enum;
6+
use non_exhaustive_enum::*;
7+
8+
fn main() {
9+
match Some(114514) {
10+
Some(v) => v,
11+
None {} => 0,
12+
};
13+
14+
match Some(1919810) {
15+
Some(v) => v,
16+
None { .. } => 0,
17+
};
18+
19+
match Some(123456) {
20+
Some(v) => v,
21+
None => 0,
22+
};
23+
24+
match Some(Some(123456)) {
25+
Some(Some(v)) => v,
26+
Some(None {}) => 0,
27+
None {} => 0,
28+
};
29+
30+
enum Custom {
31+
HasFields {
32+
field: i32,
33+
},
34+
HasBracketsNoFields {},
35+
NoBrackets,
36+
#[non_exhaustive]
37+
NoBracketsNonExhaustive,
38+
Init,
39+
};
40+
41+
match Custom::Init {
42+
Custom::HasFields { field: value } => value, // Should be ignored
43+
Custom::HasBracketsNoFields {} => 0, // Should be ignored
44+
Custom::NoBrackets {} => 0, // Should be fixed
45+
Custom::NoBracketsNonExhaustive {} => 0, // Should be fixed
46+
_ => 0,
47+
};
48+
49+
match Custom::Init {
50+
Custom::HasFields { field: value } => value, // Should be ignored
51+
Custom::HasBracketsNoFields { .. } => 0, // Should be ignored
52+
Custom::NoBrackets { .. } => 0, // Should be fixed
53+
Custom::NoBracketsNonExhaustive { .. } => 0, // Should be fixed
54+
_ => 0,
55+
};
56+
57+
match Custom::Init {
58+
Custom::NoBrackets {} if true => 0, // Should be fixed
59+
_ => 0,
60+
};
61+
62+
match Custom::Init {
63+
Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, // Should be fixed
64+
_ => 0,
65+
};
66+
}
67+
68+
fn external_crate() {
69+
use ExtNonExhaustiveVariant::*;
70+
71+
match ExhaustiveUnit {
72+
// Expected
73+
ExhaustiveUnit => 0,
74+
_ => 0,
75+
};
76+
77+
match ExhaustiveUnit {
78+
// Exhaustive variant, should be fixed
79+
ExhaustiveUnit { .. } => 0,
80+
_ => 0,
81+
};
82+
83+
match ExhaustiveUnit {
84+
// Exhaustive variant, should be fixed
85+
ExhaustiveUnit {} => 0,
86+
_ => 0,
87+
};
88+
89+
match ExhaustiveUnit {
90+
ExhaustiveUnit => 0,
91+
// vvvvv Non-exhaustive variants, should all be ignored
92+
Unit { .. } => 0,
93+
Tuple { 0: field, .. } => field,
94+
StructNoField { .. } => 0,
95+
Struct { field, .. } => field,
96+
_ => 0,
97+
};
98+
}

0 commit comments

Comments
 (0)