]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/matches/mod.rs
97ecca450fac199448a26b9fce612632c1069f88
[rustc.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 "reimplementation 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 }