]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/matches.rs
e4335abcea2a4518815d94deef7ef518f4a37634
[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_block, 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_tool_lint, impl_lint_pass};
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 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
283 declare_clippy_lint! {
284     /// **What it does:** Checks for useless match that binds to only one value.
285     ///
286     /// **Why is this bad?** Readability and needless complexity.
287     ///
288     /// **Known problems:** None.
289     ///
290     /// **Example:**
291     /// ```rust
292     /// # let a = 1;
293     /// # let b = 2;
294     ///
295     /// // Bad
296     /// match (a, b) {
297     ///     (c, d) => {
298     ///         // useless match
299     ///     }
300     /// }
301     ///
302     /// // Good
303     /// let (c, d) = (a, b);
304     /// ```
305     pub MATCH_SINGLE_BINDING,
306     complexity,
307     "a match with a single binding instead of using `let` statement"
308 }
309
310 #[derive(Default)]
311 pub struct Matches {
312     infallible_destructuring_match_linted: bool,
313 }
314
315 impl_lint_pass!(Matches => [
316     SINGLE_MATCH,
317     MATCH_REF_PATS,
318     MATCH_BOOL,
319     SINGLE_MATCH_ELSE,
320     MATCH_OVERLAPPING_ARM,
321     MATCH_WILD_ERR_ARM,
322     MATCH_AS_REF,
323     WILDCARD_ENUM_MATCH_ARM,
324     WILDCARD_IN_OR_PATTERNS,
325     MATCH_SINGLE_BINDING,
326     INFALLIBLE_DESTRUCTURING_MATCH
327 ]);
328
329 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
330     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
331         if in_external_macro(cx.sess(), expr.span) {
332             return;
333         }
334         if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
335             check_single_match(cx, ex, arms, expr);
336             check_match_bool(cx, ex, arms, expr);
337             check_overlapping_arms(cx, ex, arms);
338             check_wild_err_arm(cx, ex, arms);
339             check_wild_enum_match(cx, ex, arms);
340             check_match_as_ref(cx, ex, arms, expr);
341             check_wild_in_or_pats(cx, arms);
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             }
348         }
349         if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
350             check_match_ref_pats(cx, ex, arms, expr);
351         }
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     }
387 }
388
389 #[rustfmt::skip]
390 fn check_single_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
391     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
392         if let PatKind::Or(..) = arms[0].pat.kind {
393             // don't lint for or patterns for now, this makes
394             // the lint noisy in unnecessary situations
395             return;
396         }
397         let els = remove_blocks(&arms[1].body);
398         let els = if is_unit_expr(els) {
399             None
400         } else if let ExprKind::Block(_, _) = els.kind {
401             // matches with blocks that contain statements are prettier as `if let + else`
402             Some(els)
403         } else {
404             // allow match arms with just expressions
405             return;
406         };
407         let ty = cx.tables.expr_ty(ex);
408         if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
409             check_single_match_single_pattern(cx, ex, arms, expr, els);
410             check_single_match_opt_like(cx, ex, arms, expr, ty, els);
411         }
412     }
413 }
414
415 fn check_single_match_single_pattern(
416     cx: &LateContext<'_, '_>,
417     ex: &Expr<'_>,
418     arms: &[Arm<'_>],
419     expr: &Expr<'_>,
420     els: Option<&Expr<'_>>,
421 ) {
422     if is_wild(&arms[1].pat) {
423         report_single_match_single_pattern(cx, ex, arms, expr, els);
424     }
425 }
426
427 fn report_single_match_single_pattern(
428     cx: &LateContext<'_, '_>,
429     ex: &Expr<'_>,
430     arms: &[Arm<'_>],
431     expr: &Expr<'_>,
432     els: Option<&Expr<'_>>,
433 ) {
434     let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
435     let els_str = els.map_or(String::new(), |els| {
436         format!(" else {}", expr_block(cx, els, None, ".."))
437     });
438     span_lint_and_sugg(
439         cx,
440         lint,
441         expr.span,
442         "you seem to be trying to use match for destructuring a single pattern. Consider using `if \
443          let`",
444         "try this",
445         format!(
446             "if let {} = {} {}{}",
447             snippet(cx, arms[0].pat.span, ".."),
448             snippet(cx, ex.span, ".."),
449             expr_block(cx, &arms[0].body, None, ".."),
450             els_str,
451         ),
452         Applicability::HasPlaceholders,
453     );
454 }
455
456 fn check_single_match_opt_like(
457     cx: &LateContext<'_, '_>,
458     ex: &Expr<'_>,
459     arms: &[Arm<'_>],
460     expr: &Expr<'_>,
461     ty: Ty<'_>,
462     els: Option<&Expr<'_>>,
463 ) {
464     // list of candidate `Enum`s we know will never get any more members
465     let candidates = &[
466         (&paths::COW, "Borrowed"),
467         (&paths::COW, "Cow::Borrowed"),
468         (&paths::COW, "Cow::Owned"),
469         (&paths::COW, "Owned"),
470         (&paths::OPTION, "None"),
471         (&paths::RESULT, "Err"),
472         (&paths::RESULT, "Ok"),
473     ];
474
475     let path = match arms[1].pat.kind {
476         PatKind::TupleStruct(ref path, ref inner, _) => {
477             // Contains any non wildcard patterns (e.g., `Err(err)`)?
478             if !inner.iter().all(is_wild) {
479                 return;
480             }
481             print::to_string(print::NO_ANN, |s| s.print_qpath(path, false))
482         },
483         PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => ident.to_string(),
484         PatKind::Path(ref path) => print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)),
485         _ => return,
486     };
487
488     for &(ty_path, pat_path) in candidates {
489         if path == *pat_path && match_type(cx, ty, ty_path) {
490             report_single_match_single_pattern(cx, ex, arms, expr, els);
491         }
492     }
493 }
494
495 fn check_match_bool(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
496     // Type of expression is `bool`.
497     if cx.tables.expr_ty(ex).kind == ty::Bool {
498         span_lint_and_then(
499             cx,
500             MATCH_BOOL,
501             expr.span,
502             "you seem to be trying to match on a boolean expression",
503             move |db| {
504                 if arms.len() == 2 {
505                     // no guards
506                     let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pat.kind {
507                         if let ExprKind::Lit(ref lit) = arm_bool.kind {
508                             match lit.node {
509                                 LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
510                                 LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
511                                 _ => None,
512                             }
513                         } else {
514                             None
515                         }
516                     } else {
517                         None
518                     };
519
520                     if let Some((true_expr, false_expr)) = exprs {
521                         let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
522                             (false, false) => Some(format!(
523                                 "if {} {} else {}",
524                                 snippet(cx, ex.span, "b"),
525                                 expr_block(cx, true_expr, None, ".."),
526                                 expr_block(cx, false_expr, None, "..")
527                             )),
528                             (false, true) => Some(format!(
529                                 "if {} {}",
530                                 snippet(cx, ex.span, "b"),
531                                 expr_block(cx, true_expr, None, "..")
532                             )),
533                             (true, false) => {
534                                 let test = Sugg::hir(cx, ex, "..");
535                                 Some(format!("if {} {}", !test, expr_block(cx, false_expr, None, "..")))
536                             },
537                             (true, true) => None,
538                         };
539
540                         if let Some(sugg) = sugg {
541                             db.span_suggestion(
542                                 expr.span,
543                                 "consider using an `if`/`else` expression",
544                                 sugg,
545                                 Applicability::HasPlaceholders,
546                             );
547                         }
548                     }
549                 }
550             },
551         );
552     }
553 }
554
555 fn check_overlapping_arms<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
556     if arms.len() >= 2 && cx.tables.expr_ty(ex).is_integral() {
557         let ranges = all_ranges(cx, arms, cx.tables.expr_ty(ex));
558         let type_ranges = type_ranges(&ranges);
559         if !type_ranges.is_empty() {
560             if let Some((start, end)) = overlapping(&type_ranges) {
561                 span_lint_and_note(
562                     cx,
563                     MATCH_OVERLAPPING_ARM,
564                     start.span,
565                     "some ranges overlap",
566                     end.span,
567                     "overlaps with this",
568                 );
569             }
570         }
571     }
572 }
573
574 fn check_wild_err_arm(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
575     let ex_ty = walk_ptrs_ty(cx.tables.expr_ty(ex));
576     if match_type(cx, ex_ty, &paths::RESULT) {
577         for arm in arms {
578             if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {
579                 let path_str = print::to_string(print::NO_ANN, |s| s.print_qpath(path, false));
580                 if path_str == "Err" {
581                     let mut matching_wild = inner.iter().any(is_wild);
582                     let mut ident_bind_name = String::from("_");
583                     if !matching_wild {
584                         // Looking for unused bindings (i.e.: `_e`)
585                         inner.iter().for_each(|pat| {
586                             if let PatKind::Binding(.., ident, None) = &pat.kind {
587                                 if ident.as_str().starts_with('_') && is_unused(ident, arm.body) {
588                                     ident_bind_name = (&ident.name.as_str()).to_string();
589                                     matching_wild = true;
590                                 }
591                             }
592                         });
593                     }
594                     if_chain! {
595                         if matching_wild;
596                         if let ExprKind::Block(ref block, _) = arm.body.kind;
597                         if is_panic_block(block);
598                         then {
599                             // `Err(_)` or `Err(_e)` arm with `panic!` found
600                             span_lint_and_note(cx,
601                                 MATCH_WILD_ERR_ARM,
602                                 arm.pat.span,
603                                 &format!("`Err({})` matches all errors", &ident_bind_name),
604                                 arm.pat.span,
605                                 "match each error separately or use the error output",
606                             );
607                         }
608                     }
609                 }
610             }
611         }
612     }
613 }
614
615 fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
616     let ty = cx.tables.expr_ty(ex);
617     if !ty.is_enum() {
618         // If there isn't a nice closed set of possible values that can be conveniently enumerated,
619         // don't complain about not enumerating the mall.
620         return;
621     }
622
623     // First pass - check for violation, but don't do much book-keeping because this is hopefully
624     // the uncommon case, and the book-keeping is slightly expensive.
625     let mut wildcard_span = None;
626     let mut wildcard_ident = None;
627     for arm in arms {
628         if let PatKind::Wild = arm.pat.kind {
629             wildcard_span = Some(arm.pat.span);
630         } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind {
631             wildcard_span = Some(arm.pat.span);
632             wildcard_ident = Some(ident);
633         }
634     }
635
636     if let Some(wildcard_span) = wildcard_span {
637         // Accumulate the variants which should be put in place of the wildcard because they're not
638         // already covered.
639
640         let mut missing_variants = vec![];
641         if let ty::Adt(def, _) = ty.kind {
642             for variant in &def.variants {
643                 missing_variants.push(variant);
644             }
645         }
646
647         for arm in arms {
648             if arm.guard.is_some() {
649                 // Guards mean that this case probably isn't exhaustively covered. Technically
650                 // this is incorrect, as we should really check whether each variant is exhaustively
651                 // covered by the set of guards that cover it, but that's really hard to do.
652                 continue;
653             }
654             if let PatKind::Path(ref path) = arm.pat.kind {
655                 if let QPath::Resolved(_, p) = path {
656                     missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
657                 }
658             } else if let PatKind::TupleStruct(ref path, ..) = arm.pat.kind {
659                 if let QPath::Resolved(_, p) = path {
660                     missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
661                 }
662             }
663         }
664
665         let mut suggestion: Vec<String> = missing_variants
666             .iter()
667             .map(|v| {
668                 let suffix = match v.ctor_kind {
669                     CtorKind::Fn => "(..)",
670                     CtorKind::Const | CtorKind::Fictive => "",
671                 };
672                 let ident_str = if let Some(ident) = wildcard_ident {
673                     format!("{} @ ", ident.name)
674                 } else {
675                     String::new()
676                 };
677                 // This path assumes that the enum type is imported into scope.
678                 format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix)
679             })
680             .collect();
681
682         if suggestion.is_empty() {
683             return;
684         }
685
686         let mut message = "wildcard match will miss any future added variants";
687
688         if let ty::Adt(def, _) = ty.kind {
689             if def.is_variant_list_non_exhaustive() {
690                 message = "match on non-exhaustive enum doesn't explicitly match all known variants";
691                 suggestion.push(String::from("_"));
692             }
693         }
694
695         span_lint_and_sugg(
696             cx,
697             WILDCARD_ENUM_MATCH_ARM,
698             wildcard_span,
699             message,
700             "try this",
701             suggestion.join(" | "),
702             Applicability::MachineApplicable,
703         )
704     }
705 }
706
707 // If the block contains only a `panic!` macro (as expression or statement)
708 fn is_panic_block(block: &Block<'_>) -> bool {
709     match (&block.expr, block.stmts.len(), block.stmts.first()) {
710         (&Some(ref exp), 0, _) => {
711             is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none()
712         },
713         (&None, 1, Some(stmt)) => {
714             is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
715         },
716         _ => false,
717     }
718 }
719
720 fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
721     if has_only_ref_pats(arms) {
722         let mut suggs = Vec::new();
723         let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind {
724             let span = ex.span.source_callsite();
725             suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
726             (
727                 "you don't need to add `&` to both the expression and the patterns",
728                 "try",
729             )
730         } else {
731             let span = ex.span.source_callsite();
732             suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
733             (
734                 "you don't need to add `&` to all patterns",
735                 "instead of prefixing all patterns with `&`, you can dereference the expression",
736             )
737         };
738
739         suggs.extend(arms.iter().filter_map(|a| {
740             if let PatKind::Ref(ref refp, _) = a.pat.kind {
741                 Some((a.pat.span, snippet(cx, refp.span, "..").to_string()))
742             } else {
743                 None
744             }
745         }));
746
747         span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |db| {
748             if !expr.span.from_expansion() {
749                 multispan_sugg(db, msg.to_owned(), suggs);
750             }
751         });
752     }
753 }
754
755 fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
756     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
757         let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
758             is_ref_some_arm(&arms[1])
759         } else if is_none_arm(&arms[1]) {
760             is_ref_some_arm(&arms[0])
761         } else {
762             None
763         };
764         if let Some(rb) = arm_ref {
765             let suggestion = if rb == BindingAnnotation::Ref {
766                 "as_ref"
767             } else {
768                 "as_mut"
769             };
770
771             let output_ty = cx.tables.expr_ty(expr);
772             let input_ty = cx.tables.expr_ty(ex);
773
774             let cast = if_chain! {
775                 if let ty::Adt(_, substs) = input_ty.kind;
776                 let input_ty = substs.type_at(0);
777                 if let ty::Adt(_, substs) = output_ty.kind;
778                 let output_ty = substs.type_at(0);
779                 if let ty::Ref(_, output_ty, _) = output_ty.kind;
780                 if input_ty != output_ty;
781                 then {
782                     ".map(|x| x as _)"
783                 } else {
784                     ""
785                 }
786             };
787
788             let mut applicability = Applicability::MachineApplicable;
789             span_lint_and_sugg(
790                 cx,
791                 MATCH_AS_REF,
792                 expr.span,
793                 &format!("use `{}()` instead", suggestion),
794                 "try this",
795                 format!(
796                     "{}.{}(){}",
797                     snippet_with_applicability(cx, ex.span, "_", &mut applicability),
798                     suggestion,
799                     cast,
800                 ),
801                 applicability,
802             )
803         }
804     }
805 }
806
807 fn check_wild_in_or_pats(cx: &LateContext<'_, '_>, arms: &[Arm<'_>]) {
808     for arm in arms {
809         if let PatKind::Or(ref fields) = arm.pat.kind {
810             // look for multiple fields in this arm that contains at least one Wild pattern
811             if fields.len() > 1 && fields.iter().any(is_wild) {
812                 span_lint_and_help(
813                     cx,
814                     WILDCARD_IN_OR_PATTERNS,
815                     arm.pat.span,
816                     "wildcard pattern covers any other pattern as it will match anyway.",
817                     "Consider handling `_` separately.",
818                 );
819             }
820         }
821     }
822 }
823
824 fn check_match_single_binding(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
825     if in_macro(expr.span) {
826         return;
827     }
828     if arms.len() == 1 {
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         }
854     }
855 }
856
857 /// Gets all arms that are unbounded `PatRange`s.
858 fn all_ranges<'a, 'tcx>(
859     cx: &LateContext<'a, 'tcx>,
860     arms: &'tcx [Arm<'_>],
861     ty: Ty<'tcx>,
862 ) -> Vec<SpannedRange<Constant>> {
863     arms.iter()
864         .flat_map(|arm| {
865             if let Arm {
866                 ref pat, guard: None, ..
867             } = *arm
868             {
869                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
870                     let lhs = match lhs {
871                         Some(lhs) => constant(cx, cx.tables, lhs)?.0,
872                         None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
873                     };
874                     let rhs = match rhs {
875                         Some(rhs) => constant(cx, cx.tables, rhs)?.0,
876                         None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
877                     };
878                     let rhs = match range_end {
879                         RangeEnd::Included => Bound::Included(rhs),
880                         RangeEnd::Excluded => Bound::Excluded(rhs),
881                     };
882                     return Some(SpannedRange {
883                         span: pat.span,
884                         node: (lhs, rhs),
885                     });
886                 }
887
888                 if let PatKind::Lit(ref value) = pat.kind {
889                     let value = constant(cx, cx.tables, value)?.0;
890                     return Some(SpannedRange {
891                         span: pat.span,
892                         node: (value.clone(), Bound::Included(value)),
893                     });
894                 }
895             }
896             None
897         })
898         .collect()
899 }
900
901 #[derive(Debug, Eq, PartialEq)]
902 pub struct SpannedRange<T> {
903     pub span: Span,
904     pub node: (T, Bound<T>),
905 }
906
907 type TypedRanges = Vec<SpannedRange<u128>>;
908
909 /// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
910 /// and other types than
911 /// `Uint` and `Int` probably don't make sense.
912 fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
913     ranges
914         .iter()
915         .filter_map(|range| match range.node {
916             (Constant::Int(start), Bound::Included(Constant::Int(end))) => Some(SpannedRange {
917                 span: range.span,
918                 node: (start, Bound::Included(end)),
919             }),
920             (Constant::Int(start), Bound::Excluded(Constant::Int(end))) => Some(SpannedRange {
921                 span: range.span,
922                 node: (start, Bound::Excluded(end)),
923             }),
924             (Constant::Int(start), Bound::Unbounded) => Some(SpannedRange {
925                 span: range.span,
926                 node: (start, Bound::Unbounded),
927             }),
928             _ => None,
929         })
930         .collect()
931 }
932
933 fn is_unit_expr(expr: &Expr<'_>) -> bool {
934     match expr.kind {
935         ExprKind::Tup(ref v) if v.is_empty() => true,
936         ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
937         _ => false,
938     }
939 }
940
941 // Checks if arm has the form `None => None`
942 fn is_none_arm(arm: &Arm<'_>) -> bool {
943     match arm.pat.kind {
944         PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true,
945         _ => false,
946     }
947 }
948
949 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
950 fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
951     if_chain! {
952         if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind;
953         if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
954         if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
955         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
956         if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind;
957         if let ExprKind::Path(ref some_path) = e.kind;
958         if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
959         if let ExprKind::Path(ref qpath) = args[0].kind;
960         if let &QPath::Resolved(_, ref path2) = qpath;
961         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
962         then {
963             return Some(rb)
964         }
965     }
966     None
967 }
968
969 fn has_only_ref_pats(arms: &[Arm<'_>]) -> bool {
970     let mapped = arms
971         .iter()
972         .map(|a| {
973             match a.pat.kind {
974                 PatKind::Ref(..) => Some(true), // &-patterns
975                 PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
976                 _ => None,                      // any other pattern is not fine
977             }
978         })
979         .collect::<Option<Vec<bool>>>();
980     // look for Some(v) where there's at least one true element
981     mapped.map_or(false, |v| v.iter().any(|el| *el))
982 }
983
984 pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
985 where
986     T: Copy + Ord,
987 {
988     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
989     enum Kind<'a, T> {
990         Start(T, &'a SpannedRange<T>),
991         End(Bound<T>, &'a SpannedRange<T>),
992     }
993
994     impl<'a, T: Copy> Kind<'a, T> {
995         fn range(&self) -> &'a SpannedRange<T> {
996             match *self {
997                 Kind::Start(_, r) | Kind::End(_, r) => r,
998             }
999         }
1000
1001         fn value(self) -> Bound<T> {
1002             match self {
1003                 Kind::Start(t, _) => Bound::Included(t),
1004                 Kind::End(t, _) => t,
1005             }
1006         }
1007     }
1008
1009     impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
1010         fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1011             Some(self.cmp(other))
1012         }
1013     }
1014
1015     impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
1016         fn cmp(&self, other: &Self) -> Ordering {
1017             match (self.value(), other.value()) {
1018                 (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
1019                 // Range patterns cannot be unbounded (yet)
1020                 (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
1021                 (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
1022                     Ordering::Equal => Ordering::Greater,
1023                     other => other,
1024                 },
1025                 (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
1026                     Ordering::Equal => Ordering::Less,
1027                     other => other,
1028                 },
1029             }
1030         }
1031     }
1032
1033     let mut values = Vec::with_capacity(2 * ranges.len());
1034
1035     for r in ranges {
1036         values.push(Kind::Start(r.node.0, r));
1037         values.push(Kind::End(r.node.1, r));
1038     }
1039
1040     values.sort();
1041
1042     for (a, b) in values.iter().zip(values.iter().skip(1)) {
1043         match (a, b) {
1044             (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
1045                 if ra.node != rb.node {
1046                     return Some((ra, rb));
1047                 }
1048             },
1049             (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
1050             _ => return Some((a.range(), b.range())),
1051         }
1052     }
1053
1054     None
1055 }