Skip to content

Commit

Permalink
Add new lint unneeded_struct_pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
SpriteOvO committed Oct 15, 2024
1 parent f328623 commit d5ae91d
Show file tree
Hide file tree
Showing 10 changed files with 377 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6046,6 +6046,7 @@ Released 2018-09-13
[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
[`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern
[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

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

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

Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
Expand Down
2 changes: 1 addition & 1 deletion book/src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
A collection of lints to catch common mistakes and improve your
[Rust](https://github.com/rust-lang/rust) code.

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

Lints are divided into categories, each with a default [lint
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO,
crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO,
crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
crate::unused_async::UNUSED_ASYNC_INFO,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ mod unnecessary_owned_empty_strings;
mod unnecessary_self_imports;
mod unnecessary_struct_initialization;
mod unnecessary_wraps;
mod unneeded_struct_pattern;
mod unnested_or_patterns;
mod unsafe_removed_from_name;
mod unused_async;
Expand Down Expand Up @@ -951,5 +952,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern));
// add lints here, do not remove this comment, it's used in `new_lint`
}
71 changes: 71 additions & 0 deletions clippy_lints/src/unneeded_struct_pattern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Pat, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;

declare_clippy_lint! {
/// ### What it does
/// Checks for struct patterns that match against unit variant.
///
/// ### Why is this bad?
/// Struct pattern `{ }` or `{ .. }` is not needed for unit variant.
///
/// ### Example
/// ```no_run
/// match Some(42) {
/// Some(v) => v,
/// None { .. } => 0,
/// };
/// // Or
/// match Some(42) {
/// Some(v) => v,
/// None { } => 0,
/// };
/// ```
/// Use instead:
/// ```no_run
/// match Some(42) {
/// Some(v) => v,
/// None => 0,
/// };
/// ```
#[clippy::version = "1.83.0"]
pub UNNEEDED_STRUCT_PATTERN,
nursery,
"using struct pattern to match against unit variant"
}

declare_lint_pass!(UnneededStructPattern => [UNNEEDED_STRUCT_PATTERN]);

impl LateLintPass<'_> for UnneededStructPattern {
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
if !pat.span.from_expansion()
&& let PatKind::Struct(path, [], _) = &pat.kind
&& let QPath::Resolved(_, path) = path
&& let Res::Def(DefKind::Variant, did) = path.res
{
let enum_did = cx.tcx.parent(did);
let variant = cx.tcx.adt_def(enum_did).variant_with_id(did);

let has_fields_brackets = !(variant.ctor.is_some() && variant.fields.is_empty());
let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive();
if has_fields_brackets || non_exhaustive_activated {
return;
}

if let Some(brackets_span) = pat.span.trim_start(path.span) {
span_lint_and_sugg(
cx,
UNNEEDED_STRUCT_PATTERN,
brackets_span,
"struct pattern is not needed for a unit variant",
"remove the struct pattern",
String::new(),
Applicability::MachineApplicable,
);
}
}
}
}
21 changes: 21 additions & 0 deletions tests/ui/auxiliary/non-exhaustive-enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,24 @@ pub enum ErrorKind {
#[doc(hidden)]
Uncategorized,
}

#[non_exhaustive]
pub enum ExtNonExhaustiveEnum {
Unit,
Tuple(i32),
Struct { field: i32 },
}

pub enum ExtNonExhaustiveVariant {
ExhaustiveUnit,
#[non_exhaustive]
Unit,
#[non_exhaustive]
Tuple(i32),
#[non_exhaustive]
StructNoField {},
#[non_exhaustive]
Struct {
field: i32,
},
}
98 changes: 98 additions & 0 deletions tests/ui/unneeded_struct_pattern.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//@aux-build:non-exhaustive-enum.rs
#![allow(clippy::manual_unwrap_or_default, clippy::manual_unwrap_or)]
#![warn(clippy::unneeded_struct_pattern)]

extern crate non_exhaustive_enum;
use non_exhaustive_enum::*;

