about summary refs log tree commit diff
diff options
context:
space:
mode:
authorflip1995 <philipp.krones@embecosm.com>2021-04-27 16:55:11 +0200
committerflip1995 <philipp.krones@embecosm.com>2021-04-27 16:55:11 +0200
commitae72f1adb9cbf16141f880e9e955723a5fdabf00 (patch)
tree49c3b61b082a9f4a9ffda277482a16c4e9b2b50b
parentf801d026f501bb19620f53f2a4d03ec990614cab (diff)
downloadrust-ae72f1adb9cbf16141f880e9e955723a5fdabf00.tar.gz
rust-ae72f1adb9cbf16141f880e9e955723a5fdabf00.zip
Merge commit '7c7683c8efe447b251d6c5ca6cce51233060f6e8' into clippyup
-rw-r--r--clippy_lints/src/casts/ptr_as_ptr.rs6
-rw-r--r--clippy_lints/src/checked_conversions.rs6
-rw-r--r--clippy_lints/src/from_over_into.rs6
-rw-r--r--clippy_lints/src/if_then_some_else_none.rs6
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/loops/single_element_loop.rs8
-rw-r--r--clippy_lints/src/manual_non_exhaustive.rs6
-rw-r--r--clippy_lints/src/manual_strip.rs6
-rw-r--r--clippy_lints/src/manual_unwrap_or.rs11
-rw-r--r--clippy_lints/src/matches.rs9
-rw-r--r--clippy_lints/src/mem_replace.rs6
-rw-r--r--clippy_lints/src/methods/cloned_instead_of_copied.rs21
-rw-r--r--clippy_lints/src/methods/filter_map_next.rs6
-rw-r--r--clippy_lints/src/methods/map_unwrap_or.rs11
-rw-r--r--clippy_lints/src/methods/mod.rs2
-rw-r--r--clippy_lints/src/methods/option_as_ref_deref.rs6
-rw-r--r--clippy_lints/src/methods/utils.rs4
-rw-r--r--clippy_lints/src/missing_const_for_fn.rs6
-rw-r--r--clippy_lints/src/needless_question_mark.rs42
-rw-r--r--clippy_lints/src/ranges.rs6
-rw-r--r--clippy_lints/src/redundant_field_names.rs6
-rw-r--r--clippy_lints/src/redundant_static_lifetimes.rs6
-rw-r--r--clippy_lints/src/single_component_path_imports.rs36
-rw-r--r--clippy_lints/src/unnested_or_patterns.rs17
-rw-r--r--clippy_lints/src/unused_io_amount.rs20
-rw-r--r--clippy_lints/src/use_self.rs11
-rw-r--r--clippy_lints/src/utils/conf.rs2
-rw-r--r--clippy_utils/src/lib.rs1
-rw-r--r--clippy_utils/src/msrvs.rs29
-rw-r--r--clippy_utils/src/qualify_min_const_fn.rs10
-rw-r--r--doc/adding_lints.md24
-rw-r--r--lintcheck/src/main.rs7
-rw-r--r--tests/ui/crashes/ice-7126.rs14
-rw-r--r--tests/ui/iter_cloned_collect.fixed4
-rw-r--r--tests/ui/iter_cloned_collect.rs4
-rw-r--r--tests/ui/iter_cloned_collect.stderr8
-rw-r--r--tests/ui/manual_unwrap_or.fixed12
-rw-r--r--tests/ui/manual_unwrap_or.rs15
-rw-r--r--tests/ui/manual_unwrap_or.stderr12
-rw-r--r--tests/ui/min_rust_version_attr.rs4
-rw-r--r--tests/ui/min_rust_version_attr.stderr8
-rw-r--r--tests/ui/needless_question_mark.fixed72
-rw-r--r--tests/ui/needless_question_mark.rs72
-rw-r--r--tests/ui/needless_question_mark.stderr22
-rw-r--r--tests/ui/single_component_path_imports_macro.fixed21
-rw-r--r--tests/ui/single_component_path_imports_macro.rs21
-rw-r--r--tests/ui/single_component_path_imports_macro.stderr10
-rw-r--r--tests/ui/single_element_loop.fixed5
-rw-r--r--tests/ui/single_element_loop.rs4
-rw-r--r--tests/ui/single_element_loop.stderr18
-rw-r--r--tests/ui/unused_io_amount.rs41
-rw-r--r--tests/ui/unused_io_amount.stderr30
52 files changed, 416 insertions, 326 deletions
diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs
index 9113e5a0920..3132d3a5cf0 100644
--- a/clippy_lints/src/casts/ptr_as_ptr.rs
+++ b/clippy_lints/src/casts/ptr_as_ptr.rs
@@ -1,8 +1,8 @@
 use std::borrow::Cow;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::meets_msrv;
 use clippy_utils::sugg::Sugg;
+use clippy_utils::{meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
@@ -12,10 +12,8 @@ use rustc_semver::RustcVersion;
 
 use super::PTR_AS_PTR;
 
-const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0);
-
 pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) {
-    if !meets_msrv(msrv.as_ref(), &PTR_AS_PTR_MSRV) {
+    if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) {
         return;
     }
 
diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs
index 6a2666bc6c0..8d3123e1ec8 100644
--- a/clippy_lints/src/checked_conversions.rs
+++ b/clippy_lints/src/checked_conversions.rs
@@ -2,7 +2,7 @@
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{meets_msrv, SpanlessEq};
+use clippy_utils::{meets_msrv, msrvs, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -12,8 +12,6 @@ use rustc_middle::lint::in_external_macro;
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Checks for explicit bounds checking when casting.
     ///
@@ -58,7 +56,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(), &CHECKED_CONVERSIONS_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::TRY_FROM) {
             return;
         }
 
diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs
index 5e2baba8943..3560672a748 100644
--- a/clippy_lints/src/from_over_into.rs
+++ b/clippy_lints/src/from_over_into.rs
@@ -1,14 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::paths::INTO;
-use clippy_utils::{match_def_path, meets_msrv};
+use clippy_utils::{match_def_path, meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-const FROM_OVER_INTO_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.
     ///
@@ -57,7 +55,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
 
 impl LateLintPass<'_> for FromOverInto {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &FROM_OVER_INTO_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &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 85c95f1151f..eadcd0867a8 100644
--- a/clippy_lints/src/if_then_some_else_none.rs
+++ b/clippy_lints/src/if_then_some_else_none.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv};
+use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind};
@@ -9,8 +9,6 @@ use rustc_middle::lint::in_external_macro;
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-const IF_THEN_SOME_ELSE_NONE_MSRV: RustcVersion = RustcVersion::new(1, 50, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Checks for if-else that could be written to `bool::then`.
     ///
@@ -59,7 +57,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
 
 impl LateLintPass<'_> for IfThenSomeElseNone {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
-        if !meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) {
             return;
         }
 
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index d6134e178ca..757d7669bd8 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -1077,7 +1077,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || box from_over_into::FromOverInto::new(msrv));
     store.register_late_pass(move || box use_self::UseSelf::new(msrv));
     store.register_late_pass(move || box missing_const_for_fn::MissingConstForFn::new(msrv));
