about summary refs log tree commit diff
path: root/compiler/rustc_lint
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint')
-rw-r--r--compiler/rustc_lint/src/array_into_iter.rs11
-rw-r--r--compiler/rustc_lint/src/builtin.rs154
-rw-r--r--compiler/rustc_lint/src/context.rs110
-rw-r--r--compiler/rustc_lint/src/early.rs127
-rw-r--r--compiler/rustc_lint/src/enum_intrinsics_non_enums.rs20
-rw-r--r--compiler/rustc_lint/src/hidden_unicode_codepoints.rs4
-rw-r--r--compiler/rustc_lint/src/internal.rs135
-rw-r--r--compiler/rustc_lint/src/late.rs8
-rw-r--r--compiler/rustc_lint/src/levels.rs88
-rw-r--r--compiler/rustc_lint/src/lib.rs19
-rw-r--r--compiler/rustc_lint/src/methods.rs2
-rw-r--r--compiler/rustc_lint/src/non_ascii_idents.rs5
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs37
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs8
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs74
-rw-r--r--compiler/rustc_lint/src/pass_by_value.rs94
-rw-r--r--compiler/rustc_lint/src/traits.rs23
-rw-r--r--compiler/rustc_lint/src/types.rs16
-rw-r--r--compiler/rustc_lint/src/unused.rs11
19 files changed, 496 insertions, 450 deletions
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
index d8883b0e66d..a14d6020361 100644
--- a/compiler/rustc_lint/src/array_into_iter.rs
+++ b/compiler/rustc_lint/src/array_into_iter.rs
@@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
             if let hir::ExprKind::Call(path, [arg]) = &arg.kind {
                 if let hir::ExprKind::Path(hir::QPath::LangItem(
                     hir::LangItem::IntoIterIntoIter,
-                    _,
+                    ..,
                 )) = &path.kind
                 {
                     self.for_expr_span = arg.span;
@@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
         }
 
         // We only care about method call expressions.
-        if let hir::ExprKind::MethodCall(call, span, args, _) = &expr.kind {
+        if let hir::ExprKind::MethodCall(call, args, _) = &expr.kind {
             if call.ident.name != sym::into_iter {
                 return;
             }
@@ -79,9 +79,8 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
             let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
             let adjustments = cx.typeck_results().expr_adjustments(receiver_arg);
 
-            let target = match adjustments.last() {
-                Some(Adjustment { kind: Adjust::Borrow(_), target }) => target,
-                _ => return,
+            let Some(Adjustment { kind: Adjust::Borrow(_), target }) = adjustments.last() else {
+                return
             };
 
             let types =
@@ -120,7 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
                 // to an array or to a slice.
                 _ => bug!("array type coerced to something other than array or slice"),
             };
-            cx.struct_span_lint(ARRAY_INTO_ITER, *span, |lint| {
+            cx.struct_span_lint(ARRAY_INTO_ITER, call.ident.span, |lint| {
                 let mut diag = lint.build(&format!(
                     "this method call resolves to `<&{} as IntoIterator>::into_iter` \
                     (due to backwards compatibility), \
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index c228ecb03fd..9b24f43f7fd 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -32,8 +32,7 @@ use rustc_ast_pretty::pprust::{self, expr_to_string};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
-use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType};
-use rustc_feature::{GateIssue, Stability};
+use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
@@ -153,8 +152,8 @@ declare_lint! {
 declare_lint_pass!(BoxPointers => [BOX_POINTERS]);
 
 impl BoxPointers {
-    fn check_heap_type<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
-        for leaf in ty.walk(cx.tcx) {
+    fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) {
+        for leaf in ty.walk() {
             if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
                 if leaf_ty.is_box() {
                     cx.struct_span_lint(BOX_POINTERS, span, |lint| {
@@ -369,12 +368,12 @@ impl EarlyLintPass for UnsafeCode {
 
     fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
         match it.kind {
-            ast::ItemKind::Trait(box ast::TraitKind(_, ast::Unsafe::Yes(_), ..)) => self
+            ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => self
                 .report_unsafe(cx, it.span, |lint| {
                     lint.build("declaration of an `unsafe` trait").emit()
                 }),
 
-            ast::ItemKind::Impl(box ast::ImplKind { unsafety: ast::Unsafe::Yes(_), .. }) => self
+            ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => self
                 .report_unsafe(cx, it.span, |lint| {
                     lint.build("implementation of an `unsafe` trait").emit()
                 }),
@@ -610,14 +609,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
                 // If the trait is private, add the impl items to `private_traits` so they don't get
                 // reported for missing docs.
                 let real_trait = trait_ref.path.res.def_id();
-                if let Some(def_id) = real_trait.as_local() {
-                    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
-                    if let Some(Node::Item(item)) = cx.tcx.hir().find(hir_id) {
-                        if let hir::VisibilityKind::Inherited = item.vis.node {
-                            for impl_item_ref in items {
-                                self.private_traits.insert(impl_item_ref.id.hir_id());
-                            }
-                        }
+                let Some(def_id) = real_trait.as_local() else { return };
+                let Some(Node::Item(item)) = cx.tcx.hir().find_by_def_id(def_id) else { return };
+                if let hir::VisibilityKind::Inherited = item.vis.node {
+                    for impl_item_ref in items {
+                        self.private_traits.insert(impl_item_ref.id.hir_id());
                     }
                 }
                 return;
@@ -659,7 +655,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
 
         // If the method is an impl for an item with docs_hidden, don't doc.
         if method_context(cx, impl_item.hir_id()) == MethodLateContext::PlainImpl {
-            let parent = cx.tcx.hir().get_parent_did(impl_item.hir_id());
+            let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
             let impl_ty = cx.tcx.type_of(parent);
             let outerdef = match impl_ty.kind() {
                 ty::Adt(def, _) => Some(def.did),
@@ -830,9 +826,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
             _ => return,
         }
 
-        let debug = match cx.tcx.get_diagnostic_item(sym::Debug) {
-            Some(debug) => debug,
-            None => return,
+        let Some(debug) = cx.tcx.get_diagnostic_item(sym::Debug) else {
+            return
         };
 
         if self.impling_types.is_none() {
@@ -917,16 +912,16 @@ declare_lint_pass!(
 
 impl EarlyLintPass for AnonymousParameters {
     fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) {
-        if cx.sess.edition() != Edition::Edition2015 {
+        if cx.sess().edition() != Edition::Edition2015 {
             // This is a hard error in future editions; avoid linting and erroring
             return;
         }
-        if let ast::AssocItemKind::Fn(box FnKind(_, ref sig, _, _)) = it.kind {
+        if let ast::AssocItemKind::Fn(box Fn { ref sig, .. }) = it.kind {
             for arg in sig.decl.inputs.iter() {
                 if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind {
                     if ident.name == kw::Empty {
                         cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| {
-                            let ty_snip = cx.sess.source_map().span_to_snippet(arg.ty.span);
+                            let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span);
 
                             let (ty_snip, appl) = if let Ok(ref snip) = ty_snip {
                                 (snip.as_str(), Applicability::MachineApplicable)
@@ -959,7 +954,7 @@ impl EarlyLintPass for AnonymousParameters {
 pub struct DeprecatedAttr {
     // This is not free to compute, so we want to keep it around, rather than
     // compute it for every attribute.
-    depr_attrs: Vec<&'static (Symbol, AttributeType, AttributeTemplate, AttributeGate)>,
+    depr_attrs: Vec<&'static BuiltinAttribute>,
 }
 
 impl_lint_pass!(DeprecatedAttr => []);
@@ -990,14 +985,14 @@ fn lint_deprecated_attr(
 
 impl EarlyLintPass for DeprecatedAttr {
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
-        for &&(n, _, _, ref g) in &self.depr_attrs {
-            if attr.ident().map(|ident| ident.name) == Some(n) {
+        for BuiltinAttribute { name, gate, .. } in &self.depr_attrs {
+            if attr.ident().map(|ident| ident.name) == Some(*name) {
                 if let &AttributeGate::Gated(
                     Stability::Deprecated(link, suggestion),
                     name,
                     reason,
                     _,
-                ) = g
+                ) = gate
                 {
                     let msg =
                         format!("use of deprecated attribute `{}`: {}. See {}", name, reason, link);
@@ -1080,6 +1075,10 @@ impl EarlyLintPass for UnusedDocComment {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
         warn_if_doc(cx, expr.span, "expressions", &expr.attrs);
     }
+
+    fn check_generic_param(&mut self, cx: &EarlyContext<'_>, param: &ast::GenericParam) {
+        warn_if_doc(cx, param.ident.span, "generic parameters", &param.attrs);
+    }
 }
 
 declare_lint! {
@@ -1212,7 +1211,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
                             check_no_mangle_on_generic_fn(
                                 no_mangle_attr,
                                 Some(generics),
-                                cx.tcx.hir().get_generics(it.id.def_id.to_def_id()).unwrap(),
+                                cx.tcx.hir().get_generics(it.id.def_id).unwrap(),
                                 it.span,
                             );
                         }
@@ -1248,7 +1247,7 @@ declare_lint! {
     /// [`UnsafeCell`]: https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html
     MUTABLE_TRANSMUTES,
     Deny,
-    "mutating transmuted &mut T from &T may cause undefined behavior"
+    "transmuting &T to &mut T is undefined behavior, even if the reference is unused"
 }
 
 declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]);
@@ -1260,8 +1259,8 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
             get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))
         {
             if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not {
-                let msg = "mutating transmuted &mut T from &T may cause undefined behavior, \
-                               consider instead using an UnsafeCell";
+                let msg = "transmuting &T to &mut T is undefined behavior, \
+                    even if the reference is unused, consider instead using an UnsafeCell";
                 cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| lint.build(msg).emit());
             }
         }
@@ -1480,12 +1479,6 @@ impl TypeAliasBounds {
             err: &'a mut DiagnosticBuilder<'db>,
         }
         impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> {
-            type Map = intravisit::ErasedMap<'v>;
-
-            fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-                intravisit::NestedVisitorMap::None
-            }
-
             fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) {
                 if TypeAliasBounds::is_type_variable_assoc(qpath) {
                     self.err.span_help(
@@ -1506,9 +1499,8 @@ impl TypeAliasBounds {
 
 impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
-        let (ty, type_alias_generics) = match item.kind {
-            hir::ItemKind::TyAlias(ref ty, ref generics) => (&*ty, generics),
-            _ => return,
+        let hir::ItemKind::TyAlias(ty, type_alias_generics) = &item.kind else {
+            return
         };
         if let hir::TyKind::OpaqueDef(..) = ty.kind {
             // Bounds are respected for `type X = impl Trait`
@@ -1664,7 +1656,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
                     ConstEquate(..) |
                     TypeWellFormedFromEnv(..) => continue,
                 };
-                if predicate.is_global(cx.tcx) {
+                if predicate.is_global() {
                     cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| {
                         lint.build(&format!(
                             "{} bound {} does not depend on any type \
@@ -1783,7 +1775,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
                 };
                 if join.edition() >= Edition::Edition2021 {
                     let mut err =
-                        rustc_errors::struct_span_err!(cx.sess, pat.span, E0783, "{}", msg,);
+                        rustc_errors::struct_span_err!(cx.sess(), pat.span, E0783, "{}", msg,);
                     err.span_suggestion(
                         pat.span,
                         suggestion,
@@ -1807,7 +1799,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
                 let replace = "..=".to_owned();
                 if join.edition() >= Edition::Edition2021 {
                     let mut err =
-                        rustc_errors::struct_span_err!(cx.sess, pat.span, E0783, "{}", msg,);
+                        rustc_errors::struct_span_err!(cx.sess(), pat.span, E0783, "{}", msg,);
                     err.span_suggestion_short(
                         join,
                         suggestion,
@@ -1991,7 +1983,7 @@ impl KeywordIdents {
         UnderMacro(under_macro): UnderMacro,
         ident: Ident,
     ) {
-        let next_edition = match cx.sess.edition() {
+        let next_edition = match cx.sess().edition() {
             Edition::Edition2015 => {
                 match ident.name {
                     kw::Async | kw::Await | kw::Try => Edition::Edition2018,
@@ -2019,7 +2011,7 @@ impl KeywordIdents {
         };
 
         // Don't lint `r#foo`.
-        if cx.sess.parse_sess.raw_identifier_spans.borrow().contains(&ident.span) {
+        if cx.sess().parse_sess.raw_identifier_spans.borrow().contains(&ident.span) {
             return;
         }
 
@@ -2263,16 +2255,15 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
                         // and should check for them here.
                         match predicate.bounded_ty.kind {
                             hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
-                                if let Res::Def(DefKind::TyParam, def_id) = path.res {
-                                    let index = ty_generics.param_def_id_to_index[&def_id];
-                                    (
-                                        Self::lifetimes_outliving_type(inferred_outlives, index),
-                                        &predicate.bounds,
-                                        predicate.span,
-                                    )
-                                } else {
-                                    continue;
-                                }
+                                let Res::Def(DefKind::TyParam, def_id) = path.res else {
+                                    continue
+                                };
+                                let index = ty_generics.param_def_id_to_index[&def_id];
+                                (
+                                    Self::lifetimes_outliving_type(inferred_outlives, index),
+                                    &predicate.bounds,
+                                    predicate.span,
+                                )
                             }
                             _ => {
                                 continue;
@@ -2388,7 +2379,7 @@ declare_lint_pass!(
 
 impl EarlyLintPass for IncompleteFeatures {
     fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
-        let features = cx.sess.features_untracked();
+        let features = cx.sess().features_untracked();
         features
             .declared_lang_features
             .iter()
@@ -2503,7 +2494,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
                         _ => {}
                     }
                 }
-            } else if let hir::ExprKind::MethodCall(_, _, ref args, _) = expr.kind {
+            } else if let hir::ExprKind::MethodCall(_, ref args, _) = expr.kind {
                 // Find problematic calls to `MaybeUninit::assume_init`.
                 let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
                 if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) {
@@ -3012,7 +3003,7 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
                     this_decl_ty,
                     CItemKind::Declaration,
                 ) {
-                    let orig_fi = tcx.hir().expect_foreign_item(existing_hid);
+                    let orig_fi = tcx.hir().expect_foreign_item(existing_hid.expect_owner());
                     let orig = Self::name_of_extern_decl(tcx, orig_fi);
 
                     // We want to ensure that we use spans for both decls that include where the
@@ -3130,18 +3121,13 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
             false
         }
 
-        if let rustc_hir::ExprKind::Unary(ref un_op, ref expr_deref) = expr.kind {
-            if let rustc_hir::UnOp::Deref = un_op {
-                if is_null_ptr(cx, expr_deref) {
-                    cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| {
-                        let mut err = lint.build("dereferencing a null pointer");
-                        err.span_label(
-                            expr.span,
-                            "this code causes undefined behavior when executed",
-                        );
-                        err.emit();
-                    });
-                }
+        if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind {
+            if is_null_ptr(cx, expr_deref) {
+                cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| {
+                    let mut err = lint.build("dereferencing a null pointer");
+                    err.span_label(expr.span, "this code causes undefined behavior when executed");
+                    err.emit();
+                });
             }
         }
     }
@@ -3154,7 +3140,8 @@ declare_lint! {
     /// ### Example
     ///
     /// ```rust,compile_fail
-    /// #![feature(asm)]
+    /// use std::arch::asm;
+    ///
     /// fn main() {
     ///     unsafe {
     ///         asm!("foo: bar");
@@ -3171,10 +3158,10 @@ declare_lint! {
     /// of this, GNU assembler [local labels] *must* be used instead of labels
     /// with a name. Using named labels might cause assembler or linker errors.
     ///
-    /// See the [unstable book] for more details.
+    /// See the explanation in [Rust By Example] for more details.
     ///
     /// [local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels
-    /// [unstable book]: https://doc.rust-lang.org/nightly/unstable-book/library-features/asm.html#labels
+    /// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels
     pub NAMED_ASM_LABELS,
     Deny,
     "named labels in inline assembly",
@@ -3190,13 +3177,13 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
         } = expr
         {
             for (template_sym, template_snippet, template_span) in template_strs.iter() {
-                let template_str = &template_sym.as_str();
+                let template_str = template_sym.as_str();
                 let find_label_span = |needle: &str| -> Option<Span> {
                     if let Some(template_snippet) = template_snippet {
                         let snippet = template_snippet.as_str();
                         if let Some(pos) = snippet.find(needle) {
                             let end = pos
-                                + &snippet[pos..]
+                                + snippet[pos..]
                                     .find(|c| c == ':')
                                     .unwrap_or(snippet[pos..].len() - 1);
                             let inner = InnerSpan::new(pos, end);
@@ -3218,18 +3205,17 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
                     for (idx, _) in statement.match_indices(':') {
                         let possible_label = statement[start_idx..idx].trim();
                         let mut chars = possible_label.chars();
-                        if let Some(c) = chars.next() {
-                            // A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $
-                            if (c.is_alphabetic() || matches!(c, '.' | '_'))
-                                && chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '$'))
-                            {
-                                found_labels.push(possible_label);
-                            } else {
-                                // If we encounter a non-label, there cannot be any further labels, so stop checking
-                                break;
-                            }
-                        } else {
+                        let Some(c) = chars.next() else {
                             // Empty string means a leading ':' in this section, which is not a label
+                            break
+                        };
+                        // A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $
+                        if (c.is_alphabetic() || matches!(c, '.' | '_'))
+                            && chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '$'))
+                        {
+                            found_labels.push(possible_label);
+                        } else {
+                            // If we encounter a non-label, there cannot be any further labels, so stop checking
                             break;
                         }
 
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 5012a69e928..cb08e952586 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -16,15 +16,12 @@
 
 use self::TargetLint::*;
 
-use crate::levels::{is_known_lint_tool, LintLevelsBuilder};
+use crate::levels::LintLevelsBuilder;
 use crate::passes::{EarlyLintPassObject, LateLintPassObject};
-use ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
-use rustc_ast as ast;
+use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync;
-use rustc_errors::{
-    add_elided_lifetime_in_path_suggestion, struct_span_err, Applicability, SuggestionStyle,
-};
+use rustc_errors::{struct_span_err, Applicability, SuggestionStyle};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::{CrateNum, DefId};
@@ -34,13 +31,14 @@ use rustc_middle::middle::privacy::AccessLevels;
 use rustc_middle::middle::stability;
 use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, print::Printer, subst::GenericArg, Ty, TyCtxt};
+use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt};
 use rustc_serialize::json::Json;
 use rustc_session::lint::{BuiltinLintDiagnostics, ExternDepSpec};
 use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
 use rustc_session::Session;
 use rustc_span::lev_distance::find_best_match_for_name;
-use rustc_span::{symbol::Symbol, BytePos, MultiSpan, Span, DUMMY_SP};
+use rustc_span::symbol::{sym, Ident, Symbol};
+use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP};
 use rustc_target::abi;
 use tracing::debug;
 
@@ -315,7 +313,7 @@ impl LintStore {
         sess: &Session,
         lint_name: &str,
         level: Level,
-        crate_attrs: &[ast::Attribute],
+        registered_tools: &RegisteredTools,
     ) {
         let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
         if lint_name_only == crate::WARNINGS.name_lower() && level == Level::ForceWarn {
@@ -328,7 +326,7 @@ impl LintStore {
             )
             .emit();
         }
-        let db = match self.check_lint_name(sess, lint_name_only, tool_name, crate_attrs) {
+        let db = match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
             CheckLintNameResult::Ok(_) => None,
             CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)),
             CheckLintNameResult::NoLint(suggestion) => {
@@ -383,10 +381,10 @@ impl LintStore {
             lint_name,
             self.lint_groups.keys().collect::<Vec<_>>()
         );
-        let lint_name_str = &*lint_name.as_str();
-        self.lint_groups.contains_key(&lint_name_str) || {
+        let lint_name_str = lint_name.as_str();
+        self.lint_groups.contains_key(lint_name_str) || {
             let warnings_name_str = crate::WARNINGS.name_lower();
-            lint_name_str == &*warnings_name_str
+            lint_name_str == warnings_name_str
         }
     }
 
@@ -399,13 +397,16 @@ impl LintStore {
     /// printing duplicate warnings.
     pub fn check_lint_name(
         &self,
-        sess: &Session,
         lint_name: &str,
         tool_name: Option<Symbol>,
-        crate_attrs: &[ast::Attribute],
+        registered_tools: &RegisteredTools,
     ) -> CheckLintNameResult<'_> {
         if let Some(tool_name) = tool_name {
-            if !is_known_lint_tool(tool_name, sess, crate_attrs) {
+            // FIXME: rustc and rustdoc are considered tools for lints, but not for attributes.
+            if tool_name != sym::rustc
+                && tool_name != sym::rustdoc
+                && !registered_tools.contains(&Ident::with_dummy_span(tool_name))
+            {
                 return CheckLintNameResult::NoTool;
             }
         }
@@ -555,20 +556,9 @@ pub struct LateContext<'tcx> {
     pub only_module: bool,
 }
 
-/// Context for lint checking of the AST, after expansion, before lowering to
-/// HIR.
+/// Context for lint checking of the AST, after expansion, before lowering to HIR.
 pub struct EarlyContext<'a> {
-    /// Type context we're checking in.
-    pub sess: &'a Session,
-
-    /// The crate being checked.
-    pub krate: &'a ast::Crate,
-
     pub builder: LintLevelsBuilder<'a>,
-
-    /// The store of registered lints and the lint levels.
-    pub lint_store: &'a LintStore,
-
     pub buffered: LintBuffer,
 }
 
@@ -635,16 +625,6 @@ pub trait LintContext: Sized {
                     }
                 },
                 BuiltinLintDiagnostics::Normal => (),
-                BuiltinLintDiagnostics::BareTraitObject(span, is_global) => {
-                    let (sugg, app) = match sess.source_map().span_to_snippet(span) {
-                        Ok(s) if is_global => {
-                            (format!("dyn ({})", s), Applicability::MachineApplicable)
-                        }
-                        Ok(s) => (format!("dyn {}", s), Applicability::MachineApplicable),
-                        Err(_) => ("dyn <type>".to_string(), Applicability::HasPlaceholders),
-                    };
-                    db.span_suggestion(span, "use `dyn`", sugg, app);
-                }
                 BuiltinLintDiagnostics::AbsPathWithModule(span) => {
                     let (sugg, app) = match sess.source_map().span_to_snippet(span) {
                         Ok(ref s) => {
@@ -670,27 +650,10 @@ pub trait LintContext: Sized {
                 ) => {
                     db.span_note(span_def, "the macro is defined here");
                 }
-                BuiltinLintDiagnostics::ElidedLifetimesInPaths(
-                    n,
-                    path_span,
-                    incl_angl_brckt,
-                    insertion_span,
-                    anon_lts,
-                ) => {
-                    add_elided_lifetime_in_path_suggestion(
-                        sess.source_map(),
-                        &mut db,
-                        n,
-                        path_span,
-                        incl_angl_brckt,
-                        insertion_span,
-                        anon_lts,
-                    );
-                }
                 BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
                     db.span_suggestion(span, &note, sugg, Applicability::MaybeIncorrect);
                 }
-                BuiltinLintDiagnostics::UnusedImports(message, replaces) => {
+                BuiltinLintDiagnostics::UnusedImports(message, replaces, in_test_module) => {
                     if !replaces.is_empty() {
                         db.tool_only_multipart_suggestion(
                             &message,
@@ -698,6 +661,14 @@ pub trait LintContext: Sized {
                             Applicability::MachineApplicable,
                         );
                     }
+
+                    if let Some(span) = in_test_module {
+                        let def_span = self.sess().source_map().guess_head_span(span);
+                        db.span_help(
+                            span.shrink_to_lo().to(def_span),
+                            "consider adding a `#[cfg(test)]` to the containing module",
+                        );
+                    }
                 }
                 BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
                     for (span, is_imported) in spans {
@@ -791,7 +762,7 @@ pub trait LintContext: Sized {
                 }
                 BuiltinLintDiagnostics::NamedAsmLabel(help) => {
                     db.help(&help);
-                    db.note("see the asm section of the unstable book <https://doc.rust-lang.org/nightly/unstable-book/library-features/asm.html#labels> for more information");
+                    db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information");
                 }
             }
             // Rewrap `db`, and pass control to the user.
@@ -823,19 +794,20 @@ pub trait LintContext: Sized {
 }
 
 impl<'a> EarlyContext<'a> {
-    pub fn new(
+    pub(crate) fn new(
         sess: &'a Session,
+        warn_about_weird_lints: bool,
         lint_store: &'a LintStore,
-        krate: &'a ast::Crate,
-        crate_attrs: &'a [ast::Attribute],
+        registered_tools: &'a RegisteredTools,
         buffered: LintBuffer,
-        warn_about_weird_lints: bool,
     ) -> EarlyContext<'a> {
         EarlyContext {
-            sess,
-            krate,
-            lint_store,
-            builder: LintLevelsBuilder::new(sess, warn_about_weird_lints, lint_store, crate_attrs),
+            builder: LintLevelsBuilder::new(
+                sess,
+                warn_about_weird_lints,
+                lint_store,
+                registered_tools,
+            ),
             buffered,
         }
     }
@@ -873,11 +845,11 @@ impl LintContext for EarlyContext<'_> {
 
     /// Gets the overall compiler `Session` object.
     fn sess(&self) -> &Session {
-        &self.sess
+        &self.builder.sess()
     }
 
     fn lints(&self) -> &LintStore {
-        &*self.lint_store
+        self.builder.lint_store()
     }
 
     fn lookup<S: Into<MultiSpan>>(
@@ -1060,8 +1032,8 @@ impl<'tcx> LateContext<'tcx> {
             ) -> Result<Self::Path, Self::Error> {
                 let mut path = print_prefix(self)?;
 
-                // Skip `::{{constructor}}` on tuple/unit structs.
-                if let DefPathData::Ctor = disambiguated_data.data {
+                // Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
+                if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
                     return Ok(path);
                 }
 
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 0bba66d3838..1b2c88867d4 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -16,9 +16,11 @@
 
 use crate::context::{EarlyContext, LintContext, LintStore};
 use crate::passes::{EarlyLintPass, EarlyLintPassObject};
-use rustc_ast as ast;
-use rustc_ast::visit as ast_visit;
+use rustc_ast::ptr::P;
+use rustc_ast::visit::{self as ast_visit, Visitor};
 use rustc_ast::AstLike;
+use rustc_ast::{self as ast, walk_list};
+use rustc_middle::ty::RegisteredTools;
 use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass};
 use rustc_session::Session;
 use rustc_span::symbol::Ident;
@@ -31,7 +33,7 @@ macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({
     $cx.pass.$f(&$cx.context, $($args),*);
 }) }
 
-struct EarlyContextAndPass<'a, T: EarlyLintPass> {
+pub struct EarlyContextAndPass<'a, T: EarlyLintPass> {
     context: EarlyContext<'a>,
     pass: T,
 }
@@ -57,7 +59,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
         F: FnOnce(&mut Self),
     {
         let is_crate_node = id == ast::CRATE_NODE_ID;
-        let push = self.context.builder.push(attrs, &self.context.lint_store, is_crate_node);
+        let push = self.context.builder.push(attrs, is_crate_node);
         self.check_id(id);
         self.enter_attrs(attrs);
         f(self);
@@ -325,48 +327,89 @@ macro_rules! early_lint_pass_impl {
 
 crate::early_lint_methods!(early_lint_pass_impl, []);
 
-fn early_lint_crate<T: EarlyLintPass>(
+/// Early lints work on different nodes - either on the crate root, or on freshly loaded modules.
+/// This trait generalizes over those nodes.
+pub trait EarlyCheckNode<'a>: Copy {
+    fn id(self) -> ast::NodeId;
+    fn attrs<'b>(self) -> &'b [ast::Attribute]
+    where
+        'a: 'b;
+    fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>)
+    where
+        'a: 'b;
+}
+
+impl<'a> EarlyCheckNode<'a> for &'a ast::Crate {
+    fn id(self) -> ast::NodeId {
+        ast::CRATE_NODE_ID
+    }
+    fn attrs<'b>(self) -> &'b [ast::Attribute]
+    where
+        'a: 'b,
+    {
+        &self.attrs
+    }
+    fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>)
+    where
+        'a: 'b,
+    {
+        run_early_pass!(cx, check_crate, self);
+        ast_visit::walk_crate(cx, self);
+        run_early_pass!(cx, check_crate_post, self);
+    }
+}
+
+impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast::Item>]) {
+    fn id(self) -> ast::NodeId {
+        self.0
+    }
+    fn attrs<'b>(self) -> &'b [ast::Attribute]
+    where
+        'a: 'b,
+    {
+        self.1
+    }
+    fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>)
+    where
+        'a: 'b,
+    {
+        walk_list!(cx, visit_attribute, self.1);
+        walk_list!(cx, visit_item, self.2);
+    }
+}
+
+fn early_lint_node<'a>(
     sess: &Session,
+    warn_about_weird_lints: bool,
     lint_store: &LintStore,
-    krate: &ast::Crate,
-    crate_attrs: &[ast::Attribute],
-    pass: T,
+    registered_tools: &RegisteredTools,
     buffered: LintBuffer,
-    warn_about_weird_lints: bool,
+    pass: impl EarlyLintPass,
+    check_node: impl EarlyCheckNode<'a>,
 ) -> LintBuffer {
     let mut cx = EarlyContextAndPass {
         context: EarlyContext::new(
             sess,
+            warn_about_weird_lints,
             lint_store,
-            krate,
-            crate_attrs,
+            registered_tools,
             buffered,
-            warn_about_weird_lints,
         ),
         pass,
     };
 
-    // Visit the whole crate.
-    cx.with_lint_attrs(ast::CRATE_NODE_ID, &krate.attrs, |cx| {
-        // since the root module isn't visited as an item (because it isn't an
-        // item), warn for it here.
-        run_early_pass!(cx, check_crate, krate);
-
-        ast_visit::walk_crate(cx, krate);
-
-        run_early_pass!(cx, check_crate_post, krate);
-    });
+    cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx));
     cx.context.buffered
 }
 
-pub fn check_ast_crate<T: EarlyLintPass>(
+pub fn check_ast_node<'a>(
     sess: &Session,
-    lint_store: &LintStore,
-    krate: &ast::Crate,
-    crate_attrs: &[ast::Attribute],
     pre_expansion: bool,
+    lint_store: &LintStore,
+    registered_tools: &RegisteredTools,
     lint_buffer: Option<LintBuffer>,
-    builtin_lints: T,
+    builtin_lints: impl EarlyLintPass,
+    check_node: impl EarlyCheckNode<'a>,
 ) {
     let passes =
         if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes };
@@ -374,39 +417,39 @@ pub fn check_ast_crate<T: EarlyLintPass>(
     let mut buffered = lint_buffer.unwrap_or_default();
 
     if !sess.opts.debugging_opts.no_interleave_lints {
-        buffered = early_lint_crate(
+        buffered = early_lint_node(
             sess,
+            pre_expansion,
             lint_store,
-            krate,
-            crate_attrs,
-            builtin_lints,
+            registered_tools,
             buffered,
-            pre_expansion,
+            builtin_lints,
+            check_node,
         );
 
         if !passes.is_empty() {
-            buffered = early_lint_crate(
+            buffered = early_lint_node(
                 sess,
+                false,
                 lint_store,
-                krate,
-                crate_attrs,
-                EarlyLintPassObjects { lints: &mut passes[..] },
+                registered_tools,
                 buffered,
-                false,
+                EarlyLintPassObjects { lints: &mut passes[..] },
+                check_node,
             );
         }
     } else {
         for (i, pass) in passes.iter_mut().enumerate() {
             buffered =
                 sess.prof.extra_verbose_generic_activity("run_lint", pass.name()).run(|| {
-                    early_lint_crate(
+                    early_lint_node(
                         sess,
+                        pre_expansion && i == 0,
                         lint_store,
-                        krate,
-                        crate_attrs,
-                        EarlyLintPassObjects { lints: slice::from_mut(pass) },
+                        registered_tools,
                         buffered,
-                        pre_expansion && i == 0,
+                        EarlyLintPassObjects { lints: slice::from_mut(pass) },
+                        check_node,
                     )
                 });
         }
diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
index 876245747f6..c5e15a88fdf 100644
--- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
+++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
@@ -38,7 +38,7 @@ declare_lint_pass!(EnumIntrinsicsNonEnums => [ENUM_INTRINSICS_NON_ENUMS]);
 /// Returns `true` if we know for sure that the given type is not an enum. Note that for cases where
 /// the type is generic, we can't be certain if it will be an enum so we have to assume that it is.
 fn is_non_enum(t: Ty<'_>) -> bool {
-    !t.is_enum() && !t.potentially_needs_subst()
+    !t.is_enum() && !t.needs_subst()
 }
 
 fn enforce_mem_discriminant(
@@ -91,16 +91,14 @@ fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, sp
 
 impl<'tcx> LateLintPass<'tcx> for EnumIntrinsicsNonEnums {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
-        if let hir::ExprKind::Call(ref func, ref args) = expr.kind {
-            if let hir::ExprKind::Path(ref qpath) = func.kind {
-                if let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() {
-                    if cx.tcx.is_diagnostic_item(sym::mem_discriminant, def_id) {
-                        enforce_mem_discriminant(cx, func, expr.span, args[0].span);
-                    } else if cx.tcx.is_diagnostic_item(sym::mem_variant_count, def_id) {
-                        enforce_mem_variant_count(cx, func, expr.span);
-                    }
-                }
-            }
+        let hir::ExprKind::Call(func, args) = &expr.kind else { return };
+        let hir::ExprKind::Path(qpath) = &func.kind else { return };
+        let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() else { return };
+        let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return };
+        match name {
+            sym::mem_discriminant => enforce_mem_discriminant(cx, func, expr.span, args[0].span),
+            sym::mem_variant_count => enforce_mem_variant_count(cx, func, expr.span),
+            _ => {}
         }
     }
 }
diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
index fde84be9a7c..fc99d759a03 100644
--- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
+++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
@@ -127,7 +127,7 @@ impl HiddenUnicodeCodepoints {
 impl EarlyLintPass for HiddenUnicodeCodepoints {
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
         if let ast::AttrKind::DocComment(_, comment) = attr.kind {
-            if contains_text_flow_control_chars(&comment.as_str()) {
+            if contains_text_flow_control_chars(comment.as_str()) {
                 self.lint_text_direction_codepoint(cx, comment, attr.span, 0, false, "doc comment");
             }
         }
@@ -138,7 +138,7 @@ impl EarlyLintPass for HiddenUnicodeCodepoints {
         let (text, span, padding) = match &expr.kind {
             ast::ExprKind::Lit(ast::Lit { token, kind, span }) => {
                 let text = token.symbol;
-                if !contains_text_flow_control_chars(&text.as_str()) {
+                if !contains_text_flow_control_chars(text.as_str()) {
                     return;
                 }
                 let padding = match kind {
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 50a0d211a36..d8e1162890c 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -5,10 +5,7 @@ use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}
 use rustc_ast as ast;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::{
-    GenericArg, HirId, Item, ItemKind, MutTy, Mutability, Node, Path, PathSegment, QPath, Ty,
-    TyKind,
-};
+use rustc_hir::{GenericArg, HirId, Item, ItemKind, Node, Path, PathSegment, QPath, Ty, TyKind};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::hygiene::{ExpnKind, MacroKind};
@@ -59,13 +56,6 @@ declare_tool_lint! {
 }
 
 declare_tool_lint! {
-    pub rustc::TY_PASS_BY_REFERENCE,
-    Allow,
-    "passing `Ty` or `TyCtxt` by reference",
-    report_in_external_macro: true
-}
-
-declare_tool_lint! {
     pub rustc::USAGE_OF_QUALIFIED_TY,
     Allow,
     "using `ty::{Ty,TyCtxt}` instead of importing it",
@@ -74,7 +64,6 @@ declare_tool_lint! {
 
 declare_lint_pass!(TyTyKind => [
     USAGE_OF_TY_TYKIND,
-    TY_PASS_BY_REFERENCE,
     USAGE_OF_QUALIFIED_TY,
 ]);
 
@@ -101,58 +90,36 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
 
     fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx>) {
         match &ty.kind {
-            TyKind::Path(qpath) => {
-                if let QPath::Resolved(_, path) = qpath {
-                    if let Some(last) = path.segments.iter().last() {
-                        if lint_ty_kind_usage(cx, last) {
-                            cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| {
-                                lint.build("usage of `ty::TyKind`")
-                                    .help("try using `Ty` instead")
-                                    .emit();
-                            })
-                        } else {
-                            if ty.span.from_expansion() {
-                                return;
-                            }
-                            if let Some(t) = is_ty_or_ty_ctxt(cx, ty) {
-                                if path.segments.len() > 1 {
-                                    cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| {
-                                        lint.build(&format!("usage of qualified `ty::{}`", t))
-                                            .span_suggestion(
-                                                path.span,
-                                                "try using it unqualified",
-                                                t,
-                                                // The import probably needs to be changed
-                                                Applicability::MaybeIncorrect,
-                                            )
-                                            .emit();
-                                    })
-                                }
+            TyKind::Path(QPath::Resolved(_, path)) => {
+                if let Some(last) = path.segments.iter().last() {
+                    if lint_ty_kind_usage(cx, last) {
+                        cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| {
+                            lint.build("usage of `ty::TyKind`")
+                                .help("try using `Ty` instead")
+                                .emit();
+                        })
+                    } else {
+                        if ty.span.from_expansion() {
+                            return;
+                        }
+                        if let Some(t) = is_ty_or_ty_ctxt(cx, ty) {
+                            if path.segments.len() > 1 {
+                                cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| {
+                                    lint.build(&format!("usage of qualified `ty::{}`", t))
+                                        .span_suggestion(
+                                            path.span,
+                                            "try importing it and using it unqualified",
+                                            t,
+                                            // The import probably needs to be changed
+                                            Applicability::MaybeIncorrect,
+                                        )
+                                        .emit();
+                                })
                             }
                         }
                     }
                 }
             }
-            TyKind::Rptr(_, MutTy { ty: inner_ty, mutbl: Mutability::Not }) => {
-                if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) {
-                    if cx.tcx.impl_trait_ref(impl_did).is_some() {
-                        return;
-                    }
-                }
-                if let Some(t) = is_ty_or_ty_ctxt(cx, &inner_ty) {
-                    cx.struct_span_lint(TY_PASS_BY_REFERENCE, ty.span, |lint| {
-                        lint.build(&format!("passing `{}` by reference", t))
-                            .span_suggestion(
-                                ty.span,
-                                "try passing by value",
-                                t,
-                                // Changing type of function argument
-                                Applicability::MaybeIncorrect,
-                            )
-                            .emit();
-                    })
-                }
-            }
             _ => {}
         }
     }
@@ -169,37 +136,30 @@ fn lint_ty_kind_usage(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> bool {
 }
 
 fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, ty: &Ty<'_>) -> Option<String> {
-    if let TyKind::Path(qpath) = &ty.kind {
-        if let QPath::Resolved(_, path) = qpath {
-            match path.res {
-                Res::Def(_, def_id) => {
-                    if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(def_id)
-                    {
-                        return Some(format!(
-                            "{}{}",
-                            name,
-                            gen_args(path.segments.last().unwrap())
-                        ));
-                    }
+    if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind {
+        match path.res {
+            Res::Def(_, def_id) => {
+                if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(def_id) {
+                    return Some(format!("{}{}", name, gen_args(path.segments.last().unwrap())));
                 }
-                // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
-                Res::SelfTy(None, Some((did, _))) => {
-                    if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
-                        if let Some(name @ (sym::Ty | sym::TyCtxt)) =
-                            cx.tcx.get_diagnostic_name(adt.did)
-                        {
-                            // NOTE: This path is currently unreachable as `Ty<'tcx>` is
-                            // defined as a type alias meaning that `impl<'tcx> Ty<'tcx>`
-                            // is not actually allowed.
-                            //
-                            // I(@lcnr) still kept this branch in so we don't miss this
-                            // if we ever change it in the future.
-                            return Some(format!("{}<{}>", name, substs[0]));
-                        }
+            }
+            // Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
+            Res::SelfTy(None, Some((did, _))) => {
+                if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
+                    if let Some(name @ (sym::Ty | sym::TyCtxt)) =
+                        cx.tcx.get_diagnostic_name(adt.did)
+                    {
+                        // NOTE: This path is currently unreachable as `Ty<'tcx>` is
+                        // defined as a type alias meaning that `impl<'tcx> Ty<'tcx>`
+                        // is not actually allowed.
+                        //
+                        // I(@lcnr) still kept this branch in so we don't miss this
+                        // if we ever change it in the future.
+                        return Some(format!("{}<{}>", name, substs[0]));
                     }
                 }
-                _ => (),
             }
+            _ => (),
         }
     }
 
@@ -238,8 +198,7 @@ declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);
 
 impl EarlyLintPass for LintPassImpl {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
-        if let ast::ItemKind::Impl(box ast::ImplKind { of_trait: Some(lint_pass), .. }) = &item.kind
-        {
+        if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind {
             if let Some(last) = lint_pass.path.segments.last() {
                 if last.ident.name == sym::LintPass {
                     let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index 773e5751f13..0ce760b64d9 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -21,7 +21,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit as hir_visit;
 use rustc_hir::intravisit::Visitor;
-use rustc_middle::hir::map::Map;
+use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::lint::LintPass;
 use rustc_span::symbol::Symbol;
@@ -94,13 +94,13 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> {
 }
 
 impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPass<'tcx, T> {
-    type Map = Map<'tcx>;
+    type NestedFilter = nested_filter::All;
 
     /// Because lints are scoped lexically, we want to walk nested
     /// items in the context of the outer item, so enable
     /// deep-walking.
-    fn nested_visit_map(&mut self) -> hir_visit::NestedVisitorMap<Self::Map> {
-        hir_visit::NestedVisitorMap::All(self.context.tcx.hir())
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.context.tcx.hir()
     }
 
     fn visit_nested_body(&mut self, body_id: hir::BodyId) {
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index b6d66eb12d0..8afbd462c14 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -5,8 +5,8 @@ use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
-use rustc_hir::{intravisit, HirId, CRATE_HIR_ID};
-use rustc_middle::hir::map::Map;
+use rustc_hir::{intravisit, HirId};
+use rustc_middle::hir::nested_filter;
 use rustc_middle::lint::LevelAndSource;
 use rustc_middle::lint::LintDiagnosticBuilder;
 use rustc_middle::lint::{
@@ -14,7 +14,7 @@ use rustc_middle::lint::{
     COMMAND_LINE,
 };
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{RegisteredTools, TyCtxt};
 use rustc_session::lint::{
     builtin::{self, FORBIDDEN_LINT_GROUPS},
     Level, Lint, LintId,
@@ -27,14 +27,14 @@ use tracing::debug;
 
 fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
     let store = unerased_lint_store(tcx);
-    let crate_attrs = tcx.hir().attrs(CRATE_HIR_ID);
-    let levels = LintLevelsBuilder::new(tcx.sess, false, &store, crate_attrs);
-    let mut builder = LintLevelMapBuilder { levels, tcx, store };
+    let levels =
+        LintLevelsBuilder::new(tcx.sess, false, &store, &tcx.resolutions(()).registered_tools);
+    let mut builder = LintLevelMapBuilder { levels, tcx };
     let krate = tcx.hir().krate();
 
     builder.levels.id_to_set.reserve(krate.owners.len() + 1);
 
-    let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), &store, true);
+    let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), true);
     builder.levels.register_id(hir::CRATE_HIR_ID);
     tcx.hir().walk_toplevel_module(&mut builder);
     builder.levels.pop(push);
@@ -49,7 +49,7 @@ pub struct LintLevelsBuilder<'s> {
     cur: LintStackIndex,
     warn_about_weird_lints: bool,
     store: &'s LintStore,
-    crate_attrs: &'s [ast::Attribute],
+    registered_tools: &'s RegisteredTools,
 }
 
 pub struct BuilderPush {
@@ -62,7 +62,7 @@ impl<'s> LintLevelsBuilder<'s> {
         sess: &'s Session,
         warn_about_weird_lints: bool,
         store: &'s LintStore,
-        crate_attrs: &'s [ast::Attribute],
+        registered_tools: &'s RegisteredTools,
     ) -> Self {
         let mut builder = LintLevelsBuilder {
             sess,
@@ -71,19 +71,27 @@ impl<'s> LintLevelsBuilder<'s> {
             id_to_set: Default::default(),
             warn_about_weird_lints,
             store,
-            crate_attrs,
+            registered_tools,
         };
         builder.process_command_line(sess, store);
         assert_eq!(builder.sets.list.len(), 1);
         builder
     }
 
+    pub(crate) fn sess(&self) -> &Session {
+        self.sess
+    }
+
+    pub(crate) fn lint_store(&self) -> &LintStore {
+        self.store
+    }
+
     fn process_command_line(&mut self, sess: &Session, store: &LintStore) {
         let mut specs = FxHashMap::default();
         self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
 
         for &(ref lint_name, level) in &sess.opts.lint_opts {
-            store.check_lint_name_cmdline(sess, &lint_name, level, self.crate_attrs);
+            store.check_lint_name_cmdline(sess, &lint_name, level, self.registered_tools);
             let orig_level = level;
             let lint_flag_val = Symbol::intern(lint_name);
 
@@ -154,7 +162,7 @@ impl<'s> LintLevelsBuilder<'s> {
                         LintLevelSource::Node(_, forbid_source_span, reason) => {
                             diag_builder.span_label(forbid_source_span, "`forbid` level set here");
                             if let Some(rationale) = reason {
-                                diag_builder.note(&rationale.as_str());
+                                diag_builder.note(rationale.as_str());
                             }
                         }
                         LintLevelSource::CommandLine(_, _) => {
@@ -217,24 +225,17 @@ impl<'s> LintLevelsBuilder<'s> {
     ///   `#[allow]`
     ///
     /// Don't forget to call `pop`!
-    pub(crate) fn push(
-        &mut self,
-        attrs: &[ast::Attribute],
-        store: &LintStore,
-        is_crate_node: bool,
-    ) -> BuilderPush {
+    pub(crate) fn push(&mut self, attrs: &[ast::Attribute], is_crate_node: bool) -> BuilderPush {
         let mut specs = FxHashMap::default();
         let sess = self.sess;
         let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
         for attr in attrs {
-            let level = match Level::from_symbol(attr.name_or_empty()) {
-                None => continue,
-                Some(lvl) => lvl,
+            let Some(level) = Level::from_symbol(attr.name_or_empty()) else {
+                continue
             };
 
-            let mut metas = match attr.meta_item_list() {
-                Some(x) => x,
-                None => continue,
+            let Some(mut metas) = attr.meta_item_list() else {
+                continue
             };
 
             if metas.is_empty() {
@@ -312,7 +313,8 @@ impl<'s> LintLevelsBuilder<'s> {
                 };
                 let tool_name = tool_ident.map(|ident| ident.name);
                 let name = pprust::path_to_string(&meta_item.path);
-                let lint_result = store.check_lint_name(sess, &name, tool_name, self.crate_attrs);
+                let lint_result =
+                    self.store.check_lint_name(&name, tool_name, self.registered_tools);
                 match &lint_result {
                     CheckLintNameResult::Ok(ids) => {
                         let src = LintLevelSource::Node(
@@ -461,7 +463,7 @@ impl<'s> LintLevelsBuilder<'s> {
                     // Ignore any errors or warnings that happen because the new name is inaccurate
                     // NOTE: `new_name` already includes the tool name, so we don't have to add it again.
                     if let CheckLintNameResult::Ok(ids) =
-                        store.check_lint_name(sess, &new_name, None, self.crate_attrs)
+                        self.store.check_lint_name(&new_name, None, self.registered_tools)
                     {
                         let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason);
                         for &id in ids {
@@ -481,9 +483,8 @@ impl<'s> LintLevelsBuilder<'s> {
                     continue;
                 }
 
-                let (lint_attr_name, lint_attr_span) = match *src {
-                    LintLevelSource::Node(name, span, _) => (name, span),
-                    _ => continue,
+                let LintLevelSource::Node(lint_attr_name, lint_attr_span, _) = *src else {
+                    continue
                 };
 
                 let lint = builtin::UNUSED_ATTRIBUTES;
@@ -565,34 +566,19 @@ impl<'s> LintLevelsBuilder<'s> {
     }
 }
 
-pub fn is_known_lint_tool(m_item: Symbol, sess: &Session, attrs: &[ast::Attribute]) -> bool {
-    if [sym::clippy, sym::rustc, sym::rustdoc].contains(&m_item) {
-        return true;
-    }
-    // Look for registered tools
-    // NOTE: does no error handling; error handling is done by rustc_resolve.
-    sess.filter_by_name(attrs, sym::register_tool)
-        .filter_map(|attr| attr.meta_item_list())
-        .flatten()
-        .filter_map(|nested_meta| nested_meta.ident())
-        .map(|ident| ident.name)
-        .any(|name| name == m_item)
-}
-
-struct LintLevelMapBuilder<'a, 'tcx> {
+struct LintLevelMapBuilder<'tcx> {
     levels: LintLevelsBuilder<'tcx>,
     tcx: TyCtxt<'tcx>,
-    store: &'a LintStore,
 }
 
-impl LintLevelMapBuilder<'_, '_> {
+impl LintLevelMapBuilder<'_> {
     fn with_lint_attrs<F>(&mut self, id: hir::HirId, f: F)
     where
         F: FnOnce(&mut Self),
     {
         let is_crate_hir = id == hir::CRATE_HIR_ID;
         let attrs = self.tcx.hir().attrs(id);
-        let push = self.levels.push(attrs, self.store, is_crate_hir);
+        let push = self.levels.push(attrs, is_crate_hir);
         if push.changed {
             self.levels.register_id(id);
         }
@@ -601,11 +587,11 @@ impl LintLevelMapBuilder<'_, '_> {
     }
 }
 
-impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'_, 'tcx> {
-    type Map = Map<'tcx>;
+impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> {
+    type NestedFilter = nested_filter::All;
 
-    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
-        intravisit::NestedVisitorMap::All(self.tcx.hir())
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.tcx.hir()
     }
 
     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index f6514ddca9f..a87f2b2768d 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -30,9 +30,8 @@
 #![feature(bool_to_option)]
 #![feature(box_patterns)]
 #![feature(crate_visibility_modifier)]
-#![feature(format_args_capture)]
 #![feature(iter_order_by)]
-#![feature(iter_zip)]
+#![feature(let_else)]
 #![feature(never_type)]
 #![feature(nll)]
 #![feature(control_flow_enum)]
@@ -57,6 +56,7 @@ mod non_ascii_idents;
 mod non_fmt_panic;
 mod nonstandard_style;
 mod noop_method_call;
+mod pass_by_value;
 mod passes;
 mod redundant_semicolon;
 mod traits;
@@ -86,6 +86,7 @@ use non_ascii_idents::*;
 use non_fmt_panic::NonPanicFmt;
 use nonstandard_style::*;
 use noop_method_call::*;
+use pass_by_value::*;
 use redundant_semicolon::*;
 use traits::*;
 use types::*;
@@ -93,8 +94,9 @@ use unused::*;
 
 /// Useful for other parts of the compiler / Clippy.
 pub use builtin::SoftLints;
-pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore};
-pub use early::check_ast_crate;
+pub use context::{CheckLintNameResult, FindLintError, LintStore};
+pub use context::{EarlyContext, LateContext, LintContext};
+pub use early::{check_ast_node, EarlyCheckNode};
 pub use late::check_crate;
 pub use passes::{EarlyLintPass, LateLintPass};
 pub use rustc_session::lint::Level::{self, *};
@@ -479,6 +481,11 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
          <https://github.com/rust-lang/rust/issues/59014> for more information",
     );
     store.register_removed("plugin_as_library", "plugins have been deprecated and retired");
+    store.register_removed(
+        "unsupported_naked_functions",
+        "converted into hard error, see RFC 2972 \
+         <https://github.com/rust-lang/rfcs/blob/master/text/2972-constrained-naked.md> for more information",
+    );
 }
 
 fn register_internals(store: &mut LintStore) {
@@ -490,6 +497,8 @@ fn register_internals(store: &mut LintStore) {
     store.register_late_pass(|| Box::new(ExistingDocKeyword));
     store.register_lints(&TyTyKind::get_lints());
     store.register_late_pass(|| Box::new(TyTyKind));
+    store.register_lints(&PassByValue::get_lints());
+    store.register_late_pass(|| Box::new(PassByValue));
     store.register_group(
         false,
         "rustc::internal",
@@ -497,8 +506,8 @@ fn register_internals(store: &mut LintStore) {
         vec![
             LintId::of(DEFAULT_HASH_TYPES),
             LintId::of(USAGE_OF_TY_TYKIND),
+            LintId::of(PASS_BY_VALUE),
             LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO),
-            LintId::of(TY_PASS_BY_REFERENCE),
             LintId::of(USAGE_OF_QUALIFIED_TY),
             LintId::of(EXISTING_DOC_KEYWORD),
         ],
diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs
index 5558947de0c..ae936834754 100644
--- a/compiler/rustc_lint/src/methods.rs
+++ b/compiler/rustc_lint/src/methods.rs
@@ -44,7 +44,7 @@ fn in_macro(span: Span) -> bool {
 fn first_method_call<'tcx>(
     expr: &'tcx Expr<'tcx>,
 ) -> Option<(&'tcx PathSegment<'tcx>, &'tcx [Expr<'tcx>])> {
-    if let ExprKind::MethodCall(path, _, args, _) = &expr.kind {
+    if let ExprKind::MethodCall(path, args, _) = &expr.kind {
         if args.iter().any(|e| e.span.from_expansion()) { None } else { Some((path, *args)) }
     } else {
         None
diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs
index 9b4ee148df4..2dd6dbd67a8 100644
--- a/compiler/rustc_lint/src/non_ascii_idents.rs
+++ b/compiler/rustc_lint/src/non_ascii_idents.rs
@@ -166,7 +166,7 @@ impl EarlyLintPass for NonAsciiIdents {
         }
 
         let mut has_non_ascii_idents = false;
-        let symbols = cx.sess.parse_sess.symbol_gallery.symbols.lock();
+        let symbols = cx.sess().parse_sess.symbol_gallery.symbols.lock();
 
         // Sort by `Span` so that error messages make sense with respect to the
         // order of identifier locations in the code.
@@ -218,8 +218,7 @@ impl EarlyLintPass for NonAsciiIdents {
                             cx.struct_span_lint(CONFUSABLE_IDENTS, sp, |lint| {
                                 lint.build(&format!(
                                     "identifier pair considered confusable between `{}` and `{}`",
-                                    existing_symbol.as_str(),
-                                    symbol.as_str()
+                                    existing_symbol, symbol
                                 ))
                                 .span_label(
                                     *existing_span,
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index f2ad72f97ec..6bf25732f60 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -9,7 +9,7 @@ use rustc_middle::ty::subst::InternalSubsts;
 use rustc_parse_format::{ParseMode, Parser, Piece};
 use rustc_session::lint::FutureIncompatibilityReason;
 use rustc_span::edition::Edition;
-use rustc_span::{hygiene, sym, symbol::kw, symbol::SymbolStr, InnerSpan, Span, Symbol};
+use rustc_span::{hygiene, sym, symbol::kw, InnerSpan, Span, Symbol};
 use rustc_trait_selection::infer::InferCtxtExt;
 
 declare_lint! {
@@ -71,14 +71,14 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
     if let hir::ExprKind::Lit(lit) = &arg.kind {
         if let ast::LitKind::Str(sym, _) = lit.node {
             // The argument is a string literal.
-            check_panic_str(cx, f, arg, &sym.as_str());
+            check_panic_str(cx, f, arg, sym.as_str());
             return;
         }
     }
 
     // The argument is *not* a string literal.
 
-    let (span, panic, symbol_str) = panic_call(cx, f);
+    let (span, panic, symbol) = panic_call(cx, f);
 
     if in_external_macro(cx.sess(), span) {
         // Nothing that can be done about it in the current crate.
@@ -103,7 +103,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
 
     cx.struct_span_lint(NON_FMT_PANICS, arg_span, |lint| {
         let mut l = lint.build("panic message is not a string literal");
-        l.note(&format!("this usage of {}!() is deprecated; it will be a hard error in Rust 2021", symbol_str));
+        l.note(&format!("this usage of {}!() is deprecated; it will be a hard error in Rust 2021", symbol));
         l.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>");
         if !is_arg_inside_call(arg_span, span) {
             // No clue where this argument is coming from.
@@ -112,7 +112,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
         }
         if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
             // A case of `panic!(format!(..))`.
-            l.note(format!("the {}!() macro supports formatting, so there's no need for the format!() macro here", symbol_str).as_str());
+            l.note(format!("the {}!() macro supports formatting, so there's no need for the format!() macro here", symbol).as_str());
             if let Some((open, close, _)) = find_delimiters(cx, arg_span) {
                 l.multipart_suggestion(
                     "remove the `format!(..)` macro call",
@@ -207,7 +207,7 @@ fn check_panic_str<'tcx>(
     arg: &'tcx hir::Expr<'tcx>,
     fmt: &str,
 ) {
-    if !fmt.contains(&['{', '}'][..]) {
+    if !fmt.contains(&['{', '}']) {
         // No brace, no problem.
         return;
     }
@@ -301,7 +301,7 @@ fn find_delimiters<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<(Span, Sp
     ))
 }
 
-fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol, SymbolStr) {
+fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol, Symbol) {
     let mut expn = f.span.ctxt().outer_expn_data();
 
     let mut panic_macro = kw::Empty;
@@ -309,19 +309,26 @@ fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span,
     // Unwrap more levels of macro expansion, as panic_2015!()
     // was likely expanded from panic!() and possibly from
     // [debug_]assert!().
-    for &i in
-        &[sym::std_panic_macro, sym::core_panic_macro, sym::assert_macro, sym::debug_assert_macro]
-    {
+    loop {
         let parent = expn.call_site.ctxt().outer_expn_data();
-        if parent.macro_def_id.map_or(false, |id| cx.tcx.is_diagnostic_item(i, id)) {
-            expn = parent;
-            panic_macro = i;
+        let Some(id) = parent.macro_def_id else { break };
+        let Some(name) = cx.tcx.get_diagnostic_name(id) else { break };
+        if !matches!(
+            name,
+            sym::core_panic_macro
+                | sym::std_panic_macro
+                | sym::assert_macro
+                | sym::debug_assert_macro
+        ) {
+            break;
         }
+        expn = parent;
+        panic_macro = name;
     }
 
     let macro_symbol =
         if let hygiene::ExpnKind::Macro(_, symbol) = expn.kind { symbol } else { sym::panic };
-    (expn.call_site, panic_macro, macro_symbol.as_str())
+    (expn.call_site, panic_macro, macro_symbol)
 }
 
 fn is_arg_inside_call(arg: Span, call: Span) -> bool {
@@ -329,5 +336,5 @@ fn is_arg_inside_call(arg: Span, call: Span) -> bool {
     // panic call in the source file, to avoid invalid suggestions when macros are involved.
     // We specifically check for the spans to not be identical, as that happens sometimes when
     // proc_macros lie about spans and apply the same span to all the tokens they produce.
-    call.contains(arg) && !call.source_equal(&arg)
+    call.contains(arg) && !call.source_equal(arg)
 }
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index bcddc4f3d76..f73388c675e 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -133,7 +133,7 @@ fn to_camel_case(s: &str) -> String {
 
 impl NonCamelCaseTypes {
     fn check_case(&self, cx: &EarlyContext<'_>, sort: &str, ident: &Ident) {
-        let name = &ident.name.as_str();
+        let name = ident.name.as_str();
 
         if !is_camel_case(name) {
             cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, |lint| {
@@ -164,7 +164,7 @@ impl EarlyLintPass for NonCamelCaseTypes {
         let has_repr_c = it
             .attrs
             .iter()
-            .any(|attr| attr::find_repr_attrs(&cx.sess, attr).contains(&attr::ReprC));
+            .any(|attr| attr::find_repr_attrs(cx.sess(), attr).contains(&attr::ReprC));
 
         if has_repr_c {
             return;
@@ -276,7 +276,7 @@ impl NonSnakeCase {
             })
         }
 
-        let name = &ident.name.as_str();
+        let name = ident.name.as_str();
 
         if !is_snake_case(name) {
             cx.struct_span_lint(NON_SNAKE_CASE, ident.span, |lint| {
@@ -484,7 +484,7 @@ declare_lint_pass!(NonUpperCaseGlobals => [NON_UPPER_CASE_GLOBALS]);
 
 impl NonUpperCaseGlobals {
     fn check_upper_case(cx: &LateContext<'_>, sort: &str, ident: &Ident) {
-        let name = &ident.name.as_str();
+        let name = ident.name.as_str();
         if name.chars().any(|c| c.is_lowercase()) {
             cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, |lint| {
                 let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index d2c970468ab..39b5b7afdae 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -40,9 +40,8 @@ declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
 impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         // We only care about method calls.
-        let (call, elements) = match expr.kind {
-            ExprKind::MethodCall(call, _, elements, _) => (call, elements),
-            _ => return,
+        let ExprKind::MethodCall(call, elements, _) = &expr.kind else {
+            return
         };
         // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
         // traits and ignore any other method call.
@@ -63,50 +62,47 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
             _ => return,
         };
         let substs = cx.typeck_results().node_substs(expr.hir_id);
-        if substs.definitely_needs_subst(cx.tcx) {
+        if substs.needs_subst() {
             // We can't resolve on types that require monomorphization, so we don't handle them if
             // we need to perfom substitution.
             return;
         }
         let param_env = cx.tcx.param_env(trait_id);
         // Resolve the trait method instance.
-        let i = match ty::Instance::resolve(cx.tcx, param_env, did, substs) {
-            Ok(Some(i)) => i,
-            _ => return,
+        let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) else {
+            return
         };
         // (Re)check that it implements the noop diagnostic.
-        for s in [sym::noop_method_clone, sym::noop_method_deref, sym::noop_method_borrow].iter() {
-            if cx.tcx.is_diagnostic_item(*s, i.def_id()) {
-                let method = &call.ident.name;
-                let receiver = &elements[0];
-                let receiver_ty = cx.typeck_results().expr_ty(receiver);
-                let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
-                if receiver_ty != expr_ty {
-                    // This lint will only trigger if the receiver type and resulting expression \
-                    // type are the same, implying that the method call is unnecessary.
-                    return;
-                }
-                let expr_span = expr.span;
-                let note = format!(
-                    "the type `{:?}` which `{}` is being called on is the same as \
-                     the type returned from `{}`, so the method call does not do \
-                     anything and can be removed",
-                    receiver_ty, method, method,
-                );
-
-                let span = expr_span.with_lo(receiver.span.hi());
-                cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
-                    let method = &call.ident.name;
-                    let message = format!(
-                        "call to `.{}()` on a reference in this situation does nothing",
-                        &method,
-                    );
-                    lint.build(&message)
-                        .span_label(span, "unnecessary method call")
-                        .note(&note)
-                        .emit()
-                });
-            }
+        let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
+        if !matches!(
+            name,
+            sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref
+        ) {
+            return;
+        }
+        let method = &call.ident.name;
+        let receiver = &elements[0];
+        let receiver_ty = cx.typeck_results().expr_ty(receiver);
+        let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
+        if receiver_ty != expr_ty {
+            // This lint will only trigger if the receiver type and resulting expression \
+            // type are the same, implying that the method call is unnecessary.
+            return;
         }
+        let expr_span = expr.span;
+        let note = format!(
+            "the type `{:?}` which `{}` is being called on is the same as \
+             the type returned from `{}`, so the method call does not do \
+             anything and can be removed",
+            receiver_ty, method, method,
+        );
+
+        let span = expr_span.with_lo(receiver.span.hi());
+        cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
+            let method = &call.ident.name;
+            let message =
+                format!("call to `.{}()` on a reference in this situation does nothing", &method,);
+            lint.build(&message).span_label(span, "unnecessary method call").note(&note).emit()
+        });
     }
 }
diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs
new file mode 100644
index 00000000000..2caf929788f
--- /dev/null
+++ b/compiler/rustc_lint/src/pass_by_value.rs
@@ -0,0 +1,94 @@
+use crate::{LateContext, LateLintPass, LintContext};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_hir::{GenericArg, PathSegment, QPath, TyKind};
+use rustc_middle::ty;
+use rustc_span::symbol::sym;
+
+declare_tool_lint! {
+    /// The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to always be passed by value.
+    /// This is usually used for types that are thin wrappers around references, so there is no benefit to an extra
+    /// layer of indirection. (Example: `Ty` which is a reference to a `TyS`)
+    pub rustc::PASS_BY_VALUE,
+    Warn,
+    "pass by reference of a type flagged as `#[rustc_pass_by_value]`",
+    report_in_external_macro: true
+}
+
+declare_lint_pass!(PassByValue => [PASS_BY_VALUE]);
+
+impl<'tcx> LateLintPass<'tcx> for PassByValue {
+    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
+        match &ty.kind {
+            TyKind::Rptr(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => {
+                if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) {
+                    if cx.tcx.impl_trait_ref(impl_did).is_some() {
+                        return;
+                    }
+                }
+                if let Some(t) = path_for_pass_by_value(cx, &inner_ty) {
+                    cx.struct_span_lint(PASS_BY_VALUE, ty.span, |lint| {
+                        lint.build(&format!("passing `{}` by reference", t))
+                            .span_suggestion(
+                                ty.span,
+                                "try passing by value",
+                                t,
+                                // Changing type of function argument
+                                Applicability::MaybeIncorrect,
+                            )
+                            .emit();
+                    })
+                }
+            }
+            _ => {}
+        }
+    }
+}
+
+fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option<String> {
+    if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind {
+        match path.res {
+            Res::Def(_, def_id) if cx.tcx.has_attr(def_id, sym::rustc_pass_by_value) => {
+                let name = cx.tcx.item_name(def_id).to_ident_string();
+                let path_segment = path.segments.last().unwrap();
+                return Some(format!("{}{}", name, gen_args(cx, path_segment)));
+            }
+            Res::SelfTy(None, Some((did, _))) => {
+                if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
+                    if cx.tcx.has_attr(adt.did, sym::rustc_pass_by_value) {
+                        return Some(cx.tcx.def_path_str_with_substs(adt.did, substs));
+                    }
+                }
+            }
+            _ => (),
+        }
+    }
+
+    None
+}
+
+fn gen_args(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> String {
+    if let Some(args) = &segment.args {
+        let params = args
+            .args
+            .iter()
+            .map(|arg| match arg {
+                GenericArg::Lifetime(lt) => lt.name.ident().to_string(),
+                GenericArg::Type(ty) => {
+                    cx.tcx.sess.source_map().span_to_snippet(ty.span).unwrap_or_else(|_| "_".into())
+                }
+                GenericArg::Const(c) => {
+                    cx.tcx.sess.source_map().span_to_snippet(c.span).unwrap_or_else(|_| "_".into())
+                }
+                GenericArg::Infer(_) => String::from("_"),
+            })
+            .collect::<Vec<_>>();
+
+        if !params.is_empty() {
+            return format!("<{}>", params.join(", "));
+        }
+    }
+
+    String::new()
+}
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index 5435ff1396d..4c7f3482776 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -86,16 +86,14 @@ declare_lint_pass!(
 
 impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
-        use rustc_middle::ty;
         use rustc_middle::ty::PredicateKind::*;
 
         let predicates = cx.tcx.explicit_predicates_of(item.def_id);
         for &(predicate, span) in predicates.predicates {
-            let trait_predicate = match predicate.kind().skip_binder() {
-                Trait(trait_predicate) => trait_predicate,
-                _ => continue,
+            let Trait(trait_predicate) = predicate.kind().skip_binder() else {
+                continue
             };
-            if trait_predicate.constness == ty::BoundConstness::ConstIfConst {
+            if trait_predicate.is_const_if_const() {
                 // `~const Drop` definitely have meanings so avoid linting here.
                 continue;
             }
@@ -106,9 +104,8 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
                     continue;
                 }
                 cx.struct_span_lint(DROP_BOUNDS, span, |lint| {
-                    let needs_drop = match cx.tcx.get_diagnostic_item(sym::needs_drop) {
-                        Some(needs_drop) => needs_drop,
-                        None => return,
+                    let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
+                        return
                     };
                     let msg = format!(
                         "bounds on `{}` are most likely incorrect, consider instead \
@@ -123,17 +120,15 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
     }
 
     fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) {
-        let bounds = match &ty.kind {
-            hir::TyKind::TraitObject(bounds, _lifetime, _syntax) => bounds,
-            _ => return,
+        let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else {
+            return
         };
         for bound in &bounds[..] {
             let def_id = bound.trait_ref.trait_def_id();
             if cx.tcx.lang_items().drop_trait() == def_id {
                 cx.struct_span_lint(DYN_DROP, bound.span, |lint| {
-                    let needs_drop = match cx.tcx.get_diagnostic_item(sym::needs_drop) {
-                        Some(needs_drop) => needs_drop,
-                        None => return,
+                    let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
+                        return
                     };
                     let msg = format!(
                         "types that do not implement `Drop` can still have drop glue, consider \
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 708cd56e068..bceb5e536e7 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1175,9 +1175,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
         impl<'a, 'tcx> ty::fold::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> {
             type BreakTy = Ty<'tcx>;
-            fn tcx_for_anon_const_substs(&self) -> Option<TyCtxt<'tcx>> {
-                Some(self.cx.tcx)
-            }
 
             fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
                 match ty.kind() {
@@ -1337,14 +1334,15 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
             let layout = match cx.layout_of(ty) {
                 Ok(layout) => layout,
                 Err(
-                    ty::layout::LayoutError::Unknown(_) | ty::layout::LayoutError::SizeOverflow(_),
+                    ty::layout::LayoutError::Unknown(_)
+                    | ty::layout::LayoutError::SizeOverflow(_)
+                    | ty::layout::LayoutError::NormalizationFailure(_, _),
                 ) => return,
             };
-            let (variants, tag) = match layout.variants {
-                Variants::Multiple {
+            let Variants::Multiple {
                     tag_encoding: TagEncoding::Direct, tag, ref variants, ..
-                } => (variants, tag),
-                _ => return,
+                } = &layout.variants else {
+                return
             };
 
             let tag_size = tag.value.size(&cx.tcx).bytes();
@@ -1466,7 +1464,7 @@ impl InvalidAtomicOrdering {
             sym::AtomicI128,
         ];
         if_chain! {
-            if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind;
+            if let ExprKind::MethodCall(ref method_path, args, _) = &expr.kind;
             if recognized_names.contains(&method_path.ident.name);
             if let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
             if let Some(impl_did) = cx.tcx.impl_of_method(m_def_id);
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index da1edcf6fe3..755e24d5413 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -169,7 +169,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
         }
 
         if !(type_permits_lack_of_use || fn_warned || op_warned) {
-            cx.struct_span_lint(UNUSED_RESULTS, s.span, |lint| lint.build("unused result").emit());
+            cx.struct_span_lint(UNUSED_RESULTS, s.span, |lint| {
+                lint.build(&format!("unused result of type `{}`", ty)).emit()
+            });
         }
 
         // Returns whether an error has been emitted (and thus another does not need to be later).
@@ -313,7 +315,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                         let mut err = lint.build(&msg);
                         // check for #[must_use = "..."]
                         if let Some(note) = attr.value_str() {
-                            err.note(&note.as_str());
+                            err.note(note.as_str());
                         }
                         err.emit();
                     });
@@ -476,8 +478,11 @@ trait UnusedDelimLint {
 
         lhs_needs_parens
             || (followed_by_block
-                && match inner.kind {
+                && match &inner.kind {
                     ExprKind::Ret(_) | ExprKind::Break(..) | ExprKind::Yield(..) => true,
+                    ExprKind::Range(_lhs, Some(rhs), _limits) => {
+                        matches!(rhs.kind, ExprKind::Block(..))
+                    }
                     _ => parser::contains_exterior_struct_lit(&inner),
                 })
     }