]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/matches.rs
Rollup merge of #91391 - GuillaumeGomez:simplify-no-headless, r=jsha
[rust.git] / src / tools / clippy / clippy_lints / src / matches.rs
1 use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
2 use clippy_utils::diagnostics::{
3     multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
4 };
5 use clippy_utils::higher;
6 use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
7 use clippy_utils::sugg::Sugg;
8 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
9 use clippy_utils::visitors::is_local_used;
10 use clippy_utils::{
11     get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild,
12     meets_msrv, msrvs, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
13     remove_blocks, strip_pat_refs,
14 };
15 use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
16 use core::iter::{once, ExactSizeIterator};
17 use if_chain::if_chain;
18 use rustc_ast::ast::{Attribute, LitKind};
19 use rustc_errors::Applicability;
20 use rustc_hir::def::{CtorKind, DefKind, Res};
21 use rustc_hir::LangItem::{OptionNone, OptionSome};
22 use rustc_hir::{
23     self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
24     Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
25 };
26 use rustc_hir::{HirIdMap, HirIdSet};
27 use rustc_lint::{LateContext, LateLintPass, LintContext};
28 use rustc_middle::lint::in_external_macro;
29 use rustc_middle::ty::{self, Ty, TyS, VariantDef};
30 use rustc_semver::RustcVersion;
31 use rustc_session::{declare_tool_lint, impl_lint_pass};
32 use rustc_span::source_map::{Span, Spanned};
33 use rustc_span::sym;
34 use std::cmp::Ordering;
35 use std::collections::hash_map::Entry;
36 use std::iter;
37 use std::ops::Bound;
38
39 declare_clippy_lint! {
40     /// ### What it does
41     /// Checks for matches with a single arm where an `if let`
42     /// will usually suffice.
43     ///
44     /// ### Why is this bad?
45     /// Just readability – `if let` nests less than a `match`.
46     ///
47     /// ### Example
48     /// ```rust
49     /// # fn bar(stool: &str) {}
50     /// # let x = Some("abc");
51     /// // Bad
52     /// match x {
53     ///     Some(ref foo) => bar(foo),
54     ///     _ => (),
55     /// }
56     ///
57     /// // Good
58     /// if let Some(ref foo) = x {
59     ///     bar(foo);
60     /// }
61     /// ```
62     pub SINGLE_MATCH,
63     style,
64     "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
65 }
66
67 declare_clippy_lint! {
68     /// ### What it does
69     /// Checks for matches with two arms where an `if let else` will
70     /// usually suffice.
71     ///
72     /// ### Why is this bad?
73     /// Just readability – `if let` nests less than a `match`.
74     ///
75     /// ### Known problems
76     /// Personal style preferences may differ.
77     ///
78     /// ### Example
79     /// Using `match`:
80     ///
81     /// ```rust
82     /// # fn bar(foo: &usize) {}
83     /// # let other_ref: usize = 1;
84     /// # let x: Option<&usize> = Some(&1);
85     /// match x {
86     ///     Some(ref foo) => bar(foo),
87     ///     _ => bar(&other_ref),
88     /// }
89     /// ```
90     ///
91     /// Using `if let` with `else`:
92     ///
93     /// ```rust
94     /// # fn bar(foo: &usize) {}
95     /// # let other_ref: usize = 1;
96     /// # let x: Option<&usize> = Some(&1);
97     /// if let Some(ref foo) = x {
98     ///     bar(foo);
99     /// } else {
100     ///     bar(&other_ref);
101     /// }
102     /// ```
103     pub SINGLE_MATCH_ELSE,
104     pedantic,
105     "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
106 }
107
108 declare_clippy_lint! {
109     /// ### What it does
110     /// Checks for matches where all arms match a reference,
111     /// suggesting to remove the reference and deref the matched expression
112     /// instead. It also checks for `if let &foo = bar` blocks.
113     ///
114     /// ### Why is this bad?
115     /// It just makes the code less readable. That reference
116     /// destructuring adds nothing to the code.
117     ///
118     /// ### Example
119     /// ```rust,ignore
120     /// // Bad
121     /// match x {
122     ///     &A(ref y) => foo(y),
123     ///     &B => bar(),
124     ///     _ => frob(&x),
125     /// }
126     ///
127     /// // Good
128     /// match *x {
129     ///     A(ref y) => foo(y),
130     ///     B => bar(),
131     ///     _ => frob(x),
132     /// }
133     /// ```
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     pub MATCH_BOOL,
169     pedantic,
170     "a `match` on a boolean expression instead of an `if..else` block"
171 }
172
173 declare_clippy_lint! {
174     /// ### What it does
175     /// Checks for overlapping match arms.
176     ///
177     /// ### Why is this bad?
178     /// It is likely to be an error and if not, makes the code
179     /// less obvious.
180     ///
181     /// ### Example
182     /// ```rust
183     /// let x = 5;
184     /// match x {
185     ///     1..=10 => println!("1 ... 10"),
186     ///     5..=15 => println!("5 ... 15"),
187     ///     _ => (),
188     /// }
189     /// ```
190     pub MATCH_OVERLAPPING_ARM,
191     style,
192     "a `match` with overlapping arms"
193 }
194
195 declare_clippy_lint! {
196     /// ### What it does
197     /// Checks for arm which matches all errors with `Err(_)`
198     /// and take drastic actions like `panic!`.
199     ///
200     /// ### Why is this bad?
201     /// It is generally a bad practice, similar to
202     /// catching all exceptions in java with `catch(Exception)`
203     ///
204     /// ### Example
205     /// ```rust
206     /// let x: Result<i32, &str> = Ok(3);
207     /// match x {
208     ///     Ok(_) => println!("ok"),
209     ///     Err(_) => panic!("err"),
210     /// }
211     /// ```
212     pub MATCH_WILD_ERR_ARM,
213     pedantic,
214     "a `match` with `Err(_)` arm and take drastic actions"
215 }
216
217 declare_clippy_lint! {
218     /// ### What it does
219     /// Checks for match which is used to add a reference to an
220     /// `Option` value.
221     ///
222     /// ### Why is this bad?
223     /// Using `as_ref()` or `as_mut()` instead is shorter.
224     ///
225     /// ### Example
226     /// ```rust
227     /// let x: Option<()> = None;
228     ///
229     /// // Bad
230     /// let r: Option<&()> = match x {
231     ///     None => None,
232     ///     Some(ref v) => Some(v),
233     /// };
234     ///
235     /// // Good
236     /// let r: Option<&()> = x.as_ref();
237     /// ```
238     pub MATCH_AS_REF,
239     complexity,
240     "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
241 }
242
243 declare_clippy_lint! {
244     /// ### What it does
245     /// Checks for wildcard enum matches using `_`.
246     ///
247     /// ### Why is this bad?
248     /// New enum variants added by library updates can be missed.
249     ///
250     /// ### Known problems
251     /// Suggested replacements may be incorrect if guards exhaustively cover some
252     /// variants, and also may not use correct path to enum if it's not present in the current scope.
253     ///
254     /// ### Example
255     /// ```rust
256     /// # enum Foo { A(usize), B(usize) }
257     /// # let x = Foo::B(1);
258     /// // Bad
259     /// match x {
260     ///     Foo::A(_) => {},
261     ///     _ => {},
262     /// }
263     ///
264     /// // Good
265     /// match x {
266     ///     Foo::A(_) => {},
267     ///     Foo::B(_) => {},
268     /// }
269     /// ```
270     pub WILDCARD_ENUM_MATCH_ARM,
271     restriction,
272     "a wildcard enum match arm using `_`"
273 }
274
275 declare_clippy_lint! {
276     /// ### What it does
277     /// Checks for wildcard enum matches for a single variant.
278     ///
279     /// ### Why is this bad?
280     /// New enum variants added by library updates can be missed.
281     ///
282     /// ### Known problems
283     /// Suggested replacements may not use correct path to enum
284     /// if it's not present in the current scope.
285     ///
286     /// ### Example
287     /// ```rust
288     /// # enum Foo { A, B, C }
289     /// # let x = Foo::B;
290     /// // Bad
291     /// match x {
292     ///     Foo::A => {},
293     ///     Foo::B => {},
294     ///     _ => {},
295     /// }
296     ///
297     /// // Good
298     /// match x {
299     ///     Foo::A => {},
300     ///     Foo::B => {},
301     ///     Foo::C => {},
302     /// }
303     /// ```
304     pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
305     pedantic,
306     "a wildcard enum match for a single variant"
307 }
308
309 declare_clippy_lint! {
310     /// ### What it does
311     /// Checks for wildcard pattern used with others patterns in same match arm.
312     ///
313     /// ### Why is this bad?
314     /// Wildcard pattern already covers any other pattern as it will match anyway.
315     /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
316     ///
317     /// ### Example
318     /// ```rust
319     /// // Bad
320     /// match "foo" {
321     ///     "a" => {},
322     ///     "bar" | _ => {},
323     /// }
324     ///
325     /// // Good
326     /// match "foo" {
327     ///     "a" => {},
328     ///     _ => {},
329     /// }
330     /// ```
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     pub INFALLIBLE_DESTRUCTURING_MATCH,
367     style,
368     "a `match` statement with a single infallible arm instead of a `let`"
369 }
370
371 declare_clippy_lint! {
372     /// ### What it does
373     /// Checks for useless match that binds to only one value.
374     ///
375     /// ### Why is this bad?
376     /// Readability and needless complexity.
377     ///
378     /// ### Known problems
379     ///  Suggested replacements may be incorrect when `match`
380     /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
381     ///
382     /// ### Example
383     /// ```rust
384     /// # let a = 1;
385     /// # let b = 2;
386     ///
387     /// // Bad
388     /// match (a, b) {
389     ///     (c, d) => {
390     ///         // useless match
391     ///     }
392     /// }
393     ///
394     /// // Good
395     /// let (c, d) = (a, b);
396     /// ```
397     pub MATCH_SINGLE_BINDING,
398     complexity,
399     "a match with a single binding instead of using `let` statement"
400 }
401
402 declare_clippy_lint! {
403     /// ### What it does
404     /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
405     ///
406     /// ### Why is this bad?
407     /// Correctness and readability. It's like having a wildcard pattern after
408     /// matching all enum variants explicitly.
409     ///
410     /// ### Example
411     /// ```rust
412     /// # struct A { a: i32 }
413     /// let a = A { a: 5 };
414     ///
415     /// // Bad
416     /// match a {
417     ///     A { a: 5, .. } => {},
418     ///     _ => {},
419     /// }
420     ///
421     /// // Good
422     /// match a {
423     ///     A { a: 5 } => {},
424     ///     _ => {},
425     /// }
426     /// ```
427     pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
428     restriction,
429     "a match on a struct that binds all fields but still uses the wildcard pattern"
430 }
431
432 declare_clippy_lint! {
433     /// ### What it does
434     /// Lint for redundant pattern matching over `Result`, `Option`,
435     /// `std::task::Poll` or `std::net::IpAddr`
436     ///
437     /// ### Why is this bad?
438     /// It's more concise and clear to just use the proper
439     /// utility function
440     ///
441     /// ### Known problems
442     /// This will change the drop order for the matched type. Both `if let` and
443     /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
444     /// value before entering the block. For most types this change will not matter, but for a few
445     /// types this will not be an acceptable change (e.g. locks). See the
446     /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
447     /// drop order.
448     ///
449     /// ### Example
450     /// ```rust
451     /// # use std::task::Poll;
452     /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
453     /// if let Ok(_) = Ok::<i32, i32>(42) {}
454     /// if let Err(_) = Err::<i32, i32>(42) {}
455     /// if let None = None::<()> {}
456     /// if let Some(_) = Some(42) {}
457     /// if let Poll::Pending = Poll::Pending::<()> {}
458     /// if let Poll::Ready(_) = Poll::Ready(42) {}
459     /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
460     /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
461     /// match Ok::<i32, i32>(42) {
462     ///     Ok(_) => true,
463     ///     Err(_) => false,
464     /// };
465     /// ```
466     ///
467     /// The more idiomatic use would be:
468     ///
469     /// ```rust
470     /// # use std::task::Poll;
471     /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
472     /// if Ok::<i32, i32>(42).is_ok() {}
473     /// if Err::<i32, i32>(42).is_err() {}
474     /// if None::<()>.is_none() {}
475     /// if Some(42).is_some() {}
476     /// if Poll::Pending::<()>.is_pending() {}
477     /// if Poll::Ready(42).is_ready() {}
478     /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
479     /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
480     /// Ok::<i32, i32>(42).is_ok();
481     /// ```
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     pub MATCH_LIKE_MATCHES_MACRO,
519     style,
520     "a match that could be written with the matches! macro"
521 }
522
523 declare_clippy_lint! {
524     /// ### What it does
525     /// Checks for `match` with identical arm bodies.
526     ///
527     /// ### Why is this bad?
528     /// This is probably a copy & paste error. If arm bodies
529     /// are the same on purpose, you can factor them
530     /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
531     ///
532     /// ### Known problems
533     /// False positive possible with order dependent `match`
534     /// (see issue
535     /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
536     ///
537     /// ### Example
538     /// ```rust,ignore
539     /// match foo {
540     ///     Bar => bar(),
541     ///     Quz => quz(),
542     ///     Baz => bar(), // <= oops
543     /// }
544     /// ```
545     ///
546     /// This should probably be
547     /// ```rust,ignore
548     /// match foo {
549     ///     Bar => bar(),
550     ///     Quz => quz(),
551     ///     Baz => baz(), // <= fixed
552     /// }
553     /// ```
554     ///
555     /// or if the original code was not a typo:
556     /// ```rust,ignore
557     /// match foo {
558     ///     Bar | Baz => bar(), // <= shows the intent better
559     ///     Quz => quz(),
560     /// }
561     /// ```
562     pub MATCH_SAME_ARMS,
563     pedantic,
564     "`match` with identical arm bodies"
565 }
566
567 #[derive(Default)]
568 pub struct Matches {
569     msrv: Option<RustcVersion>,
570     infallible_destructuring_match_linted: bool,
571 }
572
573 impl Matches {
574     #[must_use]
575     pub fn new(msrv: Option<RustcVersion>) -> Self {
576         Self {
577             msrv,
578             ..Matches::default()
579         }
580     }
581 }
582
583 impl_lint_pass!(Matches => [
584     SINGLE_MATCH,
585     MATCH_REF_PATS,
586     MATCH_BOOL,
587     SINGLE_MATCH_ELSE,
588     MATCH_OVERLAPPING_ARM,
589     MATCH_WILD_ERR_ARM,
590     MATCH_AS_REF,
591     WILDCARD_ENUM_MATCH_ARM,
592     MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
593     WILDCARD_IN_OR_PATTERNS,
594     MATCH_SINGLE_BINDING,
595     INFALLIBLE_DESTRUCTURING_MATCH,
596     REST_PAT_IN_FULLY_BOUND_STRUCTS,
597     REDUNDANT_PATTERN_MATCHING,
598     MATCH_LIKE_MATCHES_MACRO,
599     MATCH_SAME_ARMS,
600 ]);
601
602 impl<'tcx> LateLintPass<'tcx> for Matches {
603     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
604         if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
605             return;
606         }
607
608         redundant_pattern_match::check(cx, expr);
609
610         if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
611             if !check_match_like_matches(cx, expr) {
612                 lint_match_arms(cx, expr);
613             }
614         } else {
615             lint_match_arms(cx, expr);
616         }
617
618         if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
619             check_single_match(cx, ex, arms, expr);
620             check_match_bool(cx, ex, arms, expr);
621             check_overlapping_arms(cx, ex, arms);
622             check_wild_err_arm(cx, ex, arms);
623             check_wild_enum_match(cx, ex, arms);
624             check_match_as_ref(cx, ex, arms, expr);
625             check_wild_in_or_pats(cx, arms);
626
627             if self.infallible_destructuring_match_linted {
628                 self.infallible_destructuring_match_linted = false;
629             } else {
630                 check_match_single_binding(cx, ex, arms, expr);
631             }
632         }
633         if let ExprKind::Match(ex, arms, _) = expr.kind {
634             check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
635         }
636         if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
637             check_match_ref_pats(cx, let_expr, once(let_pat), expr);
638         }
639     }
640
641     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
642         if_chain! {
643             if !in_external_macro(cx.sess(), local.span);
644             if !in_macro(local.span);
645             if let Some(expr) = local.init;
646             if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
647             if arms.len() == 1 && arms[0].guard.is_none();
648             if let PatKind::TupleStruct(
649                 QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
650             if args.len() == 1;
651             if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
652             let body = remove_blocks(arms[0].body);
653             if path_to_local_id(body, arg);
654
655             then {
656                 let mut applicability = Applicability::MachineApplicable;
657                 self.infallible_destructuring_match_linted = true;
658                 span_lint_and_sugg(
659                     cx,
660                     INFALLIBLE_DESTRUCTURING_MATCH,
661                     local.span,
662                     "you seem to be trying to use `match` to destructure a single infallible pattern. \
663                     Consider using `let`",
664                     "try this",
665                     format!(
666                         "let {}({}) = {};",
667                         snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
668                         snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
669                         snippet_with_applicability(cx, target.span, "..", &mut applicability),
670                     ),
671                     applicability,
672                 );
673             }
674         }
675     }
676
677     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
678         if_chain! {
679             if !in_external_macro(cx.sess(), pat.span);
680             if !in_macro(pat.span);
681             if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
682             if let Some(def_id) = path.res.opt_def_id();
683             let ty = cx.tcx.type_of(def_id);
684             if let ty::Adt(def, _) = ty.kind();
685             if def.is_struct() || def.is_union();
686             if fields.len() == def.non_enum_variant().fields.len();
687
688             then {
689                 span_lint_and_help(
690                     cx,
691                     REST_PAT_IN_FULLY_BOUND_STRUCTS,
692                     pat.span,
693                     "unnecessary use of `..` pattern in struct binding. All fields were already bound",
694                     None,
695                     "consider removing `..` from this binding",
696                 );
697             }
698         }
699     }
700
701     extract_msrv_attr!(LateContext);
702 }
703
704 #[rustfmt::skip]
705 fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
706     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
707         if in_macro(expr.span) {
708             // Don't lint match expressions present in
709             // macro_rules! block
710             return;
711         }
712         if let PatKind::Or(..) = arms[0].pat.kind {
713             // don't lint for or patterns for now, this makes
714             // the lint noisy in unnecessary situations
715             return;
716         }
717         let els = arms[1].body;
718         let els = if is_unit_expr(remove_blocks(els)) {
719             None
720         } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
721             if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
722                 // single statement/expr "else" block, don't lint
723                 return;
724             }
725             // block with 2+ statements or 1 expr and 1+ statement
726             Some(els)
727         } else {
728             // not a block, don't lint
729             return;
730         };
731
732         let ty = cx.typeck_results().expr_ty(ex);
733         if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
734             check_single_match_single_pattern(cx, ex, arms, expr, els);
735             check_single_match_opt_like(cx, ex, arms, expr, ty, els);
736         }
737     }
738 }
739
740 fn check_single_match_single_pattern(
741     cx: &LateContext<'_>,
742     ex: &Expr<'_>,
743     arms: &[Arm<'_>],
744     expr: &Expr<'_>,
745     els: Option<&Expr<'_>>,
746 ) {
747     if is_wild(arms[1].pat) {
748         report_single_match_single_pattern(cx, ex, arms, expr, els);
749     }
750 }
751
752 fn report_single_match_single_pattern(
753     cx: &LateContext<'_>,
754     ex: &Expr<'_>,
755     arms: &[Arm<'_>],
756     expr: &Expr<'_>,
757     els: Option<&Expr<'_>>,
758 ) {
759     let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
760     let els_str = els.map_or(String::new(), |els| {
761         format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
762     });
763
764     let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
765     let (msg, sugg) = if_chain! {
766         if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
767         let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
768         if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
769         if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
770         if ty.is_integral() || ty.is_char() || ty.is_str()
771             || (implements_trait(cx, ty, spe_trait_id, &[])
772                 && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
773         then {
774             // scrutinee derives PartialEq and the pattern is a constant.
775             let pat_ref_count = match pat.kind {
776                 // string literals are already a reference.
777                 PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
778                 _ => pat_ref_count,
779             };
780             // References are only implicitly added to the pattern, so no overflow here.
781             // e.g. will work: match &Some(_) { Some(_) => () }
782             // will not: match Some(_) { &Some(_) => () }
783             let ref_count_diff = ty_ref_count - pat_ref_count;
784
785             // Try to remove address of expressions first.
786             let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
787             let ref_count_diff = ref_count_diff - removed;
788
789             let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
790             let sugg = format!(
791                 "if {} == {}{} {}{}",
792                 snippet(cx, ex.span, ".."),
793                 // PartialEq for different reference counts may not exist.
794                 "&".repeat(ref_count_diff),
795                 snippet(cx, arms[0].pat.span, ".."),
796                 expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
797                 els_str,
798             );
799             (msg, sugg)
800         } else {
801             let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
802             let sugg = format!(
803                 "if let {} = {} {}{}",
804                 snippet(cx, arms[0].pat.span, ".."),
805                 snippet(cx, ex.span, ".."),
806                 expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
807                 els_str,
808             );
809             (msg, sugg)
810         }
811     };
812
813     span_lint_and_sugg(
814         cx,
815         lint,
816         expr.span,
817         msg,
818         "try this",
819         sugg,
820         Applicability::HasPlaceholders,
821     );
822 }
823
824 fn check_single_match_opt_like(
825     cx: &LateContext<'_>,
826     ex: &Expr<'_>,
827     arms: &[Arm<'_>],
828     expr: &Expr<'_>,
829     ty: Ty<'_>,
830     els: Option<&Expr<'_>>,
831 ) {
832     // list of candidate `Enum`s we know will never get any more members
833     let candidates = &[
834         (&paths::COW, "Borrowed"),
835         (&paths::COW, "Cow::Borrowed"),
836         (&paths::COW, "Cow::Owned"),
837         (&paths::COW, "Owned"),
838         (&paths::OPTION, "None"),
839         (&paths::RESULT, "Err"),
840         (&paths::RESULT, "Ok"),
841     ];
842
843     let path = match arms[1].pat.kind {
844         PatKind::TupleStruct(ref path, inner, _) => {
845             // Contains any non wildcard patterns (e.g., `Err(err)`)?
846             if !inner.iter().all(is_wild) {
847                 return;
848             }
849             rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
850         },
851         PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => ident.to_string(),
852         PatKind::Path(ref path) => {
853             rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
854         },
855         _ => return,
856     };
857
858     for &(ty_path, pat_path) in candidates {
859         if path == *pat_path && match_type(cx, ty, ty_path) {
860             report_single_match_single_pattern(cx, ex, arms, expr, els);
861         }
862     }
863 }
864
865 fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
866     // Type of expression is `bool`.
867     if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool {
868         span_lint_and_then(
869             cx,
870             MATCH_BOOL,
871             expr.span,
872             "you seem to be trying to match on a boolean expression",
873             move |diag| {
874                 if arms.len() == 2 {
875                     // no guards
876                     let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
877                         if let ExprKind::Lit(ref lit) = arm_bool.kind {
878                             match lit.node {
879                                 LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
880                                 LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
881                                 _ => None,
882                             }
883                         } else {
884                             None
885                         }
886                     } else {
887                         None
888                     };
889
890                     if let Some((true_expr, false_expr)) = exprs {
891                         let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
892                             (false, false) => Some(format!(
893                                 "if {} {} else {}",
894                                 snippet(cx, ex.span, "b"),
895                                 expr_block(cx, true_expr, None, "..", Some(expr.span)),
896                                 expr_block(cx, false_expr, None, "..", Some(expr.span))
897                             )),
898                             (false, true) => Some(format!(
899                                 "if {} {}",
900                                 snippet(cx, ex.span, "b"),
901                                 expr_block(cx, true_expr, None, "..", Some(expr.span))
902                             )),
903                             (true, false) => {
904                                 let test = Sugg::hir(cx, ex, "..");
905                                 Some(format!(
906                                     "if {} {}",
907                                     !test,
908                                     expr_block(cx, false_expr, None, "..", Some(expr.span))
909                                 ))
910                             },
911                             (true, true) => None,
912                         };
913
914                         if let Some(sugg) = sugg {
915                             diag.span_suggestion(
916                                 expr.span,
917                                 "consider using an `if`/`else` expression",
918                                 sugg,
919                                 Applicability::HasPlaceholders,
920                             );
921                         }
922                     }
923                 }
924             },
925         );
926     }
927 }
928
929 fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
930     if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() {
931         let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex));
932         if !ranges.is_empty() {
933             if let Some((start, end)) = overlapping(&ranges) {
934                 span_lint_and_note(
935                     cx,
936                     MATCH_OVERLAPPING_ARM,
937                     start.span,
938                     "some ranges overlap",
939                     Some(end.span),
940                     "overlaps with this",
941                 );
942             }
943         }
944     }
945 }
946
947 fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
948     let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
949     if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
950         for arm in arms {
951             if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
952                 let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
953                 if path_str == "Err" {
954                     let mut matching_wild = inner.iter().any(is_wild);
955                     let mut ident_bind_name = String::from("_");
956                     if !matching_wild {
957                         // Looking for unused bindings (i.e.: `_e`)
958                         for pat in inner.iter() {
959                             if let PatKind::Binding(_, id, ident, None) = pat.kind {
960                                 if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
961                                     ident_bind_name = (&ident.name.as_str()).to_string();
962                                     matching_wild = true;
963                                 }
964                             }
965                         }
966                     }
967                     if_chain! {
968                         if matching_wild;
969                         if is_panic_call(arm.body);
970                         then {
971                             // `Err(_)` or `Err(_e)` arm with `panic!` found
972                             span_lint_and_note(cx,
973                                 MATCH_WILD_ERR_ARM,
974                                 arm.pat.span,
975                                 &format!("`Err({})` matches all errors", &ident_bind_name),
976                                 None,
977                                 "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
978                             );
979                         }
980                     }
981                 }
982             }
983         }
984     }
985 }
986
987 enum CommonPrefixSearcher<'a> {
988     None,
989     Path(&'a [PathSegment<'a>]),
990     Mixed,
991 }
992 impl CommonPrefixSearcher<'a> {
993     fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
994         match path {
995             [path @ .., _] => self.with_prefix(path),
996             [] => (),
997         }
998     }
999
1000     fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
1001         match self {
1002             Self::None => *self = Self::Path(path),
1003             Self::Path(self_path)
1004                 if path
1005                     .iter()
1006                     .map(|p| p.ident.name)
1007                     .eq(self_path.iter().map(|p| p.ident.name)) => {},
1008             Self::Path(_) => *self = Self::Mixed,
1009             Self::Mixed => (),
1010         }
1011     }
1012 }
1013
1014 fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
1015     let attrs = cx.tcx.get_attrs(variant_def.def_id);
1016     clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
1017 }
1018
1019 #[allow(clippy::too_many_lines)]
1020 fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
1021     let ty = cx.typeck_results().expr_ty(ex).peel_refs();
1022     let adt_def = match ty.kind() {
1023         ty::Adt(adt_def, _)
1024             if adt_def.is_enum()
1025                 && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) =>
1026         {
1027             adt_def
1028         },
1029         _ => return,
1030     };
1031
1032     // First pass - check for violation, but don't do much book-keeping because this is hopefully
1033     // the uncommon case, and the book-keeping is slightly expensive.
1034     let mut wildcard_span = None;
1035     let mut wildcard_ident = None;
1036     let mut has_non_wild = false;
1037     for arm in arms {
1038         match peel_hir_pat_refs(arm.pat).0.kind {
1039             PatKind::Wild => wildcard_span = Some(arm.pat.span),
1040             PatKind::Binding(_, _, ident, None) => {
1041                 wildcard_span = Some(arm.pat.span);
1042                 wildcard_ident = Some(ident);
1043             },
1044             _ => has_non_wild = true,
1045         }
1046     }
1047     let wildcard_span = match wildcard_span {
1048         Some(x) if has_non_wild => x,
1049         _ => return,
1050     };
1051
1052     // Accumulate the variants which should be put in place of the wildcard because they're not
1053     // already covered.
1054     let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
1055     let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
1056
1057     let mut path_prefix = CommonPrefixSearcher::None;
1058     for arm in arms {
1059         // Guards mean that this case probably isn't exhaustively covered. Technically
1060         // this is incorrect, as we should really check whether each variant is exhaustively
1061         // covered by the set of guards that cover it, but that's really hard to do.
1062         recurse_or_patterns(arm.pat, |pat| {
1063             let path = match &peel_hir_pat_refs(pat).0.kind {
1064                 PatKind::Path(path) => {
1065                     #[allow(clippy::match_same_arms)]
1066                     let id = match cx.qpath_res(path, pat.hir_id) {
1067                         Res::Def(
1068                             DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst,
1069                             _,
1070                         ) => return,
1071                         Res::Def(_, id) => id,
1072                         _ => return,
1073                     };
1074                     if arm.guard.is_none() {
1075                         missing_variants.retain(|e| e.ctor_def_id != Some(id));
1076                     }
1077                     path
1078                 },
1079                 PatKind::TupleStruct(path, patterns, ..) => {
1080                     if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
1081                         if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
1082                             missing_variants.retain(|e| e.ctor_def_id != Some(id));
1083                         }
1084                     }
1085                     path
1086                 },
1087                 PatKind::Struct(path, patterns, ..) => {
1088                     if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
1089                         if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
1090                             missing_variants.retain(|e| e.def_id != id);
1091                         }
1092                     }
1093                     path
1094                 },
1095                 _ => return,
1096             };
1097             match path {
1098                 QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
1099                 QPath::TypeRelative(
1100                     hir::Ty {
1101                         kind: TyKind::Path(QPath::Resolved(_, path)),
1102                         ..
1103                     },
1104                     _,
1105                 ) => path_prefix.with_prefix(path.segments),
1106                 _ => (),
1107             }
1108         });
1109     }
1110
1111     let format_suggestion = |variant: &VariantDef| {
1112         format!(
1113             "{}{}{}{}",
1114             if let Some(ident) = wildcard_ident {
1115                 format!("{} @ ", ident.name)
1116             } else {
1117                 String::new()
1118             },
1119             if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
1120                 let mut s = String::new();
1121                 for seg in path_prefix {
1122                     s.push_str(&seg.ident.as_str());
1123                     s.push_str("::");
1124                 }
1125                 s
1126             } else {
1127                 let mut s = cx.tcx.def_path_str(adt_def.did);
1128                 s.push_str("::");
1129                 s
1130             },
1131             variant.ident.name,
1132             match variant.ctor_kind {
1133                 CtorKind::Fn if variant.fields.len() == 1 => "(_)",
1134                 CtorKind::Fn => "(..)",
1135                 CtorKind::Const => "",
1136                 CtorKind::Fictive => "{ .. }",
1137             }
1138         )
1139     };
1140
1141     match missing_variants.as_slice() {
1142         [] => (),
1143         [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
1144             cx,
1145             MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
1146             wildcard_span,
1147             "wildcard matches only a single variant and will also match any future added variants",
1148             "try this",
1149             format_suggestion(x),
1150             Applicability::MaybeIncorrect,
1151         ),
1152         variants => {
1153             let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
1154             let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
1155                 suggestions.push("_".into());
1156                 "wildcard matches known variants and will also match future added variants"
1157             } else {
1158                 "wildcard match will also match any future added variants"
1159             };
1160
1161             span_lint_and_sugg(
1162                 cx,
1163                 WILDCARD_ENUM_MATCH_ARM,
1164                 wildcard_span,
1165                 message,
1166                 "try this",
1167                 suggestions.join(" | "),
1168                 Applicability::MaybeIncorrect,
1169             );
1170         },
1171     };
1172 }
1173
1174 // If the block contains only a `panic!` macro (as expression or statement)
1175 fn is_panic_call(expr: &Expr<'_>) -> bool {
1176     // Unwrap any wrapping blocks
1177     let span = if let ExprKind::Block(block, _) = expr.kind {
1178         match (&block.expr, block.stmts.len(), block.stmts.first()) {
1179             (&Some(exp), 0, _) => exp.span,
1180             (&None, 1, Some(stmt)) => stmt.span,
1181             _ => return false,
1182         }
1183     } else {
1184         expr.span
1185     };
1186
1187     is_expn_of(span, "panic").is_some() && is_expn_of(span, "unreachable").is_none()
1188 }
1189
1190 fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
1191 where
1192     'b: 'a,
1193     I: Clone + Iterator<Item = &'a Pat<'b>>,
1194 {
1195     if !has_multiple_ref_pats(pats.clone()) {
1196         return;
1197     }
1198
1199     let (first_sugg, msg, title);
1200     let span = ex.span.source_callsite();
1201     if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
1202         first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
1203         msg = "try";
1204         title = "you don't need to add `&` to both the expression and the patterns";
1205     } else {
1206         first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
1207         msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
1208         title = "you don't need to add `&` to all patterns";
1209     }
1210
1211     let remaining_suggs = pats.filter_map(|pat| {
1212         if let PatKind::Ref(refp, _) = pat.kind {
1213             Some((pat.span, snippet(cx, refp.span, "..").to_string()))
1214         } else {
1215             None
1216         }
1217     });
1218
1219     span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
1220         if !expr.span.from_expansion() {
1221             multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
1222         }
1223     });
1224 }
1225
1226 fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
1227     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
1228         let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
1229             is_ref_some_arm(cx, &arms[1])
1230         } else if is_none_arm(cx, &arms[1]) {
1231             is_ref_some_arm(cx, &arms[0])
1232         } else {
1233             None
1234         };
1235         if let Some(rb) = arm_ref {
1236             let suggestion = if rb == BindingAnnotation::Ref {
1237                 "as_ref"
1238             } else {
1239                 "as_mut"
1240             };
1241
1242             let output_ty = cx.typeck_results().expr_ty(expr);
1243             let input_ty = cx.typeck_results().expr_ty(ex);
1244
1245             let cast = if_chain! {
1246                 if let ty::Adt(_, substs) = input_ty.kind();
1247                 let input_ty = substs.type_at(0);
1248                 if let ty::Adt(_, substs) = output_ty.kind();
1249                 let output_ty = substs.type_at(0);
1250                 if let ty::Ref(_, output_ty, _) = *output_ty.kind();
1251                 if input_ty != output_ty;
1252                 then {
1253                     ".map(|x| x as _)"
1254                 } else {
1255                     ""
1256                 }
1257             };
1258
1259             let mut applicability = Applicability::MachineApplicable;
1260             span_lint_and_sugg(
1261                 cx,
1262                 MATCH_AS_REF,
1263                 expr.span,
1264                 &format!("use `{}()` instead", suggestion),
1265                 "try this",
1266                 format!(
1267                     "{}.{}(){}",
1268                     snippet_with_applicability(cx, ex.span, "_", &mut applicability),
1269                     suggestion,
1270                     cast,
1271                 ),
1272                 applicability,
1273             );
1274         }
1275     }
1276 }
1277
1278 fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
1279     for arm in arms {
1280         if let PatKind::Or(fields) = arm.pat.kind {
1281             // look for multiple fields in this arm that contains at least one Wild pattern
1282             if fields.len() > 1 && fields.iter().any(is_wild) {
1283                 span_lint_and_help(
1284                     cx,
1285                     WILDCARD_IN_OR_PATTERNS,
1286                     arm.pat.span,
1287                     "wildcard pattern covers any other pattern as it will match anyway",
1288                     None,
1289                     "consider handling `_` separately",
1290                 );
1291             }
1292         }
1293     }
1294 }
1295
1296 /// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
1297 fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
1298     if let Some(higher::IfLet {
1299         let_pat,
1300         let_expr,
1301         if_then,
1302         if_else: Some(if_else),
1303     }) = higher::IfLet::hir(cx, expr)
1304     {
1305         return find_matches_sugg(
1306             cx,
1307             let_expr,
1308             IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
1309             expr,
1310             true,
1311         );
1312     }
1313
1314     if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
1315         return find_matches_sugg(
1316             cx,
1317             scrut,
1318             arms.iter().map(|arm| {
1319                 (
1320                     cx.tcx.hir().attrs(arm.hir_id),
1321                     Some(arm.pat),
1322                     arm.body,
1323                     arm.guard.as_ref(),
1324                 )
1325             }),
1326             expr,
1327             false,
1328         );
1329     }
1330
1331     false
1332 }
1333
1334 /// Lint a `match` or `if let` for replacement by `matches!`
1335 fn find_matches_sugg<'a, 'b, I>(
1336     cx: &LateContext<'_>,
1337     ex: &Expr<'_>,
1338     mut iter: I,
1339     expr: &Expr<'_>,
1340     is_if_let: bool,
1341 ) -> bool
1342 where
1343     'b: 'a,
1344     I: Clone
1345         + DoubleEndedIterator
1346         + ExactSizeIterator
1347         + Iterator<
1348             Item = (
1349                 &'a [Attribute],
1350                 Option<&'a Pat<'b>>,
1351                 &'a Expr<'b>,
1352                 Option<&'a Guard<'b>>,
1353             ),
1354         >,
1355 {
1356     if_chain! {
1357         if iter.len() >= 2;
1358         if cx.typeck_results().expr_ty(expr).is_bool();
1359         if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
1360         let iter_without_last = iter.clone();
1361         if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
1362         if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
1363         if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
1364         if b0 != b1;
1365         if first_guard.is_none() || iter.len() == 0;
1366         if first_attrs.is_empty();
1367         if iter
1368             .all(|arm| {
1369                 find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
1370             });
1371         then {
1372             if let Some(last_pat) = last_pat_opt {
1373                 if !is_wild(last_pat) {
1374                     return false;
1375                 }
1376             }
1377
1378             // The suggestion may be incorrect, because some arms can have `cfg` attributes
1379             // evaluated into `false` and so such arms will be stripped before.
1380             let mut applicability = Applicability::MaybeIncorrect;
1381             let pat = {
1382                 use itertools::Itertools as _;
1383                 iter_without_last
1384                     .filter_map(|arm| {
1385                         let pat_span = arm.1?.span;
1386                         Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
1387                     })
1388                     .join(" | ")
1389             };
1390             let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
1391                 format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
1392             } else {
1393                 pat
1394             };
1395
1396             // strip potential borrows (#6503), but only if the type is a reference
1397             let mut ex_new = ex;
1398             if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
1399                 if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
1400                     ex_new = ex_inner;
1401                 }
1402             };
1403             span_lint_and_sugg(
1404                 cx,
1405                 MATCH_LIKE_MATCHES_MACRO,
1406                 expr.span,
1407                 &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
1408                 "try this",
1409                 format!(
1410                     "{}matches!({}, {})",
1411                     if b0 { "" } else { "!" },
1412                     snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
1413                     pat_and_guard,
1414                 ),
1415                 applicability,
1416             );
1417             true
1418         } else {
1419             false
1420         }
1421     }
1422 }
1423
1424 /// Extract a `bool` or `{ bool }`
1425 fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
1426     match ex {
1427         ExprKind::Lit(Spanned {
1428             node: LitKind::Bool(b), ..
1429         }) => Some(*b),
1430         ExprKind::Block(
1431             rustc_hir::Block {
1432                 stmts: &[],
1433                 expr: Some(exp),
1434                 ..
1435             },
1436             _,
1437         ) if is_if_let => {
1438             if let ExprKind::Lit(Spanned {
1439                 node: LitKind::Bool(b), ..
1440             }) = exp.kind
1441             {
1442                 Some(b)
1443             } else {
1444                 None
1445             }
1446         },
1447         _ => None,
1448     }
1449 }
1450
1451 #[allow(clippy::too_many_lines)]
1452 fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
1453     if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
1454         return;
1455     }
1456
1457     // HACK:
1458     // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
1459     // to prevent false positives as there is currently no better way to detect if code was excluded by
1460     // a macro. See PR #6435
1461     if_chain! {
1462         if let Some(match_snippet) = snippet_opt(cx, expr.span);
1463         if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
1464         if let Some(ex_snippet) = snippet_opt(cx, ex.span);
1465         let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
1466         if rest_snippet.contains("=>");
1467         then {
1468             // The code it self contains another thick arrow "=>"
1469             // -> Either another arm or a comment
1470             return;
1471         }
1472     }
1473
1474     let matched_vars = ex.span;
1475     let bind_names = arms[0].pat.span;
1476     let match_body = remove_blocks(arms[0].body);
1477     let mut snippet_body = if match_body.span.from_expansion() {
1478         Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
1479     } else {
1480         snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
1481     };
1482
1483     // Do we need to add ';' to suggestion ?
1484     match match_body.kind {
1485         ExprKind::Block(block, _) => {
1486             // macro + expr_ty(body) == ()
1487             if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
1488                 snippet_body.push(';');
1489             }
1490         },
1491         _ => {
1492             // expr_ty(body) == ()
1493             if cx.typeck_results().expr_ty(match_body).is_unit() {
1494                 snippet_body.push(';');
1495             }
1496         },
1497     }
1498
1499     let mut applicability = Applicability::MaybeIncorrect;
1500     match arms[0].pat.kind {
1501         PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
1502             // If this match is in a local (`let`) stmt
1503             let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
1504                 (
1505                     parent_let_node.span,
1506                     format!(
1507                         "let {} = {};\n{}let {} = {};",
1508                         snippet_with_applicability(cx, bind_names, "..", &mut applicability),
1509                         snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
1510                         " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
1511                         snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
1512                         snippet_body
1513                     ),
1514                 )
1515             } else {
1516                 // If we are in closure, we need curly braces around suggestion
1517                 let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
1518                 let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string());
1519                 if let Some(parent_expr) = get_parent_expr(cx, expr) {
1520                     if let ExprKind::Closure(..) = parent_expr.kind {
1521                         cbrace_end = format!("\n{}}}", indent);
1522                         // Fix body indent due to the closure
1523                         indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
1524                         cbrace_start = format!("{{\n{}", indent);
1525                     }
1526                 }
1527                 // If the parent is already an arm, and the body is another match statement,
1528                 // we need curly braces around suggestion
1529                 let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
1530                 if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
1531                     if let ExprKind::Match(..) = arm.body.kind {
1532                         cbrace_end = format!("\n{}}}", indent);
1533                         // Fix body indent due to the match
1534                         indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
1535                         cbrace_start = format!("{{\n{}", indent);
1536                     }
1537                 }
1538                 (
1539                     expr.span,
1540                     format!(
1541                         "{}let {} = {};\n{}{}{}",
1542                         cbrace_start,
1543                         snippet_with_applicability(cx, bind_names, "..", &mut applicability),
1544                         snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
1545                         indent,
1546                         snippet_body,
1547                         cbrace_end
1548                     ),
1549                 )
1550             };
1551             span_lint_and_sugg(
1552                 cx,
1553                 MATCH_SINGLE_BINDING,
1554                 target_span,
1555                 "this match could be written as a `let` statement",
1556                 "consider using `let` statement",
1557                 sugg,
1558                 applicability,
1559             );
1560         },
1561         PatKind::Wild => {
1562             if ex.can_have_side_effects() {
1563                 let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0));
1564                 let sugg = format!(
1565                     "{};\n{}{}",
1566                     snippet_with_applicability(cx, ex.span, "..", &mut applicability),
1567                     indent,
1568                     snippet_body
1569                 );
1570                 span_lint_and_sugg(
1571                     cx,
1572                     MATCH_SINGLE_BINDING,
1573                     expr.span,
1574                     "this match could be replaced by its scrutinee and body",
1575                     "consider using the scrutinee and body instead",
1576                     sugg,
1577                     applicability,
1578                 );
1579             } else {
1580                 span_lint_and_sugg(
1581                     cx,
1582                     MATCH_SINGLE_BINDING,
1583                     expr.span,
1584                     "this match could be replaced by its body itself",
1585                     "consider using the match body instead",
1586                     snippet_body,
1587                     Applicability::MachineApplicable,
1588                 );
1589             }
1590         },
1591         _ => (),
1592     }
1593 }
1594
1595 /// Returns true if the `ex` match expression is in a local (`let`) statement
1596 fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
1597     let map = &cx.tcx.hir();
1598     if_chain! {
1599         if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
1600         if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
1601         then {
1602             return Some(parent_let_expr);
1603         }
1604     }
1605     None
1606 }
1607
1608 /// Gets all arms that are unbounded `PatRange`s.
1609 fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<FullInt>> {
1610     arms.iter()
1611         .filter_map(|arm| {
1612             if let Arm { pat, guard: None, .. } = *arm {
1613                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
1614                     let lhs = match lhs {
1615                         Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
1616                         None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
1617                     };
1618                     let rhs = match rhs {
1619                         Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
1620                         None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
1621                     };
1622
1623                     let lhs_val = lhs.int_value(cx, ty)?;
1624                     let rhs_val = rhs.int_value(cx, ty)?;
1625
1626                     let rhs_bound = match range_end {
1627                         RangeEnd::Included => Bound::Included(rhs_val),
1628                         RangeEnd::Excluded => Bound::Excluded(rhs_val),
1629                     };
1630                     return Some(SpannedRange {
1631                         span: pat.span,
1632                         node: (lhs_val, rhs_bound),
1633                     });
1634                 }
1635
1636                 if let PatKind::Lit(value) = pat.kind {
1637                     let value = constant_full_int(cx, cx.typeck_results(), value)?;
1638                     return Some(SpannedRange {
1639                         span: pat.span,
1640                         node: (value, Bound::Included(value)),
1641                     });
1642                 }
1643             }
1644             None
1645         })
1646         .collect()
1647 }
1648
1649 #[derive(Debug, Eq, PartialEq)]
1650 pub struct SpannedRange<T> {
1651     pub span: Span,
1652     pub node: (T, Bound<T>),
1653 }
1654
1655 // Checks if arm has the form `None => None`
1656 fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
1657     matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
1658 }
1659
1660 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
1661 fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
1662     if_chain! {
1663         if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
1664         if is_lang_ctor(cx, qpath, OptionSome);
1665         if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
1666         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
1667         if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
1668         if let ExprKind::Path(ref some_path) = e.kind;
1669         if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
1670         if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
1671         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
1672         then {
1673             return Some(rb)
1674         }
1675     }
1676     None
1677 }
1678
1679 fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool
1680 where
1681     'b: 'a,
1682     I: Iterator<Item = &'a Pat<'b>>,
1683 {
1684     let mut ref_count = 0;
1685     for opt in pats.map(|pat| match pat.kind {
1686         PatKind::Ref(..) => Some(true), // &-patterns
1687         PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
1688         _ => None,                      // any other pattern is not fine
1689     }) {
1690         if let Some(inner) = opt {
1691             if inner {
1692                 ref_count += 1;
1693             }
1694         } else {
1695             return false;
1696         }
1697     }
1698     ref_count > 1
1699 }
1700
1701 pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
1702 where
1703     T: Copy + Ord,
1704 {
1705     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
1706     enum Kind<'a, T> {
1707         Start(T, &'a SpannedRange<T>),
1708         End(Bound<T>, &'a SpannedRange<T>),
1709     }
1710
1711     impl<'a, T: Copy> Kind<'a, T> {
1712         fn range(&self) -> &'a SpannedRange<T> {
1713             match *self {
1714                 Kind::Start(_, r) | Kind::End(_, r) => r,
1715             }
1716         }
1717
1718         fn value(self) -> Bound<T> {
1719             match self {
1720                 Kind::Start(t, _) => Bound::Included(t),
1721                 Kind::End(t, _) => t,
1722             }
1723         }
1724     }
1725
1726     impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
1727         fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
1728             Some(self.cmp(other))
1729         }
1730     }
1731
1732     impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
1733         fn cmp(&self, other: &Self) -> Ordering {
1734             match (self.value(), other.value()) {
1735                 (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
1736                 // Range patterns cannot be unbounded (yet)
1737                 (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
1738                 (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
1739                     Ordering::Equal => Ordering::Greater,
1740                     other => other,
1741                 },
1742                 (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
1743                     Ordering::Equal => Ordering::Less,
1744                     other => other,
1745                 },
1746             }
1747         }
1748     }
1749
1750     let mut values = Vec::with_capacity(2 * ranges.len());
1751
1752     for r in ranges {
1753         values.push(Kind::Start(r.node.0, r));
1754         values.push(Kind::End(r.node.1, r));
1755     }
1756
1757     values.sort();
1758
1759     for (a, b) in iter::zip(&values, values.iter().skip(1)) {
1760         match (a, b) {
1761             (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
1762                 if ra.node != rb.node {
1763                     return Some((ra, rb));
1764                 }
1765             },
1766             (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
1767             _ => {
1768                 // skip if the range `a` is completely included into the range `b`
1769                 if let Ordering::Equal | Ordering::Less = a.cmp(b) {
1770                     let kind_a = Kind::End(a.range().node.1, a.range());
1771                     let kind_b = Kind::End(b.range().node.1, b.range());
1772                     if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) {
1773                         return None;
1774                     }
1775                 }
1776                 return Some((a.range(), b.range()));
1777             },
1778         }
1779     }
1780
1781     None
1782 }
1783
1784 mod redundant_pattern_match {
1785     use super::REDUNDANT_PATTERN_MATCHING;
1786     use clippy_utils::diagnostics::span_lint_and_then;
1787     use clippy_utils::higher;
1788     use clippy_utils::source::{snippet, snippet_with_applicability};
1789     use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
1790     use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
1791     use if_chain::if_chain;
1792     use rustc_ast::ast::LitKind;
1793     use rustc_data_structures::fx::FxHashSet;
1794     use rustc_errors::Applicability;
1795     use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
1796     use rustc_hir::{
1797         intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
1798         Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath,
1799     };
1800     use rustc_lint::LateContext;
1801     use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
1802     use rustc_span::sym;
1803
1804     pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1805         if let Some(higher::IfLet {
1806             if_else,
1807             let_pat,
1808             let_expr,
1809             ..
1810         }) = higher::IfLet::hir(cx, expr)
1811         {
1812             find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
1813         }
1814         if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
1815             find_sugg_for_match(cx, expr, op, arms);
1816         }
1817         if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
1818             find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
1819         }
1820     }
1821
1822     /// Checks if the drop order for a type matters. Some std types implement drop solely to
1823     /// deallocate memory. For these types, and composites containing them, changing the drop order
1824     /// won't result in any observable side effects.
1825     fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
1826         type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
1827     }
1828
1829     fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
1830         if !seen.insert(ty) {
1831             return false;
1832         }
1833         if !ty.needs_drop(cx.tcx, cx.param_env) {
1834             false
1835         } else if !cx
1836             .tcx
1837             .lang_items()
1838             .drop_trait()
1839             .map_or(false, |id| implements_trait(cx, ty, id, &[]))
1840         {
1841             // This type doesn't implement drop, so no side effects here.
1842             // Check if any component type has any.
1843             match ty.kind() {
1844                 ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
1845                 ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, ty, seen),
1846                 ty::Adt(adt, subs) => adt
1847                     .all_fields()
1848                     .map(|f| f.ty(cx.tcx, subs))
1849                     .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
1850                 _ => true,
1851             }
1852         }
1853         // Check for std types which implement drop, but only for memory allocation.
1854         else if is_type_diagnostic_item(cx, ty, sym::Vec)
1855             || is_type_lang_item(cx, ty, LangItem::OwnedBox)
1856             || is_type_diagnostic_item(cx, ty, sym::Rc)
1857             || is_type_diagnostic_item(cx, ty, sym::Arc)
1858             || is_type_diagnostic_item(cx, ty, sym::cstring_type)
1859             || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
1860             || is_type_diagnostic_item(cx, ty, sym::LinkedList)
1861             || match_type(cx, ty, &paths::WEAK_RC)
1862             || match_type(cx, ty, &paths::WEAK_ARC)
1863         {
1864             // Check all of the generic arguments.
1865             if let ty::Adt(_, subs) = ty.kind() {
1866                 subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
1867             } else {
1868                 true
1869             }
1870         } else {
1871             true
1872         }
1873     }
1874
1875     // Extract the generic arguments out of a type
1876     fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
1877         if_chain! {
1878             if let ty::Adt(_, subs) = ty.kind();
1879             if let Some(sub) = subs.get(index);
1880             if let GenericArgKind::Type(sub_ty) = sub.unpack();
1881             then {
1882                 Some(sub_ty)
1883             } else {
1884                 None
1885             }
1886         }
1887     }
1888
1889     // Checks if there are any temporaries created in the given expression for which drop order
1890     // matters.
1891     fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
1892         struct V<'a, 'tcx> {
1893             cx: &'a LateContext<'tcx>,
1894             res: bool,
1895         }
1896         impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
1897             type Map = ErasedMap<'tcx>;
1898             fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
1899                 NestedVisitorMap::None
1900             }
1901
1902             fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
1903                 match expr.kind {
1904                     // Taking the reference of a value leaves a temporary
1905                     // e.g. In `&String::new()` the string is a temporary value.
1906                     // Remaining fields are temporary values
1907                     // e.g. In `(String::new(), 0).1` the string is a temporary value.
1908                     ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
1909                         if !matches!(expr.kind, ExprKind::Path(_)) {
1910                             if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
1911                                 self.res = true;
1912                             } else {
1913                                 self.visit_expr(expr);
1914                             }
1915                         }
1916                     },
1917                     // the base type is alway taken by reference.
1918                     // e.g. In `(vec![0])[0]` the vector is a temporary value.
1919                     ExprKind::Index(base, index) => {
1920                         if !matches!(base.kind, ExprKind::Path(_)) {
1921                             if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
1922                                 self.res = true;
1923                             } else {
1924                                 self.visit_expr(base);
1925                             }
1926                         }
1927                         self.visit_expr(index);
1928                     },
1929                     // Method calls can take self by reference.
1930                     // e.g. In `String::new().len()` the string is a temporary value.
1931                     ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => {
1932                         if !matches!(self_arg.kind, ExprKind::Path(_)) {
1933                             let self_by_ref = self
1934                                 .cx
1935                                 .typeck_results()
1936                                 .type_dependent_def_id(expr.hir_id)
1937                                 .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
1938                             if self_by_ref
1939                                 && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg))
1940                             {
1941                                 self.res = true;
1942                             } else {
1943                                 self.visit_expr(self_arg);
1944                             }
1945                         }
1946                         args.iter().for_each(|arg| self.visit_expr(arg));
1947                     },
1948                     // Either explicitly drops values, or changes control flow.
1949                     ExprKind::DropTemps(_)
1950                     | ExprKind::Ret(_)
1951                     | ExprKind::Break(..)
1952                     | ExprKind::Yield(..)
1953                     | ExprKind::Block(Block { expr: None, .. }, _)
1954                     | ExprKind::Loop(..) => (),
1955
1956                     // Only consider the final expression.
1957                     ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
1958
1959                     _ => walk_expr(self, expr),
1960                 }
1961             }
1962         }
1963
1964         let mut v = V { cx, res: false };
1965         v.visit_expr(expr);
1966         v.res
1967     }
1968
1969     fn find_sugg_for_if_let<'tcx>(
1970         cx: &LateContext<'tcx>,
1971         expr: &'tcx Expr<'_>,
1972         let_pat: &Pat<'_>,
1973         let_expr: &'tcx Expr<'_>,
1974         keyword: &'static str,
1975         has_else: bool,
1976     ) {
1977         // also look inside refs
1978         let mut kind = &let_pat.kind;
1979         // if we have &None for example, peel it so we can detect "if let None = x"
1980         if let PatKind::Ref(inner, _mutability) = kind {
1981             kind = &inner.kind;
1982         }
1983         let op_ty = cx.typeck_results().expr_ty(let_expr);
1984         // Determine which function should be used, and the type contained by the corresponding
1985         // variant.
1986         let (good_method, inner_ty) = match kind {
1987             PatKind::TupleStruct(ref path, [sub_pat], _) => {
1988                 if let PatKind::Wild = sub_pat.kind {
1989                     if is_lang_ctor(cx, path, ResultOk) {
1990                         ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
1991                     } else if is_lang_ctor(cx, path, ResultErr) {
1992                         ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
1993                     } else if is_lang_ctor(cx, path, OptionSome) {
1994                         ("is_some()", op_ty)
1995                     } else if is_lang_ctor(cx, path, PollReady) {
1996                         ("is_ready()", op_ty)
1997                     } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
1998                         ("is_ipv4()", op_ty)
1999                     } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
2000                         ("is_ipv6()", op_ty)
2001                     } else {
2002                         return;
2003                     }
2004                 } else {
2005                     return;
2006                 }
2007             },
2008             PatKind::Path(ref path) => {
2009                 let method = if is_lang_ctor(cx, path, OptionNone) {
2010                     "is_none()"
2011                 } else if is_lang_ctor(cx, path, PollPending) {
2012                     "is_pending()"
2013                 } else {
2014                     return;
2015                 };
2016                 // `None` and `Pending` don't have an inner type.
2017                 (method, cx.tcx.types.unit)
2018             },
2019             _ => return,
2020         };
2021
2022         // If this is the last expression in a block or there is an else clause then the whole
2023         // type needs to be considered, not just the inner type of the branch being matched on.
2024         // Note the last expression in a block is dropped after all local bindings.
2025         let check_ty = if has_else
2026             || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
2027         {
2028             op_ty
2029         } else {
2030             inner_ty
2031         };
2032
2033         // All temporaries created in the scrutinee expression are dropped at the same time as the
2034         // scrutinee would be, so they have to be considered as well.
2035         // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
2036         // for the duration if body.
2037         let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
2038
2039         // check that `while_let_on_iterator` lint does not trigger
2040         if_chain! {
2041             if keyword == "while";
2042             if let ExprKind::MethodCall(method_path, _, _, _) = let_expr.kind;
2043             if method_path.ident.name == sym::next;
2044             if is_trait_method(cx, let_expr, sym::Iterator);
2045             then {
2046                 return;
2047             }
2048         }
2049
2050         let result_expr = match &let_expr.kind {
2051             ExprKind::AddrOf(_, _, borrowed) => borrowed,
2052             _ => let_expr,
2053         };
2054         span_lint_and_then(
2055             cx,
2056             REDUNDANT_PATTERN_MATCHING,
2057             let_pat.span,
2058             &format!("redundant pattern matching, consider using `{}`", good_method),
2059             |diag| {
2060                 // if/while let ... = ... { ... }
2061                 // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
2062                 let expr_span = expr.span;
2063
2064                 // if/while let ... = ... { ... }
2065                 //                 ^^^
2066                 let op_span = result_expr.span.source_callsite();
2067
2068                 // if/while let ... = ... { ... }
2069                 // ^^^^^^^^^^^^^^^^^^^
2070                 let span = expr_span.until(op_span.shrink_to_hi());
2071
2072                 let mut app = if needs_drop {
2073                     Applicability::MaybeIncorrect
2074                 } else {
2075                     Applicability::MachineApplicable
2076                 };
2077                 let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
2078
2079                 diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
2080
2081                 if needs_drop {
2082                     diag.note("this will change drop order of the result, as well as all temporaries");
2083                     diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
2084                 }
2085             },
2086         );
2087     }
2088
2089     fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
2090         if arms.len() == 2 {
2091             let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
2092
2093             let found_good_method = match node_pair {
2094                 (
2095                     PatKind::TupleStruct(ref path_left, patterns_left, _),
2096                     PatKind::TupleStruct(ref path_right, patterns_right, _),
2097                 ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
2098                     if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
2099                         find_good_method_for_match(
2100                             cx,
2101                             arms,
2102                             path_left,
2103                             path_right,
2104                             &paths::RESULT_OK,
2105                             &paths::RESULT_ERR,
2106                             "is_ok()",
2107                             "is_err()",
2108                         )
2109                         .or_else(|| {
2110                             find_good_method_for_match(
2111                                 cx,
2112                                 arms,
2113                                 path_left,
2114                                 path_right,
2115                                 &paths::IPADDR_V4,
2116                                 &paths::IPADDR_V6,
2117                                 "is_ipv4()",
2118                                 "is_ipv6()",
2119                             )
2120                         })
2121                     } else {
2122                         None
2123                     }
2124                 },
2125                 (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right))
2126                 | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _))
2127                     if patterns.len() == 1 =>
2128                 {
2129                     if let PatKind::Wild = patterns[0].kind {
2130                         find_good_method_for_match(
2131                             cx,
2132                             arms,
2133                             path_left,
2134                             path_right,
2135                             &paths::OPTION_SOME,
2136                             &paths::OPTION_NONE,
2137                             "is_some()",
2138                             "is_none()",
2139                         )
2140                         .or_else(|| {
2141                             find_good_method_for_match(
2142                                 cx,
2143                                 arms,
2144                                 path_left,
2145                                 path_right,
2146                                 &paths::POLL_READY,
2147                                 &paths::POLL_PENDING,
2148                                 "is_ready()",
2149                                 "is_pending()",
2150                             )
2151                         })
2152                     } else {
2153                         None
2154                     }
2155                 },
2156                 _ => None,
2157             };
2158
2159             if let Some(good_method) = found_good_method {
2160                 let span = expr.span.to(op.span);
2161                 let result_expr = match &op.kind {
2162                     ExprKind::AddrOf(_, _, borrowed) => borrowed,
2163                     _ => op,
2164                 };
2165                 span_lint_and_then(
2166                     cx,
2167                     REDUNDANT_PATTERN_MATCHING,
2168                     expr.span,
2169                     &format!("redundant pattern matching, consider using `{}`", good_method),
2170                     |diag| {
2171                         diag.span_suggestion(
2172                             span,
2173                             "try this",
2174                             format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method),
2175                             Applicability::MaybeIncorrect, // snippet
2176                         );
2177                     },
2178                 );
2179             }
2180         }
2181     }
2182
2183     #[allow(clippy::too_many_arguments)]
2184     fn find_good_method_for_match<'a>(
2185         cx: &LateContext<'_>,
2186         arms: &[Arm<'_>],
2187         path_left: &QPath<'_>,
2188         path_right: &QPath<'_>,
2189         expected_left: &[&str],
2190         expected_right: &[&str],
2191         should_be_left: &'a str,
2192         should_be_right: &'a str,
2193     ) -> Option<&'a str> {
2194         let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
2195             && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
2196         {
2197             (&(*arms[0].body).kind, &(*arms[1].body).kind)
2198         } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
2199             && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
2200         {
2201             (&(*arms[1].body).kind, &(*arms[0].body).kind)
2202         } else {
2203             return None;
2204         };
2205
2206         match body_node_pair {
2207             (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
2208                 (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
2209                 (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
2210                 _ => None,
2211             },
2212             _ => None,
2213         }
2214     }
2215 }
2216
2217 #[test]
2218 fn test_overlapping() {
2219     use rustc_span::source_map::DUMMY_SP;
2220
2221     let sp = |s, e| SpannedRange {
2222         span: DUMMY_SP,
2223         node: (s, e),
2224     };
2225
2226     assert_eq!(None, overlapping::<u8>(&[]));
2227     assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
2228     assert_eq!(
2229         None,
2230         overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))])
2231     );
2232     assert_eq!(
2233         None,
2234         overlapping(&[
2235             sp(1, Bound::Included(4)),
2236             sp(5, Bound::Included(6)),
2237             sp(10, Bound::Included(11))
2238         ],)
2239     );
2240     assert_eq!(
2241         Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
2242         overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
2243     );
2244     assert_eq!(
2245         Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
2246         overlapping(&[
2247             sp(1, Bound::Included(4)),
2248             sp(5, Bound::Included(6)),
2249             sp(6, Bound::Included(11))
2250         ],)
2251     );
2252 }
2253
2254 /// Implementation of `MATCH_SAME_ARMS`.
2255 fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
2256     if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind {
2257         let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
2258             let mut h = SpanlessHash::new(cx);
2259             h.hash_expr(arm.body);
2260             h.finish()
2261         };
2262
2263         let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
2264             let min_index = usize::min(lindex, rindex);
2265             let max_index = usize::max(lindex, rindex);
2266
2267             let mut local_map: HirIdMap<HirId> = HirIdMap::default();
2268             let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
2269                 if_chain! {
2270                     if let Some(a_id) = path_to_local(a);
2271                     if let Some(b_id) = path_to_local(b);
2272                     let entry = match local_map.entry(a_id) {
2273                         Entry::Vacant(entry) => entry,
2274                         // check if using the same bindings as before
2275                         Entry::Occupied(entry) => return *entry.get() == b_id,
2276                     };
2277                     // the names technically don't have to match; this makes the lint more conservative
2278                     if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
2279                     if TyS::same_type(cx.typeck_results().expr_ty(a), cx.typeck_results().expr_ty(b));
2280                     if pat_contains_local(lhs.pat, a_id);
2281                     if pat_contains_local(rhs.pat, b_id);
2282                     then {
2283                         entry.insert(b_id);
2284                         true
2285                     } else {
2286                         false
2287                     }
2288                 }
2289             };
2290             // Arms with a guard are ignored, those can’t always be merged together
2291             // This is also the case for arms in-between each there is an arm with a guard
2292             (min_index..=max_index).all(|index| arms[index].guard.is_none())
2293                 && SpanlessEq::new(cx)
2294                     .expr_fallback(eq_fallback)
2295                     .eq_expr(lhs.body, rhs.body)
2296                 // these checks could be removed to allow unused bindings
2297                 && bindings_eq(lhs.pat, local_map.keys().copied().collect())
2298                 && bindings_eq(rhs.pat, local_map.values().copied().collect())
2299         };
2300
2301         let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
2302         for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
2303             span_lint_and_then(
2304                 cx,
2305                 MATCH_SAME_ARMS,
2306                 j.body.span,
2307                 "this `match` has identical arm bodies",
2308                 |diag| {
2309                     diag.span_note(i.body.span, "same as this");
2310
2311                     // Note: this does not use `span_suggestion` on purpose:
2312                     // there is no clean way
2313                     // to remove the other arm. Building a span and suggest to replace it to ""
2314                     // makes an even more confusing error message. Also in order not to make up a
2315                     // span for the whole pattern, the suggestion is only shown when there is only
2316                     // one pattern. The user should know about `|` if they are already using it…
2317
2318                     let lhs = snippet(cx, i.pat.span, "<pat1>");
2319                     let rhs = snippet(cx, j.pat.span, "<pat2>");
2320
2321                     if let PatKind::Wild = j.pat.kind {
2322                         // if the last arm is _, then i could be integrated into _
2323                         // note that i.pat cannot be _, because that would mean that we're
2324                         // hiding all the subsequent arms, and rust won't compile
2325                         diag.span_note(
2326                             i.body.span,
2327                             &format!(
2328                                 "`{}` has the same arm body as the `_` wildcard, consider removing it",
2329                                 lhs
2330                             ),
2331                         );
2332                     } else {
2333                         diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
2334                             .help("...or consider changing the match arm bodies");
2335                     }
2336                 },
2337             );
2338         }
2339     }
2340 }
2341
2342 fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
2343     let mut result = false;
2344     pat.walk_short(|p| {
2345         result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
2346         !result
2347     });
2348     result
2349 }
2350
2351 /// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
2352 fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
2353     let mut result = true;
2354     pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
2355     result && ids.is_empty()
2356 }