mod collapsible_match;
mod infallible_destructuring_match;
mod manual_filter;
mod manual_map;
mod manual_ok_err;
mod manual_unwrap_or;
mod manual_utils;
mod match_as_ref;
mod match_bool;
mod match_like_matches;
mod match_ref_pats;
mod match_same_arms;
mod match_single_binding;
mod match_str_case_mismatch;
mod match_wild_enum;
mod match_wild_err_arm;
mod needless_match;
mod overlapping_arms;
mod redundant_guards;
mod redundant_pattern_match;
mod rest_pat_in_fully_bound_struct;
mod significant_drop_in_scrutinee;
mod single_match;
mod try_err;
mod wild_in_or_pats;

use clippy_config::Conf;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::walk_span_to_context;
use clippy_utils::{
    higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, sym,
};
use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::impl_lint_pass;
use rustc_span::{SpanData, SyntaxContext};

declare_clippy_lint! {
    /// ### What it does
    /// Checks for matches with a single arm where an `if let`
    /// will usually suffice.
    ///
    /// This intentionally does not lint if there are comments
    /// inside of the other arm, so as to allow the user to document
    /// why having another explicit pattern with an empty body is necessary,
    /// or because the comments need to be preserved for other reasons.
    ///
    /// ### Why is this bad?
    /// Just readability – `if let` nests less than a `match`.
    ///
    /// ### Example
    /// ```no_run
    /// # fn bar(stool: &str) {}
    /// # let x = Some("abc");
    /// match x {
    ///     Some(ref foo) => bar(foo),
    ///     _ => (),
    /// }
    /// ```
    ///
    /// Use instead:
    /// ```no_run
    /// # fn bar(stool: &str) {}
    /// # let x = Some("abc");
    /// if let Some(ref foo) = x {
    ///     bar(foo);
    /// }
    /// ```
    #[clippy::version = "pre 1.29.0"]
    pub SINGLE_MATCH,
    style,
    "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for matches with two arms where an `if let else` will
    /// usually suffice.
    ///
    /// ### Why is this bad?
    /// Just readability – `if let` nests less than a `match`.
    ///
    /// ### Known problems
    /// Personal style preferences may differ.
    ///
    /// ### Example
    /// Using `match`:
    ///
    /// ```no_run
    /// # fn bar(foo: &usize) {}
    /// # let other_ref: usize = 1;
    /// # let x: Option<&usize> = Some(&1);
    /// match x {
    ///     Some(ref foo) => bar(foo),
    ///     _ => bar(&other_ref),
    /// }
    /// ```
    ///
    /// Using `if let` with `else`:
    ///
    /// ```no_run
    /// # fn bar(foo: &usize) {}
    /// # let other_ref: usize = 1;
    /// # let x: Option<&usize> = Some(&1);
    /// if let Some(ref foo) = x {
    ///     bar(foo);
    /// } else {
    ///     bar(&other_ref);
    /// }
    /// ```
    #[clippy::version = "pre 1.29.0"]
    pub SINGLE_MATCH_ELSE,
    pedantic,
    "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for matches where all arms match a reference,
    /// suggesting to remove the reference and deref the matched expression
    /// instead. It also checks for `if let &foo = bar` blocks.
    ///
    /// ### Why is this bad?
    /// It just makes the code less readable. That reference
    /// destructuring adds nothing to the code.
    ///
    /// ### Example
    /// ```rust,ignore
    /// match x {
    ///     &A(ref y) => foo(y),
    ///     &B => bar(),
    ///     _ => frob(&x),
    /// }
    /// ```
    ///
    /// Use instead:
    /// ```rust,ignore
    /// match *x {
    ///     A(ref y) => foo(y),
    ///     B => bar(),
    ///     _ => frob(x),
    /// }
    /// ```
    #[clippy::version = "pre 1.29.0"]
    pub MATCH_REF_PATS,
    style,
    "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for matches where match expression is a `bool`. It
    /// suggests to replace the expression with an `if...else` block.
    ///
    /// ### Why is this bad?
    /// It makes the code less readable.
    ///
    /// ### Example
    /// ```no_run
    /// # fn foo() {}
    /// # fn bar() {}
    /// let condition: bool = true;
    /// match condition {
    ///     true => foo(),
    ///     false => bar(),
    /// }
    /// ```
    /// Use if/else instead:
    /// ```no_run
    /// # fn foo() {}
    /// # fn bar() {}
    /// let condition: bool = true;
    /// if condition {
    ///     foo();
    /// } else {
    ///     bar();
    /// }
    /// ```
    #[clippy::version = "pre 1.29.0"]
    pub MATCH_BOOL,
    pedantic,
    "a `match` on a boolean expression instead of an `if..else` block"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for overlapping match arms.
    ///
    /// ### Why is this bad?
    /// It is likely to be an error and if not, makes the code
    /// less obvious.
    ///
    /// ### Example
    /// ```no_run
    /// let x = 5;
    /// match x {
    ///     1..=10 => println!("1 ... 10"),
    ///     5..=15 => println!("5 ... 15"),
    ///     _ => (),
    /// }
    /// ```
    #[clippy::version = "pre 1.29.0"]
    pub MATCH_OVERLAPPING_ARM,
    style,
    "a `match` with overlapping arms"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for arm which matches all errors with `Err(_)`
    /// and take drastic actions like `panic!`.
    ///
    /// ### Why is this bad?
    /// It is generally a bad practice, similar to
    /// catching all exceptions in java with `catch(Exception)`
    ///
    /// ### Example
    /// ```no_run
    /// let x: Result<i32, &str> = Ok(3);
    /// match x {
    ///     Ok(_) => println!("ok"),
    ///     Err(_) => panic!("err"),
    /// }
    /// ```
    #[clippy::version = "pre 1.29.0"]
    pub MATCH_WILD_ERR_ARM,
    pedantic,
    "a `match` with `Err(_)` arm and take drastic actions"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for match which is used to add a reference to an
    /// `Option` value.
    ///
    /// ### Why is this bad?
    /// Using `as_ref()` or `as_mut()` instead is shorter.
    ///
    /// ### Example
    /// ```no_run
    /// let x: Option<()> = None;
    ///
    /// let r: Option<&()> = match x {
    ///     None => None,
    ///     Some(ref v) => Some(v),
    /// };
    /// ```
    ///
    /// Use instead:
    /// ```no_run
    /// let x: Option<()> = None;
    ///
    /// let r: Option<&()> = x.as_ref();
    /// ```
    #[clippy::version = "pre 1.29.0"]
    pub MATCH_AS_REF,
    complexity,
    "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for wildcard enum matches using `_`.
    ///
    /// ### Why restrict this?
    /// New enum variants added by library updates can be missed.
    ///
    /// ### Known problems
    /// Suggested replacements may be incorrect if guards exhaustively cover some
    /// variants, and also may not use correct path to enum if it's not present in the current scope.
    ///
    /// ### Example
    /// ```no_run
    /// # enum Foo { A(usize), B(usize) }
    /// # let x = Foo::B(1);
    /// match x {
    ///     Foo::A(_) => {},
    ///     _ => {},
    /// }
    /// ```
    ///
    /// Use instead:
    /// ```no_run
    /// # enum Foo { A(usize), B(usize) }
    /// # let x = Foo::B(1);
    /// match x {
    ///     Foo::A(_) => {},
    ///     Foo::B(_) => {},
    /// }
    /// ```
    #[clippy::version = "1.34.0"]
    pub WILDCARD_ENUM_MATCH_ARM,
    restriction,
    "a wildcard enum match arm using `_`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for wildcard enum matches for a single variant.
    ///
    /// ### Why is this bad?
    /// New enum variants added by library updates can be missed.
    ///
    /// ### Known problems
    /// Suggested replacements may not use correct path to enum
    /// if it's not present in the current scope.
    ///
    /// ### Example
    /// ```no_run
    /// # enum Foo { A, B, C }
    /// # let x = Foo::B;
    /// match x {
    ///     Foo::A => {},
    ///     Foo::B => {},
    ///     _ => {},
    /// }
    /// ```
    ///
    /// Use instead:
    /// ```no_run
    /// # enum Foo { A, B, C }
    /// # let x = Foo::B;
    /// match x {
    ///     Foo::A => {},
    ///     Foo::B => {},
    ///     Foo::C => {},
    /// }
    /// ```
    #[clippy::version = "1.45.0"]
    pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
    pedantic,
    "a wildcard enum match for a single variant"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for wildcard pattern used with others patterns in same match arm.
    ///
    /// ### Why is this bad?
    /// Wildcard pattern already covers any other pattern as it will match anyway.
    /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
    ///
    /// ### Example
    /// ```no_run
    /// # let s = "foo";
    /// match s {
    ///     "a" => {},
    ///     "bar" | _ => {},
    /// }
    /// ```
    ///
    /// Use instead:
    /// ```no_run
    /// # let s = "foo";
    /// match s {
    ///     "a" => {},
    ///     _ => {},
    /// }
    /// ```
    #[clippy::version = "1.42.0"]
    pub WILDCARD_IN_OR_PATTERNS,
    complexity,
    "a wildcard pattern used with others patterns in same match arm"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for matches being used to destructure a single-variant enum
    /// or tuple struct where a `let` will suffice.
    ///
    /// ### Why is this bad?
    /// Just readability – `let` doesn't nest, whereas a `match` does.
    ///
    /// ### Example
    /// ```no_run
    /// enum Wrapper {
    ///     Data(i32),
    /// }
    ///
    /// let wrapper = Wrapper::Data(42);
    ///
    /// let data = match wrapper {
    ///     Wrapper::Data(i) => i,
    /// };
    /// ```
    ///
    /// The correct use would be:
    /// ```no_run
    /// enum Wrapper {
    ///     Data(i32),
    /// }
    ///
    /// let wrapper = Wrapper::Data(42);
    /// let Wrapper::Data(data) = wrapper;
    /// ```
    #[clippy::version = "pre 1.29.0"]
    pub INFALLIBLE_DESTRUCTURING_MATCH,
    style,
    "a `match` statement with a single infallible arm instead of a `let`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for useless match that binds to only one value.
    ///
    /// ### Why is this bad?
    /// Readability and needless complexity.
    ///
    /// ### Known problems
    ///  Suggested replacements may be incorrect when `match`
    /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
    ///
    /// ### Example
    /// ```no_run
    /// # let a = 1;
    /// # let b = 2;
    /// match (a, b) {
    ///     (c, d) => {
    ///         // useless match
    ///     }
    /// }
    /// ```
    ///
    /// Use instead:
    /// ```no_run
    /// # let a = 1;
    /// # let b = 2;
    /// let (c, d) = (a, b);
    /// ```
    #[clippy::version = "1.43.0"]
    pub MATCH_SINGLE_BINDING,
    complexity,
    "a match with a single binding instead of using `let` statement"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
    ///
    /// ### Why restrict this?
    /// Correctness and readability. It's like having a wildcard pattern after
    /// matching all enum variants explicitly.
    ///
    /// ### Example
    /// ```no_run
    /// # struct A { a: i32 }
    /// let a = A { a: 5 };
    ///
    /// match a {
    ///     A { a: 5, .. } => {},
    ///     _ => {},
    /// }
    /// ```
    ///
    /// Use instead:
    /// ```no_run
    /// # struct A { a: i32 }
    /// # let a = A { a: 5 };
    /// match a {
    ///     A { a: 5 } => {},
    ///     _ => {},
    /// }
    /// ```
    #[clippy::version = "1.43.0"]
    pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
    restriction,
    "a match on a struct that binds all fields but still uses the wildcard pattern"
}

