diff --git a/CHANGELOG.md b/CHANGELOG.md index e6792c06894..626c39457e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -816,6 +816,7 @@ All notable changes to this project will be documented in this file. [`redundant_closure_call`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_closure_call [`redundant_field_names`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_pattern`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_pattern +[`redundant_pattern_matching`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#redundant_pattern_matching [`ref_in_deref`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#ref_in_deref [`regex_macro`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#regex_macro [`replace_consts`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#replace_consts diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs index 0067629bbd0..904036fe888 100644 --- a/clippy_lints/src/deprecated_lints.rs +++ b/clippy_lints/src/deprecated_lints.rs @@ -16,7 +16,7 @@ macro_rules! declare_deprecated_lint { /// **What it does:** Nothing. This lint has been deprecated. /// -/// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend +/// **Deprecation reason:** This used to check for `assert!(a == b)` and recommend /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011. declare_deprecated_lint! { pub SHOULD_ASSERT_EQ, @@ -102,3 +102,13 @@ declare_deprecated_lint! { pub ASSIGN_OPS, "using compound assignment operators (e.g. `+=`) is harmless" } + +/// **What it does:** Nothing. This lint has been deprecated. +/// +/// **Deprecation reason:** The original rule will only lint for `if let`. After +/// making it support to lint `match`, naming as `if let` is not suitable for it. +/// So, this lint is deprecated. +declare_deprecated_lint! { + pub IF_LET_REDUNDANT_PATTERN_MATCHING, + "this lint has been changed to redundant_pattern_matching" +} diff --git a/clippy_lints/src/if_let_redundant_pattern_matching.rs b/clippy_lints/src/if_let_redundant_pattern_matching.rs deleted file mode 100644 index bced0c9552d..00000000000 --- a/clippy_lints/src/if_let_redundant_pattern_matching.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; -use crate::rustc::{declare_tool_lint, lint_array}; -use crate::rustc::hir::*; -use crate::utils::{match_qpath, paths, snippet, span_lint_and_then}; -use crate::rustc_errors::Applicability; - -/// **What it does:** Lint for redundant pattern matching over `Result` or -/// `Option` -/// -/// **Why is this bad?** It's more concise and clear to just use the proper -/// utility function -/// -/// **Known problems:** None. -/// -/// **Example:** -/// -/// ```rust -/// if let Ok(_) = Ok::(42) {} -/// if let Err(_) = Err::(42) {} -/// if let None = None::<()> {} -/// if let Some(_) = Some(42) {} -/// ``` -/// -/// The more idiomatic use would be: -/// -/// ```rust -/// if Ok::(42).is_ok() {} -/// if Err::(42).is_err() {} -/// if None::<()>.is_none() {} -/// if Some(42).is_some() {} -/// ``` -/// -declare_clippy_lint! { - pub IF_LET_REDUNDANT_PATTERN_MATCHING, - style, - "use the proper utility function avoiding an `if let`" -} - -#[derive(Copy, Clone)] -pub struct Pass; - -impl LintPass for Pass { - fn get_lints(&self) -> LintArray { - lint_array!(IF_LET_REDUNDANT_PATTERN_MATCHING) - } -} - -impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { - #[allow(clippy::similar_names)] - fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { - if let ExprKind::Match(ref op, ref arms, MatchSource::IfLetDesugar { .. }) = expr.node { - if arms[0].pats.len() == 1 { - let good_method = match arms[0].pats[0].node { - PatKind::TupleStruct(ref path, ref pats, _) if pats.len() == 1 => { - if let PatKind::Wild = pats[0].node { - if match_qpath(path, &paths::RESULT_OK) { - "is_ok()" - } else if match_qpath(path, &paths::RESULT_ERR) { - "is_err()" - } else if match_qpath(path, &paths::OPTION_SOME) { - "is_some()" - } else { - return; - } - } else { - return; - } - }, - - PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()", - - _ => return, - }; - - span_lint_and_then( - cx, - IF_LET_REDUNDANT_PATTERN_MATCHING, - arms[0].pats[0].span, - &format!("redundant pattern matching, consider using `{}`", good_method), - |db| { - let span = expr.span.to(op.span); - db.span_suggestion_with_applicability( - span, - "try this", - format!("if {}.{}", snippet(cx, op.span, "_"), good_method), - Applicability::MachineApplicable, // snippet - ); - }, - ); - } - } - } -} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 35c89e4efde..23bd71a08ab 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -126,7 +126,6 @@ pub mod formatting; pub mod functions; pub mod identity_conversion; pub mod identity_op; -pub mod if_let_redundant_pattern_matching; pub mod if_not_else; pub mod indexing_slicing; pub mod infallible_destructuring_match; @@ -180,6 +179,7 @@ pub mod ptr_offset_with_cast; pub mod question_mark; pub mod ranges; pub mod redundant_field_names; +pub mod redundant_pattern_matching; pub mod reference; pub mod regex; pub mod replace_consts; @@ -303,6 +303,10 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { "assign_ops", "using compound assignment operators (e.g. `+=`) is harmless", ); + store.register_removed( + "if_let_redundant_pattern_matching", + "this lint has been changed to redundant_pattern_matching", + ); // end deprecated lints, do not remove this comment, it’s used in `update_lints` reg.register_late_lint_pass(box serde_api::Serde); @@ -402,7 +406,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { reg.register_late_lint_pass(box missing_doc::MissingDoc::new()); reg.register_late_lint_pass(box missing_inline::MissingInline); reg.register_late_lint_pass(box ok_if_let::Pass); - reg.register_late_lint_pass(box if_let_redundant_pattern_matching::Pass); + reg.register_late_lint_pass(box redundant_pattern_matching::Pass); reg.register_late_lint_pass(box partialeq_ne_impl::Pass); reg.register_early_lint_pass(box reference::Pass); reg.register_early_lint_pass(box reference::DerefPass); @@ -565,7 +569,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { functions::TOO_MANY_ARGUMENTS, identity_conversion::IDENTITY_CONVERSION, identity_op::IDENTITY_OP, - if_let_redundant_pattern_matching::IF_LET_REDUNDANT_PATTERN_MATCHING, indexing_slicing::OUT_OF_BOUNDS_INDEXING, infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH, infinite_iter::INFINITE_ITER, @@ -680,6 +683,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { ranges::RANGE_PLUS_ONE, ranges::RANGE_ZIP_WITH_LEN, redundant_field_names::REDUNDANT_FIELD_NAMES, + redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING, reference::DEREF_ADDROF, reference::REF_IN_DEREF, regex::INVALID_REGEX, @@ -749,7 +753,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { excessive_precision::EXCESSIVE_PRECISION, formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, formatting::SUSPICIOUS_ELSE_FORMATTING, - if_let_redundant_pattern_matching::IF_LET_REDUNDANT_PATTERN_MATCHING, infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH, len_zero::LEN_WITHOUT_IS_EMPTY, len_zero::LEN_ZERO, @@ -800,6 +803,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { ptr::PTR_ARG, question_mark::QUESTION_MARK, redundant_field_names::REDUNDANT_FIELD_NAMES, + redundant_pattern_matching::REDUNDANT_PATTERN_MATCHING, regex::REGEX_MACRO, regex::TRIVIAL_REGEX, returns::LET_AND_RETURN, diff --git a/clippy_lints/src/redundant_pattern_matching.rs b/clippy_lints/src/redundant_pattern_matching.rs new file mode 100644 index 00000000000..f8c5b29bad1 --- /dev/null +++ b/clippy_lints/src/redundant_pattern_matching.rs @@ -0,0 +1,228 @@ +// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; +use crate::rustc::{declare_tool_lint, lint_array}; +use crate::rustc::hir::*; +use crate::syntax::ptr::P; +use crate::syntax::ast::LitKind; +use crate::utils::{match_qpath, paths, snippet, span_lint_and_then}; +use crate::rustc_errors::Applicability; + +/// **What it does:** Lint for redundant pattern matching over `Result` or +/// `Option` +/// +/// **Why is this bad?** It's more concise and clear to just use the proper +/// utility function +/// +/// **Known problems:** None. +/// +/// **Example:** +/// +/// ```rust +/// if let Ok(_) = Ok::(42) {} +/// if let Err(_) = Err::(42) {} +/// if let None = None::<()> {} +/// if let Some(_) = Some(42) {} +/// match Ok::(42) { +/// Ok(_) => true, +/// Err(_) => false, +/// }; +/// ``` +/// +/// The more idiomatic use would be: +/// +/// ```rust +/// if Ok::(42).is_ok() {} +/// if Err::(42).is_err() {} +/// if None::<()>.is_none() {} +/// if Some(42).is_some() {} +/// Ok::(42).is_ok(); +/// ``` +/// +declare_clippy_lint! { + pub REDUNDANT_PATTERN_MATCHING, + style, + "use the proper utility function avoiding an `if let`" +} + +#[derive(Copy, Clone)] +pub struct Pass; + +impl LintPass for Pass { + fn get_lints(&self) -> LintArray { + lint_array!(REDUNDANT_PATTERN_MATCHING) + } +} + +impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass { + #[allow(clippy::similar_names)] + fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) { + if let ExprKind::Match(ref op, ref arms, ref match_source) = expr.node { + match match_source { + MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms), + MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms), + _ => return, + } + } + } +} + +fn find_sugg_for_if_let<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx Expr, + op: &P, + arms: &HirVec +) { + if arms[0].pats.len() == 1 { + let good_method = match arms[0].pats[0].node { + PatKind::TupleStruct(ref path, ref pats, _) if pats.len() == 1 => { + if let PatKind::Wild = pats[0].node { + if match_qpath(path, &paths::RESULT_OK) { + "is_ok()" + } else if match_qpath(path, &paths::RESULT_ERR) { + "is_err()" + } else if match_qpath(path, &paths::OPTION_SOME) { + "is_some()" + } else { + return; + } + } else { + return; + } + }, + + PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => "is_none()", + + _ => return, + }; + + span_lint_and_then( + cx, + REDUNDANT_PATTERN_MATCHING, + arms[0].pats[0].span, + &format!("redundant pattern matching, consider using `{}`", good_method), + |db| { + let span = expr.span.to(op.span); + db.span_suggestion_with_applicability( + span, + "try this", + format!("if {}.{}", snippet(cx, op.span, "_"), good_method), + Applicability::MachineApplicable, // snippet + ); + }, + ); + } else { + return; + } +} + +fn find_sugg_for_match<'a, 'tcx>( + cx: &LateContext<'a, 'tcx>, + expr: &'tcx Expr, + op: &P, + arms: &HirVec +) { + if arms.len() == 2 { + let node_pair = (&arms[0].pats[0].node, &arms[1].pats[0].node); + + let found_good_method = match node_pair { + ( + PatKind::TupleStruct(ref path_left, ref pats_left, _), + PatKind::TupleStruct(ref path_right, ref pats_right, _) + ) if pats_left.len() == 1 && pats_right.len() == 1 => { + if let (PatKind::Wild, PatKind::Wild) = (&pats_left[0].node, &pats_right[0].node) { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::RESULT_OK, + &paths::RESULT_ERR, + "is_ok()", + "is_err()" + ) + } else { + None + } + }, + ( + PatKind::TupleStruct(ref path_left, ref pats, _), + PatKind::Path(ref path_right) + ) | ( + PatKind::Path(ref path_left), + PatKind::TupleStruct(ref path_right, ref pats, _) + ) if pats.len() == 1 => { + if let PatKind::Wild = pats[0].node { + find_good_method_for_match( + arms, + path_left, + path_right, + &paths::OPTION_SOME, + &paths::OPTION_NONE, + "is_some()", + "is_none()" + ) + } else { + None + } + }, + _ => None, + }; + + if let Some(good_method) = found_good_method { + span_lint_and_then( + cx, + REDUNDANT_PATTERN_MATCHING, + expr.span, + &format!("redundant pattern matching, consider using `{}`", good_method), + |db| { + let span = expr.span.to(op.span); + db.span_suggestion_with_applicability( + span, + "try this", + format!("{}.{}", snippet(cx, op.span, "_"), good_method), + Applicability::MachineApplicable, // snippet + ); + }, + ); + } + } else { + return; + } +} + +fn find_good_method_for_match<'a>( + arms: &HirVec, + path_left: &QPath, + path_right: &QPath, + expected_left: &[&str], + expected_right: &[&str], + should_be_left: &'a str, + should_be_right: &'a str +) -> Option<&'a str> { + let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) { + (&(*arms[0].body).node, &(*arms[1].body).node) + } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) { + (&(*arms[1].body).node, &(*arms[0].body).node) + } else { + return None; + }; + + match body_node_pair { + (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => { + match (&lit_left.node, &lit_right.node) { + (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left), + (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right), + _ => None, + } + }, + _ => None, + } +} diff --git a/tests/ui/if_let_redundant_pattern_matching.stderr b/tests/ui/if_let_redundant_pattern_matching.stderr deleted file mode 100644 index 5111de67189..00000000000 --- a/tests/ui/if_let_redundant_pattern_matching.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error: redundant pattern matching, consider using `is_ok()` - --> $DIR/if_let_redundant_pattern_matching.rs:19:12 - | -19 | if let Ok(_) = Ok::(42) {} - | -------^^^^^------------------------ help: try this: `if Ok::(42).is_ok()` - | - = note: `-D clippy::if-let-redundant-pattern-matching` implied by `-D warnings` - -error: redundant pattern matching, consider using `is_err()` - --> $DIR/if_let_redundant_pattern_matching.rs:21:12 - | -21 | if let Err(_) = Err::(42) { - | _____- ^^^^^^ -22 | | } - | |_____- help: try this: `if Err::(42).is_err()` - -error: redundant pattern matching, consider using `is_none()` - --> $DIR/if_let_redundant_pattern_matching.rs:24:12 - | -24 | if let None = None::<()> { - | _____- ^^^^ -25 | | } - | |_____- help: try this: `if None::<()>.is_none()` - -error: redundant pattern matching, consider using `is_some()` - --> $DIR/if_let_redundant_pattern_matching.rs:27:12 - | -27 | if let Some(_) = Some(42) { - | _____- ^^^^^^^ -28 | | } - | |_____- help: try this: `if Some(42).is_some()` - -error: aborting due to 4 previous errors - diff --git a/tests/ui/matches.rs b/tests/ui/matches.rs index c43fead08f8..d31e97c7959 100644 --- a/tests/ui/matches.rs +++ b/tests/ui/matches.rs @@ -13,7 +13,7 @@ #![warn(clippy::all)] -#![allow(unused, clippy::if_let_redundant_pattern_matching)] +#![allow(unused, clippy::redundant_pattern_matching)] #![warn(clippy::single_match_else, clippy::match_same_arms)] enum ExprNode { diff --git a/tests/ui/needless_pass_by_value.rs b/tests/ui/needless_pass_by_value.rs index 5825d9e9074..48b7b42cc8c 100644 --- a/tests/ui/needless_pass_by_value.rs +++ b/tests/ui/needless_pass_by_value.rs @@ -11,7 +11,7 @@ #![warn(clippy::needless_pass_by_value)] -#![allow(dead_code, clippy::single_match, clippy::if_let_redundant_pattern_matching, clippy::many_single_char_names, clippy::option_option)] +#![allow(dead_code, clippy::single_match, clippy::redundant_pattern_matching, clippy::many_single_char_names, clippy::option_option)] use std::borrow::Borrow; use std::convert::AsRef; diff --git a/tests/ui/if_let_redundant_pattern_matching.rs b/tests/ui/redundant_pattern_matching.rs similarity index 61% rename from tests/ui/if_let_redundant_pattern_matching.rs rename to tests/ui/redundant_pattern_matching.rs index 1c0e7e79c68..50838584f66 100644 --- a/tests/ui/if_let_redundant_pattern_matching.rs +++ b/tests/ui/redundant_pattern_matching.rs @@ -12,7 +12,7 @@ #![warn(clippy::all)] -#![warn(clippy::if_let_redundant_pattern_matching)] +#![warn(clippy::redundant_pattern_matching)] fn main() { @@ -42,4 +42,34 @@ fn main() { if let Ok(x) = Ok::(42) { println!("{}", x); } + + match Ok::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Ok::(42) { + Ok(_) => false, + Err(_) => true, + }; + + match Err::(42) { + Ok(_) => false, + Err(_) => true, + }; + + match Err::(42) { + Ok(_) => true, + Err(_) => false, + }; + + match Some(42) { + Some(_) => true, + None => false, + }; + + match None::<()> { + Some(_) => false, + None => true, + }; } diff --git a/tests/ui/redundant_pattern_matching.stderr b/tests/ui/redundant_pattern_matching.stderr new file mode 100644 index 00000000000..a42ac7ba04d --- /dev/null +++ b/tests/ui/redundant_pattern_matching.stderr @@ -0,0 +1,88 @@ +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:19:12 + | +19 | if let Ok(_) = Ok::(42) {} + | -------^^^^^------------------------ help: try this: `if Ok::(42).is_ok()` + | + = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:21:12 + | +21 | if let Err(_) = Err::(42) { + | _____- ^^^^^^ +22 | | } + | |_____- help: try this: `if Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:24:12 + | +24 | if let None = None::<()> { + | _____- ^^^^ +25 | | } + | |_____- help: try this: `if None::<()>.is_none()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:27:12 + | +27 | if let Some(_) = Some(42) { + | _____- ^^^^^^^ +28 | | } + | |_____- help: try this: `if Some(42).is_some()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:46:5 + | +46 | / match Ok::(42) { +47 | | Ok(_) => true, +48 | | Err(_) => false, +49 | | }; + | |_____^ help: try this: `Ok::(42).is_ok()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:51:5 + | +51 | / match Ok::(42) { +52 | | Ok(_) => false, +53 | | Err(_) => true, +54 | | }; + | |_____^ help: try this: `Ok::(42).is_err()` + +error: redundant pattern matching, consider using `is_err()` + --> $DIR/redundant_pattern_matching.rs:56:5 + | +56 | / match Err::(42) { +57 | | Ok(_) => false, +58 | | Err(_) => true, +59 | | }; + | |_____^ help: try this: `Err::(42).is_err()` + +error: redundant pattern matching, consider using `is_ok()` + --> $DIR/redundant_pattern_matching.rs:61:5 + | +61 | / match Err::(42) { +62 | | Ok(_) => true, +63 | | Err(_) => false, +64 | | }; + | |_____^ help: try this: `Err::(42).is_ok()` + +error: redundant pattern matching, consider using `is_some()` + --> $DIR/redundant_pattern_matching.rs:66:5 + | +66 | / match Some(42) { +67 | | Some(_) => true, +68 | | None => false, +69 | | }; + | |_____^ help: try this: `Some(42).is_some()` + +error: redundant pattern matching, consider using `is_none()` + --> $DIR/redundant_pattern_matching.rs:71:5 + | +71 | / match None::<()> { +72 | | Some(_) => false, +73 | | None => true, +74 | | }; + | |_____^ help: try this: `None::<()>.is_none()` + +error: aborting due to 10 previous errors +