-    store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark::new(msrv));
+    store.register_late_pass(move || box needless_question_mark::NeedlessQuestionMark);
     store.register_late_pass(move || box casts::Casts::new(msrv));
     store.register_early_pass(move || box unnested_or_patterns::UnnestedOrPatterns::new(msrv));
 
diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs
index fc067e81bca..0fd09ff7197 100644
--- a/clippy_lints/src/loops/single_element_loop.rs
+++ b/clippy_lints/src/loops/single_element_loop.rs
@@ -14,8 +14,14 @@ pub(super) fn check<'tcx>(
     body: &'tcx Expr<'_>,
     expr: &'tcx Expr<'_>,
 ) {
+    let arg_expr = match arg.kind {
+        ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
+        ExprKind::MethodCall(method, _, args, _) if args.len() == 1 && method.ident.name == rustc_span::sym::iter => {
+            &args[0]
+        },
+        _ => return,
+    };
     if_chain! {
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, arg_expr) = arg.kind;
         if let PatKind::Binding(.., target, _) = pat.kind;
         if let ExprKind::Array([arg_expression]) = arg_expr.kind;
         if let ExprKind::Path(ref list_item) = arg_expression.kind;
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index dc19805b50a..54f714b54b6 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -1,7 +1,7 @@
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::meets_msrv;
 use clippy_utils::source::snippet_opt;
+use clippy_utils::{meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
 use rustc_errors::Applicability;
@@ -10,8 +10,6 @@ use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{sym, Span};
 
-const MANUAL_NON_EXHAUSTIVE_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Checks for manual implementations of the non-exhaustive pattern.
     ///
@@ -76,7 +74,7 @@ impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]);
 
 impl EarlyLintPass for ManualNonExhaustive {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if !meets_msrv(self.msrv.as_ref(), &MANUAL_NON_EXHAUSTIVE_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) {
             return;
         }
 
diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs
index dfa464ddb81..23428524dee 100644
--- a/clippy_lints/src/manual_strip.rs
+++ b/clippy_lints/src/manual_strip.rs
@@ -2,7 +2,7 @@ use crate::consts::{constant, Constant};
 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 use clippy_utils::source::snippet;
 use clippy_utils::usage::mutated_variables;
-use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths};
+use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_hir::def::Res;
@@ -17,8 +17,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Spanned;
 use rustc_span::Span;
 
-const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
-
 declare_clippy_lint! {
     /// **What it does:**
     /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
@@ -74,7 +72,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(), &MANUAL_STRIP_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
             return;
         }
 
diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs
index 65baa2552cc..520162559e5 100644
--- a/clippy_lints/src/manual_unwrap_or.rs
+++ b/clippy_lints/src/manual_unwrap_or.rs
@@ -112,6 +112,15 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         then {
             let reindented_or_body =
                 reindent_multiline(or_body_snippet.into(), true, Some(indent));
+
+            let suggestion = if scrutinee.span.from_expansion() {
+                    // we don't want parenthesis around macro, e.g. `(some_macro!()).unwrap_or(0)`
+                    sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..")
+                }
+                else {
+                    sugg::Sugg::hir(cx, scrutinee, "..").maybe_par()
+                };
+
             span_lint_and_sugg(
                 cx,
                 MANUAL_UNWRAP_OR, expr.span,
@@ -119,7 +128,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
                 "replace with",
                 format!(
                     "{}.unwrap_or({})",
-                    sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(),
+                    suggestion,
                     reindented_or_body,
                 ),
                 Applicability::MachineApplicable,
diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs
index 44b4eb29035..13b2a834b0a 100644
--- a/clippy_lints/src/matches.rs
+++ b/clippy_lints/src/matches.rs
@@ -7,8 +7,9 @@ use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
 use clippy_utils::visitors::LocalUsedVisitor;
 use clippy_utils::{
-    get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, path_to_local,
-    path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs,
+    get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, msrvs,
+    path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
+    strip_pat_refs,
 };
 use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
 use if_chain::if_chain;
@@ -578,8 +579,6 @@ impl_lint_pass!(Matches => [
     MATCH_SAME_ARMS,
 ]);
 
-const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0);
-
 impl<'tcx> LateLintPass<'tcx> for Matches {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
@@ -588,7 +587,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
 
         redundant_pattern_match::check(cx, expr);
 
-        if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
             if !check_match_like_matches(cx, expr) {
                 lint_match_arms(cx, expr);
             }
diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs
index ec60bffe955..183daee3617 100644
--- a/clippy_lints/src/mem_replace.rs
+++ b/clippy_lints/src/mem_replace.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, paths};
+use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
@@ -256,8 +256,6 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<
     }
 }
 
-const MEM_REPLACE_WITH_DEFAULT_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
 pub struct MemReplace {
     msrv: Option<RustcVersion>,
 }