declare_clippy_lint! {
    /// ### What it does
    /// Lint for redundant pattern matching over `Result`, `Option`,
    /// `std::task::Poll`, `std::net::IpAddr` or `bool`s
    ///
    /// ### Why is this bad?
    /// It's more concise and clear to just use the proper
    /// utility function or using the condition directly
    ///
    /// ### Known problems
    /// For suggestions involving bindings in patterns, this will change the drop order for the matched type.
    /// Both `if let` and `while let` will drop the value at the end of the block, both `if` and `while` will drop the
    /// value before entering the block. For most types this change will not matter, but for a few
    /// types this will not be an acceptable change (e.g. locks). See the
    /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
    /// drop order.
    ///
    /// ### Example
    /// ```no_run
    /// # use std::task::Poll;
    /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
    /// if let Ok(_) = Ok::<i32, i32>(42) {}
    /// if let Err(_) = Err::<i32, i32>(42) {}
    /// if let None = None::<()> {}
    /// if let Some(_) = Some(42) {}
    /// if let Poll::Pending = Poll::Pending::<()> {}
    /// if let Poll::Ready(_) = Poll::Ready(42) {}
    /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
    /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
    /// match Ok::<i32, i32>(42) {
    ///     Ok(_) => true,
    ///     Err(_) => false,
    /// };
    ///
    /// let cond = true;
    /// if let true = cond {}
    /// matches!(cond, true);
    /// ```
    ///
    /// The more idiomatic use would be:
    ///
    /// ```no_run
    /// # use std::task::Poll;
    /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
    /// if Ok::<i32, i32>(42).is_ok() {}
    /// if Err::<i32, i32>(42).is_err() {}
    /// if None::<()>.is_none() {}
    /// if Some(42).is_some() {}
    /// if Poll::Pending::<()>.is_pending() {}
    /// if Poll::Ready(42).is_ready() {}
    /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
    /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
    /// Ok::<i32, i32>(42).is_ok();
    ///
    /// let cond = true;
    /// if cond {}
    /// cond;
    /// ```
    #[clippy::version = "1.31.0"]
    pub REDUNDANT_PATTERN_MATCHING,
    style,
    "use the proper utility function avoiding an `if let`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for `match`  or `if let` expressions producing a
    /// `bool` that could be written using `matches!`
    ///
    /// ### Why is this bad?
    /// Readability and needless complexity.
    ///
    /// ### Known problems
    /// This lint falsely triggers, if there are arms with
    /// `cfg` attributes that remove an arm evaluating to `false`.
    ///
    /// ### Example
    /// ```no_run
    /// let x = Some(5);
    ///
    /// let a = match x {
    ///     Some(0) => true,
    ///     _ => false,
    /// };
    ///
    /// let a = if let Some(0) = x {
    ///     true
    /// } else {
    ///     false
    /// };
    /// ```
    ///
    /// Use instead:
    /// ```no_run
    /// let x = Some(5);
    /// let a = matches!(x, Some(0));
    /// ```
    #[clippy::version = "1.47.0"]
    pub MATCH_LIKE_MATCHES_MACRO,
    style,
    "a match that could be written with the matches! macro"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for `match` with identical arm bodies.
    ///
    /// Note: Does not lint on wildcards if the `non_exhaustive_omitted_patterns_lint` feature is
    /// enabled and disallowed.
    ///
    /// ### Why is this bad?
    /// This is probably a copy & paste error. If arm bodies
    /// are the same on purpose, you can factor them
    /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
    ///
    /// ### Example
    /// ```rust,ignore
    /// match foo {
    ///     Bar => bar(),
    ///     Quz => quz(),
    ///     Baz => bar(), // <= oops
    /// }
    /// ```
    ///
    /// This should probably be
    /// ```rust,ignore
    /// match foo {
    ///     Bar => bar(),
    ///     Quz => quz(),
    ///     Baz => baz(), // <= fixed
    /// }
    /// ```
    ///
    /// or if the original code was not a typo:
    /// ```rust,ignore
    /// match foo {
    ///     Bar | Baz => bar(), // <= shows the intent better
    ///     Quz => quz(),
    /// }
    /// ```
    #[clippy::version = "pre 1.29.0"]
    pub MATCH_SAME_ARMS,
    pedantic,
    "`match` with identical arm bodies"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result`
    /// when function signatures are the same.
    ///
    /// ### Why is this bad?
    /// This `match` block does nothing and might not be what the coder intended.
    ///
    /// ### Example
    /// ```rust,ignore
    /// fn foo() -> Result<(), i32> {
    ///     match result {
    ///         Ok(val) => Ok(val),
    ///         Err(err) => Err(err),
    ///     }
    /// }
    ///
    /// fn bar() -> Option<i32> {
    ///     if let Some(val) = option {
    ///         Some(val)
    ///     } else {
    ///         None
    ///     }
    /// }
    /// ```
    ///
    /// Could be replaced as
    ///
    /// ```rust,ignore
    /// fn foo() -> Result<(), i32> {
    ///     result
    /// }
    ///
    /// fn bar() -> Option<i32> {
    ///     option
    /// }
    /// ```
    #[clippy::version = "1.61.0"]
    pub NEEDLESS_MATCH,
    complexity,
    "`match` or match-like `if let` that are unnecessary"
}

