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