]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/matches.rs
Add new lint: match with a single binding statement
[rust.git] / clippy_lints / src / matches.rs
1 use crate::consts::{constant, miri_to_const, Constant};
2 use crate::utils::paths;
3 use crate::utils::sugg::Sugg;
4 use crate::utils::usage::is_unused;
5 use crate::utils::{
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,
9 };
10 use if_chain::if_chain;
11 use rustc::lint::in_external_macro;
12 use rustc::ty::{self, Ty};
13 use rustc_errors::Applicability;
14 use rustc_hir::def::CtorKind;
15 use rustc_hir::*;
16 use rustc_lint::{LateContext, LateLintPass, LintContext};
17 use rustc_session::{declare_lint_pass, declare_tool_lint};
18 use rustc_span::source_map::Span;
19 use std::cmp::Ordering;
20 use std::collections::Bound;
21 use syntax::ast::LitKind;
22
23 declare_clippy_lint! {
24     /// **What it does:** Checks for matches with a single arm where an `if let`
25     /// will usually suffice.
26     ///
27     /// **Why is this bad?** Just readability – `if let` nests less than a `match`.
28     ///
29     /// **Known problems:** None.
30     ///
31     /// **Example:**
32     /// ```rust
33     /// # fn bar(stool: &str) {}
34     /// # let x = Some("abc");
35     /// match x {
36     ///     Some(ref foo) => bar(foo),
37     ///     _ => (),
38     /// }
39     /// ```
40     pub SINGLE_MATCH,
41     style,
42     "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
43 }
44
45 declare_clippy_lint! {
46     /// **What it does:** Checks for matches with two arms where an `if let else` will
47     /// usually suffice.
48     ///
49     /// **Why is this bad?** Just readability – `if let` nests less than a `match`.
50     ///
51     /// **Known problems:** Personal style preferences may differ.
52     ///
53     /// **Example:**
54     ///
55     /// Using `match`:
56     ///
57     /// ```rust
58     /// # fn bar(foo: &usize) {}
59     /// # let other_ref: usize = 1;
60     /// # let x: Option<&usize> = Some(&1);
61     /// match x {
62     ///     Some(ref foo) => bar(foo),
63     ///     _ => bar(&other_ref),
64     /// }
65     /// ```
66     ///
67     /// Using `if let` with `else`:
68     ///
69     /// ```rust
70     /// # fn bar(foo: &usize) {}
71     /// # let other_ref: usize = 1;
72     /// # let x: Option<&usize> = Some(&1);
73     /// if let Some(ref foo) = x {
74     ///     bar(foo);
75     /// } else {
76     ///     bar(&other_ref);
77     /// }
78     /// ```
79     pub SINGLE_MATCH_ELSE,
80     pedantic,
81     "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
82 }
83
84 declare_clippy_lint! {
85     /// **What it does:** Checks for matches where all arms match a reference,
86     /// suggesting to remove the reference and deref the matched expression
87     /// instead. It also checks for `if let &foo = bar` blocks.
88     ///
89     /// **Why is this bad?** It just makes the code less readable. That reference
90     /// destructuring adds nothing to the code.
91     ///
92     /// **Known problems:** None.
93     ///
94     /// **Example:**
95     /// ```rust,ignore
96     /// match x {
97     ///     &A(ref y) => foo(y),
98     ///     &B => bar(),
99     ///     _ => frob(&x),
100     /// }
101     /// ```
102     pub MATCH_REF_PATS,
103     style,
104     "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
105 }
106
107 declare_clippy_lint! {
108     /// **What it does:** Checks for matches where match expression is a `bool`. It
109     /// suggests to replace the expression with an `if...else` block.
110     ///
111     /// **Why is this bad?** It makes the code less readable.
112     ///
113     /// **Known problems:** None.
114     ///
115     /// **Example:**
116     /// ```rust
117     /// # fn foo() {}
118     /// # fn bar() {}
119     /// let condition: bool = true;
120     /// match condition {
121     ///     true => foo(),
122     ///     false => bar(),
123     /// }
124     /// ```
125     /// Use if/else instead:
126     /// ```rust
127     /// # fn foo() {}
128     /// # fn bar() {}
129     /// let condition: bool = true;
130     /// if condition {
131     ///     foo();
132     /// } else {
133     ///     bar();
134     /// }
135     /// ```
136     pub MATCH_BOOL,
137     style,
138     "a `match` on a boolean expression instead of an `if..else` block"
139 }
140
141 declare_clippy_lint! {
142     /// **What it does:** Checks for overlapping match arms.
143     ///
144     /// **Why is this bad?** It is likely to be an error and if not, makes the code
145     /// less obvious.
146     ///
147     /// **Known problems:** None.
148     ///
149     /// **Example:**
150     /// ```rust
151     /// let x = 5;
152     /// match x {
153     ///     1...10 => println!("1 ... 10"),
154     ///     5...15 => println!("5 ... 15"),
155     ///     _ => (),
156     /// }
157     /// ```
158     pub MATCH_OVERLAPPING_ARM,
159     style,
160     "a `match` with overlapping arms"
161 }
162
163 declare_clippy_lint! {
164     /// **What it does:** Checks for arm which matches all errors with `Err(_)`
165     /// and take drastic actions like `panic!`.
166     ///
167     /// **Why is this bad?** It is generally a bad practice, just like
168     /// catching all exceptions in java with `catch(Exception)`
169     ///
170     /// **Known problems:** None.
171     ///
172     /// **Example:**
173     /// ```rust
174     /// let x: Result<i32, &str> = Ok(3);
175     /// match x {
176     ///     Ok(_) => println!("ok"),
177     ///     Err(_) => panic!("err"),
178     /// }
179     /// ```
180     pub MATCH_WILD_ERR_ARM,
181     style,
182     "a `match` with `Err(_)` arm and take drastic actions"
183 }
184
185 declare_clippy_lint! {
186     /// **What it does:** Checks for match which is used to add a reference to an
187     /// `Option` value.
188     ///
189     /// **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.
190     ///
191     /// **Known problems:** None.
192     ///
193     /// **Example:**
194     /// ```rust
195     /// let x: Option<()> = None;
196     /// let r: Option<&()> = match x {
197     ///     None => None,
198     ///     Some(ref v) => Some(v),
199     /// };
200     /// ```
201     pub MATCH_AS_REF,
202     complexity,
203     "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
204 }
205
206 declare_clippy_lint! {
207     /// **What it does:** Checks for wildcard enum matches using `_`.
208     ///
209     /// **Why is this bad?** New enum variants added by library updates can be missed.
210     ///
211     /// **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some
212     /// variants, and also may not use correct path to enum if it's not present in the current scope.
213     ///
214     /// **Example:**
215     /// ```rust
216     /// # enum Foo { A(usize), B(usize) }
217     /// # let x = Foo::B(1);
218     /// match x {
219     ///     A => {},
220     ///     _ => {},
221     /// }
222     /// ```
223     pub WILDCARD_ENUM_MATCH_ARM,
224     restriction,
225     "a wildcard enum match arm using `_`"
226 }
227
228 declare_clippy_lint! {
229     /// **What it does:** Checks for wildcard pattern used with others patterns in same match arm.
230     ///
231     /// **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.
232     /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
233     ///
234     /// **Known problems:** None.
235     ///
236     /// **Example:**
237     /// ```rust
238     /// match "foo" {
239     ///     "a" => {},
240     ///     "bar" | _ => {},
241     /// }
242     /// ```
243     pub WILDCARD_IN_OR_PATTERNS,
244     complexity,
245     "a wildcard pattern used with others patterns in same match arm"
246 }
247
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
275 declare_lint_pass!(Matches => [
276     SINGLE_MATCH,
277     MATCH_REF_PATS,
278     MATCH_BOOL,
279     SINGLE_MATCH_ELSE,
280     MATCH_OVERLAPPING_ARM,
281     MATCH_WILD_ERR_ARM,
282     MATCH_AS_REF,
283     WILDCARD_ENUM_MATCH_ARM,
284     WILDCARD_IN_OR_PATTERNS,
285     MATCH_SINGLE_BINDING,
286 ]);
287
288 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
289     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
290         if in_external_macro(cx.sess(), expr.span) {
291             return;
292         }
293         if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
294             check_single_match(cx, ex, arms, expr);
295             check_match_bool(cx, ex, arms, expr);
296             check_overlapping_arms(cx, ex, arms);
297             check_wild_err_arm(cx, ex, arms);
298             check_wild_enum_match(cx, ex, arms);
299             check_match_as_ref(cx, ex, arms, expr);
300             check_wild_in_or_pats(cx, arms);
301             check_match_single_binding(cx, ex, arms, expr);
302         }
303         if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
304             check_match_ref_pats(cx, ex, arms, expr);
305         }
306     }
307 }
308
309 #[rustfmt::skip]
310 fn check_single_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
311     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
312         if let PatKind::Or(..) = arms[0].pat.kind {
313             // don't lint for or patterns for now, this makes
314             // the lint noisy in unnecessary situations
315             return;
316         }
317         let els = remove_blocks(&arms[1].body);
318         let els = if is_unit_expr(els) {
319             None
320         } else if let ExprKind::Block(_, _) = els.kind {
321             // matches with blocks that contain statements are prettier as `if let + else`
322             Some(els)
323         } else {
324             // allow match arms with just expressions
325             return;
326         };
327         let ty = cx.tables.expr_ty(ex);
328         if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
329             check_single_match_single_pattern(cx, ex, arms, expr, els);
330             check_single_match_opt_like(cx, ex, arms, expr, ty, els);
331         }
332     }
333 }
334
335 fn check_single_match_single_pattern(
336     cx: &LateContext<'_, '_>,
337     ex: &Expr<'_>,
338     arms: &[Arm<'_>],
339     expr: &Expr<'_>,
340     els: Option<&Expr<'_>>,
341 ) {
342     if is_wild(&arms[1].pat) {
343         report_single_match_single_pattern(cx, ex, arms, expr, els);
344     }
345 }
346
347 fn report_single_match_single_pattern(
348     cx: &LateContext<'_, '_>,
349     ex: &Expr<'_>,
350     arms: &[Arm<'_>],
351     expr: &Expr<'_>,
352     els: Option<&Expr<'_>>,
353 ) {
354     let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
355     let els_str = els.map_or(String::new(), |els| {
356         format!(" else {}", expr_block(cx, els, None, ".."))
357     });
358     span_lint_and_sugg(
359         cx,
360         lint,
361         expr.span,
362         "you seem to be trying to use match for destructuring a single pattern. Consider using `if \
363          let`",
364         "try this",
365         format!(
366             "if let {} = {} {}{}",
367             snippet(cx, arms[0].pat.span, ".."),
368             snippet(cx, ex.span, ".."),
369             expr_block(cx, &arms[0].body, None, ".."),
370             els_str,
371         ),
372         Applicability::HasPlaceholders,
373     );
374 }
375
376 fn check_single_match_opt_like(
377     cx: &LateContext<'_, '_>,
378     ex: &Expr<'_>,
379     arms: &[Arm<'_>],
380     expr: &Expr<'_>,
381     ty: Ty<'_>,
382     els: Option<&Expr<'_>>,
383 ) {
384     // list of candidate `Enum`s we know will never get any more members
385     let candidates = &[
386         (&paths::COW, "Borrowed"),
387         (&paths::COW, "Cow::Borrowed"),
388         (&paths::COW, "Cow::Owned"),
389         (&paths::COW, "Owned"),
390         (&paths::OPTION, "None"),
391         (&paths::RESULT, "Err"),
392         (&paths::RESULT, "Ok"),
393     ];
394
395     let path = match arms[1].pat.kind {
396         PatKind::TupleStruct(ref path, ref inner, _) => {
397             // Contains any non wildcard patterns (e.g., `Err(err)`)?
398             if !inner.iter().all(is_wild) {
399                 return;
400             }
401             print::to_string(print::NO_ANN, |s| s.print_qpath(path, false))
402         },
403         PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => ident.to_string(),
404         PatKind::Path(ref path) => print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)),
405         _ => return,
406     };
407
408     for &(ty_path, pat_path) in candidates {
409         if path == *pat_path && match_type(cx, ty, ty_path) {
410             report_single_match_single_pattern(cx, ex, arms, expr, els);
411         }
412     }
413 }
414
415 fn check_match_bool(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
416     // Type of expression is `bool`.
417     if cx.tables.expr_ty(ex).kind == ty::Bool {
418         span_lint_and_then(
419             cx,
420             MATCH_BOOL,
421             expr.span,
422             "you seem to be trying to match on a boolean expression",
423             move |db| {
424                 if arms.len() == 2 {
425                     // no guards
426                     let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pat.kind {
427                         if let ExprKind::Lit(ref lit) = arm_bool.kind {
428                             match lit.node {
429                                 LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
430                                 LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
431                                 _ => None,
432                             }
433                         } else {
434                             None
435                         }
436                     } else {
437                         None
438                     };
439
440                     if let Some((true_expr, false_expr)) = exprs {
441                         let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
442                             (false, false) => Some(format!(
443                                 "if {} {} else {}",
444                                 snippet(cx, ex.span, "b"),
445                                 expr_block(cx, true_expr, None, ".."),
446                                 expr_block(cx, false_expr, None, "..")
447                             )),
448                             (false, true) => Some(format!(
449                                 "if {} {}",
450                                 snippet(cx, ex.span, "b"),
451                                 expr_block(cx, true_expr, None, "..")
452                             )),
453                             (true, false) => {
454                                 let test = Sugg::hir(cx, ex, "..");
455                                 Some(format!("if {} {}", !test, expr_block(cx, false_expr, None, "..")))
456                             },
457                             (true, true) => None,
458                         };
459
460                         if let Some(sugg) = sugg {
461                             db.span_suggestion(
462                                 expr.span,
463                                 "consider using an `if`/`else` expression",
464                                 sugg,
465                                 Applicability::HasPlaceholders,
466                             );
467                         }
468                     }
469                 }
470             },
471         );
472     }
473 }
474
475 fn check_overlapping_arms<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
476     if arms.len() >= 2 && cx.tables.expr_ty(ex).is_integral() {
477         let ranges = all_ranges(cx, arms, cx.tables.expr_ty(ex));
478         let type_ranges = type_ranges(&ranges);
479         if !type_ranges.is_empty() {
480             if let Some((start, end)) = overlapping(&type_ranges) {
481                 span_lint_and_note(
482                     cx,
483                     MATCH_OVERLAPPING_ARM,
484                     start.span,
485                     "some ranges overlap",
486                     end.span,
487                     "overlaps with this",
488                 );
489             }
490         }
491     }
492 }
493
494 fn check_wild_err_arm(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
495     let ex_ty = walk_ptrs_ty(cx.tables.expr_ty(ex));
496     if match_type(cx, ex_ty, &paths::RESULT) {
497         for arm in arms {
498             if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {
499                 let path_str = print::to_string(print::NO_ANN, |s| s.print_qpath(path, false));
500                 if path_str == "Err" {
501                     let mut matching_wild = inner.iter().any(is_wild);
502                     let mut ident_bind_name = String::from("_");
503                     if !matching_wild {
504                         // Looking for unused bindings (i.e.: `_e`)
505                         inner.iter().for_each(|pat| {
506                             if let PatKind::Binding(.., ident, None) = &pat.kind {
507                                 if ident.as_str().starts_with('_') && is_unused(ident, arm.body) {
508                                     ident_bind_name = (&ident.name.as_str()).to_string();
509                                     matching_wild = true;
510                                 }
511                             }
512                         });
513                     }
514                     if_chain! {
515                         if matching_wild;
516                         if let ExprKind::Block(ref block, _) = arm.body.kind;
517                         if is_panic_block(block);
518                         then {
519                             // `Err(_)` or `Err(_e)` arm with `panic!` found
520                             span_lint_and_note(cx,
521                                 MATCH_WILD_ERR_ARM,
522                                 arm.pat.span,
523                                 &format!("`Err({})` matches all errors", &ident_bind_name),
524                                 arm.pat.span,
525                                 "match each error separately or use the error output",
526                             );
527                         }
528                     }
529                 }
530             }
531         }
532     }
533 }
534
535 fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
536     let ty = cx.tables.expr_ty(ex);
537     if !ty.is_enum() {
538         // If there isn't a nice closed set of possible values that can be conveniently enumerated,
539         // don't complain about not enumerating the mall.
540         return;
541     }
542
543     // First pass - check for violation, but don't do much book-keeping because this is hopefully
544     // the uncommon case, and the book-keeping is slightly expensive.
545     let mut wildcard_span = None;
546     let mut wildcard_ident = None;
547     for arm in arms {
548         if let PatKind::Wild = arm.pat.kind {
549             wildcard_span = Some(arm.pat.span);
550         } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind {
551             wildcard_span = Some(arm.pat.span);
552             wildcard_ident = Some(ident);
553         }
554     }
555
556     if let Some(wildcard_span) = wildcard_span {
557         // Accumulate the variants which should be put in place of the wildcard because they're not
558         // already covered.
559
560         let mut missing_variants = vec![];
561         if let ty::Adt(def, _) = ty.kind {
562             for variant in &def.variants {
563                 missing_variants.push(variant);
564             }
565         }
566
567         for arm in arms {
568             if arm.guard.is_some() {
569                 // Guards mean that this case probably isn't exhaustively covered. Technically
570                 // this is incorrect, as we should really check whether each variant is exhaustively
571                 // covered by the set of guards that cover it, but that's really hard to do.
572                 continue;
573             }
574             if let PatKind::Path(ref path) = arm.pat.kind {
575                 if let QPath::Resolved(_, p) = path {
576                     missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
577                 }
578             } else if let PatKind::TupleStruct(ref path, ..) = arm.pat.kind {
579                 if let QPath::Resolved(_, p) = path {
580                     missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
581                 }
582             }
583         }
584
585         let mut suggestion: Vec<String> = missing_variants
586             .iter()
587             .map(|v| {
588                 let suffix = match v.ctor_kind {
589                     CtorKind::Fn => "(..)",
590                     CtorKind::Const | CtorKind::Fictive => "",
591                 };
592                 let ident_str = if let Some(ident) = wildcard_ident {
593                     format!("{} @ ", ident.name)
594                 } else {
595                     String::new()
596                 };
597                 // This path assumes that the enum type is imported into scope.
598                 format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix)
599             })
600             .collect();
601
602         if suggestion.is_empty() {
603             return;
604         }
605
606         let mut message = "wildcard match will miss any future added variants";
607
608         if let ty::Adt(def, _) = ty.kind {
609             if def.is_variant_list_non_exhaustive() {
610                 message = "match on non-exhaustive enum doesn't explicitly match all known variants";
611                 suggestion.push(String::from("_"));
612             }
613         }
614
615         span_lint_and_sugg(
616             cx,
617             WILDCARD_ENUM_MATCH_ARM,
618             wildcard_span,
619             message,
620             "try this",
621             suggestion.join(" | "),
622             Applicability::MachineApplicable,
623         )
624     }
625 }
626
627 // If the block contains only a `panic!` macro (as expression or statement)
628 fn is_panic_block(block: &Block<'_>) -> bool {
629     match (&block.expr, block.stmts.len(), block.stmts.first()) {
630         (&Some(ref exp), 0, _) => {
631             is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none()
632         },
633         (&None, 1, Some(stmt)) => {
634             is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
635         },
636         _ => false,
637     }
638 }
639
640 fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
641     if has_only_ref_pats(arms) {
642         let mut suggs = Vec::new();
643         let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind {
644             let span = ex.span.source_callsite();
645             suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
646             (
647                 "you don't need to add `&` to both the expression and the patterns",
648                 "try",
649             )
650         } else {
651             let span = ex.span.source_callsite();
652             suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
653             (
654                 "you don't need to add `&` to all patterns",
655                 "instead of prefixing all patterns with `&`, you can dereference the expression",
656             )
657         };
658
659         suggs.extend(arms.iter().filter_map(|a| {
660             if let PatKind::Ref(ref refp, _) = a.pat.kind {
661                 Some((a.pat.span, snippet(cx, refp.span, "..").to_string()))
662             } else {
663                 None
664             }
665         }));
666
667         span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |db| {
668             if !expr.span.from_expansion() {
669                 multispan_sugg(db, msg.to_owned(), suggs);
670             }
671         });
672     }
673 }
674
675 fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
676     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
677         let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
678             is_ref_some_arm(&arms[1])
679         } else if is_none_arm(&arms[1]) {
680             is_ref_some_arm(&arms[0])
681         } else {
682             None
683         };
684         if let Some(rb) = arm_ref {
685             let suggestion = if rb == BindingAnnotation::Ref {
686                 "as_ref"
687             } else {
688                 "as_mut"
689             };
690
691             let output_ty = cx.tables.expr_ty(expr);
692             let input_ty = cx.tables.expr_ty(ex);
693
694             let cast = if_chain! {
695                 if let ty::Adt(_, substs) = input_ty.kind;
696                 let input_ty = substs.type_at(0);
697                 if let ty::Adt(_, substs) = output_ty.kind;
698                 let output_ty = substs.type_at(0);
699                 if let ty::Ref(_, output_ty, _) = output_ty.kind;
700                 if input_ty != output_ty;
701                 then {
702                     ".map(|x| x as _)"
703                 } else {
704                     ""
705                 }
706             };
707
708             let mut applicability = Applicability::MachineApplicable;
709             span_lint_and_sugg(
710                 cx,
711                 MATCH_AS_REF,
712                 expr.span,
713                 &format!("use `{}()` instead", suggestion),
714                 "try this",
715                 format!(
716                     "{}.{}(){}",
717                     snippet_with_applicability(cx, ex.span, "_", &mut applicability),
718                     suggestion,
719                     cast,
720                 ),
721                 applicability,
722             )
723         }
724     }
725 }
726
727 fn check_wild_in_or_pats(cx: &LateContext<'_, '_>, arms: &[Arm<'_>]) {
728     for arm in arms {
729         if let PatKind::Or(ref fields) = arm.pat.kind {
730             // look for multiple fields in this arm that contains at least one Wild pattern
731             if fields.len() > 1 && fields.iter().any(is_wild) {
732                 span_lint_and_help(
733                     cx,
734                     WILDCARD_IN_OR_PATTERNS,
735                     arm.pat.span,
736                     "wildcard pattern covers any other pattern as it will match anyway.",
737                     "Consider handling `_` separately.",
738                 );
739             }
740         }
741     }
742 }
743
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
767 /// Gets all arms that are unbounded `PatRange`s.
768 fn all_ranges<'a, 'tcx>(
769     cx: &LateContext<'a, 'tcx>,
770     arms: &'tcx [Arm<'_>],
771     ty: Ty<'tcx>,
772 ) -> Vec<SpannedRange<Constant>> {
773     arms.iter()
774         .flat_map(|arm| {
775             if let Arm {
776                 ref pat, guard: None, ..
777             } = *arm
778             {
779                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
780                     let lhs = match lhs {
781                         Some(lhs) => constant(cx, cx.tables, lhs)?.0,
782                         None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
783                     };
784                     let rhs = match rhs {
785                         Some(rhs) => constant(cx, cx.tables, rhs)?.0,
786                         None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
787                     };
788                     let rhs = match range_end {
789                         RangeEnd::Included => Bound::Included(rhs),
790                         RangeEnd::Excluded => Bound::Excluded(rhs),
791                     };
792                     return Some(SpannedRange {
793                         span: pat.span,
794                         node: (lhs, rhs),
795                     });
796                 }
797
798                 if let PatKind::Lit(ref value) = pat.kind {
799                     let value = constant(cx, cx.tables, value)?.0;
800                     return Some(SpannedRange {
801                         span: pat.span,
802                         node: (value.clone(), Bound::Included(value)),
803                     });
804                 }
805             }
806             None
807         })
808         .collect()
809 }
810
811 #[derive(Debug, Eq, PartialEq)]
812 pub struct SpannedRange<T> {
813     pub span: Span,
814     pub node: (T, Bound<T>),
815 }
816
817 type TypedRanges = Vec<SpannedRange<u128>>;
818
819 /// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
820 /// and other types than
821 /// `Uint` and `Int` probably don't make sense.
822 fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
823     ranges
824         .iter()
825         .filter_map(|range| match range.node {
826             (Constant::Int(start), Bound::Included(Constant::Int(end))) => Some(SpannedRange {
827                 span: range.span,
828                 node: (start, Bound::Included(end)),
829             }),
830             (Constant::Int(start), Bound::Excluded(Constant::Int(end))) => Some(SpannedRange {
831                 span: range.span,
832                 node: (start, Bound::Excluded(end)),
833             }),
834             (Constant::Int(start), Bound::Unbounded) => Some(SpannedRange {
835                 span: range.span,
836                 node: (start, Bound::Unbounded),
837             }),
838             _ => None,
839         })
840         .collect()
841 }
842
843 fn is_unit_expr(expr: &Expr<'_>) -> bool {
844     match expr.kind {
845         ExprKind::Tup(ref v) if v.is_empty() => true,
846         ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
847         _ => false,
848     }
849 }
850
851 // Checks if arm has the form `None => None`
852 fn is_none_arm(arm: &Arm<'_>) -> bool {
853     match arm.pat.kind {
854         PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true,
855         _ => false,
856     }
857 }
858
859 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
860 fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
861     if_chain! {
862         if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind;
863         if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
864         if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
865         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
866         if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind;
867         if let ExprKind::Path(ref some_path) = e.kind;
868         if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
869         if let ExprKind::Path(ref qpath) = args[0].kind;
870         if let &QPath::Resolved(_, ref path2) = qpath;
871         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
872         then {
873             return Some(rb)
874         }
875     }
876     None
877 }
878
879 fn has_only_ref_pats(arms: &[Arm<'_>]) -> bool {
880     let mapped = arms
881         .iter()
882         .map(|a| {
883             match a.pat.kind {
884                 PatKind::Ref(..) => Some(true), // &-patterns
885                 PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
886                 _ => None,                      // any other pattern is not fine
887             }
888         })
889         .collect::<Option<Vec<bool>>>();
890     // look for Some(v) where there's at least one true element
891     mapped.map_or(false, |v| v.iter().any(|el| *el))
892 }
893
894 pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
895 where
896     T: Copy + Ord,
897 {
898     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
899     enum Kind<'a, T> {
900         Start(T, &'a SpannedRange<T>),
901         End(Bound<T>, &'a SpannedRange<T>),
902     }
903
904     impl<'a, T: Copy> Kind<'a, T> {
905         fn range(&self) -> &'a SpannedRange<T> {
906             match *self {
907                 Kind::Start(_, r) | Kind::End(_, r) => r,
908             }
909         }
910
911         fn value(self) -> Bound<T> {
912             match self {
913                 Kind::Start(t, _) => Bound::Included(t),
914                 Kind::End(t, _) => t,
915             }
916         }
917     }
918
919     impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
920         fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
921             Some(self.cmp(other))
922         }
923     }
924
925     impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
926         fn cmp(&self, other: &Self) -> Ordering {
927             match (self.value(), other.value()) {
928                 (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
929                 // Range patterns cannot be unbounded (yet)
930                 (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
931                 (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
932                     Ordering::Equal => Ordering::Greater,
933                     other => other,
934                 },
935                 (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
936                     Ordering::Equal => Ordering::Less,
937                     other => other,
938                 },
939             }
940         }
941     }
942
943     let mut values = Vec::with_capacity(2 * ranges.len());
944
945     for r in ranges {
946         values.push(Kind::Start(r.node.0, r));
947         values.push(Kind::End(r.node.1, r));
948     }
949
950     values.sort();
951
952     for (a, b) in values.iter().zip(values.iter().skip(1)) {
953         match (a, b) {
954             (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
955                 if ra.node != rb.node {
956                     return Some((ra, rb));
957                 }
958             },
959             (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
960             _ => return Some((a.range(), b.range())),
961         }
962     }
963
964     None
965 }