about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-05-08 15:00:30 +0000
committerbors <bors@rust-lang.org>2022-05-08 15:00:30 +0000
commit4667198d4f6a0fd4c2cccd0ddec4e5afbe291401 (patch)
treee0deb4a19ca2bee054e23f3dc52fb922b5acc62b
parent9c78883fdf5ccdeef833bc99095a18dcf6312d0b (diff)
parent597f61bbe3a861c3d77b784cc701e6a975fbe842 (diff)
downloadrust-4667198d4f6a0fd4c2cccd0ddec4e5afbe291401.tar.gz
rust-4667198d4f6a0fd4c2cccd0ddec4e5afbe291401.zip
Auto merge of #8802 - smoelius:allow-expect-unwrap-in-tests, r=llogiq
Optionally allow `expect` and `unwrap` in tests

This addresses #1015, except it makes the new behavior optional.

The reason for the msrv-related changes is as follows.

Rather than expand `check_methods` list of arguments, it seemed easier to make `check_methods` a method of `Methods`, so that `check_methods` could access `Methods`' fields.

`check_methods` had an `msrv` parameter, which I consequently made a field of `Methods`. But, to avoid adding a lifetime parameter to `Methods`, I made the field type `Option<RustcVersion>` instead of the parameter's existing type, `Option<&RustcVersion>`. This seemed sensible since `RustcVersion` implements `Copy`. But this broke a lot of code that expected an `Option<&RustcVersion>` or `&Option<RustcVersion>`. I changed all of those occurrences to `Option<RustcVersion>`. IMHO, the code is better as a result of these changes, though.

The msrv-related changes are in their own commit to (hopefully) ease review.

Closes #1015

changelog: optionally allow `expect` and `unwrap` in tests