@@ -281,7 +279,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(), &MEM_REPLACE_WITH_DEFAULT_MSRV) {
+                if meets_msrv(self.msrv.as_ref(), &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 ba97ab3900c..ecec6da3aa0 100644
--- a/clippy_lints/src/methods/cloned_instead_of_copied.rs
+++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs
@@ -1,23 +1,30 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_trait_method;
 use clippy_utils::ty::{get_iterator_item_ty, is_copy};
+use clippy_utils::{is_trait_method, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
+use rustc_semver::RustcVersion;
 use rustc_span::{sym, Span};
 
 use super::CLONED_INSTEAD_OF_COPIED;
 
-pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span) {
+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_type, adt.did) => subst.type_at(0),
-        _ if is_trait_method(cx, expr, sym::Iterator) => match get_iterator_item_ty(cx, recv_ty) {
-            // <T as Iterator>::Item
-            Some(ty) => ty,
-            _ => return,
+        ty::Adt(adt, subst)
+            if cx.tcx.is_diagnostic_item(sym::option_type, 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) => {
+            match get_iterator_item_ty(cx, recv_ty) {
+                // <T as Iterator>::Item
+                Some(ty) => ty,
+                _ => return,
+            }
         },
         _ => return,
     };
diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs
index 2b19e4ee8c0..f0d69a1f42e 100644
--- a/clippy_lints/src/methods/filter_map_next.rs
+++ b/clippy_lints/src/methods/filter_map_next.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::source::snippet;
-use clippy_utils::{is_trait_method, meets_msrv};
+use clippy_utils::{is_trait_method, meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -9,8 +9,6 @@ use rustc_span::sym;
 
 use super::FILTER_MAP_NEXT;
 
-const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0);
-
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx hir::Expr<'_>,
@@ -19,7 +17,7 @@ pub(super) fn check<'tcx>(
     msrv: Option<&RustcVersion>,
 ) {
     if is_trait_method(cx, expr, sym::Iterator) {
-        if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) {
+        if !meets_msrv(msrv, &msrvs::ITERATOR_FIND_MAP) {
             return;
         }
 
diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs
index 4330fea727b..4d8365fcda1 100644
--- a/clippy_lints/src/methods/map_unwrap_or.rs
+++ b/clippy_lints/src/methods/map_unwrap_or.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::meets_msrv;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::mutated_variables;
+use clippy_utils::{meets_msrv, msrvs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -11,8 +11,6 @@ use rustc_span::symbol::sym;
 
 use super::MAP_UNWRAP_OR;
 
-const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
-
 /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
 /// Return true if lint triggered
 pub(super) fn check<'tcx>(
@@ -23,13 +21,14 @@ pub(super) fn check<'tcx>(
     unwrap_arg: &'tcx hir::Expr<'_>,
     msrv: Option<&RustcVersion>,
 ) -> bool {
-    if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) {
-        return false;
-    }
     // 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_type);
     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type);
 
+    if is_result && !meets_msrv(msrv, &msrvs::RESULT_MAP_OR_ELSE) {
+        return false;
+    }
+
     if is_option || is_result {
         // Don't make a suggestion that may fail to compile due to mutably borrowing
         // the same variable twice.
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index c2cd3011d14..e15dbb899b3 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -1959,7 +1959,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             ("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),
+            ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
             ("collect", []) => match method_call!(recv) {
                 Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
                 Some(("map", [m_recv, m_arg], _)) => {
diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs
index 7e9c8fa829d..5a57135038f 100644
--- a/clippy_lints/src/methods/option_as_ref_deref.rs
+++ b/clippy_lints/src/methods/option_as_ref_deref.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks};
+use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, remove_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -12,8 +12,6 @@ use rustc_span::sym;
 
 use super::OPTION_AS_REF_DEREF;
 
-const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
-
 /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -23,7 +21,7 @@ pub(super) fn check<'tcx>(
     is_mut: bool,
     msrv: Option<&RustcVersion>,
 ) {
-    if !meets_msrv(msrv, &OPTION_AS_REF_DEREF_MSRV) {
+    if !meets_msrv(msrv, &msrvs::OPTION_AS_DEREF) {
         return;
     }
 
diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs
index f6bf37e08b9..0daea47816a 100644
--- a/clippy_lints/src/methods/utils.rs
+++ b/clippy_lints/src/methods/utils.rs
@@ -18,9 +18,7 @@ pub(super) fn derefs_to_slice<'tcx>(
             ty::Slice(_) => true,
             ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
             ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
-            ty::Array(_, size) => size
-                .try_eval_usize(cx.tcx, cx.param_env)
-                .map_or(false, |size| size < 32),
+            ty::Array(_, size) => size.try_eval_usize(cx.tcx, cx.param_env).is_some(),
             ty::Ref(_, inner, _) => may_slice(cx, inner),
             _ => false,
         }
diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs
index 60425ff853b..59cbc481ed4 100644
--- a/clippy_lints/src/missing_const_for_fn.rs
+++ b/clippy_lints/src/missing_const_for_fn.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::qualify_min_const_fn::is_min_const_fn;
 use clippy_utils::ty::has_drop;
-use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method};
+use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, msrvs, trait_ref_of_method};
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
@@ -12,8 +12,6 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
 use rustc_typeck::hir_ty_to_ty;
 
-const MISSING_CONST_FOR_FN_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
-
 declare_clippy_lint! {
     /// **What it does:**
     ///
@@ -97,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
         span: Span,
         hir_id: HirId,
     ) {
-        if !meets_msrv(self.msrv.as_ref(), &MISSING_CONST_FOR_FN_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::CONST_IF_MATCH) {
             return;
         }
 
diff --git a/clippy_lints/src/needless_question_mark.rs b/clippy_lints/src/needless_question_mark.rs
index cfe7ae6630e..7b156a8c49d 100644
--- a/clippy_lints/src/needless_question_mark.rs
+++ b/clippy_lints/src/needless_question_mark.rs
@@ -1,15 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_lang_ctor;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, meets_msrv};
+use clippy_utils::{differing_macro_contexts, is_lang_ctor};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionSome, ResultOk};
 use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
 
 declare_clippy_lint! {
@@ -63,21 +61,7 @@ declare_clippy_lint! {
     "Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
 }
 
-const NEEDLESS_QUESTION_MARK_RESULT_MSRV: RustcVersion = RustcVersion::new(1, 13, 0);
-const NEEDLESS_QUESTION_MARK_OPTION_MSRV: RustcVersion = RustcVersion::new(1, 22, 0);
-
-pub struct NeedlessQuestionMark {
-    msrv: Option<RustcVersion>,
-}
-
-impl NeedlessQuestionMark {
-    #[must_use]
-    pub fn new(msrv: Option<RustcVersion>) -> Self {
-        Self { msrv }
-    }
-}
-
-impl_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
+declare_lint_pass!(NeedlessQuestionMark => [NEEDLESS_QUESTION_MARK]);
 
 #[derive(Debug)]
 enum SomeOkCall<'a> {
@@ -111,7 +95,7 @@ impl LateLintPass<'_> for NeedlessQuestionMark {
             _ => return,
         };
 
-        if let Some(ok_some_call) = is_some_or_ok_call(self, cx, e) {
+        if let Some(ok_some_call) = is_some_or_ok_call(cx, e) {
             emit_lint(cx, &ok_some_call);
         }
     }
@@ -127,14 +111,12 @@ impl LateLintPass<'_> for NeedlessQuestionMark {
 
         if_chain! {
             if let Some(expr) = expr_opt;
-            if let Some(ok_some_call) = is_some_or_ok_call(self, cx, expr);
+            if let Some(ok_some_call) = is_some_or_ok_call(cx, expr);
             then {
                 emit_lint(cx, &ok_some_call);
             }
         };
     }
-
-    extract_msrv_attr!(LateContext);
 }
 
 fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
