]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/matches/mod.rs
Split out `overlapping_arms`
[rust.git] / clippy_lints / src / matches / mod.rs
1 use clippy_utils::diagnostics::{
2     multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
3 };
4 use clippy_utils::macros::{is_panic, root_macro_call};
5 use clippy_utils::peel_blocks_with_stmt;
6 use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
7 use clippy_utils::sugg::Sugg;
8 use clippy_utils::ty::is_type_diagnostic_item;
9 use clippy_utils::visitors::is_local_used;
10 use clippy_utils::{
11     get_parent_expr, is_lang_ctor, is_refutable, is_wild, meets_msrv, msrvs, path_to_local_id, peel_blocks,
12     peel_hir_pat_refs, recurse_or_patterns, strip_pat_refs,
13 };
14 use core::iter::once;
15 use if_chain::if_chain;
16 use rustc_errors::Applicability;
17 use rustc_hir::def::{CtorKind, DefKind, Res};
18 use rustc_hir::LangItem::{OptionNone, OptionSome};
19 use rustc_hir::{
20     self as hir, Arm, BindingAnnotation, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Node, Pat,
21     PatKind, PathSegment, QPath, TyKind,
22 };
23 use rustc_lint::{LateContext, LateLintPass};
24 use rustc_middle::ty::{self, VariantDef};
25 use rustc_semver::RustcVersion;
26 use rustc_session::{declare_tool_lint, impl_lint_pass};
27 use rustc_span::{sym, symbol::kw};
28
29 mod match_bool;
30 mod match_like_matches;
31 mod match_same_arms;
32 mod overlapping_arms;
33 mod redundant_pattern_match;
34 mod single_match;
35
36 declare_clippy_lint! {
37     /// ### What it does
38     /// Checks for matches with a single arm where an `if let`
39     /// will usually suffice.
40     ///
41     /// ### Why is this bad?
42     /// Just readability – `if let` nests less than a `match`.
43     ///
44     /// ### Example
45     /// ```rust
46     /// # fn bar(stool: &str) {}
47     /// # let x = Some("abc");
48     /// // Bad
49     /// match x {
50     ///     Some(ref foo) => bar(foo),
51     ///     _ => (),
52     /// }
53     ///
54     /// // Good
55     /// if let Some(ref foo) = x {
56     ///     bar(foo);
57     /// }
58     /// ```
59     #[clippy::version = "pre 1.29.0"]
60     pub SINGLE_MATCH,
61     style,
62     "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
63 }
64
65 declare_clippy_lint! {
66     /// ### What it does
67     /// Checks for matches with two arms where an `if let else` will
68     /// usually suffice.
69     ///
70     /// ### Why is this bad?
71     /// Just readability – `if let` nests less than a `match`.
72     ///
73     /// ### Known problems
74     /// Personal style preferences may differ.
75     ///
76     /// ### Example
77     /// Using `match`:
78     ///
79     /// ```rust
80     /// # fn bar(foo: &usize) {}
81     /// # let other_ref: usize = 1;
82     /// # let x: Option<&usize> = Some(&1);
83     /// match x {
84     ///     Some(ref foo) => bar(foo),
85     ///     _ => bar(&other_ref),
86     /// }
87     /// ```
88     ///
89     /// Using `if let` with `else`:
90     ///
91     /// ```rust
92     /// # fn bar(foo: &usize) {}
93     /// # let other_ref: usize = 1;
94     /// # let x: Option<&usize> = Some(&1);
95     /// if let Some(ref foo) = x {
96     ///     bar(foo);
97     /// } else {
98     ///     bar(&other_ref);
99     /// }
100     /// ```
101     #[clippy::version = "pre 1.29.0"]
102     pub SINGLE_MATCH_ELSE,
103     pedantic,
104     "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
105 }
106
107 declare_clippy_lint! {
108     /// ### What it does
109     /// Checks for matches where all arms match a reference,
110     /// suggesting to remove the reference and deref the matched expression
111     /// instead. It also checks for `if let &foo = bar` blocks.
112     ///
113     /// ### Why is this bad?
114     /// It just makes the code less readable. That reference
115     /// destructuring adds nothing to the code.
116     ///
117     /// ### Example
118     /// ```rust,ignore
119     /// // Bad
120     /// match x {
121     ///     &A(ref y) => foo(y),
122     ///     &B => bar(),
123     ///     _ => frob(&x),
124     /// }
125     ///
126     /// // Good
127     /// match *x {
128     ///     A(ref y) => foo(y),
129     ///     B => bar(),
130     ///     _ => frob(x),
131     /// }
132     /// ```
133     #[clippy::version = "pre 1.29.0"]
134     pub MATCH_REF_PATS,
135     style,
136     "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
137 }
138
139 declare_clippy_lint! {
140     /// ### What it does
141     /// Checks for matches where match expression is a `bool`. It
142     /// suggests to replace the expression with an `if...else` block.
143     ///
144     /// ### Why is this bad?
145     /// It makes the code less readable.
146     ///
147     /// ### Example
148     /// ```rust
149     /// # fn foo() {}
150     /// # fn bar() {}
151     /// let condition: bool = true;
152     /// match condition {
153     ///     true => foo(),
154     ///     false => bar(),
155     /// }
156     /// ```
157     /// Use if/else instead:
158     /// ```rust
159     /// # fn foo() {}
160     /// # fn bar() {}
161     /// let condition: bool = true;
162     /// if condition {
163     ///     foo();
164     /// } else {
165     ///     bar();
166     /// }
167     /// ```
168     #[clippy::version = "pre 1.29.0"]
169     pub MATCH_BOOL,
170     pedantic,
171     "a `match` on a boolean expression instead of an `if..else` block"
172 }
173
174 declare_clippy_lint! {
175     /// ### What it does
176     /// Checks for overlapping match arms.
177     ///
178     /// ### Why is this bad?
179     /// It is likely to be an error and if not, makes the code
180     /// less obvious.
181     ///
182     /// ### Example
183     /// ```rust
184     /// let x = 5;
185     /// match x {
186     ///     1..=10 => println!("1 ... 10"),
187     ///     5..=15 => println!("5 ... 15"),
188     ///     _ => (),
189     /// }
190     /// ```
191     #[clippy::version = "pre 1.29.0"]
192     pub MATCH_OVERLAPPING_ARM,
193     style,
194     "a `match` with overlapping arms"
195 }
196
197 declare_clippy_lint! {
198     /// ### What it does
199     /// Checks for arm which matches all errors with `Err(_)`
200     /// and take drastic actions like `panic!`.
201     ///
202     /// ### Why is this bad?
203     /// It is generally a bad practice, similar to
204     /// catching all exceptions in java with `catch(Exception)`
205     ///
206     /// ### Example
207     /// ```rust
208     /// let x: Result<i32, &str> = Ok(3);
209     /// match x {
210     ///     Ok(_) => println!("ok"),
211     ///     Err(_) => panic!("err"),
212     /// }
213     /// ```
214     #[clippy::version = "pre 1.29.0"]
215     pub MATCH_WILD_ERR_ARM,
216     pedantic,
217     "a `match` with `Err(_)` arm and take drastic actions"
218 }
219
220 declare_clippy_lint! {
221     /// ### What it does
222     /// Checks for match which is used to add a reference to an
223     /// `Option` value.
224     ///
225     /// ### Why is this bad?
226     /// Using `as_ref()` or `as_mut()` instead is shorter.
227     ///
228     /// ### Example
229     /// ```rust
230     /// let x: Option<()> = None;
231     ///
232     /// // Bad
233     /// let r: Option<&()> = match x {
234     ///     None => None,
235     ///     Some(ref v) => Some(v),
236     /// };
237     ///
238     /// // Good
239     /// let r: Option<&()> = x.as_ref();
240     /// ```
241     #[clippy::version = "pre 1.29.0"]
242     pub MATCH_AS_REF,
243     complexity,
244     "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
245 }
246
247 declare_clippy_lint! {
248     /// ### What it does
249     /// Checks for wildcard enum matches using `_`.
250     ///
251     /// ### Why is this bad?
252     /// New enum variants added by library updates can be missed.
253     ///
254     /// ### Known problems
255     /// Suggested replacements may be incorrect if guards exhaustively cover some
256     /// variants, and also may not use correct path to enum if it's not present in the current scope.
257     ///
258     /// ### Example
259     /// ```rust
260     /// # enum Foo { A(usize), B(usize) }
261     /// # let x = Foo::B(1);
262     /// // Bad
263     /// match x {
264     ///     Foo::A(_) => {},
265     ///     _ => {},
266     /// }
267     ///
268     /// // Good
269     /// match x {
270     ///     Foo::A(_) => {},
271     ///     Foo::B(_) => {},
272     /// }
273     /// ```
274     #[clippy::version = "1.34.0"]
275     pub WILDCARD_ENUM_MATCH_ARM,
276     restriction,
277     "a wildcard enum match arm using `_`"
278 }
279
280 declare_clippy_lint! {
281     /// ### What it does
282     /// Checks for wildcard enum matches for a single variant.
283     ///
284     /// ### Why is this bad?
285     /// New enum variants added by library updates can be missed.
286     ///
287     /// ### Known problems
288     /// Suggested replacements may not use correct path to enum
289     /// if it's not present in the current scope.
290     ///
291     /// ### Example
292     /// ```rust
293     /// # enum Foo { A, B, C }
294     /// # let x = Foo::B;
295     /// // Bad
296     /// match x {
297     ///     Foo::A => {},
298     ///     Foo::B => {},
299     ///     _ => {},
300     /// }
301     ///
302     /// // Good
303     /// match x {
304     ///     Foo::A => {},
305     ///     Foo::B => {},
306     ///     Foo::C => {},
307     /// }
308     /// ```
309     #[clippy::version = "1.45.0"]
310     pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
311     pedantic,
312     "a wildcard enum match for a single variant"
313 }
314
315 declare_clippy_lint! {
316     /// ### What it does
317     /// Checks for wildcard pattern used with others patterns in same match arm.
318     ///
319     /// ### Why is this bad?
320     /// Wildcard pattern already covers any other pattern as it will match anyway.
321     /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
322     ///
323     /// ### Example
324     /// ```rust
325     /// // Bad
326     /// match "foo" {
327     ///     "a" => {},
328     ///     "bar" | _ => {},
329     /// }
330     ///
331     /// // Good
332     /// match "foo" {
333     ///     "a" => {},
334     ///     _ => {},
335     /// }
336     /// ```
337     #[clippy::version = "1.42.0"]
338     pub WILDCARD_IN_OR_PATTERNS,
339     complexity,
340     "a wildcard pattern used with others patterns in same match arm"
341 }
342
343 declare_clippy_lint! {
344     /// ### What it does
345     /// Checks for matches being used to destructure a single-variant enum
346     /// or tuple struct where a `let` will suffice.
347     ///
348     /// ### Why is this bad?
349     /// Just readability – `let` doesn't nest, whereas a `match` does.
350     ///
351     /// ### Example
352     /// ```rust
353     /// enum Wrapper {
354     ///     Data(i32),
355     /// }
356     ///
357     /// let wrapper = Wrapper::Data(42);
358     ///
359     /// let data = match wrapper {
360     ///     Wrapper::Data(i) => i,
361     /// };
362     /// ```
363     ///
364     /// The correct use would be:
365     /// ```rust
366     /// enum Wrapper {
367     ///     Data(i32),
368     /// }
369     ///
370     /// let wrapper = Wrapper::Data(42);
371     /// let Wrapper::Data(data) = wrapper;
372     /// ```
373     #[clippy::version = "pre 1.29.0"]
374     pub INFALLIBLE_DESTRUCTURING_MATCH,
375     style,
376     "a `match` statement with a single infallible arm instead of a `let`"
377 }
378
379 declare_clippy_lint! {
380     /// ### What it does
381     /// Checks for useless match that binds to only one value.
382     ///
383     /// ### Why is this bad?
384     /// Readability and needless complexity.
385     ///
386     /// ### Known problems
387     ///  Suggested replacements may be incorrect when `match`
388     /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
389     ///
390     /// ### Example
391     /// ```rust
392     /// # let a = 1;
393     /// # let b = 2;
394     ///
395     /// // Bad
396     /// match (a, b) {
397     ///     (c, d) => {
398     ///         // useless match
399     ///     }
400     /// }
401     ///
402     /// // Good
403     /// let (c, d) = (a, b);
404     /// ```
405     #[clippy::version = "1.43.0"]
406     pub MATCH_SINGLE_BINDING,
407     complexity,
408     "a match with a single binding instead of using `let` statement"
409 }
410
411 declare_clippy_lint! {
412     /// ### What it does
413     /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
414     ///
415     /// ### Why is this bad?
416     /// Correctness and readability. It's like having a wildcard pattern after
417     /// matching all enum variants explicitly.
418     ///
419     /// ### Example
420     /// ```rust
421     /// # struct A { a: i32 }
422     /// let a = A { a: 5 };
423     ///
424     /// // Bad
425     /// match a {
426     ///     A { a: 5, .. } => {},
427     ///     _ => {},
428     /// }
429     ///
430     /// // Good
431     /// match a {
432     ///     A { a: 5 } => {},
433     ///     _ => {},
434     /// }
435     /// ```
436     #[clippy::version = "1.43.0"]
437     pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
438     restriction,
439     "a match on a struct that binds all fields but still uses the wildcard pattern"
440 }
441
442 declare_clippy_lint! {
443     /// ### What it does
444     /// Lint for redundant pattern matching over `Result`, `Option`,
445     /// `std::task::Poll` or `std::net::IpAddr`
446     ///
447     /// ### Why is this bad?
448     /// It's more concise and clear to just use the proper
449     /// utility function
450     ///
451     /// ### Known problems
452     /// This will change the drop order for the matched type. Both `if let` and
453     /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
454     /// value before entering the block. For most types this change will not matter, but for a few
455     /// types this will not be an acceptable change (e.g. locks). See the
456     /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
457     /// drop order.
458     ///
459     /// ### Example
460     /// ```rust
461     /// # use std::task::Poll;
462     /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
463     /// if let Ok(_) = Ok::<i32, i32>(42) {}
464     /// if let Err(_) = Err::<i32, i32>(42) {}
465     /// if let None = None::<()> {}
466     /// if let Some(_) = Some(42) {}
467     /// if let Poll::Pending = Poll::Pending::<()> {}
468     /// if let Poll::Ready(_) = Poll::Ready(42) {}
469     /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
470     /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
471     /// match Ok::<i32, i32>(42) {
472     ///     Ok(_) => true,
473     ///     Err(_) => false,
474     /// };
475     /// ```
476     ///
477     /// The more idiomatic use would be:
478     ///
479     /// ```rust
480     /// # use std::task::Poll;
481     /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
482     /// if Ok::<i32, i32>(42).is_ok() {}
483     /// if Err::<i32, i32>(42).is_err() {}
484     /// if None::<()>.is_none() {}
485     /// if Some(42).is_some() {}
486     /// if Poll::Pending::<()>.is_pending() {}
487     /// if Poll::Ready(42).is_ready() {}
488     /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
489     /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
490     /// Ok::<i32, i32>(42).is_ok();
491     /// ```
492     #[clippy::version = "1.31.0"]
493     pub REDUNDANT_PATTERN_MATCHING,
494     style,
495     "use the proper utility function avoiding an `if let`"
496 }
497
498 declare_clippy_lint! {
499     /// ### What it does
500     /// Checks for `match`  or `if let` expressions producing a
501     /// `bool` that could be written using `matches!`
502     ///
503     /// ### Why is this bad?
504     /// Readability and needless complexity.
505     ///
506     /// ### Known problems
507     /// This lint falsely triggers, if there are arms with
508     /// `cfg` attributes that remove an arm evaluating to `false`.
509     ///
510     /// ### Example
511     /// ```rust
512     /// let x = Some(5);
513     ///
514     /// // Bad
515     /// let a = match x {
516     ///     Some(0) => true,
517     ///     _ => false,
518     /// };
519     ///
520     /// let a = if let Some(0) = x {
521     ///     true
522     /// } else {
523     ///     false
524     /// };
525     ///
526     /// // Good
527     /// let a = matches!(x, Some(0));
528     /// ```
529     #[clippy::version = "1.47.0"]
530     pub MATCH_LIKE_MATCHES_MACRO,
531     style,
532     "a match that could be written with the matches! macro"
533 }
534
535 declare_clippy_lint! {
536     /// ### What it does
537     /// Checks for `match` with identical arm bodies.
538     ///
539     /// ### Why is this bad?
540     /// This is probably a copy & paste error. If arm bodies
541     /// are the same on purpose, you can factor them
542     /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
543     ///
544     /// ### Known problems
545     /// False positive possible with order dependent `match`
546     /// (see issue
547     /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
548     ///
549     /// ### Example
550     /// ```rust,ignore
551     /// match foo {
552     ///     Bar => bar(),
553     ///     Quz => quz(),
554     ///     Baz => bar(), // <= oops
555     /// }
556     /// ```
557     ///
558     /// This should probably be
559     /// ```rust,ignore
560     /// match foo {
561     ///     Bar => bar(),
562     ///     Quz => quz(),
563     ///     Baz => baz(), // <= fixed
564     /// }
565     /// ```
566     ///
567     /// or if the original code was not a typo:
568     /// ```rust,ignore
569     /// match foo {
570     ///     Bar | Baz => bar(), // <= shows the intent better
571     ///     Quz => quz(),
572     /// }
573     /// ```
574     #[clippy::version = "pre 1.29.0"]
575     pub MATCH_SAME_ARMS,
576     pedantic,
577     "`match` with identical arm bodies"
578 }
579
580 #[derive(Default)]
581 pub struct Matches {
582     msrv: Option<RustcVersion>,
583     infallible_destructuring_match_linted: bool,
584 }
585
586 impl Matches {
587     #[must_use]
588     pub fn new(msrv: Option<RustcVersion>) -> Self {
589         Self {
590             msrv,
591             ..Matches::default()
592         }
593     }
594 }
595
596 impl_lint_pass!(Matches => [
597     SINGLE_MATCH,
598     MATCH_REF_PATS,
599     MATCH_BOOL,
600     SINGLE_MATCH_ELSE,
601     MATCH_OVERLAPPING_ARM,
602     MATCH_WILD_ERR_ARM,
603     MATCH_AS_REF,
604     WILDCARD_ENUM_MATCH_ARM,
605     MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
606     WILDCARD_IN_OR_PATTERNS,
607     MATCH_SINGLE_BINDING,
608     INFALLIBLE_DESTRUCTURING_MATCH,
609     REST_PAT_IN_FULLY_BOUND_STRUCTS,
610     REDUNDANT_PATTERN_MATCHING,
611     MATCH_LIKE_MATCHES_MACRO,
612     MATCH_SAME_ARMS,
613 ]);
614
615 impl<'tcx> LateLintPass<'tcx> for Matches {
616     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
617         if expr.span.from_expansion() {
618             return;
619         }
620
621         redundant_pattern_match::check(cx, expr);
622
623         if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
624             if !match_like_matches::check(cx, expr) {
625                 match_same_arms::check(cx, expr);
626             }
627         } else {
628             match_same_arms::check(cx, expr);
629         }
630
631         if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
632             single_match::check(cx, ex, arms, expr);
633             match_bool::check(cx, ex, arms, expr);
634             overlapping_arms::check(cx, ex, arms);
635             check_wild_err_arm(cx, ex, arms);
636             check_wild_enum_match(cx, ex, arms);
637             check_match_as_ref(cx, ex, arms, expr);
638             check_wild_in_or_pats(cx, arms);
639
640             if self.infallible_destructuring_match_linted {
641                 self.infallible_destructuring_match_linted = false;
642             } else {
643                 check_match_single_binding(cx, ex, arms, expr);
644             }
645         }
646         if let ExprKind::Match(ex, arms, _) = expr.kind {
647             check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
648         }
649     }
650
651     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
652         if_chain! {
653             if !local.span.from_expansion();
654             if let Some(expr) = local.init;
655             if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
656             if arms.len() == 1 && arms[0].guard.is_none();
657             if let PatKind::TupleStruct(
658                 QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
659             if args.len() == 1;
660             if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
661             let body = peel_blocks(arms[0].body);
662             if path_to_local_id(body, arg);
663
664             then {
665                 let mut applicability = Applicability::MachineApplicable;
666                 self.infallible_destructuring_match_linted = true;
667                 span_lint_and_sugg(
668                     cx,
669                     INFALLIBLE_DESTRUCTURING_MATCH,
670                     local.span,
671                     "you seem to be trying to use `match` to destructure a single infallible pattern. \
672                     Consider using `let`",
673                     "try this",
674                     format!(
675                         "let {}({}) = {};",
676                         snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
677                         snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
678                         snippet_with_applicability(cx, target.span, "..", &mut applicability),
679                     ),
680                     applicability,
681                 );
682             }
683         }
684     }
685
686     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
687         if_chain! {
688             if !pat.span.from_expansion();
689             if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
690             if let Some(def_id) = path.res.opt_def_id();
691             let ty = cx.tcx.type_of(def_id);
692             if let ty::Adt(def, _) = ty.kind();
693             if def.is_struct() || def.is_union();
694             if fields.len() == def.non_enum_variant().fields.len();
695
696             then {
697                 span_lint_and_help(
698                     cx,
699                     REST_PAT_IN_FULLY_BOUND_STRUCTS,
700                     pat.span,
701                     "unnecessary use of `..` pattern in struct binding. All fields were already bound",
702                     None,
703                     "consider removing `..` from this binding",
704                 );
705             }
706         }
707     }
708
709     extract_msrv_attr!(LateContext);
710 }
711
712 fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
713     let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
714     if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
715         for arm in arms {
716             if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
717                 let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
718                 if path_str == "Err" {
719                     let mut matching_wild = inner.iter().any(is_wild);
720                     let mut ident_bind_name = kw::Underscore;
721                     if !matching_wild {
722                         // Looking for unused bindings (i.e.: `_e`)
723                         for pat in inner.iter() {
724                             if let PatKind::Binding(_, id, ident, None) = pat.kind {
725                                 if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
726                                     ident_bind_name = ident.name;
727                                     matching_wild = true;
728                                 }
729                             }
730                         }
731                     }
732                     if_chain! {
733                         if matching_wild;
734                         if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span);
735                         if is_panic(cx, macro_call.def_id);
736                         then {
737                             // `Err(_)` or `Err(_e)` arm with `panic!` found
738                             span_lint_and_note(cx,
739                                 MATCH_WILD_ERR_ARM,
740                                 arm.pat.span,
741                                 &format!("`Err({})` matches all errors", ident_bind_name),
742                                 None,
743                                 "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
744                             );
745                         }
746                     }
747                 }
748             }
749         }
750     }
751 }
752
753 enum CommonPrefixSearcher<'a> {
754     None,
755     Path(&'a [PathSegment<'a>]),
756     Mixed,
757 }
758 impl<'a> CommonPrefixSearcher<'a> {
759     fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
760         match path {
761             [path @ .., _] => self.with_prefix(path),
762             [] => (),
763         }
764     }
765
766     fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
767         match self {
768             Self::None => *self = Self::Path(path),
769             Self::Path(self_path)
770                 if path
771                     .iter()
772                     .map(|p| p.ident.name)
773                     .eq(self_path.iter().map(|p| p.ident.name)) => {},
774             Self::Path(_) => *self = Self::Mixed,
775             Self::Mixed => (),
776         }
777     }
778 }
779
780 fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
781     let attrs = cx.tcx.get_attrs(variant_def.def_id);
782     clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
783 }
784
785 #[allow(clippy::too_many_lines)]
786 fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
787     let ty = cx.typeck_results().expr_ty(ex).peel_refs();
788     let adt_def = match ty.kind() {
789         ty::Adt(adt_def, _)
790             if adt_def.is_enum()
791                 && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) =>
792         {
793             adt_def
794         },
795         _ => return,
796     };
797
798     // First pass - check for violation, but don't do much book-keeping because this is hopefully
799     // the uncommon case, and the book-keeping is slightly expensive.
800     let mut wildcard_span = None;
801     let mut wildcard_ident = None;
802     let mut has_non_wild = false;
803     for arm in arms {
804         match peel_hir_pat_refs(arm.pat).0.kind {
805             PatKind::Wild => wildcard_span = Some(arm.pat.span),
806             PatKind::Binding(_, _, ident, None) => {
807                 wildcard_span = Some(arm.pat.span);
808                 wildcard_ident = Some(ident);
809             },
810             _ => has_non_wild = true,
811         }
812     }
813     let wildcard_span = match wildcard_span {
814         Some(x) if has_non_wild => x,
815         _ => return,
816     };
817
818     // Accumulate the variants which should be put in place of the wildcard because they're not
819     // already covered.
820     let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
821     let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
822
823     let mut path_prefix = CommonPrefixSearcher::None;
824     for arm in arms {
825         // Guards mean that this case probably isn't exhaustively covered. Technically
826         // this is incorrect, as we should really check whether each variant is exhaustively
827         // covered by the set of guards that cover it, but that's really hard to do.
828         recurse_or_patterns(arm.pat, |pat| {
829             let path = match &peel_hir_pat_refs(pat).0.kind {
830                 PatKind::Path(path) => {
831                     #[allow(clippy::match_same_arms)]
832                     let id = match cx.qpath_res(path, pat.hir_id) {
833                         Res::Def(
834                             DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst,
835                             _,
836                         ) => return,
837                         Res::Def(_, id) => id,
838                         _ => return,
839                     };
840                     if arm.guard.is_none() {
841                         missing_variants.retain(|e| e.ctor_def_id != Some(id));
842                     }
843                     path
844                 },
845                 PatKind::TupleStruct(path, patterns, ..) => {
846                     if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
847                         if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
848                             missing_variants.retain(|e| e.ctor_def_id != Some(id));
849                         }
850                     }
851                     path
852                 },
853                 PatKind::Struct(path, patterns, ..) => {
854                     if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
855                         if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
856                             missing_variants.retain(|e| e.def_id != id);
857                         }
858                     }
859                     path
860                 },
861                 _ => return,
862             };
863             match path {
864                 QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
865                 QPath::TypeRelative(
866                     hir::Ty {
867                         kind: TyKind::Path(QPath::Resolved(_, path)),
868                         ..
869                     },
870                     _,
871                 ) => path_prefix.with_prefix(path.segments),
872                 _ => (),
873             }
874         });
875     }
876
877     let format_suggestion = |variant: &VariantDef| {
878         format!(
879             "{}{}{}{}",
880             if let Some(ident) = wildcard_ident {
881                 format!("{} @ ", ident.name)
882             } else {
883                 String::new()
884             },
885             if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
886                 let mut s = String::new();
887                 for seg in path_prefix {
888                     s.push_str(seg.ident.as_str());
889                     s.push_str("::");
890                 }
891                 s
892             } else {
893                 let mut s = cx.tcx.def_path_str(adt_def.did);
894                 s.push_str("::");
895                 s
896             },
897             variant.name,
898             match variant.ctor_kind {
899                 CtorKind::Fn if variant.fields.len() == 1 => "(_)",
900                 CtorKind::Fn => "(..)",
901                 CtorKind::Const => "",
902                 CtorKind::Fictive => "{ .. }",
903             }
904         )
905     };
906
907     match missing_variants.as_slice() {
908         [] => (),
909         [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
910             cx,
911             MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
912             wildcard_span,
913             "wildcard matches only a single variant and will also match any future added variants",
914             "try this",
915             format_suggestion(x),
916             Applicability::MaybeIncorrect,
917         ),
918         variants => {
919             let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
920             let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
921                 suggestions.push("_".into());
922                 "wildcard matches known variants and will also match future added variants"
923             } else {
924                 "wildcard match will also match any future added variants"
925             };
926
927             span_lint_and_sugg(
928                 cx,
929                 WILDCARD_ENUM_MATCH_ARM,
930                 wildcard_span,
931                 message,
932                 "try this",
933                 suggestions.join(" | "),
934                 Applicability::MaybeIncorrect,
935             );
936         },
937     };
938 }
939
940 fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
941 where
942     'b: 'a,
943     I: Clone + Iterator<Item = &'a Pat<'b>>,
944 {
945     if !has_multiple_ref_pats(pats.clone()) {
946         return;
947     }
948
949     let (first_sugg, msg, title);
950     let span = ex.span.source_callsite();
951     if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
952         first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
953         msg = "try";
954         title = "you don't need to add `&` to both the expression and the patterns";
955     } else {
956         first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
957         msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
958         title = "you don't need to add `&` to all patterns";
959     }
960
961     let remaining_suggs = pats.filter_map(|pat| {
962         if let PatKind::Ref(refp, _) = pat.kind {
963             Some((pat.span, snippet(cx, refp.span, "..").to_string()))
964         } else {
965             None
966         }
967     });
968
969     span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
970         if !expr.span.from_expansion() {
971             multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
972         }
973     });
974 }
975
976 fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
977     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
978         let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
979             is_ref_some_arm(cx, &arms[1])
980         } else if is_none_arm(cx, &arms[1]) {
981             is_ref_some_arm(cx, &arms[0])
982         } else {
983             None
984         };
985         if let Some(rb) = arm_ref {
986             let suggestion = if rb == BindingAnnotation::Ref {
987                 "as_ref"
988             } else {
989                 "as_mut"
990             };
991
992             let output_ty = cx.typeck_results().expr_ty(expr);
993             let input_ty = cx.typeck_results().expr_ty(ex);
994
995             let cast = if_chain! {
996                 if let ty::Adt(_, substs) = input_ty.kind();
997                 let input_ty = substs.type_at(0);
998                 if let ty::Adt(_, substs) = output_ty.kind();
999                 let output_ty = substs.type_at(0);
1000                 if let ty::Ref(_, output_ty, _) = *output_ty.kind();
1001                 if input_ty != output_ty;
1002                 then {
1003                     ".map(|x| x as _)"
1004                 } else {
1005                     ""
1006                 }
1007             };
1008
1009             let mut applicability = Applicability::MachineApplicable;
1010             span_lint_and_sugg(
1011                 cx,
1012                 MATCH_AS_REF,
1013                 expr.span,
1014                 &format!("use `{}()` instead", suggestion),
1015                 "try this",
1016                 format!(
1017                     "{}.{}(){}",
1018                     snippet_with_applicability(cx, ex.span, "_", &mut applicability),
1019                     suggestion,
1020                     cast,
1021                 ),
1022                 applicability,
1023             );
1024         }
1025     }
1026 }
1027
1028 fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
1029     for arm in arms {
1030         if let PatKind::Or(fields) = arm.pat.kind {
1031             // look for multiple fields in this arm that contains at least one Wild pattern
1032             if fields.len() > 1 && fields.iter().any(is_wild) {
1033                 span_lint_and_help(
1034                     cx,
1035                     WILDCARD_IN_OR_PATTERNS,
1036                     arm.pat.span,
1037                     "wildcard pattern covers any other pattern as it will match anyway",
1038                     None,
1039                     "consider handling `_` separately",
1040                 );
1041             }
1042         }
1043     }
1044 }
1045
1046 #[allow(clippy::too_many_lines)]
1047 fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
1048     if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
1049         return;
1050     }
1051
1052     // HACK:
1053     // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
1054     // to prevent false positives as there is currently no better way to detect if code was excluded by
1055     // a macro. See PR #6435
1056     if_chain! {
1057         if let Some(match_snippet) = snippet_opt(cx, expr.span);
1058         if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
1059         if let Some(ex_snippet) = snippet_opt(cx, ex.span);
1060         let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
1061         if rest_snippet.contains("=>");
1062         then {
1063             // The code it self contains another thick arrow "=>"
1064             // -> Either another arm or a comment
1065             return;
1066         }
1067     }
1068
1069     let matched_vars = ex.span;
1070     let bind_names = arms[0].pat.span;
1071     let match_body = peel_blocks(arms[0].body);
1072     let mut snippet_body = if match_body.span.from_expansion() {
1073         Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
1074     } else {
1075         snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
1076     };
1077
1078     // Do we need to add ';' to suggestion ?
1079     match match_body.kind {
1080         ExprKind::Block(block, _) => {
1081             // macro + expr_ty(body) == ()
1082             if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
1083                 snippet_body.push(';');
1084             }
1085         },
1086         _ => {
1087             // expr_ty(body) == ()
1088             if cx.typeck_results().expr_ty(match_body).is_unit() {
1089                 snippet_body.push(';');
1090             }
1091         },
1092     }
1093
1094     let mut applicability = Applicability::MaybeIncorrect;
1095     match arms[0].pat.kind {
1096         PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
1097             // If this match is in a local (`let`) stmt
1098             let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
1099                 (
1100                     parent_let_node.span,
1101                     format!(
1102                         "let {} = {};\n{}let {} = {};",
1103                         snippet_with_applicability(cx, bind_names, "..", &mut applicability),
1104                         snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
1105                         " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
1106                         snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
1107                         snippet_body
1108                     ),
1109                 )
1110             } else {
1111                 // If we are in closure, we need curly braces around suggestion
1112                 let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
1113                 let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string());
1114                 if let Some(parent_expr) = get_parent_expr(cx, expr) {
1115                     if let ExprKind::Closure(..) = parent_expr.kind {
1116                         cbrace_end = format!("\n{}}}", indent);
1117                         // Fix body indent due to the closure
1118                         indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
1119                         cbrace_start = format!("{{\n{}", indent);
1120                     }
1121                 }
1122                 // If the parent is already an arm, and the body is another match statement,
1123                 // we need curly braces around suggestion
1124                 let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
1125                 if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
1126                     if let ExprKind::Match(..) = arm.body.kind {
1127                         cbrace_end = format!("\n{}}}", indent);
1128                         // Fix body indent due to the match
1129                         indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
1130                         cbrace_start = format!("{{\n{}", indent);
1131                     }
1132                 }
1133                 (
1134                     expr.span,
1135                     format!(
1136                         "{}let {} = {};\n{}{}{}",
1137                         cbrace_start,
1138                         snippet_with_applicability(cx, bind_names, "..", &mut applicability),
1139                         snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
1140                         indent,
1141                         snippet_body,
1142                         cbrace_end
1143                     ),
1144                 )
1145             };
1146             span_lint_and_sugg(
1147                 cx,
1148                 MATCH_SINGLE_BINDING,
1149                 target_span,
1150                 "this match could be written as a `let` statement",
1151                 "consider using `let` statement",
1152                 sugg,
1153                 applicability,
1154             );
1155         },
1156         PatKind::Wild => {
1157             if ex.can_have_side_effects() {
1158                 let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0));
1159                 let sugg = format!(
1160                     "{};\n{}{}",
1161                     snippet_with_applicability(cx, ex.span, "..", &mut applicability),
1162                     indent,
1163                     snippet_body
1164                 );
1165                 span_lint_and_sugg(
1166                     cx,
1167                     MATCH_SINGLE_BINDING,
1168                     expr.span,
1169                     "this match could be replaced by its scrutinee and body",
1170                     "consider using the scrutinee and body instead",
1171                     sugg,
1172                     applicability,
1173                 );
1174             } else {
1175                 span_lint_and_sugg(
1176                     cx,
1177                     MATCH_SINGLE_BINDING,
1178                     expr.span,
1179                     "this match could be replaced by its body itself",
1180                     "consider using the match body instead",
1181                     snippet_body,
1182                     Applicability::MachineApplicable,
1183                 );
1184             }
1185         },
1186         _ => (),
1187     }
1188 }
1189
1190 /// Returns true if the `ex` match expression is in a local (`let`) statement
1191 fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
1192     let map = &cx.tcx.hir();
1193     if_chain! {
1194         if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
1195         if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
1196         then {
1197             return Some(parent_let_expr);
1198         }
1199     }
1200     None
1201 }
1202
1203 // Checks if arm has the form `None => None`
1204 fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1205     matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
1206 }
1207
1208 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
1209 fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
1210     if_chain! {
1211         if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
1212         if is_lang_ctor(cx, qpath, OptionSome);
1213         if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
1214         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
1215         if let ExprKind::Call(e, args) = peel_blocks(arm.body).kind;
1216         if let ExprKind::Path(ref some_path) = e.kind;
1217         if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
1218         if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
1219         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
1220         then {
1221             return Some(rb)
1222         }
1223     }
1224     None
1225 }
1226
1227 fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool
1228 where
1229     'b: 'a,
1230     I: Iterator<Item = &'a Pat<'b>>,
1231 {
1232     let mut ref_count = 0;
1233     for opt in pats.map(|pat| match pat.kind {
1234         PatKind::Ref(..) => Some(true), // &-patterns
1235         PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
1236         _ => None,                      // any other pattern is not fine
1237     }) {
1238         if let Some(inner) = opt {
1239             if inner {
1240                 ref_count += 1;
1241             }
1242         } else {
1243             return false;
1244         }
1245     }
1246     ref_count > 1
1247 }