declare_clippy_lint! {
    /// ### What it does
    /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
    /// without adding any branches.
    ///
    /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
    /// cases where merging would most likely make the code more readable.
    ///
    /// ### Why is this bad?
    /// It is unnecessarily verbose and complex.
    ///
    /// ### Example
    /// ```no_run
    /// fn func(opt: Option<Result<u64, String>>) {
    ///     let n = match opt {
    ///         Some(n) => match n {
    ///             Ok(n) => n,
    ///             _ => return,
    ///         }
    ///         None => return,
    ///     };
    /// }
    /// ```
    /// Use instead:
    /// ```no_run
    /// fn func(opt: Option<Result<u64, String>>) {
    ///     let n = match opt {
    ///         Some(Ok(n)) => n,
    ///         _ => return,
    ///     };
    /// }
    /// ```
    #[clippy::version = "1.50.0"]
    pub COLLAPSIBLE_MATCH,
    style,
    "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
}

declare_clippy_lint! {
    /// ### What it does
    /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
    ///
    /// ### Why is this bad?
    /// Concise code helps focusing on behavior instead of boilerplate.
    ///
    /// ### Example
    /// ```no_run
    /// let foo: Option<i32> = None;
    /// match foo {
    ///     Some(v) => v,
    ///     None => 1,
    /// };
    /// ```
    ///
    /// Use instead:
    /// ```no_run
    /// let foo: Option<i32> = None;
    /// foo.unwrap_or(1);
    /// ```
    #[clippy::version = "1.49.0"]
    pub MANUAL_UNWRAP_OR,
    complexity,
    "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks if a `match` or `if let` expression can be simplified using
    /// `.unwrap_or_default()`.
    ///
    /// ### Why is this bad?
    /// It can be done in one call with `.unwrap_or_default()`.
    ///
    /// ### Example
    /// ```no_run
    /// let x: Option<String> = Some(String::new());
    /// let y: String = match x {
    ///     Some(v) => v,
    ///     None => String::new(),
    /// };
    ///
    /// let x: Option<Vec<String>> = Some(Vec::new());
    /// let y: Vec<String> = if let Some(v) = x {
    ///     v
    /// } else {
    ///     Vec::new()
    /// };
    /// ```
    /// Use instead:
    /// ```no_run
    /// let x: Option<String> = Some(String::new());
    /// let y: String = x.unwrap_or_default();
    ///
    /// let x: Option<Vec<String>> = Some(Vec::new());
    /// let y: Vec<String> = x.unwrap_or_default();
    /// ```
    #[clippy::version = "1.79.0"]
    pub MANUAL_UNWRAP_OR_DEFAULT,
    suspicious,
    "check if a `match` or `if let` can be simplified with `unwrap_or_default`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for `match` expressions modifying the case of a string with non-compliant arms
    ///
    /// ### Why is this bad?
    /// The arm is unreachable, which is likely a mistake
    ///
    /// ### Example
    /// ```no_run
    /// # let text = "Foo";
    /// match &*text.to_ascii_lowercase() {
    ///     "foo" => {},
    ///     "Bar" => {},
    ///     _ => {},
    /// }
    /// ```
    /// Use instead:
    /// ```no_run
    /// # let text = "Foo";
    /// match &*text.to_ascii_lowercase() {
    ///     "foo" => {},
    ///     "bar" => {},
    ///     _ => {},
    /// }
    /// ```
    #[clippy::version = "1.58.0"]
    pub MATCH_STR_CASE_MISMATCH,
    correctness,
    "creation of a case altering match expression with non-compliant arms"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for temporaries returned from function calls in a match scrutinee that have the
    /// `clippy::has_significant_drop` attribute.
    ///
    /// ### Why is this bad?
    /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
    /// an important side-effect, such as unlocking a mutex, making it important for users to be
    /// able to accurately understand their lifetimes. When a temporary is returned in a function
    /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may
    /// be surprising.
    ///
    /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
    /// function call that returns a `MutexGuard` and then tries to lock again in one of the match
    /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
    /// the match block and thus will not unlock.
    ///
    /// ### Example
    /// ```rust,ignore
    /// # use std::sync::Mutex;
    /// # struct State {}
    /// # impl State {
    /// #     fn foo(&self) -> bool {
    /// #         true
    /// #     }
    /// #     fn bar(&self) {}
    /// # }
    /// let mutex = Mutex::new(State {});
    ///
    /// match mutex.lock().unwrap().foo() {
    ///     true => {
    ///         mutex.lock().unwrap().bar(); // Deadlock!
    ///     }
    ///     false => {}
    /// };
    ///
    /// println!("All done!");
    /// ```
    /// Use instead:
    /// ```no_run
    /// # use std::sync::Mutex;
    /// # struct State {}
    /// # impl State {
    /// #     fn foo(&self) -> bool {
    /// #         true
    /// #     }
    /// #     fn bar(&self) {}
    /// # }
    /// let mutex = Mutex::new(State {});
    ///
    /// let is_foo = mutex.lock().unwrap().foo();
    /// match is_foo {
    ///     true => {
    ///         mutex.lock().unwrap().bar();
    ///     }
    ///     false => {}
    /// };
    ///
    /// println!("All done!");
    /// ```
    #[clippy::version = "1.60.0"]
    pub SIGNIFICANT_DROP_IN_SCRUTINEE,
    nursery,
    "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for usage of `Err(x)?`.
    ///
    /// ### Why restrict this?
    /// The `?` operator is designed to allow calls that
    /// can fail to be easily chained. For example, `foo()?.bar()` or
    /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will
    /// always return), it is more clear to write `return Err(x)`.
    ///
    /// ### Example
    /// ```no_run
    /// fn foo(fail: bool) -> Result<i32, String> {
    ///     if fail {
    ///       Err("failed")?;
    ///     }
    ///     Ok(0)
    /// }
    /// ```
    /// Could be written:
    ///
    /// ```no_run
    /// fn foo(fail: bool) -> Result<i32, String> {
    ///     if fail {
    ///       return Err("failed".into());
    ///     }
    ///     Ok(0)
    /// }
    /// ```
    #[clippy::version = "1.38.0"]
    pub TRY_ERR,
    restriction,
    "return errors explicitly rather than hiding them behind a `?`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for usage of `match` which could be implemented using `map`
    ///
    /// ### Why is this bad?
    /// Using the `map` method is clearer and more concise.
    ///
    /// ### Example
    /// ```no_run
    /// match Some(0) {
    ///     Some(x) => Some(x + 1),
    ///     None => None,
    /// };
    /// ```
    /// Use instead:
    /// ```no_run
    /// Some(0).map(|x| x + 1);
    /// ```
    #[clippy::version = "1.52.0"]
    pub MANUAL_MAP,
    style,
    "reimplementation of `map`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for usage of `match` which could be implemented using `filter`
    ///
    /// ### Why is this bad?
    /// Using the `filter` method is clearer and more concise.
    ///
    /// ### Example
    /// ```no_run
    /// match Some(0) {
    ///     Some(x) => if x % 2 == 0 {
    ///                     Some(x)
    ///                } else {
    ///                     None
    ///                 },
    ///     None => None,
    /// };
    /// ```
    /// Use instead:
    /// ```no_run
    /// Some(0).filter(|&x| x % 2 == 0);
    /// ```
    #[clippy::version = "1.66.0"]
    pub MANUAL_FILTER,
    complexity,
    "reimplementation of `filter`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for unnecessary guards in match expressions.
    ///
    /// ### Why is this bad?
    /// It's more complex and much less readable. Making it part of the pattern can improve
    /// exhaustiveness checking as well.
    ///
    /// ### Example
    /// ```rust,ignore
    /// match x {
    ///     Some(x) if matches!(x, Some(1)) => ..,
    ///     Some(x) if x == Some(2) => ..,
    ///     _ => todo!(),
    /// }
    /// ```
    /// Use instead:
    /// ```rust,ignore
    /// match x {
    ///     Some(Some(1)) => ..,
    ///     Some(Some(2)) => ..,
    ///     _ => todo!(),
    /// }
    /// ```
    #[clippy::version = "1.73.0"]
    pub REDUNDANT_GUARDS,
    complexity,
    "checks for unnecessary guards in match expressions"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for manual implementation of `.ok()` or `.err()`
    /// on `Result` values.
    ///
    /// ### Why is this bad?
    /// Using `.ok()` or `.err()` rather than a `match` or
    /// `if let` is less complex and more readable.
    ///
    /// ### Example
    /// ```no_run
    /// # fn func() -> Result<u32, &'static str> { Ok(0) }
    /// let a = match func() {
    ///     Ok(v) => Some(v),
    ///     Err(_) => None,
    /// };
    /// let b = if let Err(v) = func() {
    ///     Some(v)
    /// } else {
    ///     None
    /// };
    /// ```
    /// Use instead:
    /// ```no_run
    /// # fn func() -> Result<u32, &'static str> { Ok(0) }
    /// let a = func().ok();
    /// let b = func().err();
    /// ```
    #[clippy::version = "1.86.0"]
    pub MANUAL_OK_ERR,
    complexity,
    "find manual implementations of `.ok()` or `.err()` on `Result`"
}