@@ -153,11 +135,7 @@ fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
     );
 }
 
-fn is_some_or_ok_call<'a>(
-    nqml: &NeedlessQuestionMark,
-    cx: &'a LateContext<'_>,
-    expr: &'a Expr<'_>,
-) -> Option<SomeOkCall<'a>> {
+fn is_some_or_ok_call<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<SomeOkCall<'a>> {
     if_chain! {
         // Check outer expression matches CALL_IDENT(ARGUMENT) format
         if let ExprKind::Call(path, args) = &expr.kind;
@@ -188,8 +166,7 @@ fn is_some_or_ok_call<'a>(
             let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type);
 
             // Check for Option MSRV
-            let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
-            if outer_is_some && inner_is_some && meets_option_msrv {
+            if outer_is_some && inner_is_some {
                 return Some(SomeOkCall::SomeCall(expr, inner_expr));
             }
 
@@ -202,8 +179,7 @@ fn is_some_or_ok_call<'a>(
             let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);
 
             // Must meet Result MSRV
-            let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
-            if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv {
+            if outer_is_result && inner_is_result && does_not_call_from {
                 return Some(SomeOkCall::OkCall(expr, inner_expr));
             }
         }
diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs
index 1c3c125e579..7169f96eaf1 100644
--- a/clippy_lints/src/ranges.rs
+++ b/clippy_lints/src/ranges.rs
@@ -2,7 +2,7 @@ use crate::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path};
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, single_segment_path};
 use clippy_utils::{higher, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::RangeLimits;
@@ -159,8 +159,6 @@ declare_clippy_lint! {
     "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
 }
 
-const MANUAL_RANGE_CONTAINS_MSRV: RustcVersion = RustcVersion::new(1, 35, 0);
-
 pub struct Ranges {
     msrv: Option<RustcVersion>,
 }
@@ -187,7 +185,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(), &MANUAL_RANGE_CONTAINS_MSRV) {
+                if meets_msrv(self.msrv.as_ref(), &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 abebd422797..d5ee8d3468d 100644
--- a/clippy_lints/src/redundant_field_names.rs
+++ b/clippy_lints/src/redundant_field_names.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::meets_msrv;
+use clippy_utils::{meets_msrv, msrvs};
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -7,8 +7,6 @@ use rustc_middle::lint::in_external_macro;
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-const REDUNDANT_FIELD_NAMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Checks for fields in struct literals where shorthands
     /// could be used.
@@ -52,7 +50,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(), &REDUNDANT_FIELD_NAMES_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::FIELD_INIT_SHORTHAND) {
             return;
         }
 
diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs
index 32b57698ec5..48107d9c037 100644
--- a/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/clippy_lints/src/redundant_static_lifetimes.rs
@@ -1,14 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::meets_msrv;
 use clippy_utils::source::snippet;
+use clippy_utils::{meets_msrv, msrvs};
 use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-const REDUNDANT_STATIC_LIFETIMES_MSRV: RustcVersion = RustcVersion::new(1, 17, 0);
-
 declare_clippy_lint! {
     /// **What it does:** Checks for constants and statics with an explicit `'static` lifetime.
     ///
@@ -100,7 +98,7 @@ impl RedundantStaticLifetimes {
 
 impl EarlyLintPass for RedundantStaticLifetimes {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if !meets_msrv(self.msrv.as_ref(), &REDUNDANT_STATIC_LIFETIMES_MSRV) {
+        if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) {
             return;
         }
 
diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs
index 6104103580e..a45bb102389 100644
--- a/clippy_lints/src/single_component_path_imports.rs
+++ b/clippy_lints/src/single_component_path_imports.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::in_macro;
-use rustc_ast::{ptr::P, Crate, Item, ItemKind, ModKind, UseTreeKind};
+use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -60,8 +60,21 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
     // ```
     let mut single_use_usages = Vec::new();
 
+    // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
+    // ```rust,ignore
+    // macro_rules! foo { () => {} };
+    // pub(crate) use foo;
+    // ```
+    let mut macros = Vec::new();
+
     for item in items {
-        track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages);
+        track_uses(
+            cx,
+            &item,
+            &mut imports_reused_with_self,
+            &mut single_use_usages,
+            &mut macros,
+        );
     }
 
     for single_use in &single_use_usages {
@@ -96,6 +109,7 @@ fn track_uses(
     item: &Item,
     imports_reused_with_self: &mut Vec<Symbol>,
     single_use_usages: &mut Vec<(Symbol, Span, bool)>,
+    macros: &mut Vec<Symbol>,
 ) {
     if in_macro(item.span) || item.vis.kind.is_pub() {
         return;
@@ -105,14 +119,22 @@ fn track_uses(
         ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
             check_mod(cx, &items);
         },
+        ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
+            macros.push(item.ident.name);
+        },
         ItemKind::Use(use_tree) => {
             let segments = &use_tree.prefix.segments;
 
+            let should_report =
+                |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited);
+
             // keep track of `use some_module;` usages
             if segments.len() == 1 {
                 if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
-                    let ident = &segments[0].ident;
-                    single_use_usages.push((ident.name, item.span, true));
+                    let name = segments[0].ident.name;
+                    if should_report(&name) {
+                        single_use_usages.push((name, item.span, true));
+                    }
                 }
                 return;
             }
@@ -124,8 +146,10 @@ fn track_uses(
                         let segments = &tree.0.prefix.segments;
                         if segments.len() == 1 {
                             if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
-                                let ident = &segments[0].ident;
-                                single_use_usages.push((ident.name, tree.0.span, false));
+                                let name = segments[0].ident.name;
+                                if should_report(&name) {
+                                    single_use_usages.push((name, tree.0.span, false));
+                                }
                             }
                         }
                     }
diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs
index 9376a2cf66a..3e985fa72b8 100644
--- a/clippy_lints/src/unnested_or_patterns.rs
+++ b/clippy_lints/src/unnested_or_patterns.rs
@@ -1,11 +1,8 @@
 #![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
 
+use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::over;
-use clippy_utils::{
-    ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path},
-    meets_msrv,
-};
+use clippy_utils::{meets_msrv, msrvs, over};
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
@@ -54,8 +51,6 @@ declare_clippy_lint! {
     "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
 }
 
-const UNNESTED_OR_PATTERNS_MSRV: RustcVersion = RustcVersion::new(1, 53, 0);
-
 #[derive(Clone, Copy)]
 pub struct UnnestedOrPatterns {
     msrv: Option<RustcVersion>,
@@ -72,13 +67,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(), &UNNESTED_OR_PATTERNS_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &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(), &UNNESTED_OR_PATTERNS_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
             if let ast::ExprKind::Let(pat, _) = &e.kind {
                 lint_unnested_or_patterns(cx, pat);
             }
@@ -86,13 +81,13 @@ impl EarlyLintPass for UnnestedOrPatterns {
     }
 
     fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) {
-        if meets_msrv(self.msrv.as_ref(), &UNNESTED_OR_PATTERNS_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &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(), &UNNESTED_OR_PATTERNS_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
             lint_unnested_or_patterns(cx, &l.pat);
         }
     }
diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs
index 5e8e530f480..3387f35bac3 100644
--- a/clippy_lints/src/unused_io_amount.rs
+++ b/clippy_lints/src/unused_io_amount.rs
@@ -47,25 +47,35 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
                         func.kind,
                         hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryIntoResult, _))
                     ) {
-                        check_method_call(cx, &args[0], expr);
+                        check_map_error(cx, &args[0], expr);
                     }
                 } else {
-                    check_method_call(cx, res, expr);
+                    check_map_error(cx, res, expr);
                 }
             },