fn main() {
match Some(114514) {
Some(v) => v,
None => 0,
};

match Some(1919810) {
Some(v) => v,
None => 0,
};

match Some(123456) {
Some(v) => v,
None => 0,
};

match Some(Some(123456)) {
Some(Some(v)) => v,
Some(None) => 0,
None => 0,
};

enum Custom {
HasFields {
field: i32,
},
HasBracketsNoFields {},
NoBrackets,
#[non_exhaustive]
NoBracketsNonExhaustive,
Init,
};

match Custom::Init {
Custom::HasFields { field: value } => value, // Should be ignored
Custom::HasBracketsNoFields {} => 0, // Should be ignored
Custom::NoBrackets => 0, // Should be fixed
Custom::NoBracketsNonExhaustive => 0, // Should be fixed
_ => 0,
};

match Custom::Init {
Custom::HasFields { field: value } => value, // Should be ignored
Custom::HasBracketsNoFields { .. } => 0, // Should be ignored
Custom::NoBrackets => 0, // Should be fixed
Custom::NoBracketsNonExhaustive => 0, // Should be fixed
_ => 0,
};

match Custom::Init {
Custom::NoBrackets if true => 0, // Should be fixed
_ => 0,
};

match Custom::Init {
Custom::NoBrackets | Custom::NoBracketsNonExhaustive => 0, // Should be fixed
_ => 0,
};
}

fn external_crate() {
use ExtNonExhaustiveVariant::*;

match ExhaustiveUnit {
// Expected
ExhaustiveUnit => 0,
_ => 0,
};

match ExhaustiveUnit {
// Exhaustive variant, should be fixed
ExhaustiveUnit => 0,
_ => 0,
};

match ExhaustiveUnit {
// Exhaustive variant, should be fixed
ExhaustiveUnit => 0,
_ => 0,
};

match ExhaustiveUnit {
ExhaustiveUnit => 0,
// vvvvv Non-exhaustive variants, should all be ignored
Unit { .. } => 0,
Tuple { 0: field, .. } => field,
StructNoField { .. } => 0,
Struct { field, .. } => field,
_ => 0,
};
}
98 changes: 98 additions & 0 deletions tests/ui/unneeded_struct_pattern.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//@aux-build:non-exhaustive-enum.rs
#![allow(clippy::manual_unwrap_or_default, clippy::manual_unwrap_or)]
#![warn(clippy::unneeded_struct_pattern)]

extern crate non_exhaustive_enum;
use non_exhaustive_enum::*;

fn main() {
match Some(114514) {
Some(v) => v,
None {} => 0,
};

match Some(1919810) {
Some(v) => v,
None { .. } => 0,
};

match Some(123456) {
Some(v) => v,
None => 0,
};

match Some(Some(123456)) {
Some(Some(v)) => v,
Some(None {}) => 0,
None {} => 0,
};

enum Custom {
HasFields {
field: i32,
},
HasBracketsNoFields {},
NoBrackets,
#[non_exhaustive]
NoBracketsNonExhaustive,
Init,
};

match Custom::Init {
Custom::HasFields { field: value } => value, // Should be ignored
Custom::HasBracketsNoFields {} => 0, // Should be ignored
Custom::NoBrackets {} => 0, // Should be fixed
Custom::NoBracketsNonExhaustive {} => 0, // Should be fixed
_ => 0,
};

match Custom::Init {
Custom::HasFields { field: value } => value, // Should be ignored
Custom::HasBracketsNoFields { .. } => 0, // Should be ignored
Custom::NoBrackets { .. } => 0, // Should be fixed
Custom::NoBracketsNonExhaustive { .. } => 0, // Should be fixed
_ => 0,
};

match Custom::Init {
Custom::NoBrackets {} if true => 0, // Should be fixed
_ => 0,
};

match Custom::Init {
Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, // Should be fixed
_ => 0,
};
}

fn external_crate() {
use ExtNonExhaustiveVariant::*;

match ExhaustiveUnit {
// Expected
ExhaustiveUnit => 0,
_ => 0,
};

match ExhaustiveUnit {
// Exhaustive variant, should be fixed
ExhaustiveUnit { .. } => 0,
_ => 0,
};

match ExhaustiveUnit {
// Exhaustive variant, should be fixed
ExhaustiveUnit {} => 0,
_ => 0,
};

match ExhaustiveUnit {
ExhaustiveUnit => 0,
// vvvvv Non-exhaustive variants, should all be ignored
Unit { .. } => 0,
Tuple { 0: field, .. } => field,
StructNoField { .. } => 0,
Struct { field, .. } => field,
_ => 0,
};
}
Loading

0 comments on commit d5ae91d

Please sign in to comment.