Skip to content

Commit 1e6f710

Browse files
committed
Add new lint unneeded_struct_pattern
1 parent 2ddfc92 commit 1e6f710

8 files changed

+604
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6133,6 +6133,7 @@ Released 2018-09-13
61336133
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
61346134
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
61356135
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
6136+
[`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern
61366137
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
61376138
[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
61386139
[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
753753
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
754754
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
755755
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
756+
crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO,
756757
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
757758
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
758759
crate::unused_async::UNUSED_ASYNC_INFO,

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ mod unnecessary_owned_empty_strings;
374374
mod unnecessary_self_imports;
375375
mod unnecessary_struct_initialization;
376376
mod unnecessary_wraps;
377+
mod unneeded_struct_pattern;
377378
mod unnested_or_patterns;
378379
mod unsafe_removed_from_name;
379380
mod unused_async;
@@ -967,5 +968,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
967968
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
968969
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
969970
store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
971+
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
970972
// add lints here, do not remove this comment, it's used in `new_lint`
971973
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::is_from_proc_macro;
3+
use rustc_errors::Applicability;
4+
use rustc_hir::def::{DefKind, Res};
5+
use rustc_hir::{Pat, PatKind, QPath};
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_session::declare_lint_pass;
8+
9+
declare_clippy_lint! {
10+
/// ### What it does
11+
/// Checks for struct patterns that match against unit variant.
12+
///
13+
/// ### Why is this bad?
14+
/// Struct pattern `{ }` or `{ .. }` is not needed for unit variant.
15+
///
16+
/// ### Example
17+
/// ```no_run
18+
/// match Some(42) {
19+
/// Some(v) => v,
20+
/// None { .. } => 0,
21+
/// };
22+
/// // Or
23+
/// match Some(42) {
24+
/// Some(v) => v,
25+
/// None { } => 0,
26+
/// };
27+
/// ```
28+
/// Use instead:
29+
/// ```no_run
30+
/// match Some(42) {
31+
/// Some(v) => v,
32+
/// None => 0,
33+
/// };
34+
/// ```
35+
#[clippy::version = "1.83.0"]
36+
pub UNNEEDED_STRUCT_PATTERN,
37+
style,
38+
"using struct pattern to match against unit variant"
39+
}
40+
41+
declare_lint_pass!(UnneededStructPattern => [UNNEEDED_STRUCT_PATTERN]);
42+
43+
impl LateLintPass<'_> for UnneededStructPattern {
44+
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
45+
if !pat.span.from_expansion()
46+
&& let PatKind::Struct(path, [], _) = &pat.kind
47+
&& let QPath::Resolved(_, path) = path
48+
&& let Res::Def(DefKind::Variant, did) = path.res
49+
{
50+
let enum_did = cx.tcx.parent(did);
51+
let variant = cx.tcx.adt_def(enum_did).variant_with_id(did);
52+
53+
let has_only_fields_brackets = variant.ctor.is_some() && variant.fields.is_empty();
54+
let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive();
55+
if !has_only_fields_brackets || non_exhaustive_activated {
56+
return;
57+
}
58+
59+
if is_from_proc_macro(cx, *path) {
60+
return;
61+
}
62+
63+
if let Some(brackets_span) = pat.span.trim_start(path.span) {
64+
span_lint_and_sugg(
65+
cx,
66+
UNNEEDED_STRUCT_PATTERN,
67+
brackets_span,
68+
"struct pattern is not needed for a unit variant",
69+
"remove the struct pattern",
70+
String::new(),
71+
Applicability::MachineApplicable,
72+
);
73+
}
74+
}
75+
}
76+
}

tests/ui/auxiliary/non-exhaustive-enum.rs

