Skip to content

Add new unusable_matches_bindings lint #12480

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5766,6 +5766,7 @@ Released 2018-09-13
[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
[`unusable_matches_bindings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusable_matches_bindings
[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
[`unused_enumerate_index`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_enumerate_index
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 @@ -338,6 +338,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::matches::SINGLE_MATCH_INFO,
crate::matches::SINGLE_MATCH_ELSE_INFO,
crate::matches::TRY_ERR_INFO,
crate::matches::UNUSABLE_MATCHES_BINDINGS_INFO,
crate::matches::WILDCARD_ENUM_MATCH_ARM_INFO,
crate::matches::WILDCARD_IN_OR_PATTERNS_INFO,
crate::mem_replace::MEM_REPLACE_OPTION_WITH_NONE_INFO,
Expand Down
49 changes: 49 additions & 0 deletions clippy_lints/src/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mod rest_pat_in_fully_bound_struct;
mod significant_drop_in_scrutinee;
mod single_match;
mod try_err;
mod unusable_matches_bindings;
mod wild_in_or_pats;

use clippy_config::msrvs::{self, Msrv};
Expand Down Expand Up @@ -975,6 +976,52 @@ declare_clippy_lint! {
"checks for unnecessary guards in match expressions"
}

declare_clippy_lint! {
/// ### What it does
/// Checks for redundant and error prone bindings inside `matches!` macro.
///
/// ### Why is this bad?
/// Contrary to the `==` operator, which compares two values, the `matches!`
/// macro compares a value to a pattern. An invocation such as `matches!(5, x)`
/// expands to the following code:
///
/// ```rust
/// match 5 {
/// x => true,
/// _ => false
/// }
/// ```
///
/// The identifier pattern `x` matches any value and creates a binding that stores its value.
///
/// This makes identifier patterns in `matches!` without any use in guards useless: the macro
/// always evaluates to `true` as no equality checking actually takes place and the binding that
/// is created is unusable outside of the macro.
///
/// ### Example
/// ```rust
/// let data_source = Some(5);
/// let unrelated_data_source = 5;
///
/// let x = matches!(data_source, unused_binding);
/// let y = matches!(data_source, used_binding if used_binding.is_some());
/// let z = matches!(data_source, unused_binding if unrelated_data_source > 6);
/// ```
/// Use instead:
/// ```rust
/// let data_source = Some(5);
/// let unrelated_data_source = 5;
///
/// let x = true;
/// let y = data_source.is_some();
/// let z = unrelated_data_source > 6;
/// ```
#[clippy::version = "1.75.0"]
pub UNUSABLE_MATCHES_BINDINGS,
correctness,
"checks for unusable bindings in `matches!` macro"
}

pub struct Matches {
msrv: Msrv,
infallible_destructuring_match_linted: bool,
Expand Down Expand Up @@ -1017,6 +1064,7 @@ impl_lint_pass!(Matches => [
MANUAL_MAP,
MANUAL_FILTER,
REDUNDANT_GUARDS,
UNUSABLE_MATCHES_BINDINGS,
]);

impl<'tcx> LateLintPass<'tcx> for Matches {
Expand All @@ -1032,6 +1080,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
{
redundant_pattern_match::check_match(cx, expr, ex, arms);
redundant_pattern_match::check_matches_true(cx, expr, arm, ex);
unusable_matches_bindings::check_matches(cx, arms);
}

if source == MatchSource::Normal && !is_span_match(cx, expr.span) {
Expand Down
37 changes: 37 additions & 0 deletions clippy_lints/src/matches/unusable_matches_bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use rustc_hir::{Arm, PatKind};
use rustc_lint::LateContext;

use super::UNUSABLE_MATCHES_BINDINGS;

pub(crate) fn check_matches<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
for arm in arms {
if let PatKind::Binding(_, _, _, None) = arm.pat.kind {
if let Some(guard) = arm.guard {
span_lint_and_help(
cx,
UNUSABLE_MATCHES_BINDINGS,
guard.peel_blocks().span,
"identifier pattern in `matches!` macro always evaluates to the value of the guard",
None,
"if you meant to check predicate, then try changing `matches!` macro into predicate the guard's checking",
);
} else {
span_lint_and_then(
cx,
UNUSABLE_MATCHES_BINDINGS,
arm.pat.span,
"identifier pattern in `matches!` macro always evaluates to true",
|diag| {
diag.note(
"the identifier pattern matches any value and creates an unusable binding in the process",
)
.help(
"if you meant to compare two values, use `x == y` or `discriminant(x) == discriminant(y)`",
);
},
);
}
}
}
}
23 changes: 23 additions & 0 deletions tests/ui/unusable_matches_bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@no-rustfix
#![warn(clippy::unusable_matches_binding)]

#[derive(Clone, Copy)]
struct TestingStructure(i32);

impl TestingStructure {
fn is_valid(&self) -> bool {
self.0 > 5
}
}

fn main() {
let matching_data_source = TestingStructure(5);
let unrelated_data = 5;

let _ = matches!(matching_data_source, TestingStructure(4));

let _ = matches!(matching_data_source, unusable_binding);
let _ = matches!(matching_data_source, used_binding if used_binding.is_valid());

let _ = matches!(matching_data_source, unusable_binding if unrelated_data < 4);
}
37 changes: 37 additions & 0 deletions tests/ui/unusable_matches_bindings.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
error: unknown lint: `clippy::unusable_matches_binding`
--> tests/ui/unusable_matches_bindings.rs:2:9
|
LL | #![warn(clippy::unusable_matches_binding)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::unusable_matches_bindings`
|
= note: `-D unknown-lints` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(unknown_lints)]`

error: identifier pattern in `matches!` macro always evaluates to true
--> tests/ui/unusable_matches_bindings.rs:19:44
|
LL | let _ = matches!(matching_data_source, unusable_binding);
| ^^^^^^^^^^^^^^^^
|
= note: the identifier pattern matches any value and creates an unusable binding in the process
= help: if you meant to compare two values, use `x == y` or `discriminant(x) == discriminant(y)`
= note: `#[deny(clippy::unusable_matches_bindings)]` on by default

error: identifier pattern in `matches!` macro always evaluates to the value of the guard
--> tests/ui/unusable_matches_bindings.rs:20:60
|
LL | let _ = matches!(matching_data_source, used_binding if used_binding.is_valid());
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: if you meant to check predicate, then try changing `matches!` macro into predicate the guard's checking

error: identifier pattern in `matches!` macro always evaluates to the value of the guard
--> tests/ui/unusable_matches_bindings.rs:22:64
|
LL | let _ = matches!(matching_data_source, unusable_binding if unrelated_data < 4);
| ^^^^^^^^^^^^^^^^^^
|
= help: if you meant to check predicate, then try changing `matches!` macro into predicate the guard's checking

error: aborting due to 4 previous errors