]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/matches/mod.rs
Auto merge of #105252 - bjorn3:codegen_less_pair_values, r=nagisa
[rust.git] / src / tools / clippy / clippy_lints / src / matches / mod.rs
1 mod collapsible_match;
2 mod infallible_destructuring_match;
3 mod manual_filter;
4 mod manual_map;
5 mod manual_unwrap_or;
6 mod manual_utils;
7 mod match_as_ref;
8 mod match_bool;
9 mod match_like_matches;
10 mod match_on_vec_items;
11 mod match_ref_pats;
12 mod match_same_arms;
13 mod match_single_binding;
14 mod match_str_case_mismatch;
15 mod match_wild_enum;
16 mod match_wild_err_arm;
17 mod needless_match;
18 mod overlapping_arms;
19 mod redundant_pattern_match;
20 mod rest_pat_in_fully_bound_struct;
21 mod significant_drop_in_scrutinee;
22 mod single_match;
23 mod try_err;
24 mod wild_in_or_pats;
25
26 use clippy_utils::msrvs::{self, Msrv};
27 use clippy_utils::source::{snippet_opt, walk_span_to_context};
28 use clippy_utils::{higher, in_constant, is_span_match};
29 use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
30 use rustc_lexer::{tokenize, TokenKind};
31 use rustc_lint::{LateContext, LateLintPass, LintContext};
32 use rustc_middle::lint::in_external_macro;
33 use rustc_session::{declare_tool_lint, impl_lint_pass};
34 use rustc_span::{Span, SpanData, SyntaxContext};
35
36 declare_clippy_lint! {
37     /// ### What it does
38     /// Checks for matches with a single arm where an `if let`
39     /// will usually suffice.
40     ///
41     /// ### Why is this bad?
42     /// Just readability – `if let` nests less than a `match`.
43     ///
44     /// ### Example
45     /// ```rust
46     /// # fn bar(stool: &str) {}
47     /// # let x = Some("abc");
48     /// match x {
49     ///     Some(ref foo) => bar(foo),
50     ///     _ => (),
51     /// }
52     /// ```
53     ///
54     /// Use instead:
55     /// ```rust
56     /// # fn bar(stool: &str) {}
57     /// # let x = Some("abc");
58     /// if let Some(ref foo) = x {
59     ///     bar(foo);
60     /// }
61     /// ```
62     #[clippy::version = "pre 1.29.0"]
63     pub SINGLE_MATCH,
64     style,
65     "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
66 }
67
68 declare_clippy_lint! {
69     /// ### What it does
70     /// Checks for matches with two arms where an `if let else` will
71     /// usually suffice.
72     ///
73     /// ### Why is this bad?
74     /// Just readability – `if let` nests less than a `match`.
75     ///
76     /// ### Known problems
77     /// Personal style preferences may differ.
78     ///
79     /// ### Example
80     /// Using `match`:
81     ///
82     /// ```rust
83     /// # fn bar(foo: &usize) {}
84     /// # let other_ref: usize = 1;
85     /// # let x: Option<&usize> = Some(&1);
86     /// match x {
87     ///     Some(ref foo) => bar(foo),
88     ///     _ => bar(&other_ref),
89     /// }
90     /// ```
91     ///
92     /// Using `if let` with `else`:
93     ///
94     /// ```rust
95     /// # fn bar(foo: &usize) {}
96     /// # let other_ref: usize = 1;
97     /// # let x: Option<&usize> = Some(&1);
98     /// if let Some(ref foo) = x {
99     ///     bar(foo);
100     /// } else {
101     ///     bar(&other_ref);
102     /// }
103     /// ```
104     #[clippy::version = "pre 1.29.0"]
105     pub SINGLE_MATCH_ELSE,
106     pedantic,
107     "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
108 }
109
110 declare_clippy_lint! {
111     /// ### What it does
112     /// Checks for matches where all arms match a reference,
113     /// suggesting to remove the reference and deref the matched expression
114     /// instead. It also checks for `if let &foo = bar` blocks.
115     ///
116     /// ### Why is this bad?
117     /// It just makes the code less readable. That reference
118     /// destructuring adds nothing to the code.
119     ///
120     /// ### Example
121     /// ```rust,ignore
122     /// match x {
123     ///     &A(ref y) => foo(y),
124     ///     &B => bar(),
125     ///     _ => frob(&x),
126     /// }
127     /// ```
128     ///
129     /// Use instead:
130     /// ```rust,ignore
131     /// match *x {
132     ///     A(ref y) => foo(y),
133     ///     B => bar(),
134     ///     _ => frob(x),
135     /// }
136     /// ```
137     #[clippy::version = "pre 1.29.0"]
138     pub MATCH_REF_PATS,
139     style,
140     "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
141 }
142
143 declare_clippy_lint! {
144     /// ### What it does
145     /// Checks for matches where match expression is a `bool`. It
146     /// suggests to replace the expression with an `if...else` block.
147     ///
148     /// ### Why is this bad?
149     /// It makes the code less readable.
150     ///
151     /// ### Example
152     /// ```rust
153     /// # fn foo() {}
154     /// # fn bar() {}
155     /// let condition: bool = true;
156     /// match condition {
157     ///     true => foo(),
158     ///     false => bar(),
159     /// }
160     /// ```
161     /// Use if/else instead:
162     /// ```rust
163     /// # fn foo() {}
164     /// # fn bar() {}
165     /// let condition: bool = true;
166     /// if condition {
167     ///     foo();
168     /// } else {
169     ///     bar();
170     /// }
171     /// ```
172     #[clippy::version = "pre 1.29.0"]
173     pub MATCH_BOOL,
174     pedantic,
175     "a `match` on a boolean expression instead of an `if..else` block"
176 }
177
178 declare_clippy_lint! {
179     /// ### What it does
180     /// Checks for overlapping match arms.
181     ///
182     /// ### Why is this bad?
183     /// It is likely to be an error and if not, makes the code
184     /// less obvious.
185     ///
186     /// ### Example
187     /// ```rust
188     /// let x = 5;
189     /// match x {
190     ///     1..=10 => println!("1 ... 10"),
191     ///     5..=15 => println!("5 ... 15"),
192     ///     _ => (),
193     /// }
194     /// ```
195     #[clippy::version = "pre 1.29.0"]
196     pub MATCH_OVERLAPPING_ARM,
197     style,
198     "a `match` with overlapping arms"
199 }
200
201 declare_clippy_lint! {
202     /// ### What it does
203     /// Checks for arm which matches all errors with `Err(_)`
204     /// and take drastic actions like `panic!`.
205     ///
206     /// ### Why is this bad?
207     /// It is generally a bad practice, similar to
208     /// catching all exceptions in java with `catch(Exception)`
209     ///
210     /// ### Example
211     /// ```rust
212     /// let x: Result<i32, &str> = Ok(3);
213     /// match x {
214     ///     Ok(_) => println!("ok"),
215     ///     Err(_) => panic!("err"),
216     /// }
217     /// ```
218     #[clippy::version = "pre 1.29.0"]
219     pub MATCH_WILD_ERR_ARM,
220     pedantic,
221     "a `match` with `Err(_)` arm and take drastic actions"
222 }
223
224 declare_clippy_lint! {
225     /// ### What it does
226     /// Checks for match which is used to add a reference to an
227     /// `Option` value.
228     ///
229     /// ### Why is this bad?
230     /// Using `as_ref()` or `as_mut()` instead is shorter.
231     ///
232     /// ### Example
233     /// ```rust
234     /// let x: Option<()> = None;
235     ///
236     /// let r: Option<&()> = match x {
237     ///     None => None,
238     ///     Some(ref v) => Some(v),
239     /// };
240     /// ```
241     ///
242     /// Use instead:
243     /// ```rust
244     /// let x: Option<()> = None;
245     ///
246     /// let r: Option<&()> = x.as_ref();
247     /// ```
248     #[clippy::version = "pre 1.29.0"]
249     pub MATCH_AS_REF,
250     complexity,
251     "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
252 }
253
254 declare_clippy_lint! {
255     /// ### What it does
256     /// Checks for wildcard enum matches using `_`.
257     ///
258     /// ### Why is this bad?
259     /// New enum variants added by library updates can be missed.
260     ///
261     /// ### Known problems
262     /// Suggested replacements may be incorrect if guards exhaustively cover some
263     /// variants, and also may not use correct path to enum if it's not present in the current scope.
264     ///
265     /// ### Example
266     /// ```rust
267     /// # enum Foo { A(usize), B(usize) }
268     /// # let x = Foo::B(1);
269     /// match x {
270     ///     Foo::A(_) => {},
271     ///     _ => {},
272     /// }
273     /// ```
274     ///
275     /// Use instead:
276     /// ```rust
277     /// # enum Foo { A(usize), B(usize) }
278     /// # let x = Foo::B(1);
279     /// match x {
280     ///     Foo::A(_) => {},
281     ///     Foo::B(_) => {},
282     /// }
283     /// ```
284     #[clippy::version = "1.34.0"]
285     pub WILDCARD_ENUM_MATCH_ARM,
286     restriction,
287     "a wildcard enum match arm using `_`"
288 }
289
290 declare_clippy_lint! {
291     /// ### What it does
292     /// Checks for wildcard enum matches for a single variant.
293     ///
294     /// ### Why is this bad?
295     /// New enum variants added by library updates can be missed.
296     ///
297     /// ### Known problems
298     /// Suggested replacements may not use correct path to enum
299     /// if it's not present in the current scope.
300     ///
301     /// ### Example
302     /// ```rust
303     /// # enum Foo { A, B, C }
304     /// # let x = Foo::B;
305     /// match x {
306     ///     Foo::A => {},
307     ///     Foo::B => {},
308     ///     _ => {},
309     /// }
310     /// ```
311     ///
312     /// Use instead:
313     /// ```rust
314     /// # enum Foo { A, B, C }
315     /// # let x = Foo::B;
316     /// match x {
317     ///     Foo::A => {},
318     ///     Foo::B => {},
319     ///     Foo::C => {},
320     /// }
321     /// ```
322     #[clippy::version = "1.45.0"]
323     pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
324     pedantic,
325     "a wildcard enum match for a single variant"
326 }
327
328 declare_clippy_lint! {
329     /// ### What it does
330     /// Checks for wildcard pattern used with others patterns in same match arm.
331     ///
332     /// ### Why is this bad?
333     /// Wildcard pattern already covers any other pattern as it will match anyway.
334     /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
335     ///
336     /// ### Example
337     /// ```rust
338     /// # let s = "foo";
339     /// match s {
340     ///     "a" => {},
341     ///     "bar" | _ => {},
342     /// }
343     /// ```
344     ///
345     /// Use instead:
346     /// ```rust
347     /// # let s = "foo";
348     /// match s {
349     ///     "a" => {},
350     ///     _ => {},
351     /// }
352     /// ```
353     #[clippy::version = "1.42.0"]
354     pub WILDCARD_IN_OR_PATTERNS,
355     complexity,
356     "a wildcard pattern used with others patterns in same match arm"
357 }
358
359 declare_clippy_lint! {
360     /// ### What it does
361     /// Checks for matches being used to destructure a single-variant enum
362     /// or tuple struct where a `let` will suffice.
363     ///
364     /// ### Why is this bad?
365     /// Just readability – `let` doesn't nest, whereas a `match` does.
366     ///
367     /// ### Example
368     /// ```rust
369     /// enum Wrapper {
370     ///     Data(i32),
371     /// }
372     ///
373     /// let wrapper = Wrapper::Data(42);
374     ///
375     /// let data = match wrapper {
376     ///     Wrapper::Data(i) => i,
377     /// };
378     /// ```
379     ///
380     /// The correct use would be:
381     /// ```rust
382     /// enum Wrapper {
383     ///     Data(i32),
384     /// }
385     ///
386     /// let wrapper = Wrapper::Data(42);
387     /// let Wrapper::Data(data) = wrapper;
388     /// ```
389     #[clippy::version = "pre 1.29.0"]
390     pub INFALLIBLE_DESTRUCTURING_MATCH,
391     style,
392     "a `match` statement with a single infallible arm instead of a `let`"
393 }
394
395 declare_clippy_lint! {
396     /// ### What it does
397     /// Checks for useless match that binds to only one value.
398     ///
399     /// ### Why is this bad?
400     /// Readability and needless complexity.
401     ///
402     /// ### Known problems
403     ///  Suggested replacements may be incorrect when `match`
404     /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
405     ///
406     /// ### Example
407     /// ```rust
408     /// # let a = 1;
409     /// # let b = 2;
410     /// match (a, b) {
411     ///     (c, d) => {
412     ///         // useless match
413     ///     }
414     /// }
415     /// ```
416     ///
417     /// Use instead:
418     /// ```rust
419     /// # let a = 1;
420     /// # let b = 2;
421     /// let (c, d) = (a, b);
422     /// ```
423     #[clippy::version = "1.43.0"]
424     pub MATCH_SINGLE_BINDING,
425     complexity,
426     "a match with a single binding instead of using `let` statement"
427 }
428
429 declare_clippy_lint! {
430     /// ### What it does
431     /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
432     ///
433     /// ### Why is this bad?
434     /// Correctness and readability. It's like having a wildcard pattern after
435     /// matching all enum variants explicitly.
436     ///
437     /// ### Example
438     /// ```rust
439     /// # struct A { a: i32 }
440     /// let a = A { a: 5 };
441     ///
442     /// match a {
443     ///     A { a: 5, .. } => {},
444     ///     _ => {},
445     /// }
446     /// ```
447     ///
448     /// Use instead:
449     /// ```rust
450     /// # struct A { a: i32 }
451     /// # let a = A { a: 5 };
452     /// match a {
453     ///     A { a: 5 } => {},
454     ///     _ => {},
455     /// }
456     /// ```
457     #[clippy::version = "1.43.0"]
458     pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
459     restriction,
460     "a match on a struct that binds all fields but still uses the wildcard pattern"
461 }
462
463 declare_clippy_lint! {
464     /// ### What it does
465     /// Lint for redundant pattern matching over `Result`, `Option`,
466     /// `std::task::Poll` or `std::net::IpAddr`
467     ///
468     /// ### Why is this bad?
469     /// It's more concise and clear to just use the proper
470     /// utility function
471     ///
472     /// ### Known problems
473     /// This will change the drop order for the matched type. Both `if let` and
474     /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
475     /// value before entering the block. For most types this change will not matter, but for a few
476     /// types this will not be an acceptable change (e.g. locks). See the
477     /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
478     /// drop order.
479     ///
480     /// ### Example
481     /// ```rust
482     /// # use std::task::Poll;
483     /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
484     /// if let Ok(_) = Ok::<i32, i32>(42) {}
485     /// if let Err(_) = Err::<i32, i32>(42) {}
486     /// if let None = None::<()> {}
487     /// if let Some(_) = Some(42) {}
488     /// if let Poll::Pending = Poll::Pending::<()> {}
489     /// if let Poll::Ready(_) = Poll::Ready(42) {}
490     /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
491     /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
492     /// match Ok::<i32, i32>(42) {
493     ///     Ok(_) => true,
494     ///     Err(_) => false,
495     /// };
496     /// ```
497     ///
498     /// The more idiomatic use would be:
499     ///
500     /// ```rust
501     /// # use std::task::Poll;
502     /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
503     /// if Ok::<i32, i32>(42).is_ok() {}
504     /// if Err::<i32, i32>(42).is_err() {}
505     /// if None::<()>.is_none() {}
506     /// if Some(42).is_some() {}
507     /// if Poll::Pending::<()>.is_pending() {}
508     /// if Poll::Ready(42).is_ready() {}
509     /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
510     /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
511     /// Ok::<i32, i32>(42).is_ok();
512     /// ```
513     #[clippy::version = "1.31.0"]
514     pub REDUNDANT_PATTERN_MATCHING,
515     style,
516     "use the proper utility function avoiding an `if let`"
517 }
518
519 declare_clippy_lint! {
520     /// ### What it does
521     /// Checks for `match`  or `if let` expressions producing a
522     /// `bool` that could be written using `matches!`
523     ///
524     /// ### Why is this bad?
525     /// Readability and needless complexity.
526     ///
527     /// ### Known problems
528     /// This lint falsely triggers, if there are arms with
529     /// `cfg` attributes that remove an arm evaluating to `false`.
530     ///
531     /// ### Example
532     /// ```rust
533     /// let x = Some(5);
534     ///
535     /// let a = match x {
536     ///     Some(0) => true,
537     ///     _ => false,
538     /// };
539     ///
540     /// let a = if let Some(0) = x {
541     ///     true
542     /// } else {
543     ///     false
544     /// };
545     /// ```
546     ///
547     /// Use instead:
548     /// ```rust
549     /// let x = Some(5);
550     /// let a = matches!(x, Some(0));
551     /// ```
552     #[clippy::version = "1.47.0"]
553     pub MATCH_LIKE_MATCHES_MACRO,
554     style,
555     "a match that could be written with the matches! macro"
556 }
557
558 declare_clippy_lint! {
559     /// ### What it does
560     /// Checks for `match` with identical arm bodies.
561     ///
562     /// ### Why is this bad?
563     /// This is probably a copy & paste error. If arm bodies
564     /// are the same on purpose, you can factor them
565     /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
566     ///
567     /// ### Known problems
568     /// False positive possible with order dependent `match`
569     /// (see issue
570     /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
571     ///
572     /// ### Example
573     /// ```rust,ignore
574     /// match foo {
575     ///     Bar => bar(),
576     ///     Quz => quz(),
577     ///     Baz => bar(), // <= oops
578     /// }
579     /// ```
580     ///
581     /// This should probably be
582     /// ```rust,ignore
583     /// match foo {
584     ///     Bar => bar(),
585     ///     Quz => quz(),
586     ///     Baz => baz(), // <= fixed
587     /// }
588     /// ```
589     ///
590     /// or if the original code was not a typo:
591     /// ```rust,ignore
592     /// match foo {
593     ///     Bar | Baz => bar(), // <= shows the intent better
594     ///     Quz => quz(),
595     /// }
596     /// ```
597     #[clippy::version = "pre 1.29.0"]
598     pub MATCH_SAME_ARMS,
599     pedantic,
600     "`match` with identical arm bodies"
601 }
602
603 declare_clippy_lint! {
604     /// ### What it does
605     /// Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result`
606     /// when function signatures are the same.
607     ///
608     /// ### Why is this bad?
609     /// This `match` block does nothing and might not be what the coder intended.
610     ///
611     /// ### Example
612     /// ```rust,ignore
613     /// fn foo() -> Result<(), i32> {
614     ///     match result {
615     ///         Ok(val) => Ok(val),
616     ///         Err(err) => Err(err),
617     ///     }
618     /// }
619     ///
620     /// fn bar() -> Option<i32> {
621     ///     if let Some(val) = option {
622     ///         Some(val)
623     ///     } else {
624     ///         None
625     ///     }
626     /// }
627     /// ```
628     ///
629     /// Could be replaced as
630     ///
631     /// ```rust,ignore
632     /// fn foo() -> Result<(), i32> {
633     ///     result
634     /// }
635     ///
636     /// fn bar() -> Option<i32> {
637     ///     option
638     /// }
639     /// ```
640     #[clippy::version = "1.61.0"]
641     pub NEEDLESS_MATCH,
642     complexity,
643     "`match` or match-like `if let` that are unnecessary"
644 }
645
646 declare_clippy_lint! {
647     /// ### What it does
648     /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
649     /// without adding any branches.
650     ///
651     /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
652     /// cases where merging would most likely make the code more readable.
653     ///
654     /// ### Why is this bad?
655     /// It is unnecessarily verbose and complex.
656     ///
657     /// ### Example
658     /// ```rust
659     /// fn func(opt: Option<Result<u64, String>>) {
660     ///     let n = match opt {
661     ///         Some(n) => match n {
662     ///             Ok(n) => n,
663     ///             _ => return,
664     ///         }
665     ///         None => return,
666     ///     };
667     /// }
668     /// ```
669     /// Use instead:
670     /// ```rust
671     /// fn func(opt: Option<Result<u64, String>>) {
672     ///     let n = match opt {
673     ///         Some(Ok(n)) => n,
674     ///         _ => return,
675     ///     };
676     /// }
677     /// ```
678     #[clippy::version = "1.50.0"]
679     pub COLLAPSIBLE_MATCH,
680     style,
681     "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
682 }
683
684 declare_clippy_lint! {
685     /// ### What it does
686     /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
687     ///
688     /// ### Why is this bad?
689     /// Concise code helps focusing on behavior instead of boilerplate.
690     ///
691     /// ### Example
692     /// ```rust
693     /// let foo: Option<i32> = None;
694     /// match foo {
695     ///     Some(v) => v,
696     ///     None => 1,
697     /// };
698     /// ```
699     ///
700     /// Use instead:
701     /// ```rust
702     /// let foo: Option<i32> = None;
703     /// foo.unwrap_or(1);
704     /// ```
705     #[clippy::version = "1.49.0"]
706     pub MANUAL_UNWRAP_OR,
707     complexity,
708     "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
709 }
710
711 declare_clippy_lint! {
712     /// ### What it does
713     /// Checks for `match vec[idx]` or `match vec[n..m]`.
714     ///
715     /// ### Why is this bad?
716     /// This can panic at runtime.
717     ///
718     /// ### Example
719     /// ```rust, no_run
720     /// let arr = vec![0, 1, 2, 3];
721     /// let idx = 1;
722     ///
723     /// match arr[idx] {
724     ///     0 => println!("{}", 0),
725     ///     1 => println!("{}", 3),
726     ///     _ => {},
727     /// }
728     /// ```
729     ///
730     /// Use instead:
731     /// ```rust, no_run
732     /// let arr = vec![0, 1, 2, 3];
733     /// let idx = 1;
734     ///
735     /// match arr.get(idx) {
736     ///     Some(0) => println!("{}", 0),
737     ///     Some(1) => println!("{}", 3),
738     ///     _ => {},
739     /// }
740     /// ```
741     #[clippy::version = "1.45.0"]
742     pub MATCH_ON_VEC_ITEMS,
743     pedantic,
744     "matching on vector elements can panic"
745 }
746
747 declare_clippy_lint! {
748     /// ### What it does
749     /// Checks for `match` expressions modifying the case of a string with non-compliant arms
750     ///
751     /// ### Why is this bad?
752     /// The arm is unreachable, which is likely a mistake
753     ///
754     /// ### Example
755     /// ```rust
756     /// # let text = "Foo";
757     /// match &*text.to_ascii_lowercase() {
758     ///     "foo" => {},
759     ///     "Bar" => {},
760     ///     _ => {},
761     /// }
762     /// ```
763     /// Use instead:
764     /// ```rust
765     /// # let text = "Foo";
766     /// match &*text.to_ascii_lowercase() {
767     ///     "foo" => {},
768     ///     "bar" => {},
769     ///     _ => {},
770     /// }
771     /// ```
772     #[clippy::version = "1.58.0"]
773     pub MATCH_STR_CASE_MISMATCH,
774     correctness,
775     "creation of a case altering match expression with non-compliant arms"
776 }
777
778 declare_clippy_lint! {
779     /// ### What it does
780     /// Check for temporaries returned from function calls in a match scrutinee that have the
781     /// `clippy::has_significant_drop` attribute.
782     ///
783     /// ### Why is this bad?
784     /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
785     /// an important side-effect, such as unlocking a mutex, making it important for users to be
786     /// able to accurately understand their lifetimes. When a temporary is returned in a function
787     /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may
788     /// be surprising.
789     ///
790     /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
791     /// function call that returns a `MutexGuard` and then tries to lock again in one of the match
792     /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
793     /// the match block and thus will not unlock.
794     ///
795     /// ### Example
796     /// ```rust,ignore
797     /// # use std::sync::Mutex;
798     /// # struct State {}
799     /// # impl State {
800     /// #     fn foo(&self) -> bool {
801     /// #         true
802     /// #     }
803     /// #     fn bar(&self) {}
804     /// # }
805     /// let mutex = Mutex::new(State {});
806     ///
807     /// match mutex.lock().unwrap().foo() {
808     ///     true => {
809     ///         mutex.lock().unwrap().bar(); // Deadlock!
810     ///     }
811     ///     false => {}
812     /// };
813     ///
814     /// println!("All done!");
815     /// ```
816     /// Use instead:
817     /// ```rust
818     /// # use std::sync::Mutex;
819     /// # struct State {}
820     /// # impl State {
821     /// #     fn foo(&self) -> bool {
822     /// #         true
823     /// #     }
824     /// #     fn bar(&self) {}
825     /// # }
826     /// let mutex = Mutex::new(State {});
827     ///
828     /// let is_foo = mutex.lock().unwrap().foo();
829     /// match is_foo {
830     ///     true => {
831     ///         mutex.lock().unwrap().bar();
832     ///     }
833     ///     false => {}
834     /// };
835     ///
836     /// println!("All done!");
837     /// ```
838     #[clippy::version = "1.60.0"]
839     pub SIGNIFICANT_DROP_IN_SCRUTINEE,
840     nursery,
841     "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
842 }
843
844 declare_clippy_lint! {
845     /// ### What it does
846     /// Checks for usages of `Err(x)?`.
847     ///
848     /// ### Why is this bad?
849     /// The `?` operator is designed to allow calls that
850     /// can fail to be easily chained. For example, `foo()?.bar()` or
851     /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will
852     /// always return), it is more clear to write `return Err(x)`.
853     ///
854     /// ### Example
855     /// ```rust
856     /// fn foo(fail: bool) -> Result<i32, String> {
857     ///     if fail {
858     ///       Err("failed")?;
859     ///     }
860     ///     Ok(0)
861     /// }
862     /// ```
863     /// Could be written:
864     ///
865     /// ```rust
866     /// fn foo(fail: bool) -> Result<i32, String> {
867     ///     if fail {
868     ///       return Err("failed".into());
869     ///     }
870     ///     Ok(0)
871     /// }
872     /// ```
873     #[clippy::version = "1.38.0"]
874     pub TRY_ERR,
875     restriction,
876     "return errors explicitly rather than hiding them behind a `?`"
877 }
878
879 declare_clippy_lint! {
880     /// ### What it does
881     /// Checks for usages of `match` which could be implemented using `map`
882     ///
883     /// ### Why is this bad?
884     /// Using the `map` method is clearer and more concise.
885     ///
886     /// ### Example
887     /// ```rust
888     /// match Some(0) {
889     ///     Some(x) => Some(x + 1),
890     ///     None => None,
891     /// };
892     /// ```
893     /// Use instead:
894     /// ```rust
895     /// Some(0).map(|x| x + 1);
896     /// ```
897     #[clippy::version = "1.52.0"]
898     pub MANUAL_MAP,
899     style,
900     "reimplementation of `map`"
901 }
902
903 declare_clippy_lint! {
904     /// ### What it does
905     /// Checks for usages of `match` which could be implemented using `filter`
906     ///
907     /// ### Why is this bad?
908     /// Using the `filter` method is clearer and more concise.
909     ///
910     /// ### Example
911     /// ```rust
912     /// match Some(0) {
913     ///     Some(x) => if x % 2 == 0 {
914     ///                     Some(x)
915     ///                } else {
916     ///                     None
917     ///                 },
918     ///     None => None,
919     /// };
920     /// ```
921     /// Use instead:
922     /// ```rust
923     /// Some(0).filter(|&x| x % 2 == 0);
924     /// ```
925     #[clippy::version = "1.66.0"]
926     pub MANUAL_FILTER,
927     complexity,
928     "reimplentation of `filter`"
929 }
930
931 #[derive(Default)]
932 pub struct Matches {
933     msrv: Msrv,
934     infallible_destructuring_match_linted: bool,
935 }
936
937 impl Matches {
938     #[must_use]
939     pub fn new(msrv: Msrv) -> Self {
940         Self {
941             msrv,
942             ..Matches::default()
943         }
944     }
945 }
946
947 impl_lint_pass!(Matches => [
948     SINGLE_MATCH,
949     MATCH_REF_PATS,
950     MATCH_BOOL,
951     SINGLE_MATCH_ELSE,
952     MATCH_OVERLAPPING_ARM,
953     MATCH_WILD_ERR_ARM,
954     MATCH_AS_REF,
955     WILDCARD_ENUM_MATCH_ARM,
956     MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
957     WILDCARD_IN_OR_PATTERNS,
958     MATCH_SINGLE_BINDING,
959     INFALLIBLE_DESTRUCTURING_MATCH,
960     REST_PAT_IN_FULLY_BOUND_STRUCTS,
961     REDUNDANT_PATTERN_MATCHING,
962     MATCH_LIKE_MATCHES_MACRO,
963     MATCH_SAME_ARMS,
964     NEEDLESS_MATCH,
965     COLLAPSIBLE_MATCH,
966     MANUAL_UNWRAP_OR,
967     MATCH_ON_VEC_ITEMS,
968     MATCH_STR_CASE_MISMATCH,
969     SIGNIFICANT_DROP_IN_SCRUTINEE,
970     TRY_ERR,
971     MANUAL_MAP,
972     MANUAL_FILTER,
973 ]);
974
975 impl<'tcx> LateLintPass<'tcx> for Matches {
976     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
977         if in_external_macro(cx.sess(), expr.span) {
978             return;
979         }
980         let from_expansion = expr.span.from_expansion();
981
982         if let ExprKind::Match(ex, arms, source) = expr.kind {
983             if source == MatchSource::Normal && !is_span_match(cx, expr.span) {
984                 return;
985             }
986             if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
987                 significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
988             }
989
990             collapsible_match::check_match(cx, arms);
991             if !from_expansion {
992                 // These don't depend on a relationship between multiple arms
993                 match_wild_err_arm::check(cx, ex, arms);
994                 wild_in_or_pats::check(cx, arms);
995             }
996
997             if source == MatchSource::TryDesugar {
998                 try_err::check(cx, expr, ex);
999             }
1000
1001             if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
1002                 if source == MatchSource::Normal {
1003                     if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) {
1004                         match_same_arms::check(cx, arms);
1005                     }
1006
1007                     redundant_pattern_match::check_match(cx, expr, ex, arms);
1008                     single_match::check(cx, ex, arms, expr);
1009                     match_bool::check(cx, ex, arms, expr);
1010                     overlapping_arms::check(cx, ex, arms);
1011                     match_wild_enum::check(cx, ex, arms);
1012                     match_as_ref::check(cx, ex, arms, expr);
1013                     needless_match::check_match(cx, ex, arms, expr);
1014                     match_on_vec_items::check(cx, ex);
1015                     match_str_case_mismatch::check(cx, ex, arms);
1016
1017                     if !in_constant(cx, expr.hir_id) {
1018                         manual_unwrap_or::check(cx, expr, ex, arms);
1019                         manual_map::check_match(cx, expr, ex, arms);
1020                         manual_filter::check_match(cx, ex, arms, expr);
1021                     }
1022
1023                     if self.infallible_destructuring_match_linted {
1024                         self.infallible_destructuring_match_linted = false;
1025                     } else {
1026                         match_single_binding::check(cx, ex, arms, expr);
1027                     }
1028                 }
1029                 match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
1030             }
1031         } else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
1032             collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
1033             if !from_expansion {
1034                 if let Some(else_expr) = if_let.if_else {
1035                     if self.msrv.meets(msrvs::MATCHES_MACRO) {
1036                         match_like_matches::check_if_let(
1037                             cx,
1038                             expr,
1039                             if_let.let_pat,
1040                             if_let.let_expr,
1041                             if_let.if_then,
1042                             else_expr,
1043                         );
1044                     }
1045                     if !in_constant(cx, expr.hir_id) {
1046                         manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr);
1047                         manual_filter::check_if_let(
1048                             cx,
1049                             expr,
1050                             if_let.let_pat,
1051                             if_let.let_expr,
1052                             if_let.if_then,
1053                             else_expr,
1054                         );
1055                     }
1056                 }
1057                 redundant_pattern_match::check_if_let(
1058                     cx,
1059                     expr,
1060                     if_let.let_pat,
1061                     if_let.let_expr,
1062                     if_let.if_else.is_some(),
1063                 );
1064                 needless_match::check_if_let(cx, expr, &if_let);
1065             }
1066         } else if !from_expansion {
1067             redundant_pattern_match::check(cx, expr);
1068         }
1069     }
1070
1071     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
1072         self.infallible_destructuring_match_linted |=
1073             local.els.is_none() && infallible_destructuring_match::check(cx, local);
1074     }
1075
1076     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
1077         rest_pat_in_fully_bound_struct::check(cx, pat);
1078     }
1079
1080     extract_msrv_attr!(LateContext);
1081 }
1082
1083 /// Checks if there are any arms with a `#[cfg(..)]` attribute.
1084 fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
1085     let Some(scrutinee_span) = walk_span_to_context(scrutinee.span, SyntaxContext::root()) else {
1086         // Shouldn't happen, but treat this as though a `cfg` attribute were found
1087         return true;
1088     };
1089
1090     let start = scrutinee_span.hi();
1091     let mut arm_spans = arms.iter().map(|arm| {
1092         let data = arm.span.data();
1093         (data.ctxt == SyntaxContext::root()).then_some((data.lo, data.hi))
1094     });
1095     let end = e.span.hi();
1096
1097     // Walk through all the non-code space before each match arm. The space trailing the final arm is
1098     // handled after the `try_fold` e.g.
1099     //
1100     // match foo {
1101     // _________^-                      everything between the scrutinee and arm1
1102     //|    arm1 => (),
1103     //|---^___________^                 everything before arm2
1104     //|    #[cfg(feature = "enabled")]
1105     //|    arm2 => some_code(),
1106     //|---^____________________^        everything before arm3
1107     //|    // some comment about arm3
1108     //|    arm3 => some_code(),
1109     //|---^____________________^        everything after arm3
1110     //|    #[cfg(feature = "disabled")]
1111     //|    arm4 = some_code(),
1112     //|};
1113     //|^
1114     let found = arm_spans.try_fold(start, |start, range| {
1115         let Some((end, next_start)) = range else {
1116             // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute were
1117             // found.
1118             return Err(());
1119         };
1120         let span = SpanData {
1121             lo: start,
1122             hi: end,
1123             ctxt: SyntaxContext::root(),
1124             parent: None,
1125         }
1126         .span();
1127         (!span_contains_cfg(cx, span)).then_some(next_start).ok_or(())
1128     });
1129     match found {
1130         Ok(start) => {
1131             let span = SpanData {
1132                 lo: start,
1133                 hi: end,
1134                 ctxt: SyntaxContext::root(),
1135                 parent: None,
1136             }
1137             .span();
1138             span_contains_cfg(cx, span)
1139         },
1140         Err(()) => true,
1141     }
1142 }
1143
1144 /// Checks if the given span contains a `#[cfg(..)]` attribute
1145 fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
1146     let Some(snip) = snippet_opt(cx, s) else {
1147         // Assume true. This would require either an invalid span, or one which crosses file boundaries.
1148         return true;
1149     };
1150     let mut pos = 0usize;
1151     let mut iter = tokenize(&snip).map(|t| {
1152         let start = pos;
1153         pos += t.len as usize;
1154         (t.kind, start..pos)
1155     });
1156
1157     // Search for the token sequence [`#`, `[`, `cfg`]
1158     while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
1159         let mut iter = iter.by_ref().skip_while(|(t, _)| {
1160             matches!(
1161                 t,
1162                 TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
1163             )
1164         });
1165         if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
1166             && matches!(iter.next(), Some((TokenKind::Ident, range)) if &snip[range.clone()] == "cfg")
1167         {
1168             return true;
1169         }
1170     }
1171     false
1172 }