@@ -2,7 +2,7 @@ use crate::consts::{constant, miri_to_const, Constant};
22use crate :: utils:: paths;
33use crate :: utils:: sugg:: Sugg ;
44use crate :: utils:: {
5- expr_block, is_allowed, is_expn_of, match_qpath, match_type, multispan_sugg, remove_blocks, snippet,
5+ expr_block, in_macro , is_allowed, is_expn_of, match_qpath, match_type, multispan_sugg, remove_blocks, snippet,
66 snippet_with_applicability, span_help_and_lint, span_lint_and_sugg, span_lint_and_then, span_note_and_lint,
77 walk_ptrs_ty,
88} ;
@@ -244,6 +244,33 @@ declare_clippy_lint! {
244244 "a wildcard pattern used with others patterns in same match arm"
245245}
246246
247+ declare_clippy_lint ! {
248+ /// **What it does:** Checks for useless match that binds to only one value.
249+ ///
250+ /// **Why is this bad?** Readability and needless complexity.
251+ ///
252+ /// **Known problems:** This situation frequently happen in macros, so can't lint there.
253+ ///
254+ /// **Example:**
255+ /// ```rust
256+ /// # let a = 1;
257+ /// # let b = 2;
258+ ///
259+ /// // Bad
260+ /// match (a, b) {
261+ /// (c, d) => {
262+ /// // useless match
263+ /// }
264+ /// }
265+ ///
266+ /// // Good
267+ /// let (c, d) = (a, b);
268+ /// ```
269+ pub MATCH_SINGLE_BINDING ,
270+ complexity,
271+ "a match with a single binding instead of using `let` statement"
272+ }
273+
247274declare_lint_pass ! ( Matches => [
248275 SINGLE_MATCH ,
249276 MATCH_REF_PATS ,
@@ -253,7 +280,8 @@ declare_lint_pass!(Matches => [
253280 MATCH_WILD_ERR_ARM ,
254281 MATCH_AS_REF ,
255282 WILDCARD_ENUM_MATCH_ARM ,
256- WILDCARD_IN_OR_PATTERNS
283+ WILDCARD_IN_OR_PATTERNS ,
284+ MATCH_SINGLE_BINDING ,
257285] ) ;
258286
259287impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Matches {
@@ -269,6 +297,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
269297 check_wild_enum_match ( cx, ex, arms) ;
270298 check_match_as_ref ( cx, ex, arms, expr) ;
271299 check_wild_in_or_pats ( cx, arms) ;
300+ check_match_single_binding ( cx, ex, arms, expr) ;
272301 }
273302 if let ExprKind :: Match ( ref ex, ref arms, _) = expr. kind {
274303 check_match_ref_pats ( cx, ex, arms, expr) ;
@@ -704,6 +733,29 @@ fn check_wild_in_or_pats(cx: &LateContext<'_, '_>, arms: &[Arm<'_>]) {
704733 }
705734}
706735
736+ fn check_match_single_binding ( cx : & LateContext < ' _ , ' _ > , ex : & Expr < ' _ > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' _ > ) {
737+ if in_macro ( expr. span ) {
738+ return ;
739+ }
740+ if arms. len ( ) == 1 {
741+ let bind_names = arms[ 0 ] . pat . span ;
742+ let matched_vars = ex. span ;
743+ span_lint_and_sugg (
744+ cx,
745+ MATCH_SINGLE_BINDING ,
746+ expr. span ,
747+ "this match could be written as a `let` statement" ,
748+ "try this" ,
749+ format ! (
750+ "let {} = {};" ,
751+ snippet( cx, bind_names, ".." ) ,
752+ snippet( cx, matched_vars, ".." )
753+ ) ,
754+ Applicability :: HasPlaceholders ,
755+ ) ;
756+ }
757+ }
758+
707759/// Gets all arms that are unbounded `PatRange`s.
708760fn all_ranges < ' a , ' tcx > (
709761 cx : & LateContext < ' a , ' tcx > ,
0 commit comments