r? `@llogiq`
-rw-r--r--clippy_lints/src/approx_const.rs4
-rw-r--r--clippy_lints/src/attrs.rs2
-rw-r--r--clippy_lints/src/borrow_as_ptr.rs2
-rw-r--r--clippy_lints/src/casts/cast_abs_to_unsigned.rs4
-rw-r--r--clippy_lints/src/casts/cast_lossless.rs6
-rw-r--r--clippy_lints/src/casts/cast_slice_different_sizes.rs4
-rw-r--r--clippy_lints/src/casts/mod.rs10
-rw-r--r--clippy_lints/src/casts/ptr_as_ptr.rs4
-rw-r--r--clippy_lints/src/checked_conversions.rs2
-rw-r--r--clippy_lints/src/from_over_into.rs2
-rw-r--r--clippy_lints/src/if_then_some_else_none.rs2
-rw-r--r--clippy_lints/src/index_refutable_slice.rs2
-rw-r--r--clippy_lints/src/lib.rs11
-rw-r--r--clippy_lints/src/manual_bits.rs2
-rw-r--r--clippy_lints/src/manual_non_exhaustive.rs4
-rw-r--r--clippy_lints/src/manual_strip.rs2
-rw-r--r--clippy_lints/src/map_clone.rs2
-rw-r--r--clippy_lints/src/matches/mod.rs4
-rw-r--r--clippy_lints/src/mem_replace.rs2
-rw-r--r--clippy_lints/src/methods/cloned_instead_of_copied.rs6
-rw-r--r--clippy_lints/src/methods/err_expect.rs4
-rw-r--r--clippy_lints/src/methods/expect_used.rs7
-rw-r--r--clippy_lints/src/methods/filter_map_next.rs4
-rw-r--r--clippy_lints/src/methods/is_digit_ascii_radix.rs4
-rw-r--r--clippy_lints/src/methods/map_unwrap_or.rs4
-rw-r--r--clippy_lints/src/methods/mod.rs370
-rw-r--r--clippy_lints/src/methods/option_as_ref_deref.rs4
-rw-r--r--clippy_lints/src/methods/str_splitn.rs4
-rw-r--r--clippy_lints/src/methods/unnecessary_to_owned.rs6
-rw-r--r--clippy_lints/src/methods/unwrap_used.rs7
-rw-r--r--clippy_lints/src/missing_const_for_fn.rs4
-rw-r--r--clippy_lints/src/ranges.rs2
-rw-r--r--clippy_lints/src/redundant_field_names.rs2
-rw-r--r--clippy_lints/src/redundant_static_lifetimes.rs2
-rw-r--r--clippy_lints/src/unnested_or_patterns.rs8
-rw-r--r--clippy_lints/src/use_self.rs6
-rw-r--r--clippy_lints/src/utils/conf.rs8
-rw-r--r--clippy_utils/src/lib.rs4
-rw-r--r--clippy_utils/src/qualify_min_const_fn.rs8
-rw-r--r--doc/adding_lints.md2
-rw-r--r--tests/ui-toml/expect_used/clippy.toml1
-rw-r--r--tests/ui-toml/expect_used/expect_used.rs29
-rw-r--r--tests/ui-toml/expect_used/expect_used.stderr19
-rw-r--r--tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr2
-rw-r--r--tests/ui-toml/unwrap_used/clippy.toml1
-rw-r--r--tests/ui-toml/unwrap_used/unwrap_used.rs74
-rw-r--r--tests/ui-toml/unwrap_used/unwrap_used.stderr197
47 files changed, 610 insertions, 250 deletions
diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs
index e109ee0009e..da1b646f477 100644
--- a/clippy_lints/src/approx_const.rs
+++ b/clippy_lints/src/approx_const.rs
@@ -87,9 +87,7 @@ impl ApproxConstant {
         let s = s.as_str();
         if s.parse::<f64>().is_ok() {
             for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
-                if is_approx_const(constant, s, min_digits)
-                    && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv))
-                {
+                if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) {
                     span_lint_and_help(
                         cx,
                         APPROX_CONSTANT,
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs
index 30dde2f0ef0..3de91f3d24a 100644
--- a/clippy_lints/src/attrs.rs
+++ b/clippy_lints/src/attrs.rs
@@ -613,7 +613,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
 
 fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
     if_chain! {
-        if meets_msrv(msrv.as_ref(), &msrvs::TOOL_ATTRIBUTES);
+        if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES);
         // check cfg_attr
         if attr.has_name(sym::cfg_attr);
         if let Some(items) = attr.meta_item_list();
diff --git a/clippy_lints/src/borrow_as_ptr.rs b/clippy_lints/src/borrow_as_ptr.rs
index 9f8eb488c29..0993adbae2e 100644
--- a/clippy_lints/src/borrow_as_ptr.rs
+++ b/clippy_lints/src/borrow_as_ptr.rs
@@ -57,7 +57,7 @@ impl BorrowAsPtr {
 
 impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::BORROW_AS_PTR) {
+        if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
             return;
         }
 
diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs
index e9b0f1f672d..6bac6bf83f8 100644
--- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs
+++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs
@@ -16,10 +16,10 @@ pub(super) fn check(
     cast_expr: &Expr<'_>,
     cast_from: Ty<'_>,
     cast_to: Ty<'_>,
-    msrv: &Option<RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) {
     if_chain! {
-        if meets_msrv(msrv.as_ref(), &msrvs::UNSIGNED_ABS);
+        if meets_msrv(msrv, msrvs::UNSIGNED_ABS);
         if cast_from.is_integral();
         if cast_to.is_integral();
         if cast_from.is_signed();
diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs
index 7717c1e9e31..938458e30ca 100644
--- a/clippy_lints/src/casts/cast_lossless.rs
+++ b/clippy_lints/src/casts/cast_lossless.rs
@@ -16,7 +16,7 @@ pub(super) fn check(
     cast_op: &Expr<'_>,
     cast_from: Ty<'_>,
     cast_to: Ty<'_>,
-    msrv: &Option<RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) {
     if !should_lint(cx, expr, cast_from, cast_to, msrv) {
         return;
@@ -68,7 +68,7 @@ fn should_lint(
     expr: &Expr<'_>,
     cast_from: Ty<'_>,
     cast_to: Ty<'_>,
-    msrv: &Option<RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) -> bool {
     // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
     if in_constant(cx, expr.hir_id) {
@@ -95,7 +95,7 @@ fn should_lint(
             };
             !is_isize_or_usize(cast_from) && from_nbits < to_nbits
         },
-        (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv.as_ref(), &msrvs::FROM_BOOL) => true,
+        (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true,
         (_, _) => {
             matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
         },
diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs
index 2238668abca..bb09a6708a0 100644
--- a/clippy_lints/src/casts/cast_slice_different_sizes.rs
+++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs
@@ -8,9 +8,9 @@ use rustc_semver::RustcVersion;
 
 use super::CAST_SLICE_DIFFERENT_SIZES;
 
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option<RustcVersion>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option<RustcVersion>) {
     // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
-    if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) {
+    if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) {
         return;
     }
 
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index 9c3ebbe035b..108e119c104 100644
--- a/clippy_lints/src/casts/mod.rs
+++ b/clippy_lints/src/casts/mod.rs
@@ -532,7 +532,7 @@ impl_lint_pass!(Casts => [
 impl<'tcx> LateLintPass<'tcx> for Casts {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if !in_external_macro(cx.sess(), expr.span) {
-            ptr_as_ptr::check(cx, expr, &self.msrv);
+            ptr_as_ptr::check(cx, expr, self.msrv);
         }
 
         if expr.span.from_expansion() {
@@ -562,9 +562,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
                     cast_possible_wrap::check(cx, expr, cast_from, cast_to);
                     cast_precision_loss::check(cx, expr, cast_from, cast_to);
                     cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
-                    cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
+                    cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
                 }
-                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
+                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
                 cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
             }
         }
@@ -572,8 +572,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
         cast_ref_to_mut::check(cx, expr);
         cast_ptr_alignment::check(cx, expr);
         char_lit_as_u8::check(cx, expr);
-        ptr_as_ptr::check(cx, expr, &self.msrv);
-        cast_slice_different_sizes::check(cx, expr, &self.msrv);
+        ptr_as_ptr::check(cx, expr, self.msrv);
+        cast_slice_different_sizes::check(cx, expr, self.msrv);
     }
 
     extract_msrv_attr!(LateContext);
diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs
index fb04f93fbcf..46d45d09661 100644
--- a/clippy_lints/src/casts/ptr_as_ptr.rs
+++ b/clippy_lints/src/casts/ptr_as_ptr.rs
@@ -12,8 +12,8 @@ use rustc_semver::RustcVersion;
 
 use super::PTR_AS_PTR;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
-    if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVersion>) {
+    if !meets_msrv(msrv, msrvs::POINTER_CAST) {
         return;
     }
 
diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs
index 31cc3698592..e23428e216d 100644
--- a/clippy_lints/src/checked_conversions.rs
+++ b/clippy_lints/src/checked_conversions.rs
@@ -57,7 +57,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
 
 impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
     fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::TRY_FROM) {
+        if !meets_msrv(self.msrv, msrvs::TRY_FROM) {
             return;
         }
 
diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs
index c2f52605151..5d25c1d0634 100644
--- a/clippy_lints/src/from_over_into.rs
+++ b/clippy_lints/src/from_over_into.rs
@@ -55,7 +55,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
 
 impl<'tcx> LateLintPass<'tcx> for FromOverInto {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) {
+        if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) {
             return;
         }
 
diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs
index 9525c163ece..b8d227855d9 100644
--- a/clippy_lints/src/if_then_some_else_none.rs
+++ b/clippy_lints/src/if_then_some_else_none.rs
@@ -57,7 +57,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
 
 impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) {
+        if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
             return;
         }
 
diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs
index 8a84513b779..07b0604f78d 100644
--- a/clippy_lints/src/index_refutable_slice.rs
+++ b/clippy_lints/src/index_refutable_slice.rs
@@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
             if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
             if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
             if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
-            if meets_msrv(self.msrv.as_ref(), &msrvs::SLICE_PATTERNS);
+            if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS);
 
             let found_slices = find_slice_values(cx, let_pat);
             if !found_slices.is_empty();
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 3bb821a1482..6970757b56d 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -582,8 +582,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     });
 
     let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
+    let allow_expect_in_tests = conf.allow_expect_in_tests;
+    let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
     store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
-    store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv)));
+    store.register_late_pass(move || {
+        Box::new(methods::Methods::new(
+            avoid_breaking_exported_api,
+            msrv,
+            allow_expect_in_tests,
+            allow_unwrap_in_tests,
+        ))
+    });
     store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
     store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
     store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs
index ac3d9447b6b..60bbcde4f1d 100644
--- a/clippy_lints/src/manual_bits.rs
+++ b/clippy_lints/src/manual_bits.rs
@@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]);
 
 impl<'tcx> LateLintPass<'tcx> for ManualBits {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) {
+        if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) {
             return;
         }
 
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index b8d620d8171..14f39067299 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -98,7 +98,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
 
 impl EarlyLintPass for ManualNonExhaustiveStruct {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
+        if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
             return;
         }
 
@@ -150,7 +150,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
 
 impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
+        if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
             return;
         }
 
diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs
index aacabf303a7..dfb3efc4e28 100644
--- a/clippy_lints/src/manual_strip.rs
+++ b/clippy_lints/src/manual_strip.rs
@@ -68,7 +68,7 @@ enum StripKind {
 
 impl<'tcx> LateLintPass<'tcx> for ManualStrip {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
+        if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
             return;
         }
 
diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs
index ceb66947d02..a13d191375b 100644
--- a/clippy_lints/src/map_clone.rs
+++ b/clippy_lints/src/map_clone.rs
@@ -144,7 +144,7 @@ impl MapClone {
     fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
         let mut applicability = Applicability::MachineApplicable;
 
-        let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) {
+        let (message, sugg_method) = if is_copy && meets_msrv(self.msrv, msrvs::ITERATOR_COPIED) {
             ("you are using an explicit closure for copying elements", "copied")
         } else {
             ("you are using an explicit closure for cloning elements", "cloned")
diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs
index 401ecef460c..3d8391bce2b 100644
--- a/clippy_lints/src/matches/mod.rs
+++ b/clippy_lints/src/matches/mod.rs
@@ -658,7 +658,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
             }
             if !contains_cfg_arm(cx, expr, ex, arms) {
                 if source == MatchSource::Normal {
-                    if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO)
+                    if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO)
                         && match_like_matches::check_match(cx, expr, ex, arms))
                     {
                         match_same_arms::check(cx, arms);
@@ -685,7 +685,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
             match_wild_err_arm::check(cx, ex, arms);
             wild_in_or_pats::check(cx, arms);
         } else {
-            if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
+            if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
                 match_like_matches::check(cx, expr);
             }
             redundant_pattern_match::check(cx, expr);
diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs
index 054937e3e36..41073d40f3d 100644
--- a/clippy_lints/src/mem_replace.rs
+++ b/clippy_lints/src/mem_replace.rs
@@ -254,7 +254,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
             then {
                 check_replace_option_with_none(cx, src, dest, expr.span);
                 check_replace_with_uninit(cx, src, dest, expr.span);
-                if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) {
+                if meets_msrv(self.msrv, msrvs::MEM_TAKE) {
                     check_replace_with_default(cx, src, dest, expr.span);
                 }
             }
diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs
index 6d30bb5a278..e9aeab2d5b6 100644
--- a/clippy_lints/src/methods/cloned_instead_of_copied.rs
+++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs
@@ -10,16 +10,16 @@ use rustc_span::{sym, Span};
 
 use super::CLONED_INSTEAD_OF_COPIED;
 
-pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<&RustcVersion>) {
+pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<RustcVersion>) {
     let recv_ty = cx.typeck_results().expr_ty_adjusted(recv);
     let inner_ty = match recv_ty.kind() {
         // `Option<T>` -> `T`
         ty::Adt(adt, subst)
-            if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, &msrvs::OPTION_COPIED) =>
+            if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) =>
         {
             subst.type_at(0)
         },
