]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/matches/mod.rs
Split out `match_single_binding`
[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::{snippet, snippet_with_applicability};
3 use clippy_utils::sugg::Sugg;
4 use clippy_utils::{is_wild, meets_msrv, msrvs, path_to_local_id, peel_blocks, strip_pat_refs};
5 use core::iter::once;
6 use if_chain::if_chain;
7 use rustc_errors::Applicability;
8 use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Local, MatchSource, Mutability, Pat, PatKind, QPath};
9 use rustc_lint::{LateContext, LateLintPass};
10 use rustc_middle::ty;
11 use rustc_semver::RustcVersion;
12 use rustc_session::{declare_tool_lint, impl_lint_pass};
13
14 mod match_as_ref;
15 mod match_bool;
16 mod match_like_matches;
17 mod match_same_arms;
18 mod match_single_binding;
19 mod match_wild_enum;
20 mod match_wild_err_arm;
21 mod overlapping_arms;
22 mod redundant_pattern_match;
23 mod single_match;
24
25 declare_clippy_lint! {
26     /// ### What it does
27     /// Checks for matches with a single arm where an `if let`
28     /// will usually suffice.
29     ///
30     /// ### Why is this bad?
31     /// Just readability – `if let` nests less than a `match`.
32     ///
33     /// ### Example
34     /// ```rust
35     /// # fn bar(stool: &str) {}
36     /// # let x = Some("abc");
37     /// // Bad
38     /// match x {
39     ///     Some(ref foo) => bar(foo),
40     ///     _ => (),
41     /// }
42     ///
43     /// // Good
44     /// if let Some(ref foo) = x {
45     ///     bar(foo);
46     /// }
47     /// ```
48     #[clippy::version = "pre 1.29.0"]
49     pub SINGLE_MATCH,
50     style,
51     "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
52 }
53
54 declare_clippy_lint! {
55     /// ### What it does
56     /// Checks for matches with two arms where an `if let else` will
57     /// usually suffice.
58     ///
59     /// ### Why is this bad?
60     /// Just readability – `if let` nests less than a `match`.
61     ///
62     /// ### Known problems
63     /// Personal style preferences may differ.
64     ///
65     /// ### Example
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     #[clippy::version = "pre 1.29.0"]
91     pub SINGLE_MATCH_ELSE,
92     pedantic,
93     "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
94 }
95
96 declare_clippy_lint! {
97     /// ### What it does
98     /// Checks for matches where all arms match a reference,
99     /// suggesting to remove the reference and deref the matched expression
100     /// instead. It also checks for `if let &foo = bar` blocks.
101     ///
102     /// ### Why is this bad?
103     /// It just makes the code less readable. That reference
104     /// destructuring adds nothing to the code.
105     ///
106     /// ### Example
107     /// ```rust,ignore
108     /// // Bad
109     /// match x {
110     ///     &A(ref y) => foo(y),
111     ///     &B => bar(),
112     ///     _ => frob(&x),
113     /// }
114     ///
115     /// // Good
116     /// match *x {
117     ///     A(ref y) => foo(y),
118     ///     B => bar(),
119     ///     _ => frob(x),
120     /// }
121     /// ```
122     #[clippy::version = "pre 1.29.0"]
123     pub MATCH_REF_PATS,
124     style,
125     "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
126 }
127
128 declare_clippy_lint! {
129     /// ### What it does
130     /// Checks for matches where match expression is a `bool`. It
131     /// suggests to replace the expression with an `if...else` block.
132     ///
133     /// ### Why is this bad?
134     /// It makes the code less readable.
135     ///
136     /// ### Example
137     /// ```rust
138     /// # fn foo() {}
139     /// # fn bar() {}
140     /// let condition: bool = true;
141     /// match condition {
142     ///     true => foo(),
143     ///     false => bar(),
144     /// }
145     /// ```
146     /// Use if/else instead:
147     /// ```rust
148     /// # fn foo() {}
149     /// # fn bar() {}
150     /// let condition: bool = true;
151     /// if condition {
152     ///     foo();
153     /// } else {
154     ///     bar();
155     /// }
156     /// ```
157     #[clippy::version = "pre 1.29.0"]
158     pub MATCH_BOOL,
159     pedantic,
160     "a `match` on a boolean expression instead of an `if..else` block"
161 }
162
163 declare_clippy_lint! {
164     /// ### What it does
165     /// Checks for overlapping match arms.
166     ///
167     /// ### Why is this bad?
168     /// It is likely to be an error and if not, makes the code
169     /// less obvious.
170     ///
171     /// ### Example
172     /// ```rust
173     /// let x = 5;
174     /// match x {
175     ///     1..=10 => println!("1 ... 10"),
176     ///     5..=15 => println!("5 ... 15"),
177     ///     _ => (),
178     /// }
179     /// ```
180     #[clippy::version = "pre 1.29.0"]
181     pub MATCH_OVERLAPPING_ARM,
182     style,
183     "a `match` with overlapping arms"
184 }
185
186 declare_clippy_lint! {
187     /// ### What it does
188     /// Checks for arm which matches all errors with `Err(_)`
189     /// and take drastic actions like `panic!`.
190     ///
191     /// ### Why is this bad?
192     /// It is generally a bad practice, similar to
193     /// catching all exceptions in java with `catch(Exception)`
194     ///
195     /// ### Example
196     /// ```rust
197     /// let x: Result<i32, &str> = Ok(3);
198     /// match x {
199     ///     Ok(_) => println!("ok"),
200     ///     Err(_) => panic!("err"),
201     /// }
202     /// ```
203     #[clippy::version = "pre 1.29.0"]
204     pub MATCH_WILD_ERR_ARM,
205     pedantic,
206     "a `match` with `Err(_)` arm and take drastic actions"
207 }
208
209 declare_clippy_lint! {
210     /// ### What it does
211     /// Checks for match which is used to add a reference to an
212     /// `Option` value.
213     ///
214     /// ### Why is this bad?
215     /// Using `as_ref()` or `as_mut()` instead is shorter.
216     ///
217     /// ### Example
218     /// ```rust
219     /// let x: Option<()> = None;
220     ///
221     /// // Bad
222     /// let r: Option<&()> = match x {
223     ///     None => None,
224     ///     Some(ref v) => Some(v),
225     /// };
226     ///
227     /// // Good
228     /// let r: Option<&()> = x.as_ref();
229     /// ```
230     #[clippy::version = "pre 1.29.0"]
231     pub MATCH_AS_REF,
232     complexity,
233     "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
234 }
235
236 declare_clippy_lint! {
237     /// ### What it does
238     /// Checks for wildcard enum matches using `_`.
239     ///
240     /// ### Why is this bad?
241     /// New enum variants added by library updates can be missed.
242     ///
243     /// ### Known problems
244     /// Suggested replacements may be incorrect if guards exhaustively cover some
245     /// variants, and also may not use correct path to enum if it's not present in the current scope.
246     ///
247     /// ### Example
248     /// ```rust
249     /// # enum Foo { A(usize), B(usize) }
250     /// # let x = Foo::B(1);
251     /// // Bad
252     /// match x {
253     ///     Foo::A(_) => {},
254     ///     _ => {},
255     /// }
256     ///
257     /// // Good
258     /// match x {
259     ///     Foo::A(_) => {},
260     ///     Foo::B(_) => {},
261     /// }
262     /// ```
263     #[clippy::version = "1.34.0"]
264     pub WILDCARD_ENUM_MATCH_ARM,
265     restriction,
266     "a wildcard enum match arm using `_`"
267 }
268
269 declare_clippy_lint! {
270     /// ### What it does
271     /// Checks for wildcard enum matches for a single variant.
272     ///
273     /// ### Why is this bad?
274     /// New enum variants added by library updates can be missed.
275     ///
276     /// ### Known problems
277     /// Suggested replacements may not use correct path to enum
278     /// if it's not present in the current scope.
279     ///
280     /// ### Example
281     /// ```rust
282     /// # enum Foo { A, B, C }
283     /// # let x = Foo::B;
284     /// // Bad
285     /// match x {
286     ///     Foo::A => {},
287     ///     Foo::B => {},
288     ///     _ => {},
289     /// }
290     ///
291     /// // Good
292     /// match x {
293     ///     Foo::A => {},
294     ///     Foo::B => {},
295     ///     Foo::C => {},
296     /// }
297     /// ```
298     #[clippy::version = "1.45.0"]
299     pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
300     pedantic,
301     "a wildcard enum match for a single variant"
302 }
303
304 declare_clippy_lint! {
305     /// ### What it does
306     /// Checks for wildcard pattern used with others patterns in same match arm.
307     ///
308     /// ### Why is this bad?
309     /// Wildcard pattern already covers any other pattern as it will match anyway.
310     /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
311     ///
312     /// ### Example
313     /// ```rust
314     /// // Bad
315     /// match "foo" {
316     ///     "a" => {},
317     ///     "bar" | _ => {},
318     /// }
319     ///
320     /// // Good
321     /// match "foo" {
322     ///     "a" => {},
323     ///     _ => {},
324     /// }
325     /// ```
326     #[clippy::version = "1.42.0"]
327     pub WILDCARD_IN_OR_PATTERNS,
328     complexity,
329     "a wildcard pattern used with others patterns in same match arm"
330 }
331
332 declare_clippy_lint! {
333     /// ### What it does
334     /// Checks for matches being used to destructure a single-variant enum
335     /// or tuple struct where a `let` will suffice.
336     ///
337     /// ### Why is this bad?
338     /// Just readability – `let` doesn't nest, whereas a `match` does.
339     ///
340     /// ### Example
341     /// ```rust
342     /// enum Wrapper {
343     ///     Data(i32),
344     /// }
345     ///
346     /// let wrapper = Wrapper::Data(42);
347     ///
348     /// let data = match wrapper {
349     ///     Wrapper::Data(i) => i,
350     /// };
351     /// ```
352     ///
353     /// The correct use would be:
354     /// ```rust
355     /// enum Wrapper {
356     ///     Data(i32),
357     /// }
358     ///
359     /// let wrapper = Wrapper::Data(42);
360     /// let Wrapper::Data(data) = wrapper;
361     /// ```
362     #[clippy::version = "pre 1.29.0"]
363     pub INFALLIBLE_DESTRUCTURING_MATCH,
364     style,
365     "a `match` statement with a single infallible arm instead of a `let`"
366 }
367
368 declare_clippy_lint! {
369     /// ### What it does
370     /// Checks for useless match that binds to only one value.
371     ///
372     /// ### Why is this bad?
373     /// Readability and needless complexity.
374     ///
375     /// ### Known problems
376     ///  Suggested replacements may be incorrect when `match`
377     /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
378     ///
379     /// ### Example
380     /// ```rust
381     /// # let a = 1;
382     /// # let b = 2;
383     ///
384     /// // Bad
385     /// match (a, b) {
386     ///     (c, d) => {
387     ///         // useless match
388     ///     }
389     /// }
390     ///
391     /// // Good
392     /// let (c, d) = (a, b);
393     /// ```
394     #[clippy::version = "1.43.0"]
395     pub MATCH_SINGLE_BINDING,
396     complexity,
397     "a match with a single binding instead of using `let` statement"
398 }
399
400 declare_clippy_lint! {
401     /// ### What it does
402     /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
403     ///
404     /// ### Why is this bad?
405     /// Correctness and readability. It's like having a wildcard pattern after
406     /// matching all enum variants explicitly.
407     ///
408     /// ### Example
409     /// ```rust
410     /// # struct A { a: i32 }
411     /// let a = A { a: 5 };
412     ///
413     /// // Bad
414     /// match a {
415     ///     A { a: 5, .. } => {},
416     ///     _ => {},
417     /// }
418     ///
419     /// // Good
420     /// match a {
421     ///     A { a: 5 } => {},
422     ///     _ => {},
423     /// }
424     /// ```
425     #[clippy::version = "1.43.0"]
426     pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
427     restriction,
428     "a match on a struct that binds all fields but still uses the wildcard pattern"
429 }
430
431 declare_clippy_lint! {
432     /// ### What it does
433     /// Lint for redundant pattern matching over `Result`, `Option`,
434     /// `std::task::Poll` or `std::net::IpAddr`
435     ///
436     /// ### Why is this bad?
437     /// It's more concise and clear to just use the proper
438     /// utility function
439     ///
440     /// ### Known problems
441     /// This will change the drop order for the matched type. Both `if let` and
442     /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
443     /// value before entering the block. For most types this change will not matter, but for a few
444     /// types this will not be an acceptable change (e.g. locks). See the
445     /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
446     /// drop order.
447     ///
448     /// ### Example
449     /// ```rust
450     /// # use std::task::Poll;
451     /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
452     /// if let Ok(_) = Ok::<i32, i32>(42) {}
453     /// if let Err(_) = Err::<i32, i32>(42) {}
454     /// if let None = None::<()> {}
455     /// if let Some(_) = Some(42) {}
456     /// if let Poll::Pending = Poll::Pending::<()> {}
457     /// if let Poll::Ready(_) = Poll::Ready(42) {}
458     /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
459     /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
460     /// match Ok::<i32, i32>(42) {
461     ///     Ok(_) => true,
462     ///     Err(_) => false,
463     /// };
464     /// ```
465     ///
466     /// The more idiomatic use would be:
467     ///
468     /// ```rust
469     /// # use std::task::Poll;
470     /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
471     /// if Ok::<i32, i32>(42).is_ok() {}
472     /// if Err::<i32, i32>(42).is_err() {}
473     /// if None::<()>.is_none() {}
474     /// if Some(42).is_some() {}
475     /// if Poll::Pending::<()>.is_pending() {}
476     /// if Poll::Ready(42).is_ready() {}
477     /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
478     /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
479     /// Ok::<i32, i32>(42).is_ok();
480     /// ```
481     #[clippy::version = "1.31.0"]
482     pub REDUNDANT_PATTERN_MATCHING,
483     style,
484     "use the proper utility function avoiding an `if let`"
485 }
486
487 declare_clippy_lint! {
488     /// ### What it does
489     /// Checks for `match`  or `if let` expressions producing a
490     /// `bool` that could be written using `matches!`
491     ///
492     /// ### Why is this bad?
493     /// Readability and needless complexity.
494     ///
495     /// ### Known problems
496     /// This lint falsely triggers, if there are arms with
497     /// `cfg` attributes that remove an arm evaluating to `false`.
498     ///
499     /// ### Example
500     /// ```rust
501     /// let x = Some(5);
502     ///
503     /// // Bad
504     /// let a = match x {
505     ///     Some(0) => true,
506     ///     _ => false,
507     /// };
508     ///
509     /// let a = if let Some(0) = x {
510     ///     true
511     /// } else {
512     ///     false
513     /// };
514     ///
515     /// // Good
516     /// let a = matches!(x, Some(0));
517     /// ```
518     #[clippy::version = "1.47.0"]
519     pub MATCH_LIKE_MATCHES_MACRO,
520     style,
521     "a match that could be written with the matches! macro"
522 }
523
524 declare_clippy_lint! {
525     /// ### What it does
526     /// Checks for `match` with identical arm bodies.
527     ///
528     /// ### Why is this bad?
529     /// This is probably a copy & paste error. If arm bodies
530     /// are the same on purpose, you can factor them
531     /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
532     ///
533     /// ### Known problems
534     /// False positive possible with order dependent `match`
535     /// (see issue
536     /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
537     ///
538     /// ### Example
539     /// ```rust,ignore
540     /// match foo {
541     ///     Bar => bar(),
542     ///     Quz => quz(),
543     ///     Baz => bar(), // <= oops
544     /// }
545     /// ```
546     ///
547     /// This should probably be
548     /// ```rust,ignore
549     /// match foo {
550     ///     Bar => bar(),
551     ///     Quz => quz(),
552     ///     Baz => baz(), // <= fixed
553     /// }
554     /// ```
555     ///
556     /// or if the original code was not a typo:
557     /// ```rust,ignore
558     /// match foo {
559     ///     Bar | Baz => bar(), // <= shows the intent better
560     ///     Quz => quz(),
561     /// }
562     /// ```
563     #[clippy::version = "pre 1.29.0"]
564     pub MATCH_SAME_ARMS,
565     pedantic,
566     "`match` with identical arm bodies"
567 }
568
569 #[derive(Default)]
570 pub struct Matches {
571     msrv: Option<RustcVersion>,
572     infallible_destructuring_match_linted: bool,
573 }
574
575 impl Matches {
576     #[must_use]
577     pub fn new(msrv: Option<RustcVersion>) -> Self {
578         Self {
579             msrv,
580             ..Matches::default()
581         }
582     }
583 }
584
585 impl_lint_pass!(Matches => [
586     SINGLE_MATCH,
587     MATCH_REF_PATS,
588     MATCH_BOOL,
589     SINGLE_MATCH_ELSE,
590     MATCH_OVERLAPPING_ARM,
591     MATCH_WILD_ERR_ARM,
592     MATCH_AS_REF,
593     WILDCARD_ENUM_MATCH_ARM,
594     MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
595     WILDCARD_IN_OR_PATTERNS,
596     MATCH_SINGLE_BINDING,
597     INFALLIBLE_DESTRUCTURING_MATCH,
598     REST_PAT_IN_FULLY_BOUND_STRUCTS,
599     REDUNDANT_PATTERN_MATCHING,
600     MATCH_LIKE_MATCHES_MACRO,
601     MATCH_SAME_ARMS,
602 ]);
603
604 impl<'tcx> LateLintPass<'tcx> for Matches {
605     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
606         if expr.span.from_expansion() {
607             return;
608         }
609
610         redundant_pattern_match::check(cx, expr);
611
612         if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
613             if !match_like_matches::check(cx, expr) {
614                 match_same_arms::check(cx, expr);
615             }
616         } else {
617             match_same_arms::check(cx, expr);
618         }
619
620         if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
621             single_match::check(cx, ex, arms, expr);
622             match_bool::check(cx, ex, arms, expr);
623             overlapping_arms::check(cx, ex, arms);
624             match_wild_err_arm::check(cx, ex, arms);
625             match_wild_enum::check(cx, ex, arms);
626             match_as_ref::check(cx, ex, arms, expr);
627             check_wild_in_or_pats(cx, arms);
628
629             if self.infallible_destructuring_match_linted {
630                 self.infallible_destructuring_match_linted = false;
631             } else {
632                 match_single_binding::check(cx, ex, arms, expr);
633             }
634         }
635         if let ExprKind::Match(ex, arms, _) = expr.kind {
636             check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
637         }
638     }
639
640     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
641         if_chain! {
642             if !local.span.from_expansion();
643             if let Some(expr) = local.init;
644             if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
645             if arms.len() == 1 && arms[0].guard.is_none();
646             if let PatKind::TupleStruct(
647                 QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
648             if args.len() == 1;
649             if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
650             let body = peel_blocks(arms[0].body);
651             if path_to_local_id(body, arg);
652
653             then {
654                 let mut applicability = Applicability::MachineApplicable;
655                 self.infallible_destructuring_match_linted = true;
656                 span_lint_and_sugg(
657                     cx,
658                     INFALLIBLE_DESTRUCTURING_MATCH,
659                     local.span,
660                     "you seem to be trying to use `match` to destructure a single infallible pattern. \
661                     Consider using `let`",
662                     "try this",
663                     format!(
664                         "let {}({}) = {};",
665                         snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
666                         snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
667                         snippet_with_applicability(cx, target.span, "..", &mut applicability),
668                     ),
669                     applicability,
670                 );
671             }
672         }
673     }
674
675     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
676         if_chain! {
677             if !pat.span.from_expansion();
678             if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
679             if let Some(def_id) = path.res.opt_def_id();
680             let ty = cx.tcx.type_of(def_id);
681             if let ty::Adt(def, _) = ty.kind();
682             if def.is_struct() || def.is_union();
683             if fields.len() == def.non_enum_variant().fields.len();
684
685             then {
686                 span_lint_and_help(
687                     cx,
688                     REST_PAT_IN_FULLY_BOUND_STRUCTS,
689                     pat.span,
690                     "unnecessary use of `..` pattern in struct binding. All fields were already bound",
691                     None,
692                     "consider removing `..` from this binding",
693                 );
694             }
695         }
696     }
697
698     extract_msrv_attr!(LateContext);
699 }
700
701 fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
702 where
703     'b: 'a,
704     I: Clone + Iterator<Item = &'a Pat<'b>>,
705 {
706     if !has_multiple_ref_pats(pats.clone()) {
707         return;
708     }
709
710     let (first_sugg, msg, title);
711     let span = ex.span.source_callsite();
712     if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
713         first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
714         msg = "try";
715         title = "you don't need to add `&` to both the expression and the patterns";
716     } else {
717         first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
718         msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
719         title = "you don't need to add `&` to all patterns";
720     }
721
722     let remaining_suggs = pats.filter_map(|pat| {
723         if let PatKind::Ref(refp, _) = pat.kind {
724             Some((pat.span, snippet(cx, refp.span, "..").to_string()))
725         } else {
726             None
727         }
728     });
729
730     span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
731         if !expr.span.from_expansion() {
732             multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
733         }
734     });
735 }
736
737 fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
738     for arm in arms {
739         if let PatKind::Or(fields) = arm.pat.kind {
740             // look for multiple fields in this arm that contains at least one Wild pattern
741             if fields.len() > 1 && fields.iter().any(is_wild) {
742                 span_lint_and_help(
743                     cx,
744                     WILDCARD_IN_OR_PATTERNS,
745                     arm.pat.span,
746                     "wildcard pattern covers any other pattern as it will match anyway",
747                     None,
748                     "consider handling `_` separately",
749                 );
750             }
751         }
752     }
753 }
754
755 fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool
756 where
757     'b: 'a,
758     I: Iterator<Item = &'a Pat<'b>>,
759 {
760     let mut ref_count = 0;
761     for opt in pats.map(|pat| match pat.kind {
762         PatKind::Ref(..) => Some(true), // &-patterns
763         PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
764         _ => None,                      // any other pattern is not fine
765     }) {
766         if let Some(inner) = opt {
767             if inner {
768                 ref_count += 1;
769             }
770         } else {
771             return false;
772         }
773     }
774     ref_count > 1
775 }