pub struct Matches {
    msrv: Msrv,
    infallible_destructuring_match_linted: bool,
}

impl Matches {
    pub fn new(conf: &'static Conf) -> Self {
        Self {
            msrv: conf.msrv,
            infallible_destructuring_match_linted: false,
        }
    }
}

impl_lint_pass!(Matches => [
    SINGLE_MATCH,
    MATCH_REF_PATS,
    MATCH_BOOL,
    SINGLE_MATCH_ELSE,
    MATCH_OVERLAPPING_ARM,
    MATCH_WILD_ERR_ARM,
    MATCH_AS_REF,
    WILDCARD_ENUM_MATCH_ARM,
    MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
    WILDCARD_IN_OR_PATTERNS,
    MATCH_SINGLE_BINDING,
    INFALLIBLE_DESTRUCTURING_MATCH,
    REST_PAT_IN_FULLY_BOUND_STRUCTS,
    REDUNDANT_PATTERN_MATCHING,
    MATCH_LIKE_MATCHES_MACRO,
    MATCH_SAME_ARMS,
    NEEDLESS_MATCH,
    COLLAPSIBLE_MATCH,
    MANUAL_UNWRAP_OR,
    MANUAL_UNWRAP_OR_DEFAULT,
    MATCH_STR_CASE_MISMATCH,
    SIGNIFICANT_DROP_IN_SCRUTINEE,
    TRY_ERR,
    MANUAL_MAP,
    MANUAL_FILTER,
    REDUNDANT_GUARDS,
    MANUAL_OK_ERR,
]);