-        _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) => {
+        _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => {
             match get_iterator_item_ty(cx, recv_ty) {
                 // <T as Iterator>::Item
                 Some(ty) => ty,
diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs
index be9d4ad94fb..570a1b87358 100644
--- a/clippy_lints/src/methods/err_expect.rs
+++ b/clippy_lints/src/methods/err_expect.rs
@@ -13,7 +13,7 @@ pub(super) fn check(
     cx: &LateContext<'_>,
     _expr: &rustc_hir::Expr<'_>,
     recv: &rustc_hir::Expr<'_>,
-    msrv: Option<&RustcVersion>,
+    msrv: Option<RustcVersion>,
     expect_span: Span,
     err_span: Span,
 ) {
@@ -21,7 +21,7 @@ pub(super) fn check(
         if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
         // Test the version to make sure the lint can be showed (expect_err has been
         // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982)
-        if meets_msrv(msrv, &msrvs::EXPECT_ERR);
+        if meets_msrv(msrv, msrvs::EXPECT_ERR);
 
         // Grabs the `Result<T, E>` type
         let result_type = cx.typeck_results().expr_ty(recv);
diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs
index 55be513c5bb..fbc3348f185 100644
--- a/clippy_lints/src/methods/expect_used.rs
+++ b/clippy_lints/src/methods/expect_used.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_in_test_function;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -7,7 +8,7 @@ use rustc_span::sym;
 use super::EXPECT_USED;
 
 /// lint use of `expect()` for `Option`s and `Result`s
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) {
     let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
 
     let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
@@ -18,6 +19,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
         None
     };
 
+    if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
+        return;
+    }
+
     if let Some((lint, kind, none_value)) = mess {
         span_lint_and_help(
             cx,
diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs
index f0d69a1f42e..38ec4d8e3ab 100644
--- a/clippy_lints/src/methods/filter_map_next.rs
+++ b/clippy_lints/src/methods/filter_map_next.rs
@@ -14,10 +14,10 @@ pub(super) fn check<'tcx>(
     expr: &'tcx hir::Expr<'_>,
     recv: &'tcx hir::Expr<'_>,
     arg: &'tcx hir::Expr<'_>,
-    msrv: Option<&RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) {
     if is_trait_method(cx, expr, sym::Iterator) {
-        if !meets_msrv(msrv, &msrvs::ITERATOR_FIND_MAP) {
+        if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) {
             return;
         }
 
diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs
index ad333df2f2d..aa176dcc8b4 100644
--- a/clippy_lints/src/methods/is_digit_ascii_radix.rs
+++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs
@@ -15,9 +15,9 @@ pub(super) fn check<'tcx>(
     expr: &'tcx Expr<'_>,
     self_arg: &'tcx Expr<'_>,
     radix: &'tcx Expr<'_>,
-    msrv: Option<&RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) {
-    if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) {
+    if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) {
         return;
     }
 
diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs
index 9ec84e76519..4a8e7ce4ddb 100644
--- a/clippy_lints/src/methods/map_unwrap_or.rs
+++ b/clippy_lints/src/methods/map_unwrap_or.rs
@@ -19,13 +19,13 @@ pub(super) fn check<'tcx>(
     recv: &'tcx hir::Expr<'_>,
     map_arg: &'tcx hir::Expr<'_>,
     unwrap_arg: &'tcx hir::Expr<'_>,
-    msrv: Option<&RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) -> bool {
     // lint if the caller of `map()` is an `Option`
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
 
-    if is_result && !meets_msrv(msrv, &msrvs::RESULT_MAP_OR_ELSE) {
+    if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) {
         return false;
     }
 
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index f3be71f6b8b..e452614ce17 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -2200,14 +2200,23 @@ declare_clippy_lint! {
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
+    allow_expect_in_tests: bool,
+    allow_unwrap_in_tests: bool,
 }
 
 impl Methods {
     #[must_use]
-    pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Self {
+    pub fn new(
+        avoid_breaking_exported_api: bool,
+        msrv: Option<RustcVersion>,
+        allow_expect_in_tests: bool,
+        allow_unwrap_in_tests: bool,
+    ) -> Self {
         Self {
             avoid_breaking_exported_api,
             msrv,
+            allow_expect_in_tests,
+            allow_unwrap_in_tests,
         }
     }
 }
@@ -2306,7 +2315,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             return;
         }
 
-        check_methods(cx, expr, self.msrv.as_ref());
+        self.check_methods(cx, expr);
 
         match expr.kind {
             hir::ExprKind::Call(func, args) => {
@@ -2322,7 +2331,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                 single_char_add_str::check(cx, expr, args);
                 into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
                 single_char_pattern::check(cx, expr, method_call.ident.name, args);
-                unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref());
+                unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv);
             },
             hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
                 let mut info = BinaryExprInfo {
@@ -2505,196 +2514,201 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
     extract_msrv_attr!(LateContext);
 }
 
-#[allow(clippy::too_many_lines)]
-fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
-    if let Some((name, [recv, args @ ..], span)) = method_call(expr) {
-        match (name, args) {
-            ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
-                zst_offset::check(cx, expr, recv);
-            },
-            ("and_then", [arg]) => {
-                let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
-                let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
-                if !biom_option_linted && !biom_result_linted {
-                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
-                }
-            },
-            ("as_deref" | "as_deref_mut", []) => {
-                needless_option_as_deref::check(cx, expr, recv, name);
-            },
-            ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
-            ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
-            ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
-            ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
-            ("collect", []) => match method_call(recv) {
-                Some((name @ ("cloned" | "copied"), [recv2], _)) => {
-                    iter_cloned_collect::check(cx, name, expr, recv2);
+impl Methods {
+    #[allow(clippy::too_many_lines)]
+    fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let Some((name, [recv, args @ ..], span)) = method_call(expr) {
+            match (name, args) {
+                ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
+                    zst_offset::check(cx, expr, recv);
+                },
+                ("and_then", [arg]) => {
+                    let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
+                    let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
+                    if !biom_option_linted && !biom_result_linted {
+                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
+                    }
+                },
+                ("as_deref" | "as_deref_mut", []) => {
+                    needless_option_as_deref::check(cx, expr, recv, name);
+                },
+                ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
+                ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
+                ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
+                ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
+                ("collect", []) => match method_call(recv) {
+                    Some((name @ ("cloned" | "copied"), [recv2], _)) => {
+                        iter_cloned_collect::check(cx, name, expr, recv2);
+                    },
+                    Some(("map", [m_recv, m_arg], _)) => {
+                        map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
+                    },
+                    Some(("take", [take_self_arg, take_arg], _)) => {
+                        if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
+                            manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
+                        }
+                    },
+                    _ => {},
+                },
+                (name @ "count", args @ []) => match method_call(recv) {
+                    Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+                    Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
+                        iter_count::check(cx, expr, recv2, name2);
+                    },
+                    Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
+                    _ => {},
+                },
+                ("drain", [arg]) => {
+                    iter_with_drain::check(cx, expr, recv, span, arg);
+                },
+                ("expect", [_]) => match method_call(recv) {
+                    Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
+                    Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
+                    _ => expect_used::check(cx, expr, recv, self.allow_expect_in_tests),
+                },
+                ("extend", [arg]) => {
+                    string_extend_chars::check(cx, expr, recv, arg);
+                    extend_with_drain::check(cx, expr, recv, arg);
+                },
+                ("filter_map", [arg]) => {
+                    unnecessary_filter_map::check(cx, expr, arg, name);
+                    filter_map_identity::check(cx, expr, arg, span);
+                },
+                ("find_map", [arg]) => {
+                    unnecessary_filter_map::check(cx, expr, arg, name);
                 },
-                Some(("map", [m_recv, m_arg], _)) => {
-                    map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
+                ("flat_map", [arg]) => {
+                    flat_map_identity::check(cx, expr, arg, span);
+                    flat_map_option::check(cx, expr, arg, span);
                 },
-                Some(("take", [take_self_arg, take_arg], _)) => {
-                    if meets_msrv(msrv, &msrvs::STR_REPEAT) {
-                        manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
+                (name @ "flatten", args @ []) => match method_call(recv) {
+                    Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
+                    Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+                    _ => {},
+                },
+                ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
+                ("for_each", [_]) => {
+                    if let Some(("inspect", [_, _], span2)) = method_call(recv) {
+                        inspect_for_each::check(cx, expr, span2);
                     }
                 },
-                _ => {},
-            },
-            (name @ "count", args @ []) => match method_call(recv) {
-                Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
-                Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
-                    iter_count::check(cx, expr, recv2, name2);
+                ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
+                ("is_file", []) => filetype_is_file::check(cx, expr, recv),
+                ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
+                ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
+                ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
+                ("join", [join_arg]) => {
+                    if let Some(("collect", _, span)) = method_call(recv) {
+                        unnecessary_join::check(cx, expr, recv, join_arg, span);
+                    }
                 },
-                Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
-                _ => {},
-            },
-            ("drain", [arg]) => {
-                iter_with_drain::check(cx, expr, recv, span, arg);
-            },
-            ("expect", [_]) => match method_call(recv) {
-                Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
-                Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, msrv, span, err_span),
-                _ => expect_used::check(cx, expr, recv),
-            },
-            ("extend", [arg]) => {
-                string_extend_chars::check(cx, expr, recv, arg);
-                extend_with_drain::check(cx, expr, recv, arg);
-            },
-            ("filter_map", [arg]) => {
-                unnecessary_filter_map::check(cx, expr, arg, name);
-                filter_map_identity::check(cx, expr, arg, span);
-            },
-            ("find_map", [arg]) => {
-                unnecessary_filter_map::check(cx, expr, arg, name);
-            },
-            ("flat_map", [arg]) => {
-                flat_map_identity::check(cx, expr, arg, span);
-                flat_map_option::check(cx, expr, arg, span);
-            },
-            (name @ "flatten", args @ []) => match method_call(recv) {
-                Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
-                Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
-                _ => {},
-            },
-            ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
-            ("for_each", [_]) => {
-                if let Some(("inspect", [_, _], span2)) = method_call(recv) {
-                    inspect_for_each::check(cx, expr, span2);
-                }
-            },
-            ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
-            ("is_file", []) => filetype_is_file::check(cx, expr, recv),
-            ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv),
-            ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
-            ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
-            ("join", [join_arg]) => {
-                if let Some(("collect", _, span)) = method_call(recv) {
-                    unnecessary_join::check(cx, expr, recv, join_arg, span);
-                }
-            },
-            ("last", args @ []) | ("skip", args @ [_]) => {
-                if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
-                    if let ("cloned", []) = (name2, args2) {
-                        iter_overeager_cloned::check(cx, expr, recv2, name, args);
+                ("last", args @ []) | ("skip", args @ [_]) => {
+                    if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
+                        if let ("cloned", []) = (name2, args2) {
+                            iter_overeager_cloned::check(cx, expr, recv2, name, args);
+                        }
                     }
-                }
-            },
-            (name @ ("map" | "map_err"), [m_arg]) => {
-                if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
-                    match (name, args) {
-                        ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
-                        ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
-                        ("filter", [f_arg]) => {
-                            filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
-                        },
-                        ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
-                        _ => {},
+                },
+                (name @ ("map" | "map_err"), [m_arg]) => {
+                    if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
+                        match (name, args) {
+                            ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
+                            ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
+                            ("filter", [f_arg]) => {
+                                filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
+                            },
+                            ("find", [f_arg]) => {
+                                filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true);
+                            },
+                            _ => {},
+                        }
                     }
-                }
-                map_identity::check(cx, expr, recv, m_arg, name, span);
-            },
-            ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
-            (name @ "next", args @ []) => {
-                if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
-                    match (name2, args2) {
-                        ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
-                        ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
-                        ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, msrv),
-                        ("iter", []) => iter_next_slice::check(cx, expr, recv2),
-                        ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
-                        ("skip_while", [_]) => skip_while_next::check(cx, expr),
-                        _ => {},
+                    map_identity::check(cx, expr, recv, m_arg, name, span);
+                },
+                ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
+                (name @ "next", args @ []) => {
+                    if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
+                        match (name2, args2) {
+                            ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+                            ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
+                            ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
+                            ("iter", []) => iter_next_slice::check(cx, expr, recv2),
+                            ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
+                            ("skip_while", [_]) => skip_while_next::check(cx, expr),
+                            _ => {},
+                        }
                     }
-                }
-            },
-            ("nth", args @ [n_arg]) => match method_call(recv) {
-                Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
-                Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
-                Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
-                Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
-                _ => iter_nth_zero::check(cx, expr, recv, n_arg),
-            },
-            ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
-            ("or_else", [arg]) => {
-                if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
-                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
-                }
-            },
-            ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
-                if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
-                    suspicious_splitn::check(cx, name, expr, recv, count);
-                    str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv);
-                }
-            },
-            ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
-                if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
-                    suspicious_splitn::check(cx, name, expr, recv, count);
-                }
-            },
-            ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
-            ("take", args @ [_arg]) => {
-                if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
-                    if let ("cloned", []) = (name2, args2) {
-                        iter_overeager_cloned::check(cx, expr, recv2, name, args);
+                },
+                ("nth", args @ [n_arg]) => match method_call(recv) {
+                    Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
+                    Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+                    Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
+                    Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
+                    _ => iter_nth_zero::check(cx, expr, recv, n_arg),
+                },
+                ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
+                ("or_else", [arg]) => {
+                    if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
+                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
                     }
-                }
-            },
-            ("take", []) => needless_option_take::check(cx, expr, recv),
-            ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
-                implicit_clone::check(cx, name, expr, recv);
-            },
-            ("unwrap", []) => {
-                match method_call(recv) {
-                    Some(("get", [recv, get_arg], _)) => {
-                        get_unwrap::check(cx, expr, recv, get_arg, false);
-                    },
-                    Some(("get_mut", [recv, get_arg], _)) => {
-                        get_unwrap::check(cx, expr, recv, get_arg, true);
+                },
+                ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
+                    if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
+                        suspicious_splitn::check(cx, name, expr, recv, count);
+                        str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv);
+                    }
+                },
+                ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
+                    if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
+                        suspicious_splitn::check(cx, name, expr, recv, count);
+                    }
+                },
+                ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
+                ("take", args @ [_arg]) => {
+                    if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
+                        if let ("cloned", []) = (name2, args2) {
+                            iter_overeager_cloned::check(cx, expr, recv2, name, args);
+                        }
+                    }
+                },
+                ("take", []) => needless_option_take::check(cx, expr, recv),
+                ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
+                    implicit_clone::check(cx, name, expr, recv);
+                },
+                ("unwrap", []) => {
+                    match method_call(recv) {
+                        Some(("get", [recv, get_arg], _)) => {
+                            get_unwrap::check(cx, expr, recv, get_arg, false);
+                        },
+                        Some(("get_mut", [recv, get_arg], _)) => {
+                            get_unwrap::check(cx, expr, recv, get_arg, true);
+                        },
+                        Some(("or", [recv, or_arg], or_span)) => {
+                            or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
+                        },
+                        _ => {},
+                    }
+                    unwrap_used::check(cx, expr, recv, self.allow_unwrap_in_tests);
+                },
+                ("unwrap_or", [u_arg]) => match method_call(recv) {
+                    Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
+                        manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
                     },
