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