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