-                    Some(("or", [recv, or_arg], or_span)) => {
-                        or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
+                    Some(("map", [m_recv, m_arg], span)) => {
+                        option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
                     },
                     _ => {},
-                }
-                unwrap_used::check(cx, expr, recv);
-            },
-            ("unwrap_or", [u_arg]) => match method_call(recv) {
-                Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
-                    manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
                 },
-                Some(("map", [m_recv, m_arg], span)) => {
-                    option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
+                ("unwrap_or_else", [u_arg]) => match method_call(recv) {
+                    Some(("map", [recv, map_arg], _))
+                        if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
+                    _ => {
+                        unwrap_or_else_default::check(cx, expr, recv, u_arg);
+                        unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
+                    },
                 },
                 _ => {},
-            },
-            ("unwrap_or_else", [u_arg]) => match method_call(recv) {
-                Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
-                _ => {
-                    unwrap_or_else_default::check(cx, expr, recv, u_arg);
-                    unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
-                },
-            },
-            _ => {},
+            }
         }
     }
 }
diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs
index ba2d2914315..b50a173d835 100644
--- a/clippy_lints/src/methods/option_as_ref_deref.rs
+++ b/clippy_lints/src/methods/option_as_ref_deref.rs
@@ -19,9 +19,9 @@ pub(super) fn check<'tcx>(
     as_ref_recv: &hir::Expr<'_>,
     map_arg: &hir::Expr<'_>,
     is_mut: bool,
