@@ -5,7 +5,7 @@ use crate::utils::usage::is_unused;
5
5
use crate :: utils:: {
6
6
span_lint_and_help, span_lint_and_note,
7
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,
8
+ snippet, snippet_block , snippet_with_applicability, span_lint_and_sugg, span_lint_and_then,
9
9
} ;
10
10
use if_chain:: if_chain;
11
11
use rustc:: lint:: in_external_macro;
@@ -14,7 +14,7 @@ use rustc_errors::Applicability;
14
14
use rustc_hir:: def:: CtorKind ;
15
15
use rustc_hir:: * ;
16
16
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
17
- use rustc_session:: { declare_lint_pass , declare_tool_lint } ;
17
+ use rustc_session:: { declare_tool_lint , impl_lint_pass } ;
18
18
use rustc_span:: source_map:: Span ;
19
19
use std:: cmp:: Ordering ;
20
20
use std:: collections:: Bound ;
@@ -245,12 +245,47 @@ declare_clippy_lint! {
245
245
"a wildcard pattern used with others patterns in same match arm"
246
246
}
247
247
248
+ declare_clippy_lint ! {
249
+ /// **What it does:** Checks for matches being used to destructure a single-variant enum
250
+ /// or tuple struct where a `let` will suffice.
251
+ ///
252
+ /// **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.
253
+ ///
254
+ /// **Known problems:** None.
255
+ ///
256
+ /// **Example:**
257
+ /// ```rust
258
+ /// enum Wrapper {
259
+ /// Data(i32),
260
+ /// }
261
+ ///
262
+ /// let wrapper = Wrapper::Data(42);
263
+ ///
264
+ /// let data = match wrapper {
265
+ /// Wrapper::Data(i) => i,
266
+ /// };
267
+ /// ```
268
+ ///
269
+ /// The correct use would be:
270
+ /// ```rust
271
+ /// enum Wrapper {
272
+ /// Data(i32),
273
+ /// }
274
+ ///
275
+ /// let wrapper = Wrapper::Data(42);
276
+ /// let Wrapper::Data(data) = wrapper;
277
+ /// ```
278
+ pub INFALLIBLE_DESTRUCTURING_MATCH ,
279
+ style,
280
+ "a `match` statement with a single infallible arm instead of a `let`"
281
+ }
282
+
248
283
declare_clippy_lint ! {
249
284
/// **What it does:** Checks for useless match that binds to only one value.
250
285
///
251
286
/// **Why is this bad?** Readability and needless complexity.
252
287
///
253
- /// **Known problems:** This situation frequently happen in macros, so can't lint there .
288
+ /// **Known problems:** None .
254
289
///
255
290
/// **Example:**
256
291
/// ```rust
@@ -272,7 +307,12 @@ declare_clippy_lint! {
272
307
"a match with a single binding instead of using `let` statement"
273
308
}
274
309
275
- declare_lint_pass ! ( Matches => [
310
+ #[ derive( Default ) ]
311
+ pub struct Matches {
312
+ infallible_destructuring_match_linted : bool ,
313
+ }
314
+
315
+ impl_lint_pass ! ( Matches => [
276
316
SINGLE_MATCH ,
277
317
MATCH_REF_PATS ,
278
318
MATCH_BOOL ,
@@ -283,6 +323,7 @@ declare_lint_pass!(Matches => [
283
323
WILDCARD_ENUM_MATCH_ARM ,
284
324
WILDCARD_IN_OR_PATTERNS ,
285
325
MATCH_SINGLE_BINDING ,
326
+ INFALLIBLE_DESTRUCTURING_MATCH
286
327
] ) ;
287
328
288
329
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Matches {
@@ -298,12 +339,51 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
298
339
check_wild_enum_match ( cx, ex, arms) ;
299
340
check_match_as_ref ( cx, ex, arms, expr) ;
300
341
check_wild_in_or_pats ( cx, arms) ;
301
- check_match_single_binding ( cx, ex, arms, expr) ;
342
+
343
+ if self . infallible_destructuring_match_linted {
344
+ self . infallible_destructuring_match_linted = false ;
345
+ } else {
346
+ check_match_single_binding ( cx, ex, arms, expr) ;
347
+ }
302
348
}
303
349
if let ExprKind :: Match ( ref ex, ref arms, _) = expr. kind {
304
350
check_match_ref_pats ( cx, ex, arms, expr) ;
305
351
}
306
352
}
353
+
354
+ fn check_local ( & mut self , cx : & LateContext < ' a , ' tcx > , local : & ' tcx Local < ' _ > ) {
355
+ if_chain ! {
356
+ if let Some ( ref expr) = local. init;
357
+ if let ExprKind :: Match ( ref target, ref arms, MatchSource :: Normal ) = expr. kind;
358
+ if arms. len( ) == 1 && arms[ 0 ] . guard. is_none( ) ;
359
+ if let PatKind :: TupleStruct (
360
+ QPath :: Resolved ( None , ref variant_name) , ref args, _) = arms[ 0 ] . pat. kind;
361
+ if args. len( ) == 1 ;
362
+ if let Some ( arg) = get_arg_name( & args[ 0 ] ) ;
363
+ let body = remove_blocks( & arms[ 0 ] . body) ;
364
+ if match_var( body, arg) ;
365
+
366
+ then {
367
+ let mut applicability = Applicability :: MachineApplicable ;
368
+ self . infallible_destructuring_match_linted = true ;
369
+ span_lint_and_sugg(
370
+ cx,
371
+ INFALLIBLE_DESTRUCTURING_MATCH ,
372
+ local. span,
373
+ "you seem to be trying to use `match` to destructure a single infallible pattern. \
374
+ Consider using `let`",
375
+ "try this" ,
376
+ format!(
377
+ "let {}({}) = {};" ,
378
+ snippet_with_applicability( cx, variant_name. span, ".." , & mut applicability) ,
379
+ snippet_with_applicability( cx, local. pat. span, ".." , & mut applicability) ,
380
+ snippet_with_applicability( cx, target. span, ".." , & mut applicability) ,
381
+ ) ,
382
+ applicability,
383
+ ) ;
384
+ }
385
+ }
386
+ }
307
387
}
308
388
309
389
#[ rustfmt:: skip]
@@ -746,21 +826,31 @@ fn check_match_single_binding(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[A
746
826
return ;
747
827
}
748
828
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
- ) ;
829
+ if is_refutable ( cx, arms[ 0 ] . pat ) {
830
+ return ;
831
+ }
832
+ match arms[ 0 ] . pat . kind {
833
+ PatKind :: Binding ( ..) | PatKind :: Tuple ( _, _) => {
834
+ let bind_names = arms[ 0 ] . pat . span ;
835
+ let matched_vars = ex. span ;
836
+ let match_body = remove_blocks ( & arms[ 0 ] . body ) ;
837
+ span_lint_and_sugg (
838
+ cx,
839
+ MATCH_SINGLE_BINDING ,
840
+ expr. span ,
841
+ "this match could be written as a `let` statement" ,
842
+ "consider using `let` statement" ,
843
+ format ! (
844
+ "let {} = {};\n {}" ,
845
+ snippet( cx, bind_names, ".." ) ,
846
+ snippet( cx, matched_vars, ".." ) ,
847
+ snippet_block( cx, match_body. span, ".." )
848
+ ) ,
849
+ Applicability :: MachineApplicable ,
850
+ ) ;
851
+ } ,
852
+ _ => ( ) ,
853
+ }
764
854
}
765
855
}
766
856
0 commit comments