-
             hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() {
                 "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
-                    check_method_call(cx, &args[0], expr);
+                    check_map_error(cx, &args[0], expr);
                 },
                 _ => (),
             },
-
             _ => (),
         }
     }
 }
 
+fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
+    let mut call = call;
+    while let hir::ExprKind::MethodCall(ref path, _, ref args, _) = call.kind {
+        if matches!(&*path.ident.as_str(), "or" | "or_else" | "ok") {
+            call = &args[0];
+        } else {
+            break;
+        }
+    }
+    check_method_call(cx, call, expr);
+}
+
 fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
     if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind {
         let symbol = &*path.ident.as_str();
diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs
index c6a3c58a9a2..aa4d16633ff 100644
--- a/clippy_lints/src/use_self.rs
+++ b/clippy_lints/src/use_self.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{in_macro, meets_msrv};
+use clippy_utils::{in_macro, meets_msrv, msrvs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -62,8 +62,6 @@ pub struct UseSelf {
     stack: Vec<StackItem>,
 }
 
-const USE_SELF_MSRV: RustcVersion = RustcVersion::new(1, 37, 0);
-
 impl UseSelf {
     #[must_use]
     pub fn new(msrv: Option<RustcVersion>) -> Self {
@@ -236,7 +234,10 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
     }
 
     fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
-        if in_macro(hir_ty.span) | in_impl(cx, hir_ty) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
+        if in_macro(hir_ty.span)
+            || in_impl(cx, hir_ty)
+            || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS)
+        {
             return;
         }
 
@@ -288,7 +289,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             }
         }
 
-        if in_macro(expr.span) | !meets_msrv(self.msrv.as_ref(), &USE_SELF_MSRV) {
+        if in_macro(expr.span) || !meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS) {
             return;
         }
 
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index 147f823491d..d56855a71c1 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -106,7 +106,7 @@ macro_rules! define_Conf {
 
 pub use self::helpers::Conf;
 define_Conf! {
-    /// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, NEEDLESS_QUESTION_MARK, PTR_AS_PTR. The minimum rust version that the project supports
+    /// Lint: CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR. The minimum rust version that the project supports
     (msrv, "msrv": Option<String>, None),
     /// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
     (blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 9564432ee48..cd85c487798 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -38,6 +38,7 @@ pub mod diagnostics;
 pub mod eager_or_lazy;
 pub mod higher;
 mod hir_utils;
+pub mod msrvs;
 pub mod numeric_literal;
 pub mod paths;
 pub mod ptr;
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
new file mode 100644
index 00000000000..00df04c0144
--- /dev/null
+++ b/clippy_utils/src/msrvs.rs
@@ -0,0 +1,29 @@
+use rustc_semver::RustcVersion;
+
+macro_rules! msrv_aliases {
+    ($($major:literal,$minor:literal,$patch:literal {
+        $($name:ident),* $(,)?
+    })*) => {
+        $($(
+        pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
+        )*)*
+    };
+}
+
+// names may refer to stabilized feature flags or library items
+msrv_aliases! {
+    1,53,0 { OR_PATTERNS }
+    1,50,0 { BOOL_THEN }
+    1,46,0 { CONST_IF_MATCH }
+    1,45,0 { STR_STRIP_PREFIX }
+    1,42,0 { MATCHES_MACRO }
+    1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
+    1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
+    1,38,0 { POINTER_CAST }
+    1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
+    1,36,0 { ITERATOR_COPIED }
+    1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
+    1,34,0 { TRY_FROM }
+    1,30,0 { ITERATOR_FIND_MAP }
+    1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST }
+}
diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index b2ce58b597b..a08dcf19e5b 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -364,7 +364,7 @@ fn check_terminator(
 
 fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool {
     rustc_mir::const_eval::is_const_fn(tcx, def_id)
-        && if let Some(const_stab) = tcx.lookup_const_stability(def_id) {
+        && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
             if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level {
                 // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
                 // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
@@ -375,10 +375,8 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> b
                         .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
                 )
             } else {
-                // `rustc_mir::const_eval::is_const_fn` should return false for unstably const functions.
-                unreachable!();
+                // Unstable const fn with the feature enabled.
+                msrv.is_none()
             }
-        } else {
-            true
-        }
+        })
 }