-    msrv: Option<&RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) {
-    if !meets_msrv(msrv, &msrvs::OPTION_AS_DEREF) {
+    if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) {
         return;
     }
 
diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs
index 52891eeed06..fc375763542 100644
--- a/clippy_lints/src/methods/str_splitn.rs
+++ b/clippy_lints/src/methods/str_splitn.rs
@@ -24,7 +24,7 @@ pub(super) fn check(
     self_arg: &Expr<'_>,
     pat_arg: &Expr<'_>,
     count: u128,
-    msrv: Option<&RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) {
     if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
         return;
@@ -34,7 +34,7 @@ pub(super) fn check(
         IterUsageKind::Nth(n) => count > n + 1,
         IterUsageKind::NextTuple => count > 2,
     };
-    let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE);
+    let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE);
 
     match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) {
         Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg),
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index 1f426979c4a..97c4feb3122 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -26,7 +26,7 @@ pub fn check<'tcx>(
     expr: &'tcx Expr<'tcx>,
     method_name: Symbol,
     args: &'tcx [Expr<'tcx>],
-    msrv: Option<&RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) {
     if_chain! {
         if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
@@ -198,7 +198,7 @@ fn check_into_iter_call_arg(
     expr: &Expr<'_>,
     method_name: Symbol,
     receiver: &Expr<'_>,
-    msrv: Option<&RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) -> bool {
     if_chain! {
         if let Some(parent) = get_parent_expr(cx, expr);
@@ -213,7 +213,7 @@ fn check_into_iter_call_arg(
             if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
                 return true;
             }
-            let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) {
+            let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
                 "copied"
             } else {
                 "cloned"
diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs
index 44676d78c60..5c761014927 100644
--- a/clippy_lints/src/methods/unwrap_used.rs
+++ b/clippy_lints/src/methods/unwrap_used.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_in_test_function;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -7,7 +8,7 @@ use rustc_span::sym;
 use super::UNWRAP_USED;
 
 /// lint use of `unwrap()` for `Option`s and `Result`s
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) {
     let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
 
     let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
@@ -18,6 +19,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
         None
     };
 
+    if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
+        return;
+    }
+
     if let Some((lint, kind, none_value)) = mess {
         span_lint_and_help(
             cx,
diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs
index 06209bfe7b0..16d65966c10 100644
--- a/clippy_lints/src/missing_const_for_fn.rs
+++ b/clippy_lints/src/missing_const_for_fn.rs
@@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
         span: Span,
         hir_id: HirId,
     ) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::CONST_IF_MATCH) {
+        if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) {
             return;
         }
 
@@ -146,7 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
 
         let mir = cx.tcx.optimized_mir(def_id);
 
-        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) {
+        if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) {
             if cx.tcx.is_const_fn_raw(def_id.to_def_id()) {
                 cx.tcx.sess.span_err(span, err.as_ref());
             }
diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs
index f26f3650cb3..a47dc26f603 100644
--- a/clippy_lints/src/ranges.rs
+++ b/clippy_lints/src/ranges.rs
@@ -193,7 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges {
                 check_range_zip_with_len(cx, path, args, expr.span);
             },
             ExprKind::Binary(ref op, l, r) => {
-                if meets_msrv(self.msrv.as_ref(), &msrvs::RANGE_CONTAINS) {
+                if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
                     check_possible_range_contains(cx, op.node, l, r, expr);
                 }
             },
diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs
index 40a62fd6d20..40b03068f6c 100644
--- a/clippy_lints/src/redundant_field_names.rs
+++ b/clippy_lints/src/redundant_field_names.rs
@@ -51,7 +51,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
 
 impl EarlyLintPass for RedundantFieldNames {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::FIELD_INIT_SHORTHAND) {
+        if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) {
             return;
         }
 
diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs
index ea5064217ab..2d26c49252f 100644
--- a/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/clippy_lints/src/redundant_static_lifetimes.rs
@@ -99,7 +99,7 @@ impl RedundantStaticLifetimes {
 
 impl EarlyLintPass for RedundantStaticLifetimes {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) {
+        if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) {
             return;
         }
 
diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs
index ae431aac83b..04e2f301bfd 100644
--- a/clippy_lints/src/unnested_or_patterns.rs
+++ b/clippy_lints/src/unnested_or_patterns.rs
@@ -61,13 +61,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]);
 
 impl EarlyLintPass for UnnestedOrPatterns {
     fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) {
-        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
+        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &a.pat);
         }
     }
 
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
-        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
+        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
             if let ast::ExprKind::Let(pat, _, _) = &e.kind {
                 lint_unnested_or_patterns(cx, pat);
             }
