-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new lint
unneeded_struct_pattern
- Loading branch information
Showing
8 changed files
with
376 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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::Unspecified, | ||
); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
//@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, | ||
StructNoField { .. } => 0, | ||
Struct { field, .. } => field, | ||
_ => 0, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
//@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, | ||
StructNoField { .. } => 0, | ||
Struct { field, .. } => field, | ||
_ => 0, | ||
}; | ||
} |
Oops, something went wrong.