]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/matches.rs
Rollup merge of #74418 - rye:gha-dedup-shell-setting, r=pietroalbini
[rust.git] / src / tools / clippy / 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, get_parent_expr, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
7     is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, multispan_sugg, remove_blocks, snippet,
8     snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
9     span_lint_and_then, walk_ptrs_ty,
10 };
11 use if_chain::if_chain;
12 use rustc_ast::ast::LitKind;
13 use rustc_errors::Applicability;
14 use rustc_hir::def::CtorKind;
15 use rustc_hir::{
16     Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, Local, MatchSource, Mutability, Node, Pat,
17     PatKind, QPath, RangeEnd,
18 };
19 use rustc_lint::{LateContext, LateLintPass, LintContext};
20 use rustc_middle::lint::in_external_macro;
21 use rustc_middle::ty::{self, Ty};
22 use rustc_session::{declare_tool_lint, impl_lint_pass};
23 use rustc_span::source_map::{Span, Spanned};
24 use std::cmp::Ordering;
25 use std::collections::Bound;
26
27 declare_clippy_lint! {
28     /// **What it does:** Checks for matches with a single arm where an `if let`
29     /// will usually suffice.
30     ///
31     /// **Why is this bad?** Just readability – `if let` nests less than a `match`.
32     ///
33     /// **Known problems:** None.
34     ///
35     /// **Example:**
36     /// ```rust
37     /// # fn bar(stool: &str) {}
38     /// # let x = Some("abc");
39     ///
40     /// // Bad
41     /// match x {
42     ///     Some(ref foo) => bar(foo),
43     ///     _ => (),
44     /// }
45     ///
46     /// // Good
47     /// if let Some(ref foo) = x {
48     ///     bar(foo);
49     /// }
50     /// ```
51     pub SINGLE_MATCH,
52     style,
53     "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
54 }
55
56 declare_clippy_lint! {
57     /// **What it does:** Checks for matches with two arms where an `if let else` will
58     /// usually suffice.
59     ///
60     /// **Why is this bad?** Just readability – `if let` nests less than a `match`.
61     ///
62     /// **Known problems:** Personal style preferences may differ.
63     ///
64     /// **Example:**
65     ///
66     /// Using `match`:
67     ///
68     /// ```rust
69     /// # fn bar(foo: &usize) {}
70     /// # let other_ref: usize = 1;
71     /// # let x: Option<&usize> = Some(&1);
72     /// match x {
73     ///     Some(ref foo) => bar(foo),
74     ///     _ => bar(&other_ref),
75     /// }
76     /// ```
77     ///
78     /// Using `if let` with `else`:
79     ///
80     /// ```rust
81     /// # fn bar(foo: &usize) {}
82     /// # let other_ref: usize = 1;
83     /// # let x: Option<&usize> = Some(&1);
84     /// if let Some(ref foo) = x {
85     ///     bar(foo);
86     /// } else {
87     ///     bar(&other_ref);
88     /// }
89     /// ```
90     pub SINGLE_MATCH_ELSE,
91     pedantic,
92     "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
93 }
94
95 declare_clippy_lint! {
96     /// **What it does:** Checks for matches where all arms match a reference,
97     /// suggesting to remove the reference and deref the matched expression
98     /// instead. It also checks for `if let &foo = bar` blocks.
99     ///
100     /// **Why is this bad?** It just makes the code less readable. That reference
101     /// destructuring adds nothing to the code.
102     ///
103     /// **Known problems:** None.
104     ///
105     /// **Example:**
106     /// ```rust,ignore
107     /// // Bad
108     /// match x {
109     ///     &A(ref y) => foo(y),
110     ///     &B => bar(),
111     ///     _ => frob(&x),
112     /// }
113     ///
114     /// // Good
115     /// match *x {
116     ///     A(ref y) => foo(y),
117     ///     B => bar(),
118     ///     _ => frob(x),
119     /// }
120     /// ```
121     pub MATCH_REF_PATS,
122     style,
123     "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
124 }
125
126 declare_clippy_lint! {
127     /// **What it does:** Checks for matches where match expression is a `bool`. It
128     /// suggests to replace the expression with an `if...else` block.
129     ///
130     /// **Why is this bad?** It makes the code less readable.
131     ///
132     /// **Known problems:** None.
133     ///
134     /// **Example:**
135     /// ```rust
136     /// # fn foo() {}
137     /// # fn bar() {}
138     /// let condition: bool = true;
139     /// match condition {
140     ///     true => foo(),
141     ///     false => bar(),
142     /// }
143     /// ```
144     /// Use if/else instead:
145     /// ```rust
146     /// # fn foo() {}
147     /// # fn bar() {}
148     /// let condition: bool = true;
149     /// if condition {
150     ///     foo();
151     /// } else {
152     ///     bar();
153     /// }
154     /// ```
155     pub MATCH_BOOL,
156     pedantic,
157     "a `match` on a boolean expression instead of an `if..else` block"
158 }
159
160 declare_clippy_lint! {
161     /// **What it does:** Checks for overlapping match arms.
162     ///
163     /// **Why is this bad?** It is likely to be an error and if not, makes the code
164     /// less obvious.
165     ///
166     /// **Known problems:** None.
167     ///
168     /// **Example:**
169     /// ```rust
170     /// let x = 5;
171     /// match x {
172     ///     1...10 => println!("1 ... 10"),
173     ///     5...15 => println!("5 ... 15"),
174     ///     _ => (),
175     /// }
176     /// ```
177     pub MATCH_OVERLAPPING_ARM,
178     style,
179     "a `match` with overlapping arms"
180 }
181
182 declare_clippy_lint! {
183     /// **What it does:** Checks for arm which matches all errors with `Err(_)`
184     /// and take drastic actions like `panic!`.
185     ///
186     /// **Why is this bad?** It is generally a bad practice, similar to
187     /// catching all exceptions in java with `catch(Exception)`
188     ///
189     /// **Known problems:** None.
190     ///
191     /// **Example:**
192     /// ```rust
193     /// let x: Result<i32, &str> = Ok(3);
194     /// match x {
195     ///     Ok(_) => println!("ok"),
196     ///     Err(_) => panic!("err"),
197     /// }
198     /// ```
199     pub MATCH_WILD_ERR_ARM,
200     pedantic,
201     "a `match` with `Err(_)` arm and take drastic actions"
202 }
203
204 declare_clippy_lint! {
205     /// **What it does:** Checks for match which is used to add a reference to an
206     /// `Option` value.
207     ///
208     /// **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.
209     ///
210     /// **Known problems:** None.
211     ///
212     /// **Example:**
213     /// ```rust
214     /// let x: Option<()> = None;
215     ///
216     /// // Bad
217     /// let r: Option<&()> = match x {
218     ///     None => None,
219     ///     Some(ref v) => Some(v),
220     /// };
221     ///
222     /// // Good
223     /// let r: Option<&()> = x.as_ref();
224     /// ```
225     pub MATCH_AS_REF,
226     complexity,
227     "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
228 }
229
230 declare_clippy_lint! {
231     /// **What it does:** Checks for wildcard enum matches using `_`.
232     ///
233     /// **Why is this bad?** New enum variants added by library updates can be missed.
234     ///
235     /// **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some
236     /// variants, and also may not use correct path to enum if it's not present in the current scope.
237     ///
238     /// **Example:**
239     /// ```rust
240     /// # enum Foo { A(usize), B(usize) }
241     /// # let x = Foo::B(1);
242     ///
243     /// // Bad
244     /// match x {
245     ///     Foo::A(_) => {},
246     ///     _ => {},
247     /// }
248     ///
249     /// // Good
250     /// match x {
251     ///     Foo::A(_) => {},
252     ///     Foo::B(_) => {},
253     /// }
254     /// ```
255     pub WILDCARD_ENUM_MATCH_ARM,
256     restriction,
257     "a wildcard enum match arm using `_`"
258 }
259
260 declare_clippy_lint! {
261     /// **What it does:** Checks for wildcard enum matches for a single variant.
262     ///
263     /// **Why is this bad?** New enum variants added by library updates can be missed.
264     ///
265     /// **Known problems:** Suggested replacements may not use correct path to enum
266     /// if it's not present in the current scope.
267     ///
268     /// **Example:**
269     ///
270     /// ```rust
271     /// # enum Foo { A, B, C }
272     /// # let x = Foo::B;
273     /// // Bad
274     /// match x {
275     ///     Foo::A => {},
276     ///     Foo::B => {},
277     ///     _ => {},
278     /// }
279     ///
280     /// // Good
281     /// match x {
282     ///     Foo::A => {},
283     ///     Foo::B => {},
284     ///     Foo::C => {},
285     /// }
286     /// ```
287     pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
288     pedantic,
289     "a wildcard enum match for a single variant"
290 }
291
292 declare_clippy_lint! {
293     /// **What it does:** Checks for wildcard pattern used with others patterns in same match arm.
294     ///
295     /// **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.
296     /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
297     ///
298     /// **Known problems:** None.
299     ///
300     /// **Example:**
301     /// ```rust
302     /// // Bad
303     /// match "foo" {
304     ///     "a" => {},
305     ///     "bar" | _ => {},
306     /// }
307     ///
308     /// // Good
309     /// match "foo" {
310     ///     "a" => {},
311     ///     _ => {},
312     /// }
313     /// ```
314     pub WILDCARD_IN_OR_PATTERNS,
315     complexity,
316     "a wildcard pattern used with others patterns in same match arm"
317 }
318
319 declare_clippy_lint! {
320     /// **What it does:** Checks for matches being used to destructure a single-variant enum
321     /// or tuple struct where a `let` will suffice.
322     ///
323     /// **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.
324     ///
325     /// **Known problems:** None.
326     ///
327     /// **Example:**
328     /// ```rust
329     /// enum Wrapper {
330     ///     Data(i32),
331     /// }
332     ///
333     /// let wrapper = Wrapper::Data(42);
334     ///
335     /// let data = match wrapper {
336     ///     Wrapper::Data(i) => i,
337     /// };
338     /// ```
339     ///
340     /// The correct use would be:
341     /// ```rust
342     /// enum Wrapper {
343     ///     Data(i32),
344     /// }
345     ///
346     /// let wrapper = Wrapper::Data(42);
347     /// let Wrapper::Data(data) = wrapper;
348     /// ```
349     pub INFALLIBLE_DESTRUCTURING_MATCH,
350     style,
351     "a `match` statement with a single infallible arm instead of a `let`"
352 }
353
354 declare_clippy_lint! {
355     /// **What it does:** Checks for useless match that binds to only one value.
356     ///
357     /// **Why is this bad?** Readability and needless complexity.
358     ///
359     /// **Known problems:**  Suggested replacements may be incorrect when `match`
360     /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
361     ///
362     /// **Example:**
363     /// ```rust
364     /// # let a = 1;
365     /// # let b = 2;
366     ///
367     /// // Bad
368     /// match (a, b) {
369     ///     (c, d) => {
370     ///         // useless match
371     ///     }
372     /// }
373     ///
374     /// // Good
375     /// let (c, d) = (a, b);
376     /// ```
377     pub MATCH_SINGLE_BINDING,
378     complexity,
379     "a match with a single binding instead of using `let` statement"
380 }
381
382 declare_clippy_lint! {
383     /// **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
384     ///
385     /// **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after
386     /// matching all enum variants explicitly.
387     ///
388     /// **Known problems:** None.
389     ///
390     /// **Example:**
391     /// ```rust
392     /// # struct A { a: i32 }
393     /// let a = A { a: 5 };
394     ///
395     /// // Bad
396     /// match a {
397     ///     A { a: 5, .. } => {},
398     ///     _ => {},
399     /// }
400     ///
401     /// // Good
402     /// match a {
403     ///     A { a: 5 } => {},
404     ///     _ => {},
405     /// }
406     /// ```
407     pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
408     restriction,
409     "a match on a struct that binds all fields but still uses the wildcard pattern"
410 }
411
412 declare_clippy_lint! {
413     /// **What it does:** Lint for redundant pattern matching over `Result` or
414     /// `Option`
415     ///
416     /// **Why is this bad?** It's more concise and clear to just use the proper
417     /// utility function
418     ///
419     /// **Known problems:** None.
420     ///
421     /// **Example:**
422     ///
423     /// ```rust
424     /// if let Ok(_) = Ok::<i32, i32>(42) {}
425     /// if let Err(_) = Err::<i32, i32>(42) {}
426     /// if let None = None::<()> {}
427     /// if let Some(_) = Some(42) {}
428     /// match Ok::<i32, i32>(42) {
429     ///     Ok(_) => true,
430     ///     Err(_) => false,
431     /// };
432     /// ```
433     ///
434     /// The more idiomatic use would be:
435     ///
436     /// ```rust
437     /// if Ok::<i32, i32>(42).is_ok() {}
438     /// if Err::<i32, i32>(42).is_err() {}
439     /// if None::<()>.is_none() {}
440     /// if Some(42).is_some() {}
441     /// Ok::<i32, i32>(42).is_ok();
442     /// ```
443     pub REDUNDANT_PATTERN_MATCHING,
444     style,
445     "use the proper utility function avoiding an `if let`"
446 }
447
448 declare_clippy_lint! {
449     /// **What it does:** Checks for `match`  or `if let` expressions producing a
450     /// `bool` that could be written using `matches!`
451     ///
452     /// **Why is this bad?** Readability and needless complexity.
453     ///
454     /// **Known problems:** None
455     ///
456     /// **Example:**
457     /// ```rust
458     /// let x = Some(5);
459     ///
460     /// // Bad
461     /// let a = match x {
462     ///     Some(0) => true,
463     ///     _ => false,
464     /// };
465     ///
466     /// let a = if let Some(0) = x {
467     ///     true
468     /// } else {
469     ///     false
470     /// };
471     ///
472     /// // Good
473     /// let a = matches!(x, Some(0));
474     /// ```
475     pub MATCH_LIKE_MATCHES_MACRO,
476     style,
477     "a match that could be written with the matches! macro"
478 }
479
480 #[derive(Default)]
481 pub struct Matches {
482     infallible_destructuring_match_linted: bool,
483 }
484
485 impl_lint_pass!(Matches => [
486     SINGLE_MATCH,
487     MATCH_REF_PATS,
488     MATCH_BOOL,
489     SINGLE_MATCH_ELSE,
490     MATCH_OVERLAPPING_ARM,
491     MATCH_WILD_ERR_ARM,
492     MATCH_AS_REF,
493     WILDCARD_ENUM_MATCH_ARM,
494     MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
495     WILDCARD_IN_OR_PATTERNS,
496     MATCH_SINGLE_BINDING,
497     INFALLIBLE_DESTRUCTURING_MATCH,
498     REST_PAT_IN_FULLY_BOUND_STRUCTS,
499     REDUNDANT_PATTERN_MATCHING,
500     MATCH_LIKE_MATCHES_MACRO
501 ]);
502
503 impl<'tcx> LateLintPass<'tcx> for Matches {
504     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
505         if in_external_macro(cx.sess(), expr.span) {
506             return;
507         }
508
509         redundant_pattern_match::check(cx, expr);
510         check_match_like_matches(cx, expr);
511
512         if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
513             check_single_match(cx, ex, arms, expr);
514             check_match_bool(cx, ex, arms, expr);
515             check_overlapping_arms(cx, ex, arms);
516             check_wild_err_arm(cx, ex, arms);
517             check_wild_enum_match(cx, ex, arms);
518             check_match_as_ref(cx, ex, arms, expr);
519             check_wild_in_or_pats(cx, arms);
520
521             if self.infallible_destructuring_match_linted {
522                 self.infallible_destructuring_match_linted = false;
523             } else {
524                 check_match_single_binding(cx, ex, arms, expr);
525             }
526         }
527         if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
528             check_match_ref_pats(cx, ex, arms, expr);
529         }
530     }
531
532     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
533         if_chain! {
534             if !in_external_macro(cx.sess(), local.span);
535             if !in_macro(local.span);
536             if let Some(ref expr) = local.init;
537             if let ExprKind::Match(ref target, ref arms, MatchSource::Normal) = expr.kind;
538             if arms.len() == 1 && arms[0].guard.is_none();
539             if let PatKind::TupleStruct(
540                 QPath::Resolved(None, ref variant_name), ref args, _) = arms[0].pat.kind;
541             if args.len() == 1;
542             if let Some(arg) = get_arg_name(&args[0]);
543             let body = remove_blocks(&arms[0].body);
544             if match_var(body, arg);
545
546             then {
547                 let mut applicability = Applicability::MachineApplicable;
548                 self.infallible_destructuring_match_linted = true;
549                 span_lint_and_sugg(
550                     cx,
551                     INFALLIBLE_DESTRUCTURING_MATCH,
552                     local.span,
553                     "you seem to be trying to use `match` to destructure a single infallible pattern. \
554                     Consider using `let`",
555                     "try this",
556                     format!(
557                         "let {}({}) = {};",
558                         snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
559                         snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
560                         snippet_with_applicability(cx, target.span, "..", &mut applicability),
561                     ),
562                     applicability,
563                 );
564             }
565         }
566     }
567
568     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
569         if_chain! {
570             if !in_external_macro(cx.sess(), pat.span);
571             if !in_macro(pat.span);
572             if let PatKind::Struct(ref qpath, fields, true) = pat.kind;
573             if let QPath::Resolved(_, ref path) = qpath;
574             if let Some(def_id) = path.res.opt_def_id();
575             let ty = cx.tcx.type_of(def_id);
576             if let ty::Adt(def, _) = ty.kind;
577             if def.is_struct() || def.is_union();
578             if fields.len() == def.non_enum_variant().fields.len();
579
580             then {
581                 span_lint_and_help(
582                     cx,
583                     REST_PAT_IN_FULLY_BOUND_STRUCTS,
584                     pat.span,
585                     "unnecessary use of `..` pattern in struct binding. All fields were already bound",
586                     None,
587                     "consider removing `..` from this binding",
588                 );
589             }
590         }
591     }
592 }
593
594 #[rustfmt::skip]
595 fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
596     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
597         if in_macro(expr.span) {
598             // Don't lint match expressions present in
599             // macro_rules! block
600             return;
601         }
602         if let PatKind::Or(..) = arms[0].pat.kind {
603             // don't lint for or patterns for now, this makes
604             // the lint noisy in unnecessary situations
605             return;
606         }
607         let els = arms[1].body;
608         let els = if is_unit_expr(remove_blocks(els)) {
609             None
610         } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
611             if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
612                 // single statement/expr "else" block, don't lint
613                 return;
614             } else {
615                 // block with 2+ statements or 1 expr and 1+ statement
616                 Some(els)
617             }
618         } else {
619             // not a block, don't lint
620             return; 
621         };
622
623         let ty = cx.typeck_results().expr_ty(ex);
624         if ty.kind != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
625             check_single_match_single_pattern(cx, ex, arms, expr, els);
626             check_single_match_opt_like(cx, ex, arms, expr, ty, els);
627         }
628     }
629 }
630
631 fn check_single_match_single_pattern(
632     cx: &LateContext<'_>,
633     ex: &Expr<'_>,
634     arms: &[Arm<'_>],
635     expr: &Expr<'_>,
636     els: Option<&Expr<'_>>,
637 ) {
638     if is_wild(&arms[1].pat) {
639         report_single_match_single_pattern(cx, ex, arms, expr, els);
640     }
641 }
642
643 fn report_single_match_single_pattern(
644     cx: &LateContext<'_>,
645     ex: &Expr<'_>,
646     arms: &[Arm<'_>],
647     expr: &Expr<'_>,
648     els: Option<&Expr<'_>>,
649 ) {
650     let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
651     let els_str = els.map_or(String::new(), |els| {
652         format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
653     });
654     span_lint_and_sugg(
655         cx,
656         lint,
657         expr.span,
658         "you seem to be trying to use match for destructuring a single pattern. Consider using `if \
659          let`",
660         "try this",
661         format!(
662             "if let {} = {} {}{}",
663             snippet(cx, arms[0].pat.span, ".."),
664             snippet(cx, ex.span, ".."),
665             expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
666             els_str,
667         ),
668         Applicability::HasPlaceholders,
669     );
670 }
671
672 fn check_single_match_opt_like(
673     cx: &LateContext<'_>,
674     ex: &Expr<'_>,
675     arms: &[Arm<'_>],
676     expr: &Expr<'_>,
677     ty: Ty<'_>,
678     els: Option<&Expr<'_>>,
679 ) {
680     // list of candidate `Enum`s we know will never get any more members
681     let candidates = &[
682         (&paths::COW, "Borrowed"),
683         (&paths::COW, "Cow::Borrowed"),
684         (&paths::COW, "Cow::Owned"),
685         (&paths::COW, "Owned"),
686         (&paths::OPTION, "None"),
687         (&paths::RESULT, "Err"),
688         (&paths::RESULT, "Ok"),
689     ];
690
691     let path = match arms[1].pat.kind {
692         PatKind::TupleStruct(ref path, ref inner, _) => {
693             // Contains any non wildcard patterns (e.g., `Err(err)`)?
694             if !inner.iter().all(is_wild) {
695                 return;
696             }
697             rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
698         },
699         PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => ident.to_string(),
700         PatKind::Path(ref path) => {
701             rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
702         },
703         _ => return,
704     };
705
706     for &(ty_path, pat_path) in candidates {
707         if path == *pat_path && match_type(cx, ty, ty_path) {
708             report_single_match_single_pattern(cx, ex, arms, expr, els);
709         }
710     }
711 }
712
713 fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
714     // Type of expression is `bool`.
715     if cx.typeck_results().expr_ty(ex).kind == ty::Bool {
716         span_lint_and_then(
717             cx,
718             MATCH_BOOL,
719             expr.span,
720             "you seem to be trying to match on a boolean expression",
721             move |diag| {
722                 if arms.len() == 2 {
723                     // no guards
724                     let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pat.kind {
725                         if let ExprKind::Lit(ref lit) = arm_bool.kind {
726                             match lit.node {
727                                 LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
728                                 LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
729                                 _ => None,
730                             }
731                         } else {
732                             None
733                         }
734                     } else {
735                         None
736                     };
737
738                     if let Some((true_expr, false_expr)) = exprs {
739                         let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
740                             (false, false) => Some(format!(
741                                 "if {} {} else {}",
742                                 snippet(cx, ex.span, "b"),
743                                 expr_block(cx, true_expr, None, "..", Some(expr.span)),
744                                 expr_block(cx, false_expr, None, "..", Some(expr.span))
745                             )),
746                             (false, true) => Some(format!(
747                                 "if {} {}",
748                                 snippet(cx, ex.span, "b"),
749                                 expr_block(cx, true_expr, None, "..", Some(expr.span))
750                             )),
751                             (true, false) => {
752                                 let test = Sugg::hir(cx, ex, "..");
753                                 Some(format!(
754                                     "if {} {}",
755                                     !test,
756                                     expr_block(cx, false_expr, None, "..", Some(expr.span))
757                                 ))
758                             },
759                             (true, true) => None,
760                         };
761
762                         if let Some(sugg) = sugg {
763                             diag.span_suggestion(
764                                 expr.span,
765                                 "consider using an `if`/`else` expression",
766                                 sugg,
767                                 Applicability::HasPlaceholders,
768                             );
769                         }
770                     }
771                 }
772             },
773         );
774     }
775 }
776
777 fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
778     if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() {
779         let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex));
780         let type_ranges = type_ranges(&ranges);
781         if !type_ranges.is_empty() {
782             if let Some((start, end)) = overlapping(&type_ranges) {
783                 span_lint_and_note(
784                     cx,
785                     MATCH_OVERLAPPING_ARM,
786                     start.span,
787                     "some ranges overlap",
788                     Some(end.span),
789                     "overlaps with this",
790                 );
791             }
792         }
793     }
794 }
795
796 fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
797     let ex_ty = walk_ptrs_ty(cx.typeck_results().expr_ty(ex));
798     if is_type_diagnostic_item(cx, ex_ty, sym!(result_type)) {
799         for arm in arms {
800             if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {
801                 let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
802                 if path_str == "Err" {
803                     let mut matching_wild = inner.iter().any(is_wild);
804                     let mut ident_bind_name = String::from("_");
805                     if !matching_wild {
806                         // Looking for unused bindings (i.e.: `_e`)
807                         inner.iter().for_each(|pat| {
808                             if let PatKind::Binding(.., ident, None) = &pat.kind {
809                                 if ident.as_str().starts_with('_') && is_unused(ident, arm.body) {
810                                     ident_bind_name = (&ident.name.as_str()).to_string();
811                                     matching_wild = true;
812                                 }
813                             }
814                         });
815                     }
816                     if_chain! {
817                         if matching_wild;
818                         if let ExprKind::Block(ref block, _) = arm.body.kind;
819                         if is_panic_block(block);
820                         then {
821                             // `Err(_)` or `Err(_e)` arm with `panic!` found
822                             span_lint_and_note(cx,
823                                 MATCH_WILD_ERR_ARM,
824                                 arm.pat.span,
825                                 &format!("`Err({})` matches all errors", &ident_bind_name),
826                                 None,
827                                 "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
828                             );
829                         }
830                     }
831                 }
832             }
833         }
834     }
835 }
836
837 fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
838     let ty = cx.typeck_results().expr_ty(ex);
839     if !ty.is_enum() {
840         // If there isn't a nice closed set of possible values that can be conveniently enumerated,
841         // don't complain about not enumerating the mall.
842         return;
843     }
844
845     // First pass - check for violation, but don't do much book-keeping because this is hopefully
846     // the uncommon case, and the book-keeping is slightly expensive.
847     let mut wildcard_span = None;
848     let mut wildcard_ident = None;
849     for arm in arms {
850         if let PatKind::Wild = arm.pat.kind {
851             wildcard_span = Some(arm.pat.span);
852         } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind {
853             wildcard_span = Some(arm.pat.span);
854             wildcard_ident = Some(ident);
855         }
856     }
857
858     if let Some(wildcard_span) = wildcard_span {
859         // Accumulate the variants which should be put in place of the wildcard because they're not
860         // already covered.
861
862         let mut missing_variants = vec![];
863         if let ty::Adt(def, _) = ty.kind {
864             for variant in &def.variants {
865                 missing_variants.push(variant);
866             }
867         }
868
869         for arm in arms {
870             if arm.guard.is_some() {
871                 // Guards mean that this case probably isn't exhaustively covered. Technically
872                 // this is incorrect, as we should really check whether each variant is exhaustively
873                 // covered by the set of guards that cover it, but that's really hard to do.
874                 continue;
875             }
876             if let PatKind::Path(ref path) = arm.pat.kind {
877                 if let QPath::Resolved(_, p) = path {
878                     missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
879                 }
880             } else if let PatKind::TupleStruct(ref path, ref patterns, ..) = arm.pat.kind {
881                 if let QPath::Resolved(_, p) = path {
882                     // Some simple checks for exhaustive patterns.
883                     // There is a room for improvements to detect more cases,
884                     // but it can be more expensive to do so.
885                     let is_pattern_exhaustive =
886                         |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
887                     if patterns.iter().all(is_pattern_exhaustive) {
888                         missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
889                     }
890                 }
891             }
892         }
893
894         let mut suggestion: Vec<String> = missing_variants
895             .iter()
896             .map(|v| {
897                 let suffix = match v.ctor_kind {
898                     CtorKind::Fn => "(..)",
899                     CtorKind::Const | CtorKind::Fictive => "",
900                 };
901                 let ident_str = if let Some(ident) = wildcard_ident {
902                     format!("{} @ ", ident.name)
903                 } else {
904                     String::new()
905                 };
906                 // This path assumes that the enum type is imported into scope.
907                 format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix)
908             })
909             .collect();
910
911         if suggestion.is_empty() {
912             return;
913         }
914
915         let mut message = "wildcard match will miss any future added variants";
916
917         if let ty::Adt(def, _) = ty.kind {
918             if def.is_variant_list_non_exhaustive() {
919                 message = "match on non-exhaustive enum doesn't explicitly match all known variants";
920                 suggestion.push(String::from("_"));
921             }
922         }
923
924         if suggestion.len() == 1 {
925             // No need to check for non-exhaustive enum as in that case len would be greater than 1
926             span_lint_and_sugg(
927                 cx,
928                 MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
929                 wildcard_span,
930                 message,
931                 "try this",
932                 suggestion[0].clone(),
933                 Applicability::MaybeIncorrect,
934             )
935         };
936
937         span_lint_and_sugg(
938             cx,
939             WILDCARD_ENUM_MATCH_ARM,
940             wildcard_span,
941             message,
942             "try this",
943             suggestion.join(" | "),
944             Applicability::MaybeIncorrect,
945         )
946     }
947 }
948
949 // If the block contains only a `panic!` macro (as expression or statement)
950 fn is_panic_block(block: &Block<'_>) -> bool {
951     match (&block.expr, block.stmts.len(), block.stmts.first()) {
952         (&Some(ref exp), 0, _) => {
953             is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none()
954         },
955         (&None, 1, Some(stmt)) => {
956             is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
957         },
958         _ => false,
959     }
960 }
961
962 fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
963     if has_only_ref_pats(arms) {
964         let mut suggs = Vec::with_capacity(arms.len() + 1);
965         let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind {
966             let span = ex.span.source_callsite();
967             suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
968             (
969                 "you don't need to add `&` to both the expression and the patterns",
970                 "try",
971             )
972         } else {
973             let span = ex.span.source_callsite();
974             suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
975             (
976                 "you don't need to add `&` to all patterns",
977                 "instead of prefixing all patterns with `&`, you can dereference the expression",
978             )
979         };
980
981         suggs.extend(arms.iter().filter_map(|a| {
982             if let PatKind::Ref(ref refp, _) = a.pat.kind {
983                 Some((a.pat.span, snippet(cx, refp.span, "..").to_string()))
984             } else {
985                 None
986             }
987         }));
988
989         span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
990             if !expr.span.from_expansion() {
991                 multispan_sugg(diag, msg, suggs);
992             }
993         });
994     }
995 }
996
997 fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
998     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
999         let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
1000             is_ref_some_arm(&arms[1])
1001         } else if is_none_arm(&arms[1]) {
1002             is_ref_some_arm(&arms[0])
1003         } else {
1004             None
1005         };
1006         if let Some(rb) = arm_ref {
1007             let suggestion = if rb == BindingAnnotation::Ref {
1008                 "as_ref"
1009             } else {
1010                 "as_mut"
1011             };
1012
1013             let output_ty = cx.typeck_results().expr_ty(expr);
1014             let input_ty = cx.typeck_results().expr_ty(ex);
1015
1016             let cast = if_chain! {
1017                 if let ty::Adt(_, substs) = input_ty.kind;
1018                 let input_ty = substs.type_at(0);
1019                 if let ty::Adt(_, substs) = output_ty.kind;
1020                 let output_ty = substs.type_at(0);
1021                 if let ty::Ref(_, output_ty, _) = output_ty.kind;
1022                 if input_ty != output_ty;
1023                 then {
1024                     ".map(|x| x as _)"
1025                 } else {
1026                     ""
1027                 }
1028             };
1029
1030             let mut applicability = Applicability::MachineApplicable;
1031             span_lint_and_sugg(
1032                 cx,
1033                 MATCH_AS_REF,
1034                 expr.span,
1035                 &format!("use `{}()` instead", suggestion),
1036                 "try this",
1037                 format!(
1038                     "{}.{}(){}",
1039                     snippet_with_applicability(cx, ex.span, "_", &mut applicability),
1040                     suggestion,
1041                     cast,
1042                 ),
1043                 applicability,
1044             )
1045         }
1046     }
1047 }
1048
1049 fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
1050     for arm in arms {
1051         if let PatKind::Or(ref fields) = arm.pat.kind {
1052             // look for multiple fields in this arm that contains at least one Wild pattern
1053             if fields.len() > 1 && fields.iter().any(is_wild) {
1054                 span_lint_and_help(
1055                     cx,
1056                     WILDCARD_IN_OR_PATTERNS,
1057                     arm.pat.span,
1058                     "wildcard pattern covers any other pattern as it will match anyway.",
1059                     None,
1060                     "Consider handling `_` separately.",
1061                 );
1062             }
1063         }
1064     }
1065 }
1066
1067 /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
1068 fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1069     if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind {
1070         match match_source {
1071             MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false),
1072             MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true),
1073             _ => return,
1074         }
1075     }
1076 }
1077
1078 /// Lint a `match` or desugared `if let` for replacement by `matches!`
1079 fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) {
1080     if_chain! {
1081         if arms.len() == 2;
1082         if cx.typeck_results().expr_ty(expr).is_bool();
1083         if is_wild(&arms[1].pat);
1084         if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared);
1085         if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared);
1086         if first != second;
1087         then {
1088             let mut applicability = Applicability::MachineApplicable;
1089
1090             let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard {
1091                 format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability))
1092             } else {
1093                 format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability))
1094             };
1095             span_lint_and_sugg(
1096                 cx,
1097                 MATCH_LIKE_MATCHES_MACRO,
1098                 expr.span,
1099                 &format!("{} expression looks like `matches!` macro", if desugared { "if let .. else" } else { "match" }),
1100                 "try this",
1101                 format!(
1102                     "{}matches!({}, {})",
1103                     if first { "" } else { "!" },
1104                     snippet_with_applicability(cx, ex.span, "..", &mut applicability),
1105                     pat_and_guard,
1106                 ),
1107                 applicability,
1108             )
1109         }
1110     }
1111 }
1112
1113 /// Extract a `bool` or `{ bool }`
1114 fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> {
1115     match ex {
1116         ExprKind::Lit(Spanned {
1117             node: LitKind::Bool(b), ..
1118         }) => Some(*b),
1119         ExprKind::Block(
1120             rustc_hir::Block {
1121                 stmts: &[],
1122                 expr: Some(exp),
1123                 ..
1124             },
1125             _,
1126         ) if desugared => {
1127             if let ExprKind::Lit(Spanned {
1128                 node: LitKind::Bool(b), ..
1129             }) = exp.kind
1130             {
1131                 Some(b)
1132             } else {
1133                 None
1134             }
1135         },
1136         _ => None,
1137     }
1138 }
1139
1140 fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
1141     if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
1142         return;
1143     }
1144     let matched_vars = ex.span;
1145     let bind_names = arms[0].pat.span;
1146     let match_body = remove_blocks(&arms[0].body);
1147     let mut snippet_body = if match_body.span.from_expansion() {
1148         Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
1149     } else {
1150         snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
1151     };
1152
1153     // Do we need to add ';' to suggestion ?
1154     match match_body.kind {
1155         ExprKind::Block(block, _) => {
1156             // macro + expr_ty(body) == ()
1157             if block.span.from_expansion() && cx.typeck_results().expr_ty(&match_body).is_unit() {
1158                 snippet_body.push(';');
1159             }
1160         },
1161         _ => {
1162             // expr_ty(body) == ()
1163             if cx.typeck_results().expr_ty(&match_body).is_unit() {
1164                 snippet_body.push(';');
1165             }
1166         },
1167     }
1168
1169     let mut applicability = Applicability::MaybeIncorrect;
1170     match arms[0].pat.kind {
1171         PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
1172             // If this match is in a local (`let`) stmt
1173             let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
1174                 (
1175                     parent_let_node.span,
1176                     format!(
1177                         "let {} = {};\n{}let {} = {};",
1178                         snippet_with_applicability(cx, bind_names, "..", &mut applicability),
1179                         snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
1180                         " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
1181                         snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
1182                         snippet_body
1183                     ),
1184                 )
1185             } else {
1186                 // If we are in closure, we need curly braces around suggestion
1187                 let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
1188                 let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string());
1189                 if let Some(parent_expr) = get_parent_expr(cx, expr) {
1190                     if let ExprKind::Closure(..) = parent_expr.kind {
1191                         cbrace_end = format!("\n{}}}", indent);
1192                         // Fix body indent due to the closure
1193                         indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
1194                         cbrace_start = format!("{{\n{}", indent);
1195                     }
1196                 };
1197                 (
1198                     expr.span,
1199                     format!(
1200                         "{}let {} = {};\n{}{}{}",
1201                         cbrace_start,
1202                         snippet_with_applicability(cx, bind_names, "..", &mut applicability),
1203                         snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
1204                         indent,
1205                         snippet_body,
1206                         cbrace_end
1207                     ),
1208                 )
1209             };
1210             span_lint_and_sugg(
1211                 cx,
1212                 MATCH_SINGLE_BINDING,
1213                 target_span,
1214                 "this match could be written as a `let` statement",
1215                 "consider using `let` statement",
1216                 sugg,
1217                 applicability,
1218             );
1219         },
1220         PatKind::Wild => {
1221             span_lint_and_sugg(
1222                 cx,
1223                 MATCH_SINGLE_BINDING,
1224                 expr.span,
1225                 "this match could be replaced by its body itself",
1226                 "consider using the match body instead",
1227                 snippet_body,
1228                 Applicability::MachineApplicable,
1229             );
1230         },
1231         _ => (),
1232     }
1233 }
1234
1235 /// Returns true if the `ex` match expression is in a local (`let`) statement
1236 fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
1237     if_chain! {
1238         let map = &cx.tcx.hir();
1239         if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
1240         if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
1241         then {
1242             return Some(parent_let_expr);
1243         }
1244     }
1245     None
1246 }
1247
1248 /// Gets all arms that are unbounded `PatRange`s.
1249 fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
1250     arms.iter()
1251         .flat_map(|arm| {
1252             if let Arm {
1253                 ref pat, guard: None, ..
1254             } = *arm
1255             {
1256                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
1257                     let lhs = match lhs {
1258                         Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
1259                         None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
1260                     };
1261                     let rhs = match rhs {
1262                         Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
1263                         None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
1264                     };
1265                     let rhs = match range_end {
1266                         RangeEnd::Included => Bound::Included(rhs),
1267                         RangeEnd::Excluded => Bound::Excluded(rhs),
1268                     };
1269                     return Some(SpannedRange {
1270                         span: pat.span,
1271                         node: (lhs, rhs),
1272                     });
1273                 }
1274
1275                 if let PatKind::Lit(ref value) = pat.kind {
1276                     let value = constant(cx, cx.typeck_results(), value)?.0;
1277                     return Some(SpannedRange {
1278                         span: pat.span,
1279                         node: (value.clone(), Bound::Included(value)),
1280                     });
1281                 }
1282             }
1283             None
1284         })
1285         .collect()
1286 }
1287
1288 #[derive(Debug, Eq, PartialEq)]
1289 pub struct SpannedRange<T> {
1290     pub span: Span,
1291     pub node: (T, Bound<T>),
1292 }
1293
1294 type TypedRanges = Vec<SpannedRange<u128>>;
1295
1296 /// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
1297 /// and other types than
1298 /// `Uint` and `Int` probably don't make sense.
1299 fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
1300     ranges
1301         .iter()
1302         .filter_map(|range| match range.node {
1303             (Constant::Int(start), Bound::Included(Constant::Int(end))) => Some(SpannedRange {
1304                 span: range.span,
1305                 node: (start, Bound::Included(end)),
1306             }),
1307             (Constant::Int(start), Bound::Excluded(Constant::Int(end))) => Some(SpannedRange {
1308                 span: range.span,
1309                 node: (start, Bound::Excluded(end)),
1310             }),
1311             (Constant::Int(start), Bound::Unbounded) => Some(SpannedRange {
1312                 span: range.span,
1313                 node: (start, Bound::Unbounded),
1314             }),
1315             _ => None,
1316         })
1317         .collect()
1318 }
1319
1320 fn is_unit_expr(expr: &Expr<'_>) -> bool {
1321     match expr.kind {
1322         ExprKind::Tup(ref v) if v.is_empty() => true,
1323         ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
1324         _ => false,
1325     }
1326 }
1327
1328 // Checks if arm has the form `None => None`
1329 fn is_none_arm(arm: &Arm<'_>) -> bool {
1330     matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE))
1331 }
1332
1333 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
1334 fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
1335     if_chain! {
1336         if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind;
1337         if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
1338         if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
1339         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
1340         if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind;
1341         if let ExprKind::Path(ref some_path) = e.kind;
1342         if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
1343         if let ExprKind::Path(ref qpath) = args[0].kind;
1344         if let &QPath::Resolved(_, ref path2) = qpath;
1345         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
1346         then {
1347             return Some(rb)
1348         }
1349     }
1350     None
1351 }
1352
1353 fn has_only_ref_pats(arms: &[Arm<'_>]) -> bool {
1354     let mapped = arms
1355         .iter()
1356         .map(|a| {
1357             match a.pat.kind {
1358                 PatKind::Ref(..) => Some(true), // &-patterns
1359                 PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
1360                 _ => None,                      // any other pattern is not fine
1361             }
1362         })
1363         .collect::<Option<Vec<bool>>>();
1364     // look for Some(v) where there's at least one true element
1365     mapped.map_or(false, |v| v.iter().any(|el| *el))
1366 }
1367
1368 pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
1369 where
1370     T: Copy + Ord,
1371 {
1372     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
1373     enum Kind<'a, T> {
1374         Start(T, &'a SpannedRange<T>),
1375         End(Bound<T>, &'a SpannedRange<T>),
1376     }
1377
1378     impl<'a, T: Copy> Kind<'a, T> {
1379         fn range(&self) -> &'a SpannedRange<T> {
1380             match *self {
1381                 Kind::Start(_, r) | Kind::End(_, r) => r,
1382             }
1383         }
1384
1385         fn value(self) -> Bound<T> {
1386             match self {
1387                 Kind::Start(t, _) => Bound::Included(t),
1388                 Kind::End(t, _) => t,
1389             }
1390         }
1391     }
1392
1393     impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
1394         fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1395             Some(self.cmp(other))
1396         }
1397     }
1398
1399     impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
1400         fn cmp(&self, other: &Self) -> Ordering {
1401             match (self.value(), other.value()) {
1402                 (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
1403                 // Range patterns cannot be unbounded (yet)
1404                 (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
1405                 (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
1406                     Ordering::Equal => Ordering::Greater,
1407                     other => other,
1408                 },
1409                 (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
1410                     Ordering::Equal => Ordering::Less,
1411                     other => other,
1412                 },
1413             }
1414         }
1415     }
1416
1417     let mut values = Vec::with_capacity(2 * ranges.len());
1418
1419     for r in ranges {
1420         values.push(Kind::Start(r.node.0, r));
1421         values.push(Kind::End(r.node.1, r));
1422     }
1423
1424     values.sort();
1425
1426     for (a, b) in values.iter().zip(values.iter().skip(1)) {
1427         match (a, b) {
1428             (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
1429                 if ra.node != rb.node {
1430                     return Some((ra, rb));
1431                 }
1432             },
1433             (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
1434             _ => return Some((a.range(), b.range())),
1435         }
1436     }
1437
1438     None
1439 }
1440
1441 mod redundant_pattern_match {
1442     use super::REDUNDANT_PATTERN_MATCHING;
1443     use crate::utils::{in_constant, match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
1444     use if_chain::if_chain;
1445     use rustc_ast::ast::LitKind;
1446     use rustc_errors::Applicability;
1447     use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, PatKind, QPath};
1448     use rustc_lint::LateContext;
1449     use rustc_middle::ty;
1450     use rustc_mir::const_eval::is_const_fn;
1451     use rustc_span::source_map::Symbol;
1452
1453     pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1454         if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
1455             match match_source {
1456                 MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
1457                 MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
1458                 MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
1459                 _ => {},
1460             }
1461         }
1462     }
1463
1464     fn find_sugg_for_if_let<'tcx>(
1465         cx: &LateContext<'tcx>,
1466         expr: &'tcx Expr<'_>,
1467         op: &Expr<'_>,
1468         arms: &[Arm<'_>],
1469         keyword: &'static str,
1470     ) {
1471         fn find_suggestion(cx: &LateContext<'_>, hir_id: HirId, path: &QPath<'_>) -> Option<&'static str> {
1472             if match_qpath(path, &paths::RESULT_OK) && can_suggest(cx, hir_id, sym!(result_type), "is_ok") {
1473                 return Some("is_ok()");
1474             }
1475             if match_qpath(path, &paths::RESULT_ERR) && can_suggest(cx, hir_id, sym!(result_type), "is_err") {
1476                 return Some("is_err()");
1477             }
1478             if match_qpath(path, &paths::OPTION_SOME) && can_suggest(cx, hir_id, sym!(option_type), "is_some") {
1479                 return Some("is_some()");
1480             }
1481             if match_qpath(path, &paths::OPTION_NONE) && can_suggest(cx, hir_id, sym!(option_type), "is_none") {
1482                 return Some("is_none()");
1483             }
1484             None
1485         }
1486
1487         let hir_id = expr.hir_id;
1488         let good_method = match arms[0].pat.kind {
1489             PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
1490                 if let PatKind::Wild = patterns[0].kind {
1491                     find_suggestion(cx, hir_id, path)
1492                 } else {
1493                     None
1494                 }
1495             },
1496             PatKind::Path(ref path) => find_suggestion(cx, hir_id, path),
1497             _ => None,
1498         };
1499         let good_method = match good_method {
1500             Some(method) => method,
1501             None => return,
1502         };
1503
1504         // check that `while_let_on_iterator` lint does not trigger
1505         if_chain! {
1506             if keyword == "while";
1507             if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
1508             if method_path.ident.name == sym!(next);
1509             if match_trait_method(cx, op, &paths::ITERATOR);
1510             then {
1511                 return;
1512             }
1513         }
1514
1515         span_lint_and_then(
1516             cx,
1517             REDUNDANT_PATTERN_MATCHING,
1518             arms[0].pat.span,
1519             &format!("redundant pattern matching, consider using `{}`", good_method),
1520             |diag| {
1521                 // while let ... = ... { ... }
1522                 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
1523                 let expr_span = expr.span;
1524
1525                 // while let ... = ... { ... }
1526                 //                 ^^^
1527                 let op_span = op.span.source_callsite();
1528
1529                 // while let ... = ... { ... }
1530                 // ^^^^^^^^^^^^^^^^^^^
1531                 let span = expr_span.until(op_span.shrink_to_hi());
1532                 diag.span_suggestion(
1533                     span,
1534                     "try this",
1535                     format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
1536                     Applicability::MachineApplicable, // snippet
1537                 );
1538             },
1539         );
1540     }
1541
1542     fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
1543         if arms.len() == 2 {
1544             let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
1545
1546             let hir_id = expr.hir_id;
1547             let found_good_method = match node_pair {
1548                 (
1549                     PatKind::TupleStruct(ref path_left, ref patterns_left, _),
1550                     PatKind::TupleStruct(ref path_right, ref patterns_right, _),
1551                 ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
1552                     if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
1553                         find_good_method_for_match(
1554                             arms,
1555                             path_left,
1556                             path_right,
1557                             &paths::RESULT_OK,
1558                             &paths::RESULT_ERR,
1559                             "is_ok()",
1560                             "is_err()",
1561                             || can_suggest(cx, hir_id, sym!(result_type), "is_ok"),
1562                             || can_suggest(cx, hir_id, sym!(result_type), "is_err"),
1563                         )
1564                     } else {
1565                         None
1566                     }
1567                 },
1568                 (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
1569                 | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
1570                     if patterns.len() == 1 =>
1571                 {
1572                     if let PatKind::Wild = patterns[0].kind {
1573                         find_good_method_for_match(
1574                             arms,
1575                             path_left,
1576                             path_right,
1577                             &paths::OPTION_SOME,
1578                             &paths::OPTION_NONE,
1579                             "is_some()",
1580                             "is_none()",
1581                             || can_suggest(cx, hir_id, sym!(option_type), "is_some"),
1582                             || can_suggest(cx, hir_id, sym!(option_type), "is_none"),
1583                         )
1584                     } else {
1585                         None
1586                     }
1587                 },
1588                 _ => None,
1589             };
1590
1591             if let Some(good_method) = found_good_method {
1592                 span_lint_and_then(
1593                     cx,
1594                     REDUNDANT_PATTERN_MATCHING,
1595                     expr.span,
1596                     &format!("redundant pattern matching, consider using `{}`", good_method),
1597                     |diag| {
1598                         let span = expr.span.to(op.span);
1599                         diag.span_suggestion(
1600                             span,
1601                             "try this",
1602                             format!("{}.{}", snippet(cx, op.span, "_"), good_method),
1603                             Applicability::MaybeIncorrect, // snippet
1604                         );
1605                     },
1606                 );
1607             }
1608         }
1609     }
1610
1611     #[allow(clippy::too_many_arguments)]
1612     fn find_good_method_for_match<'a>(
1613         arms: &[Arm<'_>],
1614         path_left: &QPath<'_>,
1615         path_right: &QPath<'_>,
1616         expected_left: &[&str],
1617         expected_right: &[&str],
1618         should_be_left: &'a str,
1619         should_be_right: &'a str,
1620         can_suggest_left: impl Fn() -> bool,
1621         can_suggest_right: impl Fn() -> bool,
1622     ) -> Option<&'a str> {
1623         let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
1624             (&(*arms[0].body).kind, &(*arms[1].body).kind)
1625         } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
1626             (&(*arms[1].body).kind, &(*arms[0].body).kind)
1627         } else {
1628             return None;
1629         };
1630
1631         match body_node_pair {
1632             (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
1633                 (LitKind::Bool(true), LitKind::Bool(false)) if can_suggest_left() => Some(should_be_left),
1634                 (LitKind::Bool(false), LitKind::Bool(true)) if can_suggest_right() => Some(should_be_right),
1635                 _ => None,
1636             },
1637             _ => None,
1638         }
1639     }
1640
1641     fn can_suggest(cx: &LateContext<'_>, hir_id: HirId, diag_item: Symbol, name: &str) -> bool {
1642         if !in_constant(cx, hir_id) {
1643             return true;
1644         }
1645
1646         // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
1647         cx.tcx
1648             .get_diagnostic_item(diag_item)
1649             .and_then(|def_id| {
1650                 cx.tcx.inherent_impls(def_id).iter().find_map(|imp| {
1651                     cx.tcx
1652                         .associated_items(*imp)
1653                         .in_definition_order()
1654                         .find_map(|item| match item.kind {
1655                             ty::AssocKind::Fn if item.ident.name.as_str() == name => Some(item.def_id),
1656                             _ => None,
1657                         })
1658                 })
1659             })
1660             .map_or(false, |def_id| is_const_fn(cx.tcx, def_id))
1661     }
1662 }
1663
1664 #[test]
1665 fn test_overlapping() {
1666     use rustc_span::source_map::DUMMY_SP;
1667
1668     let sp = |s, e| SpannedRange {
1669         span: DUMMY_SP,
1670         node: (s, e),
1671     };
1672
1673     assert_eq!(None, overlapping::<u8>(&[]));
1674     assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
1675     assert_eq!(
1676         None,
1677         overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))])
1678     );
1679     assert_eq!(
1680         None,
1681         overlapping(&[
1682             sp(1, Bound::Included(4)),
1683             sp(5, Bound::Included(6)),
1684             sp(10, Bound::Included(11))
1685         ],)
1686     );
1687     assert_eq!(
1688         Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
1689         overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
1690     );
1691     assert_eq!(
1692         Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
1693         overlapping(&[
1694             sp(1, Bound::Included(4)),
1695             sp(5, Bound::Included(6)),
1696             sp(6, Bound::Included(11))
1697         ],)
1698     );
1699 }