]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/matches.rs
Manage macros case + move to MaybeIncorrect when binding values
[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, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, match_type,
7     match_var, multispan_sugg, remove_blocks, snippet, snippet_block, snippet_with_applicability, span_lint_and_help,
8     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, ".."))
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, ".."),
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, ".."),
527                                 expr_block(cx, false_expr, None, "..")
528                             )),
529                             (false, true) => Some(format!(
530                                 "if {} {}",
531                                 snippet(cx, ex.span, "b"),
532                                 expr_block(cx, true_expr, None, "..")
533                             )),
534                             (true, false) => {
535                                 let test = Sugg::hir(cx, ex, "..");
536                                 Some(format!("if {} {}", !test, expr_block(cx, false_expr, None, "..")))
537                             },
538                             (true, true) => None,
539                         };
540
541                         if let Some(sugg) = sugg {
542                             db.span_suggestion(
543                                 expr.span,
544                                 "consider using an `if`/`else` expression",
545                                 sugg,
546                                 Applicability::HasPlaceholders,
547                             );
548                         }
549                     }
550                 }
551             },
552         );
553     }
554 }
555
556 fn check_overlapping_arms<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
557     if arms.len() >= 2 && cx.tables.expr_ty(ex).is_integral() {
558         let ranges = all_ranges(cx, arms, cx.tables.expr_ty(ex));
559         let type_ranges = type_ranges(&ranges);
560         if !type_ranges.is_empty() {
561             if let Some((start, end)) = overlapping(&type_ranges) {
562                 span_lint_and_note(
563                     cx,
564                     MATCH_OVERLAPPING_ARM,
565                     start.span,
566                     "some ranges overlap",
567                     end.span,
568                     "overlaps with this",
569                 );
570             }
571         }
572     }
573 }
574
575 fn check_wild_err_arm(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
576     let ex_ty = walk_ptrs_ty(cx.tables.expr_ty(ex));
577     if match_type(cx, ex_ty, &paths::RESULT) {
578         for arm in arms {
579             if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {
580                 let path_str = print::to_string(print::NO_ANN, |s| s.print_qpath(path, false));
581                 if path_str == "Err" {
582                     let mut matching_wild = inner.iter().any(is_wild);
583                     let mut ident_bind_name = String::from("_");
584                     if !matching_wild {
585                         // Looking for unused bindings (i.e.: `_e`)
586                         inner.iter().for_each(|pat| {
587                             if let PatKind::Binding(.., ident, None) = &pat.kind {
588                                 if ident.as_str().starts_with('_') && is_unused(ident, arm.body) {
589                                     ident_bind_name = (&ident.name.as_str()).to_string();
590                                     matching_wild = true;
591                                 }
592                             }
593                         });
594                     }
595                     if_chain! {
596                         if matching_wild;
597                         if let ExprKind::Block(ref block, _) = arm.body.kind;
598                         if is_panic_block(block);
599                         then {
600                             // `Err(_)` or `Err(_e)` arm with `panic!` found
601                             span_lint_and_note(cx,
602                                 MATCH_WILD_ERR_ARM,
603                                 arm.pat.span,
604                                 &format!("`Err({})` matches all errors", &ident_bind_name),
605                                 arm.pat.span,
606                                 "match each error separately or use the error output",
607                             );
608                         }
609                     }
610                 }
611             }
612         }
613     }
614 }
615
616 fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
617     let ty = cx.tables.expr_ty(ex);
618     if !ty.is_enum() {
619         // If there isn't a nice closed set of possible values that can be conveniently enumerated,
620         // don't complain about not enumerating the mall.
621         return;
622     }
623
624     // First pass - check for violation, but don't do much book-keeping because this is hopefully
625     // the uncommon case, and the book-keeping is slightly expensive.
626     let mut wildcard_span = None;
627     let mut wildcard_ident = None;
628     for arm in arms {
629         if let PatKind::Wild = arm.pat.kind {
630             wildcard_span = Some(arm.pat.span);
631         } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind {
632             wildcard_span = Some(arm.pat.span);
633             wildcard_ident = Some(ident);
634         }
635     }
636
637     if let Some(wildcard_span) = wildcard_span {
638         // Accumulate the variants which should be put in place of the wildcard because they're not
639         // already covered.
640
641         let mut missing_variants = vec![];
642         if let ty::Adt(def, _) = ty.kind {
643             for variant in &def.variants {
644                 missing_variants.push(variant);
645             }
646         }
647
648         for arm in arms {
649             if arm.guard.is_some() {
650                 // Guards mean that this case probably isn't exhaustively covered. Technically
651                 // this is incorrect, as we should really check whether each variant is exhaustively
652                 // covered by the set of guards that cover it, but that's really hard to do.
653                 continue;
654             }
655             if let PatKind::Path(ref path) = arm.pat.kind {
656                 if let QPath::Resolved(_, p) = path {
657                     missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
658                 }
659             } else if let PatKind::TupleStruct(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             }
664         }
665
666         let mut suggestion: Vec<String> = missing_variants
667             .iter()
668             .map(|v| {
669                 let suffix = match v.ctor_kind {
670                     CtorKind::Fn => "(..)",
671                     CtorKind::Const | CtorKind::Fictive => "",
672                 };
673                 let ident_str = if let Some(ident) = wildcard_ident {
674                     format!("{} @ ", ident.name)
675                 } else {
676                     String::new()
677                 };
678                 // This path assumes that the enum type is imported into scope.
679                 format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix)
680             })
681             .collect();
682
683         if suggestion.is_empty() {
684             return;
685         }
686
687         let mut message = "wildcard match will miss any future added variants";
688
689         if let ty::Adt(def, _) = ty.kind {
690             if def.is_variant_list_non_exhaustive() {
691                 message = "match on non-exhaustive enum doesn't explicitly match all known variants";
692                 suggestion.push(String::from("_"));
693             }
694         }
695
696         span_lint_and_sugg(
697             cx,
698             WILDCARD_ENUM_MATCH_ARM,
699             wildcard_span,
700             message,
701             "try this",
702             suggestion.join(" | "),
703             Applicability::MachineApplicable,
704         )
705     }
706 }
707
708 // If the block contains only a `panic!` macro (as expression or statement)
709 fn is_panic_block(block: &Block<'_>) -> bool {
710     match (&block.expr, block.stmts.len(), block.stmts.first()) {
711         (&Some(ref exp), 0, _) => {
712             is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none()
713         },
714         (&None, 1, Some(stmt)) => {
715             is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
716         },
717         _ => false,
718     }
719 }
720
721 fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
722     if has_only_ref_pats(arms) {
723         let mut suggs = Vec::new();
724         let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind {
725             let span = ex.span.source_callsite();
726             suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
727             (
728                 "you don't need to add `&` to both the expression and the patterns",
729                 "try",
730             )
731         } else {
732             let span = ex.span.source_callsite();
733             suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
734             (
735                 "you don't need to add `&` to all patterns",
736                 "instead of prefixing all patterns with `&`, you can dereference the expression",
737             )
738         };
739
740         suggs.extend(arms.iter().filter_map(|a| {
741             if let PatKind::Ref(ref refp, _) = a.pat.kind {
742                 Some((a.pat.span, snippet(cx, refp.span, "..").to_string()))
743             } else {
744                 None
745             }
746         }));
747
748         span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |db| {
749             if !expr.span.from_expansion() {
750                 multispan_sugg(db, msg.to_owned(), suggs);
751             }
752         });
753     }
754 }
755
756 fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
757     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
758         let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
759             is_ref_some_arm(&arms[1])
760         } else if is_none_arm(&arms[1]) {
761             is_ref_some_arm(&arms[0])
762         } else {
763             None
764         };
765         if let Some(rb) = arm_ref {
766             let suggestion = if rb == BindingAnnotation::Ref {
767                 "as_ref"
768             } else {
769                 "as_mut"
770             };
771
772             let output_ty = cx.tables.expr_ty(expr);
773             let input_ty = cx.tables.expr_ty(ex);
774
775             let cast = if_chain! {
776                 if let ty::Adt(_, substs) = input_ty.kind;
777                 let input_ty = substs.type_at(0);
778                 if let ty::Adt(_, substs) = output_ty.kind;
779                 let output_ty = substs.type_at(0);
780                 if let ty::Ref(_, output_ty, _) = output_ty.kind;
781                 if input_ty != output_ty;
782                 then {
783                     ".map(|x| x as _)"
784                 } else {
785                     ""
786                 }
787             };
788
789             let mut applicability = Applicability::MachineApplicable;
790             span_lint_and_sugg(
791                 cx,
792                 MATCH_AS_REF,
793                 expr.span,
794                 &format!("use `{}()` instead", suggestion),
795                 "try this",
796                 format!(
797                     "{}.{}(){}",
798                     snippet_with_applicability(cx, ex.span, "_", &mut applicability),
799                     suggestion,
800                     cast,
801                 ),
802                 applicability,
803             )
804         }
805     }
806 }
807
808 fn check_wild_in_or_pats(cx: &LateContext<'_, '_>, arms: &[Arm<'_>]) {
809     for arm in arms {
810         if let PatKind::Or(ref fields) = arm.pat.kind {
811             // look for multiple fields in this arm that contains at least one Wild pattern
812             if fields.len() > 1 && fields.iter().any(is_wild) {
813                 span_lint_and_help(
814                     cx,
815                     WILDCARD_IN_OR_PATTERNS,
816                     arm.pat.span,
817                     "wildcard pattern covers any other pattern as it will match anyway.",
818                     "Consider handling `_` separately.",
819                 );
820             }
821         }
822     }
823 }
824
825 fn check_match_single_binding(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
826     if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
827         return;
828     }
829     let matched_vars = ex.span;
830     let bind_names = arms[0].pat.span;
831     let match_body = remove_blocks(&arms[0].body);
832     let mut snippet_body = if match_body.span.from_expansion() {
833         Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
834     } else {
835         snippet_block(cx, match_body.span, "..").to_owned().to_string()
836     };
837
838     // Do we need to add ';' to suggestion ?
839     match match_body.kind {
840         ExprKind::Block(block, _) => {
841             // macro + expr_ty(body) == ()
842             if block.span.from_expansion() && cx.tables.expr_ty(&match_body).is_unit() {
843                 snippet_body.push(';');
844             }
845         },
846         _ => {
847             // expr_ty(body) == ()
848             if cx.tables.expr_ty(&match_body).is_unit() {
849                 snippet_body.push(';');
850             }
851         },
852     }
853
854     let mut applicability = Applicability::MaybeIncorrect;
855     match arms[0].pat.kind {
856         PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
857             span_lint_and_sugg(
858                 cx,
859                 MATCH_SINGLE_BINDING,
860                 expr.span,
861                 "this match could be written as a `let` statement",
862                 "consider using `let` statement",
863                 format!(
864                     "let {} = {};\n{}",
865                     snippet_with_applicability(cx, bind_names, "..", &mut applicability),
866                     snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
867                     snippet_body
868                 ),
869                 applicability,
870             );
871         },
872         PatKind::Wild => {
873             span_lint_and_sugg(
874                 cx,
875                 MATCH_SINGLE_BINDING,
876                 expr.span,
877                 "this match could be replaced by its body itself",
878                 "consider using the match body instead",
879                 snippet_body,
880                 Applicability::MachineApplicable,
881             );
882         },
883         _ => (),
884     }
885 }
886
887 /// Gets all arms that are unbounded `PatRange`s.
888 fn all_ranges<'a, 'tcx>(
889     cx: &LateContext<'a, 'tcx>,
890     arms: &'tcx [Arm<'_>],
891     ty: Ty<'tcx>,
892 ) -> Vec<SpannedRange<Constant>> {
893     arms.iter()
894         .flat_map(|arm| {
895             if let Arm {
896                 ref pat, guard: None, ..
897             } = *arm
898             {
899                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
900                     let lhs = match lhs {
901                         Some(lhs) => constant(cx, cx.tables, lhs)?.0,
902                         None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
903                     };
904                     let rhs = match rhs {
905                         Some(rhs) => constant(cx, cx.tables, rhs)?.0,
906                         None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
907                     };
908                     let rhs = match range_end {
909                         RangeEnd::Included => Bound::Included(rhs),
910                         RangeEnd::Excluded => Bound::Excluded(rhs),
911                     };
912                     return Some(SpannedRange {
913                         span: pat.span,
914                         node: (lhs, rhs),
915                     });
916                 }
917
918                 if let PatKind::Lit(ref value) = pat.kind {
919                     let value = constant(cx, cx.tables, value)?.0;
920                     return Some(SpannedRange {
921                         span: pat.span,
922                         node: (value.clone(), Bound::Included(value)),
923                     });
924                 }
925             }
926             None
927         })
928         .collect()
929 }
930
931 #[derive(Debug, Eq, PartialEq)]
932 pub struct SpannedRange<T> {
933     pub span: Span,
934     pub node: (T, Bound<T>),
935 }
936
937 type TypedRanges = Vec<SpannedRange<u128>>;
938
939 /// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
940 /// and other types than
941 /// `Uint` and `Int` probably don't make sense.
942 fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
943     ranges
944         .iter()
945         .filter_map(|range| match range.node {
946             (Constant::Int(start), Bound::Included(Constant::Int(end))) => Some(SpannedRange {
947                 span: range.span,
948                 node: (start, Bound::Included(end)),
949             }),
950             (Constant::Int(start), Bound::Excluded(Constant::Int(end))) => Some(SpannedRange {
951                 span: range.span,
952                 node: (start, Bound::Excluded(end)),
953             }),
954             (Constant::Int(start), Bound::Unbounded) => Some(SpannedRange {
955                 span: range.span,
956                 node: (start, Bound::Unbounded),
957             }),
958             _ => None,
959         })
960         .collect()
961 }
962
963 fn is_unit_expr(expr: &Expr<'_>) -> bool {
964     match expr.kind {
965         ExprKind::Tup(ref v) if v.is_empty() => true,
966         ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
967         _ => false,
968     }
969 }
970
971 // Checks if arm has the form `None => None`
972 fn is_none_arm(arm: &Arm<'_>) -> bool {
973     match arm.pat.kind {
974         PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE) => true,
975         _ => false,
976     }
977 }
978
979 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
980 fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
981     if_chain! {
982         if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind;
983         if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
984         if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
985         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
986         if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind;
987         if let ExprKind::Path(ref some_path) = e.kind;
988         if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
989         if let ExprKind::Path(ref qpath) = args[0].kind;
990         if let &QPath::Resolved(_, ref path2) = qpath;
991         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
992         then {
993             return Some(rb)
994         }
995     }
996     None
997 }
998
999 fn has_only_ref_pats(arms: &[Arm<'_>]) -> bool {
1000     let mapped = arms
1001         .iter()
1002         .map(|a| {
1003             match a.pat.kind {
1004                 PatKind::Ref(..) => Some(true), // &-patterns
1005                 PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
1006                 _ => None,                      // any other pattern is not fine
1007             }
1008         })
1009         .collect::<Option<Vec<bool>>>();
1010     // look for Some(v) where there's at least one true element
1011     mapped.map_or(false, |v| v.iter().any(|el| *el))
1012 }
1013
1014 pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
1015 where
1016     T: Copy + Ord,
1017 {
1018     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
1019     enum Kind<'a, T> {
1020         Start(T, &'a SpannedRange<T>),
1021         End(Bound<T>, &'a SpannedRange<T>),
1022     }
1023
1024     impl<'a, T: Copy> Kind<'a, T> {
1025         fn range(&self) -> &'a SpannedRange<T> {
1026             match *self {
1027                 Kind::Start(_, r) | Kind::End(_, r) => r,
1028             }
1029         }
1030
1031         fn value(self) -> Bound<T> {
1032             match self {
1033                 Kind::Start(t, _) => Bound::Included(t),
1034                 Kind::End(t, _) => t,
1035             }
1036         }
1037     }
1038
1039     impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
1040         fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1041             Some(self.cmp(other))
1042         }
1043     }
1044
1045     impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
1046         fn cmp(&self, other: &Self) -> Ordering {
1047             match (self.value(), other.value()) {
1048                 (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
1049                 // Range patterns cannot be unbounded (yet)
1050                 (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
1051                 (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
1052                     Ordering::Equal => Ordering::Greater,
1053                     other => other,
1054                 },
1055                 (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
1056                     Ordering::Equal => Ordering::Less,
1057                     other => other,
1058                 },
1059             }
1060         }
1061     }
1062
1063     let mut values = Vec::with_capacity(2 * ranges.len());
1064
1065     for r in ranges {
1066         values.push(Kind::Start(r.node.0, r));
1067         values.push(Kind::End(r.node.1, r));
1068     }
1069
1070     values.sort();
1071
1072     for (a, b) in values.iter().zip(values.iter().skip(1)) {
1073         match (a, b) {
1074             (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
1075                 if ra.node != rb.node {
1076                     return Some((ra, rb));
1077                 }
1078             },
1079             (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
1080             _ => return Some((a.range(), b.range())),
1081         }
1082     }
1083
1084     None
1085 }