Skip to content

Commit 79bed8a

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

9 files changed

+614
-2
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/arbitrary_source_item_ordering.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,8 +445,8 @@ fn convert_assoc_item_kind(value: AssocItemKind) -> SourceItemOrderingTraitAssoc
445445
#[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
446446
use SourceItemOrderingTraitAssocItemKind::*;
447447
match value {
448-
AssocItemKind::Const { .. } => Const,
449-
AssocItemKind::Type { .. } => Type,
448+
AssocItemKind::Const => Const,
449+
AssocItemKind::Type => Type,
450450
AssocItemKind::Fn { .. } => Fn,
451451
}
452452
}

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: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//@aux-build:non-exhaustive-enum.rs
2+
#![allow(
3+
clippy::manual_unwrap_or_default,
4+
clippy::manual_unwrap_or,
5+
clippy::redundant_pattern_matching
6+
)]
7+
#![warn(clippy::unneeded_struct_pattern)]
8+
9+
extern crate non_exhaustive_enum;
10+
use non_exhaustive_enum::*;
11+
12+
fn main() {
13+
match Some(114514) {
14+
Some(v) => v,
15+
None => 0,
16+
};
17+
18+
match Some(1919810) {
19+
Some(v) => v,
20+
None => 0,
21+
};
22+
23+
match Some(123456) {
24+
Some(v) => v,
25+
None => 0,
26+
};
27+
28+
match Some(Some(123456)) {
29+
Some(Some(v)) => v,
30+
Some(None) => 0,
31+
None => 0,
32+
};
33+
34+
if let None = Some(0) {}
35+
if let None = Some(0) {}
36+
if let Some(None) = Some(Some(0)) {}
37+
let None = Some(0) else { panic!() };
38+
let None = Some(0) else { panic!() };
39+
let Some(None) = Some(Some(0)) else { panic!() };
40+
41+
enum Custom {
42+
HasFields {
43+
field: i32,
44+
},
45+
HasBracketsNoFields {},
46+
NoBrackets,
47+
#[non_exhaustive]
48+
NoBracketsNonExhaustive,
49+
Init,
50+
};
51+
52+
match Custom::Init {
53+
Custom::HasFields { field: value } => value, // Should be ignored
54+
Custom::HasBracketsNoFields {} => 0, // Should be ignored
55+
Custom::NoBrackets => 0, // Should be fixed
56+
Custom::NoBracketsNonExhaustive => 0, // Should be fixed
57+
_ => 0,
58+
};
59+
60+
match Custom::Init {
61+
Custom::HasFields { field: value } => value, // Should be ignored
62+
Custom::HasBracketsNoFields { .. } => 0, // Should be ignored
63+
Custom::NoBrackets => 0, // Should be fixed
64+
Custom::NoBracketsNonExhaustive => 0, // Should be fixed
65+
_ => 0,
66+
};
67+
68+
match Custom::Init {
69+
Custom::NoBrackets if true => 0, // Should be fixed
70+
_ => 0,
71+
};
72+
73+
match Custom::Init {
74+
Custom::NoBrackets | Custom::NoBracketsNonExhaustive => 0, // Should be fixed
75+
_ => 0,
76+
};
77+
78+
if let Custom::HasFields { field: value } = Custom::Init {} // Should be ignored
79+
if let Custom::HasBracketsNoFields {} = Custom::Init {} // Should be ignored
80+
if let Custom::HasBracketsNoFields { .. } = Custom::Init {} // Should be ignored
81+
if let Custom::NoBrackets = Custom::Init {} // Should be fixed
82+
if let Custom::NoBrackets = Custom::Init {} // Should be fixed
83+
if let Custom::NoBrackets | Custom::NoBracketsNonExhaustive = Custom::Init {} // Should be fixed
84+
if let Custom::NoBracketsNonExhaustive = Custom::Init {} // Should be fixed
85+
if let Custom::NoBracketsNonExhaustive = Custom::Init {} // Should be fixed
86+
87+
// Should be ignored
88+
let Custom::HasFields { field: value } = Custom::Init else {
89+
panic!()
90+
};
91+
// Should be ignored
92+
let Custom::HasBracketsNoFields {} = Custom::Init else {
93+
panic!()
94+
};
95+
// Should be ignored
96+
let Custom::HasBracketsNoFields { .. } = Custom::Init else {
97+
panic!()
98+
};
99+
let Custom::NoBrackets = Custom::Init else { panic!() }; // Should be fixed
100+
// Should be fixed
101+
let Custom::NoBrackets = Custom::Init else {
102+
panic!()
103+
};
104+
// Should be fixed
105+
let Custom::NoBracketsNonExhaustive = Custom::Init else {
106+
panic!()
107+
};
108+
// Should be fixed
109+
let Custom::NoBracketsNonExhaustive = Custom::Init else {
110+
panic!()
111+
};
112+
113+
enum Refutable {
114+
Variant,
115+
}
116+
117+
fn pat_in_fn_param_1(Refutable::Variant: Refutable) {} // Should be fixed
118+
fn pat_in_fn_param_2(Refutable::Variant: Refutable) {} // Should be fixed
119+
120+
for Refutable::Variant in [] {} // Should be fixed
121+
for Refutable::Variant in [] {} // Should be fixed
122+
}
123+
124+
fn external_crate() {
125+
use ExtNonExhaustiveVariant::*;
126+
127+
match ExhaustiveUnit {
128+
// Expected
129+
ExhaustiveUnit => 0,
130+
_ => 0,
131+
};
132+
133+
match ExhaustiveUnit {
134+
// Exhaustive variant, should be fixed
135+
ExhaustiveUnit => 0,
136+
_ => 0,
137+
};
138+
139+
match ExhaustiveUnit {
140+
// Exhaustive variant, should be fixed
141+
ExhaustiveUnit => 0,
142+
_ => 0,
143+
};
144+
145+
match ExhaustiveUnit {
146+
ExhaustiveUnit => 0,
147+
// vvvvv Non-exhaustive variants, should all be ignored
148+
Unit { .. } => 0,
149+
Tuple { 0: field, .. } => field,
150+
StructNoField { .. } => 0,
151+
Struct { field, .. } => field,
152+
_ => 0,
153+
};
154+
}

0 commit comments

Comments
 (0)