diff --git a/doc/adding_lints.md b/doc/adding_lints.md
index 50f0d724016..d6cc6d0c2c7 100644
--- a/doc/adding_lints.md
+++ b/doc/adding_lints.md
@@ -390,17 +390,23 @@ pass.
 
 ## Specifying the lint's minimum supported Rust version (MSRV)
 
-Projects supporting older versions of Rust would need to disable a lint if it
-targets features present in later versions. Support for this can be added by
-specifying an MSRV in your lint like so,
+Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests
+using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to
+ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are
+required, just use the one with a lower MSRV.
+
+First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be
+accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
 
 ```rust
-const MANUAL_STRIP_MSRV: RustcVersion = RustcVersion::new(1, 45, 0);
+msrv_aliases! {
+    ..
+    1,45,0 { STR_STRIP_PREFIX }
+}
 ```
 
-The project's MSRV will also have to be an attribute in the lint so you'll have
-to add a struct and constructor for your lint. The project's MSRV needs to be
-passed when the lint is registered in `lib.rs`
+In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a
+constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`.
 
 ```rust
 pub struct ManualStrip {
@@ -415,11 +421,11 @@ impl ManualStrip {
 }
 ```
 
-The project's MSRV can then be matched against the lint's `msrv` in the LintPass
+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(), &MANUAL_STRIP_MSRV) {
+if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) {
     return;
 }
 ```
diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs
index bfb0c3b3f74..f6a75595c98 100644
--- a/lintcheck/src/main.rs
+++ b/lintcheck/src/main.rs
@@ -22,9 +22,16 @@ use rayon::prelude::*;
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
 
+#[cfg(not(windows))]
 const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver";
+#[cfg(not(windows))]
 const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy";
 
+#[cfg(windows)]
+const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver.exe";
+#[cfg(windows)]
+const CARGO_CLIPPY_PATH: &str = "target/debug/cargo-clippy.exe";
+
 const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
 const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
 
diff --git a/tests/ui/crashes/ice-7126.rs b/tests/ui/crashes/ice-7126.rs
new file mode 100644
index 00000000000..ca563ba0978
--- /dev/null
+++ b/tests/ui/crashes/ice-7126.rs
@@ -0,0 +1,14 @@
+// This test requires a feature gated const fn and will stop working in the future.
+
+#![feature(const_btree_new)]
+
+use std::collections::BTreeMap;
+
+struct Foo(BTreeMap<i32, i32>);
+impl Foo {
+    fn new() -> Self {
+        Self(BTreeMap::new())
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/iter_cloned_collect.fixed b/tests/ui/iter_cloned_collect.fixed
index 2773227e26b..39cc58cd298 100644
--- a/tests/ui/iter_cloned_collect.fixed
+++ b/tests/ui/iter_cloned_collect.fixed
@@ -19,4 +19,8 @@ fn main() {
         let _: Vec<u8> = std::ffi::CStr::from_ptr(std::ptr::null())
             .to_bytes().to_vec();
     }
+
+    // Issue #6808
+    let arr: [u8; 64] = [0; 64];
+    let _: Vec<_> = arr.to_vec();
 }
diff --git a/tests/ui/iter_cloned_collect.rs b/tests/ui/iter_cloned_collect.rs
index 60a4eac23c7..c2a036ec09f 100644
--- a/tests/ui/iter_cloned_collect.rs
+++ b/tests/ui/iter_cloned_collect.rs
@@ -22,4 +22,8 @@ fn main() {
             .cloned()
             .collect();
     }
+
+    // Issue #6808
+    let arr: [u8; 64] = [0; 64];
+    let _: Vec<_> = arr.iter().cloned().collect();
 }
diff --git a/tests/ui/iter_cloned_collect.stderr b/tests/ui/iter_cloned_collect.stderr
index b90a1e6c919..e1df61794ce 100644
--- a/tests/ui/iter_cloned_collect.stderr
+++ b/tests/ui/iter_cloned_collect.stderr
@@ -22,5 +22,11 @@ LL | |             .cloned()
 LL | |             .collect();
    | |______________________^ help: try: `.to_vec()`
 
-error: aborting due to 3 previous errors
+error: called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+  --> $DIR/iter_cloned_collect.rs:28:24
+   |
+LL |     let _: Vec<_> = arr.iter().cloned().collect();
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/manual_unwrap_or.fixed b/tests/ui/manual_unwrap_or.fixed
index f1d3252230b..e7a29596b73 100644
--- a/tests/ui/manual_unwrap_or.fixed
+++ b/tests/ui/manual_unwrap_or.fixed
@@ -151,4 +151,16 @@ const fn const_fn_result_unwrap_or() {
     };
 }
 