Lines changed: 21 additions & 0 deletions
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+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
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+
if let None = Some(0) {}
31+
if let None = Some(0) {}
32+
if let Some(None) = Some(Some(0)) {}
33+
let None = Some(0) else { panic!() };
34+
let None = Some(0) else { panic!() };
35+
let Some(None) = Some(Some(0)) else { panic!() };
36+
37+
enum Custom {
38+
HasFields {
39+
field: i32,
40+
},
41+
HasBracketsNoFields {},
42+
NoBrackets,
43+
#[non_exhaustive]
44+
NoBracketsNonExhaustive,
45+
Init,
46+
};
47+
48+
match Custom::Init {
49+
Custom::HasFields { field: value } => value, // Should be ignored
50+
Custom::HasBracketsNoFields {} => 0, // Should be ignored
51+
Custom::NoBrackets => 0, // Should be fixed
52+
Custom::NoBracketsNonExhaustive => 0, // Should be fixed
53+
_ => 0,
54+
};
55+
56+
match Custom::Init {
57+
Custom::HasFields { field: value } => value, // Should be ignored
58+
Custom::HasBracketsNoFields { .. } => 0, // Should be ignored
59+
Custom::NoBrackets => 0, // Should be fixed
60+
Custom::NoBracketsNonExhaustive => 0, // Should be fixed
61+
_ => 0,
62+
};
63+
64+
match Custom::Init {
65+
Custom::NoBrackets if true => 0, // Should be fixed
66+
_ => 0,
67+
};
68+
69+
match Custom::Init {
70+
Custom::NoBrackets | Custom::NoBracketsNonExhaustive => 0, // Should be fixed
71+
_ => 0,
72+
};
73+
74+
if let Custom::HasFields { field: value } = Custom::Init {} // Should be ignored
75+
if let Custom::HasBracketsNoFields {} = Custom::Init {} // Should be ignored
76+
if let Custom::HasBracketsNoFields { .. } = Custom::Init {} // Should be ignored
77+
if let Custom::NoBrackets = Custom::Init {} // Should be fixed
78+
if let Custom::NoBrackets = Custom::Init {} // Should be fixed
79+
if let Custom::NoBrackets | Custom::NoBracketsNonExhaustive = Custom::Init {} // Should be fixed
80+
if let Custom::NoBracketsNonExhaustive = Custom::Init {} // Should be fixed
81+
if let Custom::NoBracketsNonExhaustive = Custom::Init {} // Should be fixed
82+
83+
// Should be ignored
84+
let Custom::HasFields { field: value } = Custom::Init else {
85+
panic!()
86+
};
87+
// Should be ignored
88+
let Custom::HasBracketsNoFields {} = Custom::Init else {
89+
panic!()
90+
};
91+
// Should be ignored
92+
let Custom::HasBracketsNoFields { .. } = Custom::Init else {
93+
panic!()
94+
};
95+
let Custom::NoBrackets = Custom::Init else { panic!() }; // Should be fixed
96+
// Should be fixed
97+
let Custom::NoBrackets = Custom::Init else {
98+
panic!()
99+
};
100+
// Should be fixed
101+
let Custom::NoBracketsNonExhaustive = Custom::Init else {
102+
panic!()
103+
};
104+
// Should be fixed
105+
let Custom::NoBracketsNonExhaustive = Custom::Init else {
106+
panic!()
107+
};
108+
109+
enum Refutable {
110+
Variant,
111+
}
112+
113+
fn pat_in_fn_param_1(Refutable::Variant: Refutable) {} // Should be fixed
114+
fn pat_in_fn_param_2(Refutable::Variant: Refutable) {} // Should be fixed
115+
116+
for Refutable::Variant in [] {} // Should be fixed
117+
for Refutable::Variant in [] {} // Should be fixed
118+
}
119+
120+
fn external_crate() {
121+
use ExtNonExhaustiveVariant::*;
122+
123+
match ExhaustiveUnit {
124+
// Expected
125+
ExhaustiveUnit => 0,
126+
_ => 0,
127+
};
128+
129+
match ExhaustiveUnit {
130+
// Exhaustive variant, should be fixed
131+
ExhaustiveUnit => 0,
132+
_ => 0,
133+
};
134+
135+
match ExhaustiveUnit {
136+
// Exhaustive variant, should be fixed
137+
ExhaustiveUnit => 0,
138+
_ => 0,
139+
};
140+
141+
match ExhaustiveUnit {
142+
ExhaustiveUnit => 0,
143+
// vvvvv Non-exhaustive variants, should all be ignored
144+
Unit { .. } => 0,
145+
Tuple { 0: field, .. } => field,
146+
StructNoField { .. } => 0,
147+
Struct { field, .. } => field,
148+
_ => 0,
149+
};
150+
}

0 commit comments

Comments
 (0)