@@ -75,13 +75,13 @@ impl EarlyLintPass for UnnestedOrPatterns {
     }
 
     fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
-        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
+        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &p.pat);
         }
     }
 
     fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) {
-        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
+        if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &l.pat);
         }
     }
diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs
index 138f8bccb3f..66f7748e9e0 100644
--- a/clippy_lints/src/use_self.rs
+++ b/clippy_lints/src/use_self.rs
@@ -198,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
         if_chain! {
             if !hir_ty.span.from_expansion();
-            if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+            if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check {
                 impl_id,
                 in_body,
@@ -225,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         if_chain! {
             if !expr.span.from_expansion();
-            if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+            if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
             if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
             then {} else { return; }
@@ -256,7 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
         if_chain! {
             if !pat.span.from_expansion();
-            if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
+            if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
             if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
             if let PatKind::Path(QPath::Resolved(_, path)) = pat.kind;
             if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _));
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index 74b0168a179..bdcd76d153f 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -316,6 +316,14 @@ define_Conf! {
     ///
     /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
     (max_include_file_size: u64 = 1_000_000),
+    /// Lint: EXPECT_USED.
+    ///
+    /// Whether `expect` should be allowed in test functions
+    (allow_expect_in_tests: bool = false),
+    /// Lint: UNWRAP_USED.
+    ///
+    /// Whether `unwrap` should be allowed in test functions
+    (allow_unwrap_in_tests: bool = false),
 }
 
 /// Search for the configuration file.
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 7d46952d971..ed511004ea0 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -117,8 +117,8 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Opt
     None
 }
 
-pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool {
-    msrv.map_or(true, |msrv| msrv.meets(*lint_msrv))
+pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
+    msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
 }
 
 #[macro_export]
diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index 75808b1b174..6a8ed4e7bd7 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -19,7 +19,7 @@ use std::borrow::Cow;
 
 type McfResult = Result<(), (Span, Cow<'static, str>)>;
 
-pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult {
+pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult {
     let def_id = body.source.def_id();
     let mut current = def_id;
     loop {
@@ -269,7 +269,7 @@ fn check_terminator<'a, 'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &'a Body<'tcx>,
     terminator: &Terminator<'tcx>,
-    msrv: Option<&RustcVersion>,
+    msrv: Option<RustcVersion>,
 ) -> McfResult {
     let span = terminator.source_info.span;
     match &terminator.kind {
@@ -353,7 +353,7 @@ fn check_terminator<'a, 'tcx>(
     }
 }
 
-fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
+fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
     tcx.is_const_fn(def_id)
         && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
             if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
@@ -362,7 +362,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> b
                 // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
                 crate::meets_msrv(
                     msrv,
-                    &RustcVersion::parse(since.as_str())
+                    RustcVersion::parse(since.as_str())
                         .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
                 )
             } else {
diff --git a/doc/adding_lints.md b/doc/adding_lints.md
index 307cf2f3a90..5607e2264a6 100644
--- a/doc/adding_lints.md
+++ b/doc/adding_lints.md
@@ -432,7 +432,7 @@ The project's MSRV can then be matched against the feature MSRV in the LintPass
 using the `meets_msrv` utility function.
 
 ``` rust
-if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
+if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
     return;
 }
 ```
diff --git a/tests/ui-toml/expect_used/clippy.toml b/tests/ui-toml/expect_used/clippy.toml
new file mode 100644
index 00000000000..6933b816419
--- /dev/null
+++ b/tests/ui-toml/expect_used/clippy.toml
@@ -0,0 +1 @@
+allow-expect-in-tests = true
diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs
new file mode 100644
index 00000000000..22dcd3ae9d6
--- /dev/null
+++ b/tests/ui-toml/expect_used/expect_used.rs
@@ -0,0 +1,29 @@
+// compile-flags: --test
+#![warn(clippy::expect_used)]
+
+fn expect_option() {
+    let opt = Some(0);
+    let _ = opt.expect("");
+}
+
+fn expect_result() {
+    let res: Result<u8, ()> = Ok(0);
+    let _ = res.expect("");
+}
+
+fn main() {
+    expect_option();
+    expect_result();
+}
+
+#[test]
+fn test_expect_option() {
+    let opt = Some(0);
+    let _ = opt.expect("");
+}
+
+#[test]
+fn test_expect_result() {
+    let res: Result<u8, ()> = Ok(0);
+    let _ = res.expect("");
+}
diff --git a/tests/ui-toml/expect_used/expect_used.stderr b/tests/ui-toml/expect_used/expect_used.stderr
new file mode 100644
index 00000000000..9cb2199ed21
--- /dev/null
+++ b/tests/ui-toml/expect_used/expect_used.stderr
@@ -0,0 +1,19 @@
+error: used `expect()` on `an Option` value
+  --> $DIR/expect_used.rs:6:13
+   |
+LL |     let _ = opt.expect("");
+   |             ^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::expect-used` implied by `-D warnings`
+   = help: if this value is an `None`, it will panic
+
+error: used `expect()` on `a Result` value
+  --> $DIR/expect_used.rs:11:13
+   |
+LL |     let _ = res.expect("");
+   |             ^^^^^^^^^^^^^^
+   |
+   = help: if this value is an `Err`, it will panic
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 8701809b4da..65277dd03e8 100644
--- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -1,4 +1,4 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `allow-expect-in-tests`, `allow-unwrap-in-tests`, `third-party` at line 5 column 1
 
 error: aborting due to previous error
 
diff --git a/tests/ui-toml/unwrap_used/clippy.toml b/tests/ui-toml/unwrap_used/clippy.toml
new file mode 100644
index 00000000000..154626ef4e8
--- /dev/null
+++ b/tests/ui-toml/unwrap_used/clippy.toml
@@ -0,0 +1 @@
+allow-unwrap-in-tests = true
diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs
new file mode 100644
index 00000000000..a639de07971
--- /dev/null
+++ b/tests/ui-toml/unwrap_used/unwrap_used.rs
@@ -0,0 +1,74 @@
+// compile-flags: --test
+
+#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
+#![warn(clippy::unwrap_used)]
+#![deny(clippy::get_unwrap)]
+
+use std::collections::BTreeMap;
+use std::collections::HashMap;
+use std::collections::VecDeque;
+use std::iter::FromIterator;
+
+struct GetFalsePositive {
+    arr: [u32; 3],
+}
+
+impl GetFalsePositive {
+    fn get(&self, pos: usize) -> Option<&u32> {
+        self.arr.get(pos)
+    }
+    fn get_mut(&mut self, pos: usize) -> Option<&mut u32> {
+        self.arr.get_mut(pos)
+    }
+}
+
+fn main() {
+    let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
+    let mut some_slice = &mut [0, 1, 2, 3];
+    let mut some_vec = vec![0, 1, 2, 3];
+    let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect();
+    let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]);
+    let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]);
+    let mut false_positive = GetFalsePositive { arr: [0, 1, 2] };
+
+    {
+        // Test `get().unwrap()`
+        let _ = boxed_slice.get(1).unwrap();
+        let _ = some_slice.get(0).unwrap();
+        let _ = some_vec.get(0).unwrap();
+        let _ = some_vecdeque.get(0).unwrap();
+        let _ = some_hashmap.get(&1).unwrap();
+        let _ = some_btreemap.get(&1).unwrap();
+        #[allow(clippy::unwrap_used)]
+        let _ = false_positive.get(0).unwrap();
+        // Test with deref
+        let _: u8 = *boxed_slice.get(1).unwrap();
+    }
+
+    {
+        // Test `get_mut().unwrap()`
+        *boxed_slice.get_mut(0).unwrap() = 1;
+        *some_slice.get_mut(0).unwrap() = 1;
+        *some_vec.get_mut(0).unwrap() = 1;
+        *some_vecdeque.get_mut(0).unwrap() = 1;
+        // Check false positives
+        #[allow(clippy::unwrap_used)]
+        {
+            *some_hashmap.get_mut(&1).unwrap() = 'b';
+            *some_btreemap.get_mut(&1).unwrap() = 'b';
+            *false_positive.get_mut(0).unwrap() = 1;
+        }
+    }
+
+    {
+        // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()`
+        let _ = some_vec.get(0..1).unwrap().to_vec();
+        let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+    }
+}
+
+#[test]
+fn test() {
+    let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
+    let _ = boxed_slice.get(1).unwrap();
+}
diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr
new file mode 100644
index 00000000000..6c3adc5395f
--- /dev/null
+++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr
@@ -0,0 +1,197 @@
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:36:17
+   |
+LL |         let _ = boxed_slice.get(1).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
+   |
+note: the lint level is defined here
+  --> $DIR/unwrap_used.rs:5:9
+   |
+LL | #![deny(clippy::get_unwrap)]
+   |         ^^^^^^^^^^^^^^^^^^
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:36:17
+   |
+LL |         let _ = boxed_slice.get(1).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::unwrap-used` implied by `-D warnings`
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:37:17
+   |
+LL |         let _ = some_slice.get(0).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:37:17
+   |
+LL |         let _ = some_slice.get(0).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:38:17
+   |
+LL |         let _ = some_vec.get(0).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:38:17
+   |
+LL |         let _ = some_vec.get(0).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:39:17
+   |
+LL |         let _ = some_vecdeque.get(0).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:39:17
+   |
+LL |         let _ = some_vecdeque.get(0).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:40:17
+   |
+LL |         let _ = some_hashmap.get(&1).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:40:17
+   |
+LL |         let _ = some_hashmap.get(&1).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:41:17
+   |
+LL |         let _ = some_btreemap.get(&1).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:41:17
+   |
+LL |         let _ = some_btreemap.get(&1).unwrap();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:45:21
+   |
+LL |         let _: u8 = *boxed_slice.get(1).unwrap();
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:45:22
+   |
+LL |         let _: u8 = *boxed_slice.get(1).unwrap();
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:50:9
+   |
+LL |         *boxed_slice.get_mut(0).unwrap() = 1;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:50:10
+   |
+LL |         *boxed_slice.get_mut(0).unwrap() = 1;
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:51:9
+   |
+LL |         *some_slice.get_mut(0).unwrap() = 1;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:51:10
+   |
+LL |         *some_slice.get_mut(0).unwrap() = 1;
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:52:9
+   |
+LL |         *some_vec.get_mut(0).unwrap() = 1;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:52:10
+   |
+LL |         *some_vec.get_mut(0).unwrap() = 1;
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:53:9
+   |
+LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:53:10
+   |
+LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:65:17
+   |
+LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:65:17
+   |
+LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:66:17
+   |
+LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
+
+error: used `unwrap()` on `an Option` value
+  --> $DIR/unwrap_used.rs:66:17
+   |
+LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+  --> $DIR/unwrap_used.rs:73:13
+   |
+LL |     let _ = boxed_slice.get(1).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
+
+error: aborting due to 27 previous errors
+