impl<'tcx> LateLintPass<'tcx> for Matches {
    #[expect(clippy::too_many_lines)]
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if is_direct_expn_of(expr.span, sym::matches).is_none() && expr.span.in_external_macro(cx.sess().source_map()) {
            return;
        }
        let from_expansion = expr.span.from_expansion();

        if let ExprKind::Match(ex, arms, source) = expr.kind {
            if is_direct_expn_of(expr.span, sym::matches).is_some()
                && let [arm, _] = arms
            {
                redundant_pattern_match::check_match(cx, expr, ex, arms);
                redundant_pattern_match::check_matches_true(cx, expr, arm, ex);
            }

            if source == MatchSource::Normal && !is_span_match(cx, expr.span) {
                return;
            }
            if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
                significant_drop_in_scrutinee::check_match(cx, expr, ex, arms, source);
            }

            collapsible_match::check_match(cx, ex, arms, self.msrv);
            if !from_expansion {
                // These don't depend on a relationship between multiple arms
                match_wild_err_arm::check(cx, ex, arms);
                wild_in_or_pats::check(cx, ex, arms);
            }

            if let MatchSource::TryDesugar(_) = source {
                try_err::check(cx, expr, ex);
            }

            if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
                if source == MatchSource::Normal {
                    if !(self.msrv.meets(cx, msrvs::MATCHES_MACRO)
                        && match_like_matches::check_match(cx, expr, ex, arms))
                    {
                        match_same_arms::check(cx, arms);
                    }

                    redundant_pattern_match::check_match(cx, expr, ex, arms);
                    let source_map = cx.tcx.sess.source_map();
                    let mut match_comments = span_extract_comments(source_map, expr.span);
                    // We remove comments from inside arms block.
                    if !match_comments.is_empty() {
                        for arm in arms {
                            for comment in span_extract_comments(source_map, arm.body.span) {
                                if let Some(index) = match_comments
                                    .iter()
                                    .enumerate()
                                    .find(|(_, cm)| **cm == comment)
                                    .map(|(index, _)| index)
                                {
                                    match_comments.remove(index);
                                }
                            }
                        }
                    }
                    // If there are still comments, it means they are outside of the arms. Tell the lint
                    // code about it.
                    single_match::check(cx, ex, arms, expr, !match_comments.is_empty());
                    match_bool::check(cx, ex, arms, expr);
                    overlapping_arms::check(cx, ex, arms);
                    match_wild_enum::check(cx, ex, arms);
                    match_as_ref::check(cx, ex, arms, expr);
                    needless_match::check_match(cx, ex, arms, expr);
                    match_str_case_mismatch::check(cx, ex, arms);
                    redundant_guards::check(cx, arms, self.msrv);

                    if !is_in_const_context(cx) {
                        manual_unwrap_or::check_match(cx, expr, ex, arms);
                        manual_map::check_match(cx, expr, ex, arms);
                        manual_filter::check_match(cx, ex, arms, expr);
                        manual_ok_err::check_match(cx, expr, ex, arms);
                    }

                    if self.infallible_destructuring_match_linted {
                        self.infallible_destructuring_match_linted = false;
                    } else {
                        match_single_binding::check(cx, ex, arms, expr);
                    }
                }
                match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
            }
        } else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
            collapsible_match::check_if_let(
                cx,
                if_let.let_pat,
                if_let.if_then,
                if_let.if_else,
                if_let.let_expr,
                self.msrv,
            );
            significant_drop_in_scrutinee::check_if_let(cx, expr, if_let.let_expr, if_let.if_then, if_let.if_else);
            if !from_expansion {
                if let Some(else_expr) = if_let.if_else {
                    if self.msrv.meets(cx, msrvs::MATCHES_MACRO) {
                        match_like_matches::check_if_let(
                            cx,
                            expr,
                            if_let.let_pat,
                            if_let.let_expr,
                            if_let.if_then,
                            else_expr,
                        );
                    }
                    if !is_in_const_context(cx) {
                        manual_unwrap_or::check_if_let(
                            cx,
                            expr,
                            if_let.let_pat,
                            if_let.let_expr,
                            if_let.if_then,
                            else_expr,
                        );
                        manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr);
                        manual_filter::check_if_let(
                            cx,
                            expr,
                            if_let.let_pat,
                            if_let.let_expr,
                            if_let.if_then,
                            else_expr,
                        );
                        manual_ok_err::check_if_let(
                            cx,
                            expr,
                            if_let.let_pat,
                            if_let.let_expr,
                            if_let.if_then,
                            else_expr,
                        );
                    }
                }
                redundant_pattern_match::check_if_let(
                    cx,
                    expr,
                    if_let.let_pat,
                    if_let.let_expr,
                    if_let.if_else.is_some(),
                    if_let.let_span,
                );
                needless_match::check_if_let(cx, expr, &if_let);
            }
        } else {
            if let Some(while_let) = higher::WhileLet::hir(expr) {
                significant_drop_in_scrutinee::check_while_let(cx, expr, while_let.let_expr, while_let.if_then);
            }
            if !from_expansion {
                redundant_pattern_match::check(cx, expr);
            }
        }
    }

    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
        self.infallible_destructuring_match_linted |=
            local.els.is_none() && infallible_destructuring_match::check(cx, local);
    }

    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
        rest_pat_in_fully_bound_struct::check(cx, pat);
    }
}