+mod issue6965 {
+    macro_rules! some_macro {
+        () => {
+            if 1 > 2 { Some(1) } else { None }
+        };
+    }
+
+    fn test() {
+        let _ = some_macro!().unwrap_or(0);
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/manual_unwrap_or.rs b/tests/ui/manual_unwrap_or.rs
index c9eee25a5b1..66006b6c616 100644
--- a/tests/ui/manual_unwrap_or.rs
+++ b/tests/ui/manual_unwrap_or.rs
@@ -190,4 +190,19 @@ const fn const_fn_result_unwrap_or() {
     };
 }
 
+mod issue6965 {
+    macro_rules! some_macro {
+        () => {
+            if 1 > 2 { Some(1) } else { None }
+        };
+    }
+
+    fn test() {
+        let _ = match some_macro!() {
+            Some(val) => val,
+            None => 0,
+        };
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/manual_unwrap_or.stderr b/tests/ui/manual_unwrap_or.stderr
index fc174c4c270..99625b789b6 100644
--- a/tests/ui/manual_unwrap_or.stderr
+++ b/tests/ui/manual_unwrap_or.stderr
@@ -141,5 +141,15 @@ LL | |         Err(_) => "Alice",
 LL | |     };
    | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")`
 
-error: aborting due to 13 previous errors
+error: this pattern reimplements `Option::unwrap_or`
+  --> $DIR/manual_unwrap_or.rs:201:17
+   |
+LL |           let _ = match some_macro!() {
+   |  _________________^
+LL | |             Some(val) => val,
+LL | |             None => 0,
+LL | |         };
+   | |_________^ help: replace with: `some_macro!().unwrap_or(0)`
+
+error: aborting due to 14 previous errors
 
diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs
index 49ace1ca128..7f9f7ddc535 100644
--- a/tests/ui/min_rust_version_attr.rs
+++ b/tests/ui/min_rust_version_attr.rs
@@ -4,6 +4,10 @@
 
 use std::ops::{Deref, RangeFrom};
 
+fn cloned_instead_of_copied() {
+    let _ = [1].iter().cloned();
+}
+
 fn option_as_ref_deref() {
     let mut opt = Some(String::from("123"));
 
diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr
index 8d3575c2da8..ddb1e1f3724 100644
--- a/tests/ui/min_rust_version_attr.stderr
+++ b/tests/ui/min_rust_version_attr.stderr
@@ -1,12 +1,12 @@
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:156:24
+  --> $DIR/min_rust_version_attr.rs:160:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::manual-strip` implied by `-D warnings`
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:155:9
+  --> $DIR/min_rust_version_attr.rs:159:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,13 +17,13 @@ LL |             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
    |
 
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:168:24
+  --> $DIR/min_rust_version_attr.rs:172:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:167:9
+  --> $DIR/min_rust_version_attr.rs:171:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/needless_question_mark.fixed b/tests/ui/needless_question_mark.fixed
index fd8433870bb..52ddd9d2dc8 100644
--- a/tests/ui/needless_question_mark.fixed
+++ b/tests/ui/needless_question_mark.fixed
@@ -96,78 +96,6 @@ where
 
 fn main() {}
 
-mod question_mark_none {
-    #![clippy::msrv = "1.12.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        Some(to.magic?) // should not be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        Ok(to.magic?) // should not be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
-mod question_mark_result {
-    #![clippy::msrv = "1.21.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        Some(to.magic?) // should not be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        to.magic // should be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
-mod question_mark_both {
-    #![clippy::msrv = "1.22.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        to.magic // should be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        to.magic // should be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
 // #6921 if a macro wraps an expr in Some(  ) and the ? is in the macro use,
 // the suggestion fails to apply; do not lint
 macro_rules! some_in_macro {
diff --git a/tests/ui/needless_question_mark.rs b/tests/ui/needless_question_mark.rs
index 36d45ac7e03..1ea4ba0d83f 100644
--- a/tests/ui/needless_question_mark.rs
+++ b/tests/ui/needless_question_mark.rs
@@ -96,78 +96,6 @@ where
 
 fn main() {}
 
-mod question_mark_none {
-    #![clippy::msrv = "1.12.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        Some(to.magic?) // should not be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        Ok(to.magic?) // should not be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
-mod question_mark_result {
-    #![clippy::msrv = "1.21.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        Some(to.magic?) // should not be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        Ok(to.magic?) // should be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
-mod question_mark_both {
-    #![clippy::msrv = "1.22.0"]
-    fn needless_question_mark_option() -> Option<usize> {
-        struct TO {
-            magic: Option<usize>,
-        }
-        let to = TO { magic: None };
-        Some(to.magic?) // should be triggered
-    }
-
-    fn needless_question_mark_result() -> Result<usize, bool> {
-        struct TO {
-            magic: Result<usize, bool>,
-        }
-        let to = TO { magic: Ok(1_usize) };
-        Ok(to.magic?) // should be triggered
-    }
-
-    fn main() {
-        needless_question_mark_option();
-        needless_question_mark_result();
-    }
-}
-
 // #6921 if a macro wraps an expr in Some(  ) and the ? is in the macro use,
 // the suggestion fails to apply; do not lint
 macro_rules! some_in_macro {
diff --git a/tests/ui/needless_question_mark.stderr b/tests/ui/needless_question_mark.stderr
index 7cbf1e505ad..afd68d91e51 100644
--- a/tests/ui/needless_question_mark.stderr
+++ b/tests/ui/needless_question_mark.stderr
@@ -67,25 +67,7 @@ LL |         return Ok(t.magic?);
    |                ^^^^^^^^^^^^ help: try: `t.magic`
 
 error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:138:9
-   |
-LL |         Ok(to.magic?) // should be triggered
-   |         ^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:154:9
-   |
-LL |         Some(to.magic?) // should be triggered
-   |         ^^^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:162:9
-   |
-LL |         Ok(to.magic?) // should be triggered
-   |         ^^^^^^^^^^^^^ help: try: `to.magic`
-
-error: question mark operator is useless here
-  --> $DIR/needless_question_mark.rs:187:27
+  --> $DIR/needless_question_mark.rs:115:27
    |
 LL |         || -> Option<_> { Some(Some($expr)?) }()
    |                           ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)`
@@ -95,5 +77,5 @@ LL |     let _x = some_and_qmark_in_macro!(x?);
    |
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 15 previous errors
+error: aborting due to 12 previous errors
 
diff --git a/tests/ui/single_component_path_imports_macro.fixed b/tests/ui/single_component_path_imports_macro.fixed
new file mode 100644
index 00000000000..05863f9a2bf
--- /dev/null
+++ b/tests/ui/single_component_path_imports_macro.fixed
@@ -0,0 +1,21 @@
+// run-rustfix
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+// #7106: use statements exporting a macro within a crate should not trigger lint
+
+macro_rules! m1 {
+    () => {};
+}
+pub(crate) use m1; // ok
+
+macro_rules! m2 {
+    () => {};
+}
+ // fail
+
+fn main() {
+    m1!();
+    m2!();
+}
diff --git a/tests/ui/single_component_path_imports_macro.rs b/tests/ui/single_component_path_imports_macro.rs
new file mode 100644
index 00000000000..633deea348b
--- /dev/null
+++ b/tests/ui/single_component_path_imports_macro.rs
@@ -0,0 +1,21 @@
+// run-rustfix
+// edition:2018
+#![warn(clippy::single_component_path_imports)]
+#![allow(unused_imports)]
+
+// #7106: use statements exporting a macro within a crate should not trigger lint
+
+macro_rules! m1 {
+    () => {};
+}
+pub(crate) use m1; // ok
+
+macro_rules! m2 {
+    () => {};
+}
+use m2; // fail
+
+fn main() {
+    m1!();
+    m2!();
+}
diff --git a/tests/ui/single_component_path_imports_macro.stderr b/tests/ui/single_component_path_imports_macro.stderr
new file mode 100644
index 00000000000..239efb393b1
--- /dev/null
+++ b/tests/ui/single_component_path_imports_macro.stderr
@@ -0,0 +1,10 @@
+error: this import is redundant
+  --> $DIR/single_component_path_imports_macro.rs:16:1
+   |
+LL | use m2; // fail
+   | ^^^^^^^ help: remove it entirely
+   |
+   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/single_element_loop.fixed b/tests/ui/single_element_loop.fixed
index 8ca068293a6..c307afffcb8 100644
--- a/tests/ui/single_element_loop.fixed
+++ b/tests/ui/single_element_loop.fixed
@@ -8,4 +8,9 @@ fn main() {
         let item = &item1;
         println!("{}", item);
     }
+
+    {
+        let item = &item1;
+        println!("{:?}", item);
+    }
 }
diff --git a/tests/ui/single_element_loop.rs b/tests/ui/single_element_loop.rs
index 57e9336a31f..2c0c03b7211 100644
--- a/tests/ui/single_element_loop.rs
+++ b/tests/ui/single_element_loop.rs
@@ -7,4 +7,8 @@ fn main() {
     for item in &[item1] {
         println!("{}", item);
     }
+
+    for item in [item1].iter() {
+        println!("{:?}", item);
+    }
 }
diff --git a/tests/ui/single_element_loop.stderr b/tests/ui/single_element_loop.stderr
index 90be1dc3283..0e35a33ded5 100644
--- a/tests/ui/single_element_loop.stderr
+++ b/tests/ui/single_element_loop.stderr
@@ -15,5 +15,21 @@ LL |         println!("{}", item);
 LL |     }
    |
 
-error: aborting due to previous error
+error: for loop over a single element
+  --> $DIR/single_element_loop.rs:11:5
+   |
+LL | /     for item in [item1].iter() {
+LL | |         println!("{:?}", item);
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL |     {
+LL |         let item = &item1;
+LL |         println!("{:?}", item);
+LL |     }
+   |
+
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/unused_io_amount.rs b/tests/ui/unused_io_amount.rs
index ebaba9629db..8b141e25942 100644
--- a/tests/ui/unused_io_amount.rs
+++ b/tests/ui/unused_io_amount.rs
@@ -1,7 +1,7 @@
 #![allow(dead_code)]
 #![warn(clippy::unused_io_amount)]
 
-use std::io;
+use std::io::{self, Read};
 
 fn question_mark<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
     s.write(b"test")?;
@@ -22,4 +22,43 @@ fn vectored<T: io::Read + io::Write>(s: &mut T) -> io::Result<()> {
     Ok(())
 }
 
+fn ok(file: &str) -> Option<()> {
+    let mut reader = std::fs::File::open(file).ok()?;
+    let mut result = [0u8; 0];
+    reader.read(&mut result).ok()?;
+    Some(())
+}
+
+#[allow(clippy::redundant_closure)]
+#[allow(clippy::bind_instead_of_map)]
+fn or_else(file: &str) -> io::Result<()> {
+    let mut reader = std::fs::File::open(file)?;
+    let mut result = [0u8; 0];
+    reader.read(&mut result).or_else(|err| Err(err))?;
+    Ok(())
+}
+
+#[derive(Debug)]
+enum Error {
+    Kind,
+}
+
+fn or(file: &str) -> Result<(), Error> {
+    let mut reader = std::fs::File::open(file).unwrap();
+    let mut result = [0u8; 0];
+    reader.read(&mut result).or(Err(Error::Kind))?;
+    Ok(())
+}
+
+fn combine_or(file: &str) -> Result<(), Error> {
+    let mut reader = std::fs::File::open(file).unwrap();
+    let mut result = [0u8; 0];
+    reader
+        .read(&mut result)
+        .or(Err(Error::Kind))
+        .or(Err(Error::Kind))
+        .expect("error");
+    Ok(())
+}
+
 fn main() {}
diff --git a/tests/ui/unused_io_amount.stderr b/tests/ui/unused_io_amount.stderr
index 5219d63980b..d8dfc0e5a79 100644
--- a/tests/ui/unused_io_amount.stderr
+++ b/tests/ui/unused_io_amount.stderr
@@ -36,5 +36,33 @@ error: written amount is not handled
 LL |     s.write_vectored(&[io::IoSlice::new(&[])])?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 6 previous errors
+error: read amount is not handled. Use `Read::read_exact` instead
+  --> $DIR/unused_io_amount.rs:28:5
+   |
+LL |     reader.read(&mut result).ok()?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: read amount is not handled. Use `Read::read_exact` instead
+  --> $DIR/unused_io_amount.rs:37:5
+   |
+LL |     reader.read(&mut result).or_else(|err| Err(err))?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: read amount is not handled. Use `Read::read_exact` instead
+  --> $DIR/unused_io_amount.rs:49:5
+   |
+LL |     reader.read(&mut result).or(Err(Error::Kind))?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: read amount is not handled. Use `Read::read_exact` instead
+  --> $DIR/unused_io_amount.rs:56:5
+   |
+LL | /     reader
+LL | |         .read(&mut result)
+LL | |         .or(Err(Error::Kind))
+LL | |         .or(Err(Error::Kind))
+LL | |         .expect("error");
+   | |________________________^
+
+error: aborting due to 10 previous errors