@@ -3,9 +3,9 @@ use crate::utils::paths;
33use crate :: utils:: sugg:: Sugg ;
44use crate :: utils:: usage:: is_unused;
55use crate :: utils:: {
6- expr_block , is_allowed , is_expn_of , is_wild , match_qpath , match_type , multispan_sugg , remove_blocks , snippet ,
7- snippet_with_applicability , span_lint_and_help , span_lint_and_note , span_lint_and_sugg , span_lint_and_then ,
8- walk_ptrs_ty ,
6+ span_lint_and_help , span_lint_and_note ,
7+ expr_block , in_macro , is_allowed , is_expn_of , is_wild , match_qpath , match_type , multispan_sugg , remove_blocks ,
8+ snippet , snippet_with_applicability , span_lint_and_sugg , span_lint_and_then ,
99} ;
1010use if_chain:: if_chain;
1111use rustc:: lint:: in_external_macro;
@@ -245,6 +245,33 @@ declare_clippy_lint! {
245245 "a wildcard pattern used with others patterns in same match arm"
246246}
247247
248+ declare_clippy_lint ! {
249+ /// **What it does:** Checks for useless match that binds to only one value.
250+ ///
251+ /// **Why is this bad?** Readability and needless complexity.
252+ ///
253+ /// **Known problems:** This situation frequently happen in macros, so can't lint there.
254+ ///
255+ /// **Example:**
256+ /// ```rust
257+ /// # let a = 1;
258+ /// # let b = 2;
259+ ///
260+ /// // Bad
261+ /// match (a, b) {
262+ /// (c, d) => {
263+ /// // useless match
264+ /// }
265+ /// }
266+ ///
267+ /// // Good
268+ /// let (c, d) = (a, b);
269+ /// ```
270+ pub MATCH_SINGLE_BINDING ,
271+ complexity,
272+ "a match with a single binding instead of using `let` statement"
273+ }
274+
248275declare_lint_pass ! ( Matches => [
249276 SINGLE_MATCH ,
250277 MATCH_REF_PATS ,
@@ -254,7 +281,8 @@ declare_lint_pass!(Matches => [
254281 MATCH_WILD_ERR_ARM ,
255282 MATCH_AS_REF ,
256283 WILDCARD_ENUM_MATCH_ARM ,
257- WILDCARD_IN_OR_PATTERNS
284+ WILDCARD_IN_OR_PATTERNS ,
285+ MATCH_SINGLE_BINDING ,
258286] ) ;
259287
260288impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Matches {
@@ -270,6 +298,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
270298 check_wild_enum_match ( cx, ex, arms) ;
271299 check_match_as_ref ( cx, ex, arms, expr) ;
272300 check_wild_in_or_pats ( cx, arms) ;
301+ check_match_single_binding ( cx, ex, arms, expr) ;
273302 }
274303 if let ExprKind :: Match ( ref ex, ref arms, _) = expr. kind {
275304 check_match_ref_pats ( cx, ex, arms, expr) ;
@@ -712,6 +741,29 @@ fn check_wild_in_or_pats(cx: &LateContext<'_, '_>, arms: &[Arm<'_>]) {
712741 }
713742}
714743
744+ fn check_match_single_binding ( cx : & LateContext < ' _ , ' _ > , ex : & Expr < ' _ > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' _ > ) {
745+ if in_macro ( expr. span ) {
746+ return ;
747+ }
748+ if arms. len ( ) == 1 {
749+ let bind_names = arms[ 0 ] . pat . span ;
750+ let matched_vars = ex. span ;
751+ span_lint_and_sugg (
752+ cx,
753+ MATCH_SINGLE_BINDING ,
754+ expr. span ,
755+ "this match could be written as a `let` statement" ,
756+ "try this" ,
757+ format ! (
758+ "let {} = {};" ,
759+ snippet( cx, bind_names, ".." ) ,
760+ snippet( cx, matched_vars, ".." )
761+ ) ,
762+ Applicability :: HasPlaceholders ,
763+ ) ;
764+ }
765+ }
766+
715767/// Gets all arms that are unbounded `PatRange`s.
716768fn all_ranges < ' a , ' tcx > (
717769 cx : & LateContext < ' a , ' tcx > ,
0 commit comments