/// Checks if there are any arms with a `#[cfg(..)]` attribute.
fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
    let Some(scrutinee_span) = walk_span_to_context(scrutinee.span, SyntaxContext::root()) else {
        // Shouldn't happen, but treat this as though a `cfg` attribute were found
        return true;
    };

    let start = scrutinee_span.hi();
    let mut arm_spans = arms.iter().map(|arm| {
        let data = arm.span.data();
        (data.ctxt == SyntaxContext::root()).then_some((data.lo, data.hi))
    });
    let end = e.span.hi();

    // Walk through all the non-code space before each match arm. The space trailing the final arm is
    // handled after the `try_fold` e.g.
    //
    // match foo {
    // _________^-                      everything between the scrutinee and arm1
    //|    arm1 => (),
    //|---^___________^                 everything before arm2
    //|    #[cfg(feature = "enabled")]
    //|    arm2 => some_code(),
    //|---^____________________^        everything before arm3
    //|    // some comment about arm3
    //|    arm3 => some_code(),
    //|---^____________________^        everything after arm3
    //|    #[cfg(feature = "disabled")]
    //|    arm4 = some_code(),
    //|};
    //|^
    let found = arm_spans.try_fold(start, |start, range| {
        let Some((end, next_start)) = range else {
            // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute
            // were found.
            return Err(());
        };
        let span = SpanData {
            lo: start,
            hi: end,
            ctxt: SyntaxContext::root(),
            parent: None,
        }
        .span();
        (!span_contains_cfg(cx, span)).then_some(next_start).ok_or(())
    });
    match found {
        Ok(start) => {
            let span = SpanData {
                lo: start,
                hi: end,
                ctxt: SyntaxContext::root(),
                parent: None,
            }
            .span();
            span_contains_cfg(cx, span)
        },
        Err(()) => true,
    }
}

/// Checks if `pat` contains OR patterns that cannot be nested due to a too low MSRV.
fn pat_contains_disallowed_or(cx: &LateContext<'_>, pat: &Pat<'_>, msrv: Msrv) -> bool {
    let mut contains_or = false;
    pat.walk(|p| {
        let is_or = matches!(p.kind, PatKind::Or(_));
        contains_or |= is_or;
        !is_or
    });
    contains_or && !msrv.meets(cx, msrvs::OR_PATTERNS)
}
