Skip to content

Commit 36b1025

Browse files
committed
Add new lint unneeded_struct_pattern
1 parent c771204 commit 36b1025

8 files changed

+375
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6042,6 +6042,7 @@ Released 2018-09-13
60426042
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
60436043
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
60446044
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
6045+
[`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern
60456046
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
60466047
[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
60476048
[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
740740
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
741741
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
742742
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
743+
crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO,
743744
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
744745
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
745746
crate::unused_async::UNUSED_ASYNC_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ mod unnecessary_owned_empty_strings;
368368
mod unnecessary_self_imports;
369369
mod unnecessary_struct_initialization;
370370
mod unnecessary_wraps;
371+
mod unneeded_struct_pattern;
371372
mod unnested_or_patterns;
372373
mod unsafe_removed_from_name;
373374
mod unused_async;
@@ -944,5 +945,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
944945
store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo));
945946
store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions));
946947
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
948+
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
947949
// add lints here, do not remove this comment, it's used in `new_lint`
948950
}
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+
}

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+
}
+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+
}

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)