about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-11-01 12:18:36 +0000
committerbors <bors@rust-lang.org>2023-11-01 12:18:36 +0000
commit11cd1f00268061dff447bc0df3d679b35d460875 (patch)
tree11646a3a20b2cc806709e1294a0648f9b0a34372
parentd1611e39c43e26333ba989cedbd63117be798741 (diff)
parent515fdbf687e7191b70a5541fe4b43a2351e4f15b (diff)
downloadrust-11cd1f00268061dff447bc0df3d679b35d460875.tar.gz
rust-11cd1f00268061dff447bc0df3d679b35d460875.zip
Auto merge of #117482 - matthiaskrgr:rollup-doc6jgm, r=matthiaskrgr
Rollup of 4 pull requests

Successful merges:

 - #115626 (Clean up unchecked_math, separate out unchecked_shifts)
 - #117397 (Don't emit delayed good-path bugs on panic)
 - #117401 (Refactor: move suggestion functions from demand to suggestions)
 - #117475 (Inline and remove `create_session`.)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_errors/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs1276
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs1269
-rw-r--r--compiler/rustc_interface/src/interface.rs64
-rw-r--r--compiler/rustc_interface/src/util.rs87
-rw-r--r--library/core/src/lib.rs3
-rw-r--r--library/core/src/num/int_macros.rs43
-rw-r--r--library/core/src/num/uint_macros.rs18
-rw-r--r--src/tools/miri/tests/fail/intrinsics/unchecked_shl.rs2
-rw-r--r--src/tools/miri/tests/fail/intrinsics/unchecked_shr.rs2
-rw-r--r--tests/codegen/unchecked_shifts.rs2
-rw-r--r--tests/mir-opt/inline/unchecked_shifts.rs2
12 files changed, 1376 insertions, 1394 deletions
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 5e3fdf170bc..dd462cc6486 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -556,7 +556,7 @@ impl Drop for HandlerInner {
         // instead of "require some error happened". Sadly that isn't ideal, as
         // lints can be `#[allow]`'d, potentially leading to this triggering.
         // Also, "good path" should be replaced with a better naming.
-        if !self.has_any_message() && !self.suppressed_expected_diag {
+        if !self.has_any_message() && !self.suppressed_expected_diag && !std::thread::panicking() {
             let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new());
             self.flush_delayed(
                 bugs,
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index abb83228030..acaa3e02f09 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -1,30 +1,21 @@
 use crate::FnCtxt;
-use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::MultiSpan;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, Res};
+use rustc_hir::def::Res;
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::lang_items::LangItem;
-use rustc_hir::{is_range_literal, Node};
 use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
-use rustc_middle::lint::in_external_macro;
-use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeFoldable, TypeVisitableExt};
+use rustc_middle::ty::{self, AssocItem, Ty, TypeFoldable, TypeVisitableExt};
 use rustc_span::symbol::sym;
-use rustc_span::{BytePos, Span, DUMMY_SP};
-use rustc_trait_selection::infer::InferCtxtExt as _;
+use rustc_span::{Span, DUMMY_SP};
 use rustc_trait_selection::traits::ObligationCause;
 
 use super::method::probe;
 
-use std::cmp::min;
-use std::iter;
-
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn emit_type_mismatch_suggestions(
         &self,
@@ -955,301 +946,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
     }
 
-    pub(crate) fn suggest_coercing_result_via_try_operator(
-        &self,
-        err: &mut Diagnostic,
-        expr: &hir::Expr<'tcx>,
-        expected: Ty<'tcx>,
-        found: Ty<'tcx>,
-    ) -> bool {
-        let map = self.tcx.hir();
-        let returned = matches!(
-            map.find_parent(expr.hir_id),
-            Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }))
-        ) || map.get_return_block(expr.hir_id).is_some();
-        if returned
-            && let ty::Adt(e, args_e) = expected.kind()
-            && let ty::Adt(f, args_f) = found.kind()
-            && e.did() == f.did()
-            && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
-            && let e_ok = args_e.type_at(0)
-            && let f_ok = args_f.type_at(0)
-            && self.infcx.can_eq(self.param_env, f_ok, e_ok)
-            && let e_err = args_e.type_at(1)
-            && let f_err = args_f.type_at(1)
-            && self
-                .infcx
-                .type_implements_trait(
-                    self.tcx.get_diagnostic_item(sym::Into).unwrap(),
-                    [f_err, e_err],
-                    self.param_env,
-                )
-                .must_apply_modulo_regions()
-        {
-            err.multipart_suggestion(
-                "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
-                 in `Ok` so the expression remains of type `Result`",
-                vec![
-                    (expr.span.shrink_to_lo(), "Ok(".to_string()),
-                    (expr.span.shrink_to_hi(), "?)".to_string()),
-                ],
-                Applicability::MaybeIncorrect,
-            );
-            return true;
-        }
-        false
-    }
-
-    /// If the expected type is an enum (Issue #55250) with any variants whose
-    /// sole field is of the found type, suggest such variants. (Issue #42764)
-    fn suggest_compatible_variants(
-        &self,
-        err: &mut Diagnostic,
-        expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
-        expr_ty: Ty<'tcx>,
-    ) -> bool {
-        if in_external_macro(self.tcx.sess, expr.span) {
-            return false;
-        }
-        if let ty::Adt(expected_adt, args) = expected.kind() {
-            if let hir::ExprKind::Field(base, ident) = expr.kind {
-                let base_ty = self.typeck_results.borrow().expr_ty(base);
-                if self.can_eq(self.param_env, base_ty, expected)
-                    && let Some(base_span) = base.span.find_ancestor_inside(expr.span)
-                {
-                    err.span_suggestion_verbose(
-                        expr.span.with_lo(base_span.hi()),
-                        format!("consider removing the tuple struct field `{ident}`"),
-                        "",
-                        Applicability::MaybeIncorrect,
-                    );
-                    return true;
-                }
-            }
-
-            // If the expression is of type () and it's the return expression of a block,
-            // we suggest adding a separate return expression instead.
-            // (To avoid things like suggesting `Ok(while .. { .. })`.)
-            if expr_ty.is_unit() {
-                let mut id = expr.hir_id;
-                let mut parent;
-
-                // Unroll desugaring, to make sure this works for `for` loops etc.
-                loop {
-                    parent = self.tcx.hir().parent_id(id);
-                    if let Some(parent_span) = self.tcx.hir().opt_span(parent) {
-                        if parent_span.find_ancestor_inside(expr.span).is_some() {
-                            // The parent node is part of the same span, so is the result of the
-                            // same expansion/desugaring and not the 'real' parent node.
-                            id = parent;
-                            continue;
-                        }
-                    }
-                    break;
-                }
-
-                if let Some(hir::Node::Block(&hir::Block {
-                    span: block_span, expr: Some(e), ..
-                })) = self.tcx.hir().find(parent)
-                {
-                    if e.hir_id == id {
-                        if let Some(span) = expr.span.find_ancestor_inside(block_span) {
-                            let return_suggestions = if self
-                                .tcx
-                                .is_diagnostic_item(sym::Result, expected_adt.did())
-                            {
-                                vec!["Ok(())"]
-                            } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
-                                vec!["None", "Some(())"]
-                            } else {
-                                return false;
-                            };
-                            if let Some(indent) =
-                                self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
-                            {
-                                // Add a semicolon, except after `}`.
-                                let semicolon =
-                                    match self.tcx.sess.source_map().span_to_snippet(span) {
-                                        Ok(s) if s.ends_with('}') => "",
-                                        _ => ";",
-                                    };
-                                err.span_suggestions(
-                                    span.shrink_to_hi(),
-                                    "try adding an expression at the end of the block",
-                                    return_suggestions
-                                        .into_iter()
-                                        .map(|r| format!("{semicolon}\n{indent}{r}")),
-                                    Applicability::MaybeIncorrect,
-                                );
-                            }
-                            return true;
-                        }
-                    }
-                }
-            }
-
-            let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
-                .variants()
-                .iter()
-                .filter(|variant| {
-                    variant.fields.len() == 1
-                })
-                .filter_map(|variant| {
-                    let sole_field = &variant.single_field();
-
-                    let field_is_local = sole_field.did.is_local();
-                    let field_is_accessible =
-                        sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
-                        // Skip suggestions for unstable public fields (for example `Pin::pointer`)
-                        && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
-
-                    if !field_is_local && !field_is_accessible {
-                        return None;
-                    }
-
-                    let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
-                        .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
-
-                    let sole_field_ty = sole_field.ty(self.tcx, args);
-                    if self.can_coerce(expr_ty, sole_field_ty) {
-                        let variant_path =
-                            with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
-                        // FIXME #56861: DRYer prelude filtering
-                        if let Some(path) = variant_path.strip_prefix("std::prelude::")
-                            && let Some((_, path)) = path.split_once("::")
-                        {
-                            return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy));
-                        }
-                        Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy))
-                    } else {
-                        None
-                    }
-                })
-                .collect();
-
-            let suggestions_for = |variant: &_, ctor_kind, field_name| {
-                let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
-                    Some(ident) => format!("{ident}: "),
-                    None => String::new(),
-                };
-
-                let (open, close) = match ctor_kind {
-                    Some(CtorKind::Fn) => ("(".to_owned(), ")"),
-                    None => (format!(" {{ {field_name}: "), " }"),
-
-                    // unit variants don't have fields
-                    Some(CtorKind::Const) => unreachable!(),
-                };
-
-                // Suggest constructor as deep into the block tree as possible.
-                // This fixes https://github.com/rust-lang/rust/issues/101065,
-                // and also just helps make the most minimal suggestions.
-                let mut expr = expr;
-                while let hir::ExprKind::Block(block, _) = &expr.kind
-                    && let Some(expr_) = &block.expr
-                {
-                    expr = expr_
-                }
-
-                vec![
-                    (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
-                    (expr.span.shrink_to_hi(), close.to_owned()),
-                ]
-            };
-
-            match &compatible_variants[..] {
-                [] => { /* No variants to format */ }
-                [(variant, ctor_kind, field_name, note)] => {
-                    // Just a single matching variant.
-                    err.multipart_suggestion_verbose(
-                        format!(
-                            "try wrapping the expression in `{variant}`{note}",
-                            note = note.as_deref().unwrap_or("")
-                        ),
-                        suggestions_for(&**variant, *ctor_kind, *field_name),
-                        Applicability::MaybeIncorrect,
-                    );
-                    return true;
-                }
-                _ => {
-                    // More than one matching variant.
-                    err.multipart_suggestions(
-                        format!(
-                            "try wrapping the expression in a variant of `{}`",
-                            self.tcx.def_path_str(expected_adt.did())
-                        ),
-                        compatible_variants.into_iter().map(
-                            |(variant, ctor_kind, field_name, _)| {
-                                suggestions_for(&variant, ctor_kind, field_name)
-                            },
-                        ),
-                        Applicability::MaybeIncorrect,
-                    );
-                    return true;
-                }
-            }
-        }
-
-        false
-    }
-
-    fn suggest_non_zero_new_unwrap(
-        &self,
-        err: &mut Diagnostic,
-        expr: &hir::Expr<'_>,
-        expected: Ty<'tcx>,
-        expr_ty: Ty<'tcx>,
-    ) -> bool {
-        let tcx = self.tcx;
-        let (adt, unwrap) = match expected.kind() {
-            // In case Option<NonZero*> is wanted, but * is provided, suggest calling new
-            ty::Adt(adt, args) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
-                // Unwrap option
-                let ty::Adt(adt, _) = args.type_at(0).kind() else {
-                    return false;
-                };
-
-                (adt, "")
-            }
-            // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types
-            ty::Adt(adt, _) => (adt, ".unwrap()"),
-            _ => return false,
-        };
-
-        let map = [
-            (sym::NonZeroU8, tcx.types.u8),
-            (sym::NonZeroU16, tcx.types.u16),
-            (sym::NonZeroU32, tcx.types.u32),
-            (sym::NonZeroU64, tcx.types.u64),
-            (sym::NonZeroU128, tcx.types.u128),
-            (sym::NonZeroI8, tcx.types.i8),
-            (sym::NonZeroI16, tcx.types.i16),
-            (sym::NonZeroI32, tcx.types.i32),
-            (sym::NonZeroI64, tcx.types.i64),
-            (sym::NonZeroI128, tcx.types.i128),
-        ];
-
-        let Some((s, _)) = map.iter().find(|&&(s, t)| {
-            self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t)
-        }) else {
-            return false;
-        };
-
-        let path = self.tcx.def_path_str(adt.non_enum_variant().def_id);
-
-        err.multipart_suggestion(
-            format!("consider calling `{s}::new`"),
-            vec![
-                (expr.span.shrink_to_lo(), format!("{path}::new(")),
-                (expr.span.shrink_to_hi(), format!("){unwrap}")),
-            ],
-            Applicability::MaybeIncorrect,
-        );
-
-        true
-    }
-
     pub fn get_conversion_methods(
         &self,
         span: Span,
@@ -1296,82 +992,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    /// Identify some cases where `as_ref()` would be appropriate and suggest it.
-    ///
-    /// Given the following code:
-    /// ```compile_fail,E0308
-    /// struct Foo;
-    /// fn takes_ref(_: &Foo) {}
-    /// let ref opt = Some(Foo);
-    ///
-    /// opt.map(|param| takes_ref(param));
-    /// ```
-    /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead.
-    ///
-    /// It only checks for `Option` and `Result` and won't work with
-    /// ```ignore (illustrative)
-    /// opt.map(|param| { takes_ref(param) });
-    /// ```
-    fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> {
-        let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind else {
-            return None;
-        };
-
-        let hir::def::Res::Local(local_id) = path.res else {
-            return None;
-        };
-
-        let local_parent = self.tcx.hir().parent_id(local_id);
-        let Some(Node::Param(hir::Param { hir_id: param_hir_id, .. })) =
-            self.tcx.hir().find(local_parent)
-        else {
-            return None;
-        };
-
-        let param_parent = self.tcx.hir().parent_id(*param_hir_id);
-        let Some(Node::Expr(hir::Expr {
-            hir_id: expr_hir_id,
-            kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
-            ..
-        })) = self.tcx.hir().find(param_parent)
-        else {
-            return None;
-        };
-
-        let expr_parent = self.tcx.hir().parent_id(*expr_hir_id);
-        let hir = self.tcx.hir().find(expr_parent);
-        let closure_params_len = closure_fn_decl.inputs.len();
-        let (
-            Some(Node::Expr(hir::Expr {
-                kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
-                ..
-            })),
-            1,
-        ) = (hir, closure_params_len)
-        else {
-            return None;
-        };
-
-        let self_ty = self.typeck_results.borrow().expr_ty(receiver);
-        let name = method_path.ident.name;
-        let is_as_ref_able = match self_ty.peel_refs().kind() {
-            ty::Adt(def, _) => {
-                (self.tcx.is_diagnostic_item(sym::Option, def.did())
-                    || self.tcx.is_diagnostic_item(sym::Result, def.did()))
-                    && (name == sym::map || name == sym::and_then)
-            }
-            _ => false,
-        };
-        if is_as_ref_able {
-            Some((
-                vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())],
-                "consider using `as_ref` instead",
-            ))
-        } else {
-            None
-        }
-    }
-
     /// If the given `HirId` corresponds to a block with a trailing expression, return that expression
     pub(crate) fn maybe_get_block_expr(
         &self,
@@ -1383,21 +1003,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    /// Returns whether the given expression is an `else if`.
-    pub(crate) fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
-        if let hir::ExprKind::If(..) = expr.kind {
-            let parent_id = self.tcx.hir().parent_id(expr.hir_id);
-            if let Some(Node::Expr(hir::Expr {
-                kind: hir::ExprKind::If(_, _, Some(else_expr)),
-                ..
-            })) = self.tcx.hir().find(parent_id)
-            {
-                return else_expr.hir_id == expr.hir_id;
-            }
-        }
-        false
-    }
-
     // Returns whether the given expression is a destruct assignment desugaring.
     // For example, `(a, b) = (1, &2);`
     // Here we try to find the pattern binding of the expression,
@@ -1422,881 +1027,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         return false;
     }
 
-    /// This function is used to determine potential "simple" improvements or users' errors and
-    /// provide them useful help. For example:
-    ///
-    /// ```compile_fail,E0308
-    /// fn some_fn(s: &str) {}
-    ///
-    /// let x = "hey!".to_owned();
-    /// some_fn(x); // error
-    /// ```
-    ///
-    /// No need to find every potential function which could make a coercion to transform a
-    /// `String` into a `&str` since a `&` would do the trick!
-    ///
-    /// In addition of this check, it also checks between references mutability state. If the
-    /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
-    /// `&mut`!".
-    pub fn suggest_deref_or_ref(
-        &self,
-        expr: &hir::Expr<'tcx>,
-        checked_ty: Ty<'tcx>,
-        expected: Ty<'tcx>,
-    ) -> Option<(
-        Vec<(Span, String)>,
-        String,
-        Applicability,
-        bool, /* verbose */
-        bool, /* suggest `&` or `&mut` type annotation */
-    )> {
-        let sess = self.sess();
-        let sp = expr.span;
-
-        // If the span is from an external macro, there's no suggestion we can make.
-        if in_external_macro(sess, sp) {
-            return None;
-        }
-
-        let sm = sess.source_map();
-
-        let replace_prefix = |s: &str, old: &str, new: &str| {
-            s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
-        };
-
-        // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
-        let expr = expr.peel_drop_temps();
-
-        match (&expr.kind, expected.kind(), checked_ty.kind()) {
-            (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
-                (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
-                    if let hir::ExprKind::Lit(_) = expr.kind
-                        && let Ok(src) = sm.span_to_snippet(sp)
-                        && replace_prefix(&src, "b\"", "\"").is_some()
-                    {
-                        let pos = sp.lo() + BytePos(1);
-                        return Some((
-                            vec![(sp.with_hi(pos), String::new())],
-                            "consider removing the leading `b`".to_string(),
-                            Applicability::MachineApplicable,
-                            true,
-                            false,
-                        ));
-                    }
-                }
-                (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
-                    if let hir::ExprKind::Lit(_) = expr.kind
-                        && let Ok(src) = sm.span_to_snippet(sp)
-                        && replace_prefix(&src, "\"", "b\"").is_some()
-                    {
-                        return Some((
-                            vec![(sp.shrink_to_lo(), "b".to_string())],
-                            "consider adding a leading `b`".to_string(),
-                            Applicability::MachineApplicable,
-                            true,
-                            false,
-                        ));
-                    }
-                }
-                _ => {}
-            },
-            (_, &ty::Ref(_, _, mutability), _) => {
-                // Check if it can work when put into a ref. For example:
-                //
-                // ```
-                // fn bar(x: &mut i32) {}
-                //
-                // let x = 0u32;
-                // bar(&x); // error, expected &mut
-                // ```
-                let ref_ty = match mutability {
-                    hir::Mutability::Mut => {
-                        Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
-                    }
-                    hir::Mutability::Not => {
-                        Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
-                    }
-                };
-                if self.can_coerce(ref_ty, expected) {
-                    let mut sugg_sp = sp;
-                    if let hir::ExprKind::MethodCall(ref segment, receiver, args, _) = expr.kind {
-                        let clone_trait =
-                            self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span));
-                        if args.is_empty()
-                            && self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
-                                |did| {
-                                    let ai = self.tcx.associated_item(did);
-                                    ai.trait_container(self.tcx) == Some(clone_trait)
-                                },
-                            ) == Some(true)
-                            && segment.ident.name == sym::clone
-                        {
-                            // If this expression had a clone call when suggesting borrowing
-                            // we want to suggest removing it because it'd now be unnecessary.
-                            sugg_sp = receiver.span;
-                        }
-                    }
-
-                    if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) = expr.kind
-                        && let Some(1) = self.deref_steps(expected, checked_ty)
-                    {
-                        // We have `*&T`, check if what was expected was `&T`.
-                        // If so, we may want to suggest removing a `*`.
-                        sugg_sp = sugg_sp.with_hi(inner.span.lo());
-                        return Some((
-                            vec![(sugg_sp, String::new())],
-                            "consider removing deref here".to_string(),
-                            Applicability::MachineApplicable,
-                            true,
-                            false,
-                        ));
-                    }
-
-                    let needs_parens = match expr.kind {
-                        // parenthesize if needed (Issue #46756)
-                        hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
-                        // parenthesize borrows of range literals (Issue #54505)
-                        _ if is_range_literal(expr) => true,
-                        _ => false,
-                    };
-
-                    if let Some((sugg, msg)) = self.can_use_as_ref(expr) {
-                        return Some((
-                            sugg,
-                            msg.to_string(),
-                            Applicability::MachineApplicable,
-                            true,
-                            false,
-                        ));
-                    }
-
-                    let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr)
-                    {
-                        Some(ident) => format!("{ident}: "),
-                        None => String::new(),
-                    };
-
-                    if let Some(hir::Node::Expr(hir::Expr {
-                        kind: hir::ExprKind::Assign(..),
-                        ..
-                    })) = self.tcx.hir().find_parent(expr.hir_id)
-                    {
-                        if mutability.is_mut() {
-                            // Suppressing this diagnostic, we'll properly print it in `check_expr_assign`
-                            return None;
-                        }
-                    }
-
-                    let sugg = mutability.ref_prefix_str();
-                    let (sugg, verbose) = if needs_parens {
-                        (
-                            vec![
-                                (sp.shrink_to_lo(), format!("{prefix}{sugg}(")),
-                                (sp.shrink_to_hi(), ")".to_string()),
-                            ],
-                            false,
-                        )
-                    } else {
-                        (vec![(sp.shrink_to_lo(), format!("{prefix}{sugg}"))], true)
-                    };
-                    return Some((
-                        sugg,
-                        format!("consider {}borrowing here", mutability.mutably_str()),
-                        Applicability::MachineApplicable,
-                        verbose,
-                        false,
-                    ));
-                }
-            }
-            (
-                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr),
-                _,
-                &ty::Ref(_, checked, _),
-            ) if self.can_sub(self.param_env, checked, expected) => {
-                let make_sugg = |start: Span, end: BytePos| {
-                    // skip `(` for tuples such as `(c) = (&123)`.
-                    // make sure we won't suggest like `(c) = 123)` which is incorrect.
-                    let sp = sm
-                        .span_extend_while(start.shrink_to_lo(), |c| c == '(' || c.is_whitespace())
-                        .map_or(start, |s| s.shrink_to_hi());
-                    Some((
-                        vec![(sp.with_hi(end), String::new())],
-                        "consider removing the borrow".to_string(),
-                        Applicability::MachineApplicable,
-                        true,
-                        true,
-                    ))
-                };
-
-                // We have `&T`, check if what was expected was `T`. If so,
-                // we may want to suggest removing a `&`.
-                if sm.is_imported(expr.span) {
-                    // Go through the spans from which this span was expanded,
-                    // and find the one that's pointing inside `sp`.
-                    //
-                    // E.g. for `&format!("")`, where we want the span to the
-                    // `format!()` invocation instead of its expansion.
-                    if let Some(call_span) =
-                        iter::successors(Some(expr.span), |s| s.parent_callsite())
-                            .find(|&s| sp.contains(s))
-                        && sm.is_span_accessible(call_span)
-                    {
-                        return make_sugg(sp, call_span.lo());
-                    }
-                    return None;
-                }
-                if sp.contains(expr.span) && sm.is_span_accessible(expr.span) {
-                    return make_sugg(sp, expr.span.lo());
-                }
-            }
-            (
-                _,
-                &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }),
-                &ty::Ref(_, ty_a, mutbl_a),
-            ) => {
-                if let Some(steps) = self.deref_steps(ty_a, ty_b)
-                    // Only suggest valid if dereferencing needed.
-                    && steps > 0
-                    // The pointer type implements `Copy` trait so the suggestion is always valid.
-                    && let Ok(src) = sm.span_to_snippet(sp)
-                {
-                    let derefs = "*".repeat(steps);
-                    let old_prefix = mutbl_a.ref_prefix_str();
-                    let new_prefix = mutbl_b.ref_prefix_str().to_owned() + &derefs;
-
-                    let suggestion = replace_prefix(&src, old_prefix, &new_prefix).map(|_| {
-                        // skip `&` or `&mut ` if both mutabilities are mutable
-                        let lo = sp.lo()
-                            + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _);
-                        // skip `&` or `&mut `
-                        let hi = sp.lo() + BytePos(old_prefix.len() as _);
-                        let sp = sp.with_lo(lo).with_hi(hi);
-
-                        (
-                            sp,
-                            format!(
-                                "{}{derefs}",
-                                if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }
-                            ),
-                            if mutbl_b <= mutbl_a {
-                                Applicability::MachineApplicable
-                            } else {
-                                Applicability::MaybeIncorrect
-                            },
-                        )
-                    });
-
-                    if let Some((span, src, applicability)) = suggestion {
-                        return Some((
-                            vec![(span, src)],
-                            "consider dereferencing".to_string(),
-                            applicability,
-                            true,
-                            false,
-                        ));
-                    }
-                }
-            }
-            _ if sp == expr.span => {
-                if let Some(mut steps) = self.deref_steps(checked_ty, expected) {
-                    let mut expr = expr.peel_blocks();
-                    let mut prefix_span = expr.span.shrink_to_lo();
-                    let mut remove = String::new();
-
-                    // Try peeling off any existing `&` and `&mut` to reach our target type
-                    while steps > 0 {
-                        if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
-                            // If the expression has `&`, removing it would fix the error
-                            prefix_span = prefix_span.with_hi(inner.span.lo());
-                            expr = inner;
-                            remove.push_str(mutbl.ref_prefix_str());
-                            steps -= 1;
-                        } else {
-                            break;
-                        }
-                    }
-                    // If we've reached our target type with just removing `&`, then just print now.
-                    if steps == 0 && !remove.trim().is_empty() {
-                        return Some((
-                            vec![(prefix_span, String::new())],
-                            format!("consider removing the `{}`", remove.trim()),
-                            // Do not remove `&&` to get to bool, because it might be something like
-                            // { a } && b, which we have a separate fixup suggestion that is more
-                            // likely correct...
-                            if remove.trim() == "&&" && expected == self.tcx.types.bool {
-                                Applicability::MaybeIncorrect
-                            } else {
-                                Applicability::MachineApplicable
-                            },
-                            true,
-                            false,
-                        ));
-                    }
-
-                    // For this suggestion to make sense, the type would need to be `Copy`,
-                    // or we have to be moving out of a `Box<T>`
-                    if self.type_is_copy_modulo_regions(self.param_env, expected)
-                        // FIXME(compiler-errors): We can actually do this if the checked_ty is
-                        // `steps` layers of boxes, not just one, but this is easier and most likely.
-                        || (checked_ty.is_box() && steps == 1)
-                        // We can always deref a binop that takes its arguments by ref.
-                        || matches!(
-                            self.tcx.hir().get_parent(expr.hir_id),
-                            hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, ..), .. })
-                                if !op.node.is_by_value()
-                        )
-                    {
-                        let deref_kind = if checked_ty.is_box() {
-                            "unboxing the value"
-                        } else if checked_ty.is_ref() {
-                            "dereferencing the borrow"
-                        } else {
-                            "dereferencing the type"
-                        };
-
-                        // Suggest removing `&` if we have removed any, otherwise suggest just
-                        // dereferencing the remaining number of steps.
-                        let message = if remove.is_empty() {
-                            format!("consider {deref_kind}")
-                        } else {
-                            format!(
-                                "consider removing the `{}` and {} instead",
-                                remove.trim(),
-                                deref_kind
-                            )
-                        };
-
-                        let prefix =
-                            match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
-                                Some(ident) => format!("{ident}: "),
-                                None => String::new(),
-                            };
-
-                        let (span, suggestion) = if self.is_else_if_block(expr) {
-                            // Don't suggest nonsense like `else *if`
-                            return None;
-                        } else if let Some(expr) = self.maybe_get_block_expr(expr) {
-                            // prefix should be empty here..
-                            (expr.span.shrink_to_lo(), "*".to_string())
-                        } else {
-                            (prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
-                        };
-                        if suggestion.trim().is_empty() {
-                            return None;
-                        }
-
-                        return Some((
-                            vec![(span, suggestion)],
-                            message,
-                            Applicability::MachineApplicable,
-                            true,
-                            false,
-                        ));
-                    }
-                }
-            }
-            _ => {}
-        }
-        None
-    }
-
-    pub fn suggest_cast(
-        &self,
-        err: &mut Diagnostic,
-        expr: &hir::Expr<'_>,
-        checked_ty: Ty<'tcx>,
-        expected_ty: Ty<'tcx>,
-        expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
-    ) -> bool {
-        if self.tcx.sess.source_map().is_imported(expr.span) {
-            // Ignore if span is from within a macro.
-            return false;
-        }
-
-        let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) else {
-            return false;
-        };
-
-        // If casting this expression to a given numeric type would be appropriate in case of a type
-        // mismatch.
-        //
-        // We want to minimize the amount of casting operations that are suggested, as it can be a
-        // lossy operation with potentially bad side effects, so we only suggest when encountering
-        // an expression that indicates that the original type couldn't be directly changed.
-        //
-        // For now, don't suggest casting with `as`.
-        let can_cast = false;
-
-        let mut sugg = vec![];
-
-        if let Some(hir::Node::ExprField(field)) = self.tcx.hir().find_parent(expr.hir_id) {
-            // `expr` is a literal field for a struct, only suggest if appropriate
-            if field.is_shorthand {
-                // This is a field literal
-                sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
-            } else {
-                // Likely a field was meant, but this field wasn't found. Do not suggest anything.
-                return false;
-            }
-        };
-
-        if let hir::ExprKind::Call(path, args) = &expr.kind
-            && let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
-                (&path.kind, args.len())
-            // `expr` is a conversion like `u32::from(val)`, do not suggest anything (#63697).
-            && let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
-                (&base_ty.kind, path_segment.ident.name)
-        {
-            if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
-                match ident.name {
-                    sym::i128
-                    | sym::i64
-                    | sym::i32
-                    | sym::i16
-                    | sym::i8
-                    | sym::u128
-                    | sym::u64
-                    | sym::u32
-                    | sym::u16
-                    | sym::u8
-                    | sym::isize
-                    | sym::usize
-                        if base_ty_path.segments.len() == 1 =>
-                    {
-                        return false;
-                    }
-                    _ => {}
-                }
-            }
-        }
-
-        let msg = format!(
-            "you can convert {} `{}` to {} `{}`",
-            checked_ty.kind().article(),
-            checked_ty,
-            expected_ty.kind().article(),
-            expected_ty,
-        );
-        let cast_msg = format!(
-            "you can cast {} `{}` to {} `{}`",
-            checked_ty.kind().article(),
-            checked_ty,
-            expected_ty.kind().article(),
-            expected_ty,
-        );
-        let lit_msg = format!(
-            "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
-        );
-
-        let close_paren = if expr.precedence().order() < PREC_POSTFIX {
-            sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
-            ")"
-        } else {
-            ""
-        };
-
-        let mut cast_suggestion = sugg.clone();
-        cast_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren} as {expected_ty}")));
-        let mut into_suggestion = sugg.clone();
-        into_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren}.into()")));
-        let mut suffix_suggestion = sugg.clone();
-        suffix_suggestion.push((
-            if matches!(
-                (&expected_ty.kind(), &checked_ty.kind()),
-                (ty::Int(_) | ty::Uint(_), ty::Float(_))
-            ) {
-                // Remove fractional part from literal, for example `42.0f32` into `42`
-                let src = src.trim_end_matches(&checked_ty.to_string());
-                let len = src.split('.').next().unwrap().len();
-                expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
-            } else {
-                let len = src.trim_end_matches(&checked_ty.to_string()).len();
-                expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
-            },
-            if expr.precedence().order() < PREC_POSTFIX {
-                // Readd `)`
-                format!("{expected_ty})")
-            } else {
-                expected_ty.to_string()
-            },
-        ));
-        let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
-            if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
-        };
-        let is_negative_int =
-            |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..));
-        let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
-
-        let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);
-
-        let suggest_fallible_into_or_lhs_from =
-            |err: &mut Diagnostic, exp_to_found_is_fallible: bool| {
-                // If we know the expression the expected type is derived from, we might be able
-                // to suggest a widening conversion rather than a narrowing one (which may
-                // panic). For example, given x: u8 and y: u32, if we know the span of "x",
-                //   x > y
-                // can be given the suggestion "u32::from(x) > y" rather than
-                // "x > y.try_into().unwrap()".
-                let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
-                    self.tcx
-                        .sess
-                        .source_map()
-                        .span_to_snippet(expr.span)
-                        .ok()
-                        .map(|src| (expr, src))
-                });
-                let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
-                    (lhs_expr_and_src, exp_to_found_is_fallible)
-                {
-                    let msg = format!(
-                        "you can convert `{lhs_src}` from `{expected_ty}` to `{checked_ty}`, matching the type of `{src}`",
-                    );
-                    let suggestion = vec![
-                        (lhs_expr.span.shrink_to_lo(), format!("{checked_ty}::from(")),
-                        (lhs_expr.span.shrink_to_hi(), ")".to_string()),
-                    ];
-                    (msg, suggestion)
-                } else {
-                    let msg =
-                        format!("{} and panic if the converted value doesn't fit", msg.clone());
-                    let mut suggestion = sugg.clone();
-                    suggestion.push((
-                        expr.span.shrink_to_hi(),
-                        format!("{close_paren}.try_into().unwrap()"),
-                    ));
-                    (msg, suggestion)
-                };
-                err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
-            };
-
-        let suggest_to_change_suffix_or_into =
-            |err: &mut Diagnostic,
-             found_to_exp_is_fallible: bool,
-             exp_to_found_is_fallible: bool| {
-                let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir().is_lhs(e.hir_id));
-
-                if exp_is_lhs {
-                    return;
-                }
-
-                let always_fallible = found_to_exp_is_fallible
-                    && (exp_to_found_is_fallible || expected_ty_expr.is_none());
-                let msg = if literal_is_ty_suffixed(expr) {
-                    lit_msg.clone()
-                } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
-                    // We now know that converting either the lhs or rhs is fallible. Before we
-                    // suggest a fallible conversion, check if the value can never fit in the
-                    // expected type.
-                    let msg = format!("`{src}` cannot fit into type `{expected_ty}`");
-                    err.note(msg);
-                    return;
-                } else if in_const_context {
-                    // Do not recommend `into` or `try_into` in const contexts.
-                    return;
-                } else if found_to_exp_is_fallible {
-                    return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
-                } else {
-                    msg.clone()
-                };
-                let suggestion = if literal_is_ty_suffixed(expr) {
-                    suffix_suggestion.clone()
-                } else {
-                    into_suggestion.clone()
-                };
-                err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
-            };
-
-        match (&expected_ty.kind(), &checked_ty.kind()) {
-            (ty::Int(exp), ty::Int(found)) => {
-                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
-                {
-                    (Some(exp), Some(found)) if exp < found => (true, false),
-                    (Some(exp), Some(found)) if exp > found => (false, true),
-                    (None, Some(8 | 16)) => (false, true),
-                    (Some(8 | 16), None) => (true, false),
-                    (None, _) | (_, None) => (true, true),
-                    _ => (false, false),
-                };
-                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
-                true
-            }
-            (ty::Uint(exp), ty::Uint(found)) => {
-                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
-                {
-                    (Some(exp), Some(found)) if exp < found => (true, false),
-                    (Some(exp), Some(found)) if exp > found => (false, true),
-                    (None, Some(8 | 16)) => (false, true),
-                    (Some(8 | 16), None) => (true, false),
-                    (None, _) | (_, None) => (true, true),
-                    _ => (false, false),
-                };
-                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
-                true
-            }
-            (&ty::Int(exp), &ty::Uint(found)) => {
-                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
-                {
-                    (Some(exp), Some(found)) if found < exp => (false, true),
-                    (None, Some(8)) => (false, true),
-                    _ => (true, true),
-                };
-                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
-                true
-            }
-            (&ty::Uint(exp), &ty::Int(found)) => {
-                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
-                {
-                    (Some(exp), Some(found)) if found > exp => (true, false),
-                    (Some(8), None) => (true, false),
-                    _ => (true, true),
-                };
-                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
-                true
-            }
-            (ty::Float(exp), ty::Float(found)) => {
-                if found.bit_width() < exp.bit_width() {
-                    suggest_to_change_suffix_or_into(err, false, true);
-                } else if literal_is_ty_suffixed(expr) {
-                    err.multipart_suggestion_verbose(
-                        lit_msg,
-                        suffix_suggestion,
-                        Applicability::MachineApplicable,
-                    );
-                } else if can_cast {
-                    // Missing try_into implementation for `f64` to `f32`
-                    err.multipart_suggestion_verbose(
-                        format!("{cast_msg}, producing the closest possible value"),
-                        cast_suggestion,
-                        Applicability::MaybeIncorrect, // lossy conversion
-                    );
-                }
-                true
-            }
-            (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
-                if literal_is_ty_suffixed(expr) {
-                    err.multipart_suggestion_verbose(
-                        lit_msg,
-                        suffix_suggestion,
-                        Applicability::MachineApplicable,
-                    );
-                } else if can_cast {
-                    // Missing try_into implementation for `{float}` to `{integer}`
-                    err.multipart_suggestion_verbose(
-                        format!("{msg}, rounding the float towards zero"),
-                        cast_suggestion,
-                        Applicability::MaybeIncorrect, // lossy conversion
-                    );
-                }
-                true
-            }
-            (ty::Float(exp), ty::Uint(found)) => {
-                // if `found` is `None` (meaning found is `usize`), don't suggest `.into()`
-                if exp.bit_width() > found.bit_width().unwrap_or(256) {
-                    err.multipart_suggestion_verbose(
-                        format!(
-                            "{msg}, producing the floating point representation of the integer",
-                        ),
-                        into_suggestion,
-                        Applicability::MachineApplicable,
-                    );
-                } else if literal_is_ty_suffixed(expr) {
-                    err.multipart_suggestion_verbose(
-                        lit_msg,
-                        suffix_suggestion,
-                        Applicability::MachineApplicable,
-                    );
-                } else {
-                    // Missing try_into implementation for `{integer}` to `{float}`
-                    err.multipart_suggestion_verbose(
-                        format!(
-                            "{cast_msg}, producing the floating point representation of the integer, \
-                                 rounded if necessary",
-                        ),
-                        cast_suggestion,
-                        Applicability::MaybeIncorrect, // lossy conversion
-                    );
-                }
-                true
-            }
-            (ty::Float(exp), ty::Int(found)) => {
-                // if `found` is `None` (meaning found is `isize`), don't suggest `.into()`
-                if exp.bit_width() > found.bit_width().unwrap_or(256) {
-                    err.multipart_suggestion_verbose(
-                        format!(
-                            "{}, producing the floating point representation of the integer",
-                            msg.clone(),
-                        ),
-                        into_suggestion,
-                        Applicability::MachineApplicable,
-                    );
-                } else if literal_is_ty_suffixed(expr) {
-                    err.multipart_suggestion_verbose(
-                        lit_msg,
-                        suffix_suggestion,
-                        Applicability::MachineApplicable,
-                    );
-                } else {
-                    // Missing try_into implementation for `{integer}` to `{float}`
-                    err.multipart_suggestion_verbose(
-                        format!(
-                            "{}, producing the floating point representation of the integer, \
-                                rounded if necessary",
-                            &msg,
-                        ),
-                        cast_suggestion,
-                        Applicability::MaybeIncorrect, // lossy conversion
-                    );
-                }
-                true
-            }
-            (
-                &ty::Uint(ty::UintTy::U32 | ty::UintTy::U64 | ty::UintTy::U128)
-                | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128),
-                &ty::Char,
-            ) => {
-                err.multipart_suggestion_verbose(
-                    format!("{cast_msg}, since a `char` always occupies 4 bytes"),
-                    cast_suggestion,
-                    Applicability::MachineApplicable,
-                );
-                true
-            }
-            _ => false,
-        }
-    }
-
-    /// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
-    pub fn suggest_method_call_on_range_literal(
-        &self,
-        err: &mut Diagnostic,
-        expr: &hir::Expr<'tcx>,
-        checked_ty: Ty<'tcx>,
-        expected_ty: Ty<'tcx>,
-    ) {
-        if !hir::is_range_literal(expr) {
-            return;
-        }
-        let hir::ExprKind::Struct(hir::QPath::LangItem(LangItem::Range, ..), [start, end], _) =
-            expr.kind
-        else {
-            return;
-        };
-        let parent = self.tcx.hir().parent_id(expr.hir_id);
-        if let Some(hir::Node::ExprField(_)) = self.tcx.hir().find(parent) {
-            // Ignore `Foo { field: a..Default::default() }`
-            return;
-        }
-        let mut expr = end.expr;
-        let mut expectation = Some(expected_ty);
-        while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
-            // Getting to the root receiver and asserting it is a fn call let's us ignore cases in
-            // `tests/ui/methods/issues/issue-90315.stderr`.
-            expr = rcvr;
-            // If we have more than one layer of calls, then the expected ty
-            // cannot guide the method probe.
-            expectation = None;
-        }
-        let hir::ExprKind::Call(method_name, _) = expr.kind else {
-            return;
-        };
-        let ty::Adt(adt, _) = checked_ty.kind() else {
-            return;
-        };
-        if self.tcx.lang_items().range_struct() != Some(adt.did()) {
-            return;
-        }
-        if let ty::Adt(adt, _) = expected_ty.kind()
-            && self.tcx.lang_items().range_struct() == Some(adt.did())
-        {
-            return;
-        }
-        // Check if start has method named end.
-        let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else {
-            return;
-        };
-        let [hir::PathSegment { ident, .. }] = p.segments else {
-            return;
-        };
-        let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
-        let Ok(_pick) = self.lookup_probe_for_diagnostic(
-            *ident,
-            self_ty,
-            expr,
-            probe::ProbeScope::AllTraits,
-            expectation,
-        ) else {
-            return;
-        };
-        let mut sugg = ".";
-        let mut span = start.expr.span.between(end.expr.span);
-        if span.lo() + BytePos(2) == span.hi() {
-            // There's no space between the start, the range op and the end, suggest removal which
-            // will be more noticeable than the replacement of `..` with `.`.
-            span = span.with_lo(span.lo() + BytePos(1));
-            sugg = "";
-        }
-        err.span_suggestion_verbose(
-            span,
-            "you likely meant to write a method call instead of a range",
-            sugg,
-            Applicability::MachineApplicable,
-        );
-    }
-
-    /// Identify when the type error is because `()` is found in a binding that was assigned a
-    /// block without a tail expression.
-    fn suggest_return_binding_for_missing_tail_expr(
-        &self,
-        err: &mut Diagnostic,
-        expr: &hir::Expr<'_>,
-        checked_ty: Ty<'tcx>,
-        expected_ty: Ty<'tcx>,
-    ) {
-        if !checked_ty.is_unit() {
-            return;
-        }
-        let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {
-            return;
-        };
-        let hir::def::Res::Local(hir_id) = path.res else {
-            return;
-        };
-        let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else {
-            return;
-        };
-        let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) =
-            self.tcx.hir().find_parent(pat.hir_id)
-        else {
-            return;
-        };
-        let hir::ExprKind::Block(block, None) = init.kind else {
-            return;
-        };
-        if block.expr.is_some() {
-            return;
-        }
-        let [.., stmt] = block.stmts else {
-            err.span_label(block.span, "this empty block is missing a tail expression");
-            return;
-        };
-        let hir::StmtKind::Semi(tail_expr) = stmt.kind else {
-            return;
-        };
-        let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else {
-            return;
-        };
-        if self.can_eq(self.param_env, expected_ty, ty) {
-            err.span_suggestion_short(
-                stmt.span.with_lo(tail_expr.span.hi()),
-                "remove this semicolon",
-                "",
-                Applicability::MachineApplicable,
-            );
-        } else {
-            err.span_label(block.span, "this block is missing a tail expression");
-        }
-    }
-
     fn note_wrong_return_ty_due_to_generic_arg(
         &self,
         err: &mut Diagnostic,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index ca7dc298de6..c43d4932fb9 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -2,7 +2,14 @@ use super::FnCtxt;
 
 use crate::errors;
 use crate::fluent_generated as fluent;
+use crate::fn_ctxt::rustc_span::BytePos;
+use crate::hir::is_range_literal;
+use crate::method::probe;
 use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
+use crate::rustc_middle::ty::Article;
+use crate::ty::TypeAndMut;
+use core::cmp::min;
+use core::iter;
 use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
 use rustc_errors::{Applicability, Diagnostic, MultiSpan};
 use rustc_hir as hir;
@@ -16,6 +23,7 @@ use rustc_hir::{
 use rustc_hir_analysis::astconv::AstConv;
 use rustc_infer::traits::{self, StatementAsExpression};
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{
     self, suggest_constraining_type_params, Binder, IsSuggestable, ToPredicate, Ty,
@@ -1816,4 +1824,1265 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
         return true;
     }
+
+    pub(crate) fn suggest_coercing_result_via_try_operator(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'tcx>,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) -> bool {
+        let map = self.tcx.hir();
+        let returned = matches!(
+            map.find_parent(expr.hir_id),
+            Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }))
+        ) || map.get_return_block(expr.hir_id).is_some();
+        if returned
+            && let ty::Adt(e, args_e) = expected.kind()
+            && let ty::Adt(f, args_f) = found.kind()
+            && e.did() == f.did()
+            && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
+            && let e_ok = args_e.type_at(0)
+            && let f_ok = args_f.type_at(0)
+            && self.infcx.can_eq(self.param_env, f_ok, e_ok)
+            && let e_err = args_e.type_at(1)
+            && let f_err = args_f.type_at(1)
+            && self
+                .infcx
+                .type_implements_trait(
+                    self.tcx.get_diagnostic_item(sym::Into).unwrap(),
+                    [f_err, e_err],
+                    self.param_env,
+                )
+                .must_apply_modulo_regions()
+        {
+            err.multipart_suggestion(
+                "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
+                 in `Ok` so the expression remains of type `Result`",
+                vec![
+                    (expr.span.shrink_to_lo(), "Ok(".to_string()),
+                    (expr.span.shrink_to_hi(), "?)".to_string()),
+                ],
+                Applicability::MaybeIncorrect,
+            );
+            return true;
+        }
+        false
+    }
+
+    /// If the expected type is an enum (Issue #55250) with any variants whose
+    /// sole field is of the found type, suggest such variants. (Issue #42764)
+    pub(crate) fn suggest_compatible_variants(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        expr_ty: Ty<'tcx>,
+    ) -> bool {
+        if in_external_macro(self.tcx.sess, expr.span) {
+            return false;
+        }
+        if let ty::Adt(expected_adt, args) = expected.kind() {
+            if let hir::ExprKind::Field(base, ident) = expr.kind {
+                let base_ty = self.typeck_results.borrow().expr_ty(base);
+                if self.can_eq(self.param_env, base_ty, expected)
+                    && let Some(base_span) = base.span.find_ancestor_inside(expr.span)
+                {
+                    err.span_suggestion_verbose(
+                        expr.span.with_lo(base_span.hi()),
+                        format!("consider removing the tuple struct field `{ident}`"),
+                        "",
+                        Applicability::MaybeIncorrect,
+                    );
+                    return true;
+                }
+            }
+
+            // If the expression is of type () and it's the return expression of a block,
+            // we suggest adding a separate return expression instead.
+            // (To avoid things like suggesting `Ok(while .. { .. })`.)
+            if expr_ty.is_unit() {
+                let mut id = expr.hir_id;
+                let mut parent;
+
+                // Unroll desugaring, to make sure this works for `for` loops etc.
+                loop {
+                    parent = self.tcx.hir().parent_id(id);
+                    if let Some(parent_span) = self.tcx.hir().opt_span(parent) {
+                        if parent_span.find_ancestor_inside(expr.span).is_some() {
+                            // The parent node is part of the same span, so is the result of the
+                            // same expansion/desugaring and not the 'real' parent node.
+                            id = parent;
+                            continue;
+                        }
+                    }
+                    break;
+                }
+
+                if let Some(hir::Node::Block(&hir::Block {
+                    span: block_span, expr: Some(e), ..
+                })) = self.tcx.hir().find(parent)
+                {
+                    if e.hir_id == id {
+                        if let Some(span) = expr.span.find_ancestor_inside(block_span) {
+                            let return_suggestions = if self
+                                .tcx
+                                .is_diagnostic_item(sym::Result, expected_adt.did())
+                            {
+                                vec!["Ok(())"]
+                            } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
+                                vec!["None", "Some(())"]
+                            } else {
+                                return false;
+                            };
+                            if let Some(indent) =
+                                self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
+                            {
+                                // Add a semicolon, except after `}`.
+                                let semicolon =
+                                    match self.tcx.sess.source_map().span_to_snippet(span) {
+                                        Ok(s) if s.ends_with('}') => "",
+                                        _ => ";",
+                                    };
+                                err.span_suggestions(
+                                    span.shrink_to_hi(),
+                                    "try adding an expression at the end of the block",
+                                    return_suggestions
+                                        .into_iter()
+                                        .map(|r| format!("{semicolon}\n{indent}{r}")),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            }
+                            return true;
+                        }
+                    }
+                }
+            }
+
+            let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
+                .variants()
+                .iter()
+                .filter(|variant| {
+                    variant.fields.len() == 1
+                })
+                .filter_map(|variant| {
+                    let sole_field = &variant.single_field();
+
+                    let field_is_local = sole_field.did.is_local();
+                    let field_is_accessible =
+                        sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
+                        // Skip suggestions for unstable public fields (for example `Pin::pointer`)
+                        && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
+
+                    if !field_is_local && !field_is_accessible {
+                        return None;
+                    }
+
+                    let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
+                        .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
+
+                    let sole_field_ty = sole_field.ty(self.tcx, args);
+                    if self.can_coerce(expr_ty, sole_field_ty) {
+                        let variant_path =
+                            with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
+                        // FIXME #56861: DRYer prelude filtering
+                        if let Some(path) = variant_path.strip_prefix("std::prelude::")
+                            && let Some((_, path)) = path.split_once("::")
+                        {
+                            return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy));
+                        }
+                        Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy))
+                    } else {
+                        None
+                    }
+                })
+                .collect();
+
+            let suggestions_for = |variant: &_, ctor_kind, field_name| {
+                let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+                    Some(ident) => format!("{ident}: "),
+                    None => String::new(),
+                };
+
+                let (open, close) = match ctor_kind {
+                    Some(CtorKind::Fn) => ("(".to_owned(), ")"),
+                    None => (format!(" {{ {field_name}: "), " }"),
+
+                    // unit variants don't have fields
+                    Some(CtorKind::Const) => unreachable!(),
+                };
+
+                // Suggest constructor as deep into the block tree as possible.
+                // This fixes https://github.com/rust-lang/rust/issues/101065,
+                // and also just helps make the most minimal suggestions.
+                let mut expr = expr;
+                while let hir::ExprKind::Block(block, _) = &expr.kind
+                    && let Some(expr_) = &block.expr
+                {
+                    expr = expr_
+                }
+
+                vec![
+                    (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
+                    (expr.span.shrink_to_hi(), close.to_owned()),
+                ]
+            };
+
+            match &compatible_variants[..] {
+                [] => { /* No variants to format */ }
+                [(variant, ctor_kind, field_name, note)] => {
+                    // Just a single matching variant.
+                    err.multipart_suggestion_verbose(
+                        format!(
+                            "try wrapping the expression in `{variant}`{note}",
+                            note = note.as_deref().unwrap_or("")
+                        ),
+                        suggestions_for(&**variant, *ctor_kind, *field_name),
+                        Applicability::MaybeIncorrect,
+                    );
+                    return true;
+                }
+                _ => {
+                    // More than one matching variant.
+                    err.multipart_suggestions(
+                        format!(
+                            "try wrapping the expression in a variant of `{}`",
+                            self.tcx.def_path_str(expected_adt.did())
+                        ),
+                        compatible_variants.into_iter().map(
+                            |(variant, ctor_kind, field_name, _)| {
+                                suggestions_for(&variant, ctor_kind, field_name)
+                            },
+                        ),
+                        Applicability::MaybeIncorrect,
+                    );
+                    return true;
+                }
+            }
+        }
+
+        false
+    }
+
+    pub(crate) fn suggest_non_zero_new_unwrap(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        expr_ty: Ty<'tcx>,
+    ) -> bool {
+        let tcx = self.tcx;
+        let (adt, unwrap) = match expected.kind() {
+            // In case Option<NonZero*> is wanted, but * is provided, suggest calling new
+            ty::Adt(adt, args) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
+                // Unwrap option
+                let ty::Adt(adt, _) = args.type_at(0).kind() else {
+                    return false;
+                };
+
+                (adt, "")
+            }
+            // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types
+            ty::Adt(adt, _) => (adt, ".unwrap()"),
+            _ => return false,
+        };
+
+        let map = [
+            (sym::NonZeroU8, tcx.types.u8),
+            (sym::NonZeroU16, tcx.types.u16),
+            (sym::NonZeroU32, tcx.types.u32),
+            (sym::NonZeroU64, tcx.types.u64),
+            (sym::NonZeroU128, tcx.types.u128),
+            (sym::NonZeroI8, tcx.types.i8),
+            (sym::NonZeroI16, tcx.types.i16),
+            (sym::NonZeroI32, tcx.types.i32),
+            (sym::NonZeroI64, tcx.types.i64),
+            (sym::NonZeroI128, tcx.types.i128),
+        ];
+
+        let Some((s, _)) = map.iter().find(|&&(s, t)| {
+            self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t)
+        }) else {
+            return false;
+        };
+
+        let path = self.tcx.def_path_str(adt.non_enum_variant().def_id);
+
+        err.multipart_suggestion(
+            format!("consider calling `{s}::new`"),
+            vec![
+                (expr.span.shrink_to_lo(), format!("{path}::new(")),
+                (expr.span.shrink_to_hi(), format!("){unwrap}")),
+            ],
+            Applicability::MaybeIncorrect,
+        );
+
+        true
+    }
+
+    /// Identify some cases where `as_ref()` would be appropriate and suggest it.
+    ///
+    /// Given the following code:
+    /// ```compile_fail,E0308
+    /// struct Foo;
+    /// fn takes_ref(_: &Foo) {}
+    /// let ref opt = Some(Foo);
+    ///
+    /// opt.map(|param| takes_ref(param));
+    /// ```
+    /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead.
+    ///
+    /// It only checks for `Option` and `Result` and won't work with
+    /// ```ignore (illustrative)
+    /// opt.map(|param| { takes_ref(param) });
+    /// ```
+    fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> {
+        let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind else {
+            return None;
+        };
+
+        let hir::def::Res::Local(local_id) = path.res else {
+            return None;
+        };
+
+        let local_parent = self.tcx.hir().parent_id(local_id);
+        let Some(Node::Param(hir::Param { hir_id: param_hir_id, .. })) =
+            self.tcx.hir().find(local_parent)
+        else {
+            return None;
+        };
+
+        let param_parent = self.tcx.hir().parent_id(*param_hir_id);
+        let Some(Node::Expr(hir::Expr {
+            hir_id: expr_hir_id,
+            kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
+            ..
+        })) = self.tcx.hir().find(param_parent)
+        else {
+            return None;
+        };
+
+        let expr_parent = self.tcx.hir().parent_id(*expr_hir_id);
+        let hir = self.tcx.hir().find(expr_parent);
+        let closure_params_len = closure_fn_decl.inputs.len();
+        let (
+            Some(Node::Expr(hir::Expr {
+                kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
+                ..
+            })),
+            1,
+        ) = (hir, closure_params_len)
+        else {
+            return None;
+        };
+
+        let self_ty = self.typeck_results.borrow().expr_ty(receiver);
+        let name = method_path.ident.name;
+        let is_as_ref_able = match self_ty.peel_refs().kind() {
+            ty::Adt(def, _) => {
+                (self.tcx.is_diagnostic_item(sym::Option, def.did())
+                    || self.tcx.is_diagnostic_item(sym::Result, def.did()))
+                    && (name == sym::map || name == sym::and_then)
+            }
+            _ => false,
+        };
+        if is_as_ref_able {
+            Some((
+                vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())],
+                "consider using `as_ref` instead",
+            ))
+        } else {
+            None
+        }
+    }
+
+    /// This function is used to determine potential "simple" improvements or users' errors and
+    /// provide them useful help. For example:
+    ///
+    /// ```compile_fail,E0308
+    /// fn some_fn(s: &str) {}
+    ///
+    /// let x = "hey!".to_owned();
+    /// some_fn(x); // error
+    /// ```
+    ///
+    /// No need to find every potential function which could make a coercion to transform a
+    /// `String` into a `&str` since a `&` would do the trick!
+    ///
+    /// In addition of this check, it also checks between references mutability state. If the
+    /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
+    /// `&mut`!".
+    pub(crate) fn suggest_deref_or_ref(
+        &self,
+        expr: &hir::Expr<'tcx>,
+        checked_ty: Ty<'tcx>,
+        expected: Ty<'tcx>,
+    ) -> Option<(
+        Vec<(Span, String)>,
+        String,
+        Applicability,
+        bool, /* verbose */
+        bool, /* suggest `&` or `&mut` type annotation */
+    )> {
+        let sess = self.sess();
+        let sp = expr.span;
+
+        // If the span is from an external macro, there's no suggestion we can make.
+        if in_external_macro(sess, sp) {
+            return None;
+        }
+
+        let sm = sess.source_map();
+
+        let replace_prefix = |s: &str, old: &str, new: &str| {
+            s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
+        };
+
+        // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
+        let expr = expr.peel_drop_temps();
+
+        match (&expr.kind, expected.kind(), checked_ty.kind()) {
+            (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
+                (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
+                    if let hir::ExprKind::Lit(_) = expr.kind
+                        && let Ok(src) = sm.span_to_snippet(sp)
+                        && replace_prefix(&src, "b\"", "\"").is_some()
+                    {
+                        let pos = sp.lo() + BytePos(1);
+                        return Some((
+                            vec![(sp.with_hi(pos), String::new())],
+                            "consider removing the leading `b`".to_string(),
+                            Applicability::MachineApplicable,
+                            true,
+                            false,
+                        ));
+                    }
+                }
+                (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
+                    if let hir::ExprKind::Lit(_) = expr.kind
+                        && let Ok(src) = sm.span_to_snippet(sp)
+                        && replace_prefix(&src, "\"", "b\"").is_some()
+                    {
+                        return Some((
+                            vec![(sp.shrink_to_lo(), "b".to_string())],
+                            "consider adding a leading `b`".to_string(),
+                            Applicability::MachineApplicable,
+                            true,
+                            false,
+                        ));
+                    }
+                }
+                _ => {}
+            },
+            (_, &ty::Ref(_, _, mutability), _) => {
+                // Check if it can work when put into a ref. For example:
+                //
+                // ```
+                // fn bar(x: &mut i32) {}
+                //
+                // let x = 0u32;
+                // bar(&x); // error, expected &mut
+                // ```
+                let ref_ty = match mutability {
+                    hir::Mutability::Mut => {
+                        Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
+                    }
+                    hir::Mutability::Not => {
+                        Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
+                    }
+                };
+                if self.can_coerce(ref_ty, expected) {
+                    let mut sugg_sp = sp;
+                    if let hir::ExprKind::MethodCall(ref segment, receiver, args, _) = expr.kind {
+                        let clone_trait =
+                            self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span));
+                        if args.is_empty()
+                            && self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
+                                |did| {
+                                    let ai = self.tcx.associated_item(did);
+                                    ai.trait_container(self.tcx) == Some(clone_trait)
+                                },
+                            ) == Some(true)
+                            && segment.ident.name == sym::clone
+                        {
+                            // If this expression had a clone call when suggesting borrowing
+                            // we want to suggest removing it because it'd now be unnecessary.
+                            sugg_sp = receiver.span;
+                        }
+                    }
+
+                    if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) = expr.kind
+                        && let Some(1) = self.deref_steps(expected, checked_ty)
+                    {
+                        // We have `*&T`, check if what was expected was `&T`.
+                        // If so, we may want to suggest removing a `*`.
+                        sugg_sp = sugg_sp.with_hi(inner.span.lo());
+                        return Some((
+                            vec![(sugg_sp, String::new())],
+                            "consider removing deref here".to_string(),
+                            Applicability::MachineApplicable,
+                            true,
+                            false,
+                        ));
+                    }
+
+                    let needs_parens = match expr.kind {
+                        // parenthesize if needed (Issue #46756)
+                        hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
+                        // parenthesize borrows of range literals (Issue #54505)
+                        _ if is_range_literal(expr) => true,
+                        _ => false,
+                    };
+
+                    if let Some((sugg, msg)) = self.can_use_as_ref(expr) {
+                        return Some((
+                            sugg,
+                            msg.to_string(),
+                            Applicability::MachineApplicable,
+                            true,
+                            false,
+                        ));
+                    }
+
+                    let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr)
+                    {
+                        Some(ident) => format!("{ident}: "),
+                        None => String::new(),
+                    };
+
+                    if let Some(hir::Node::Expr(hir::Expr {
+                        kind: hir::ExprKind::Assign(..),
+                        ..
+                    })) = self.tcx.hir().find_parent(expr.hir_id)
+                    {
+                        if mutability.is_mut() {
+                            // Suppressing this diagnostic, we'll properly print it in `check_expr_assign`
+                            return None;
+                        }
+                    }
+
+                    let sugg = mutability.ref_prefix_str();
+                    let (sugg, verbose) = if needs_parens {
+                        (
+                            vec![
+                                (sp.shrink_to_lo(), format!("{prefix}{sugg}(")),
+                                (sp.shrink_to_hi(), ")".to_string()),
+                            ],
+                            false,
+                        )
+                    } else {
+                        (vec![(sp.shrink_to_lo(), format!("{prefix}{sugg}"))], true)
+                    };
+                    return Some((
+                        sugg,
+                        format!("consider {}borrowing here", mutability.mutably_str()),
+                        Applicability::MachineApplicable,
+                        verbose,
+                        false,
+                    ));
+                }
+            }
+            (
+                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr),
+                _,
+                &ty::Ref(_, checked, _),
+            ) if self.can_sub(self.param_env, checked, expected) => {
+                let make_sugg = |start: Span, end: BytePos| {
+                    // skip `(` for tuples such as `(c) = (&123)`.
+                    // make sure we won't suggest like `(c) = 123)` which is incorrect.
+                    let sp = sm
+                        .span_extend_while(start.shrink_to_lo(), |c| c == '(' || c.is_whitespace())
+                        .map_or(start, |s| s.shrink_to_hi());
+                    Some((
+                        vec![(sp.with_hi(end), String::new())],
+                        "consider removing the borrow".to_string(),
+                        Applicability::MachineApplicable,
+                        true,
+                        true,
+                    ))
+                };
+
+                // We have `&T`, check if what was expected was `T`. If so,
+                // we may want to suggest removing a `&`.
+                if sm.is_imported(expr.span) {
+                    // Go through the spans from which this span was expanded,
+                    // and find the one that's pointing inside `sp`.
+                    //
+                    // E.g. for `&format!("")`, where we want the span to the
+                    // `format!()` invocation instead of its expansion.
+                    if let Some(call_span) =
+                        iter::successors(Some(expr.span), |s| s.parent_callsite())
+                            .find(|&s| sp.contains(s))
+                        && sm.is_span_accessible(call_span)
+                    {
+                        return make_sugg(sp, call_span.lo());
+                    }
+                    return None;
+                }
+                if sp.contains(expr.span) && sm.is_span_accessible(expr.span) {
+                    return make_sugg(sp, expr.span.lo());
+                }
+            }
+            (
+                _,
+                &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }),
+                &ty::Ref(_, ty_a, mutbl_a),
+            ) => {
+                if let Some(steps) = self.deref_steps(ty_a, ty_b)
+                    // Only suggest valid if dereferencing needed.
+                    && steps > 0
+                    // The pointer type implements `Copy` trait so the suggestion is always valid.
+                    && let Ok(src) = sm.span_to_snippet(sp)
+                {
+                    let derefs = "*".repeat(steps);
+                    let old_prefix = mutbl_a.ref_prefix_str();
+                    let new_prefix = mutbl_b.ref_prefix_str().to_owned() + &derefs;
+
+                    let suggestion = replace_prefix(&src, old_prefix, &new_prefix).map(|_| {
+                        // skip `&` or `&mut ` if both mutabilities are mutable
+                        let lo = sp.lo()
+                            + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _);
+                        // skip `&` or `&mut `
+                        let hi = sp.lo() + BytePos(old_prefix.len() as _);
+                        let sp = sp.with_lo(lo).with_hi(hi);
+
+                        (
+                            sp,
+                            format!(
+                                "{}{derefs}",
+                                if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }
+                            ),
+                            if mutbl_b <= mutbl_a {
+                                Applicability::MachineApplicable
+                            } else {
+                                Applicability::MaybeIncorrect
+                            },
+                        )
+                    });
+
+                    if let Some((span, src, applicability)) = suggestion {
+                        return Some((
+                            vec![(span, src)],
+                            "consider dereferencing".to_string(),
+                            applicability,
+                            true,
+                            false,
+                        ));
+                    }
+                }
+            }
+            _ if sp == expr.span => {
+                if let Some(mut steps) = self.deref_steps(checked_ty, expected) {
+                    let mut expr = expr.peel_blocks();
+                    let mut prefix_span = expr.span.shrink_to_lo();
+                    let mut remove = String::new();
+
+                    // Try peeling off any existing `&` and `&mut` to reach our target type
+                    while steps > 0 {
+                        if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
+                            // If the expression has `&`, removing it would fix the error
+                            prefix_span = prefix_span.with_hi(inner.span.lo());
+                            expr = inner;
+                            remove.push_str(mutbl.ref_prefix_str());
+                            steps -= 1;
+                        } else {
+                            break;
+                        }
+                    }
+                    // If we've reached our target type with just removing `&`, then just print now.
+                    if steps == 0 && !remove.trim().is_empty() {
+                        return Some((
+                            vec![(prefix_span, String::new())],
+                            format!("consider removing the `{}`", remove.trim()),
+                            // Do not remove `&&` to get to bool, because it might be something like
+                            // { a } && b, which we have a separate fixup suggestion that is more
+                            // likely correct...
+                            if remove.trim() == "&&" && expected == self.tcx.types.bool {
+                                Applicability::MaybeIncorrect
+                            } else {
+                                Applicability::MachineApplicable
+                            },
+                            true,
+                            false,
+                        ));
+                    }
+
+                    // For this suggestion to make sense, the type would need to be `Copy`,
+                    // or we have to be moving out of a `Box<T>`
+                    if self.type_is_copy_modulo_regions(self.param_env, expected)
+                        // FIXME(compiler-errors): We can actually do this if the checked_ty is
+                        // `steps` layers of boxes, not just one, but this is easier and most likely.
+                        || (checked_ty.is_box() && steps == 1)
+                        // We can always deref a binop that takes its arguments by ref.
+                        || matches!(
+                            self.tcx.hir().get_parent(expr.hir_id),
+                            hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, ..), .. })
+                                if !op.node.is_by_value()
+                        )
+                    {
+                        let deref_kind = if checked_ty.is_box() {
+                            "unboxing the value"
+                        } else if checked_ty.is_ref() {
+                            "dereferencing the borrow"
+                        } else {
+                            "dereferencing the type"
+                        };
+
+                        // Suggest removing `&` if we have removed any, otherwise suggest just
+                        // dereferencing the remaining number of steps.
+                        let message = if remove.is_empty() {
+                            format!("consider {deref_kind}")
+                        } else {
+                            format!(
+                                "consider removing the `{}` and {} instead",
+                                remove.trim(),
+                                deref_kind
+                            )
+                        };
+
+                        let prefix =
+                            match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
+                                Some(ident) => format!("{ident}: "),
+                                None => String::new(),
+                            };
+
+                        let (span, suggestion) = if self.is_else_if_block(expr) {
+                            // Don't suggest nonsense like `else *if`
+                            return None;
+                        } else if let Some(expr) = self.maybe_get_block_expr(expr) {
+                            // prefix should be empty here..
+                            (expr.span.shrink_to_lo(), "*".to_string())
+                        } else {
+                            (prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
+                        };
+                        if suggestion.trim().is_empty() {
+                            return None;
+                        }
+
+                        return Some((
+                            vec![(span, suggestion)],
+                            message,
+                            Applicability::MachineApplicable,
+                            true,
+                            false,
+                        ));
+                    }
+                }
+            }
+            _ => {}
+        }
+        None
+    }
+
+    /// Returns whether the given expression is an `else if`.
+    fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
+        if let hir::ExprKind::If(..) = expr.kind {
+            let parent_id = self.tcx.hir().parent_id(expr.hir_id);
+            if let Some(Node::Expr(hir::Expr {
+                kind: hir::ExprKind::If(_, _, Some(else_expr)),
+                ..
+            })) = self.tcx.hir().find(parent_id)
+            {
+                return else_expr.hir_id == expr.hir_id;
+            }
+        }
+        false
+    }
+
+    pub(crate) fn suggest_cast(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        checked_ty: Ty<'tcx>,
+        expected_ty: Ty<'tcx>,
+        expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
+    ) -> bool {
+        if self.tcx.sess.source_map().is_imported(expr.span) {
+            // Ignore if span is from within a macro.
+            return false;
+        }
+
+        let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) else {
+            return false;
+        };
+
+        // If casting this expression to a given numeric type would be appropriate in case of a type
+        // mismatch.
+        //
+        // We want to minimize the amount of casting operations that are suggested, as it can be a
+        // lossy operation with potentially bad side effects, so we only suggest when encountering
+        // an expression that indicates that the original type couldn't be directly changed.
+        //
+        // For now, don't suggest casting with `as`.
+        let can_cast = false;
+
+        let mut sugg = vec![];
+
+        if let Some(hir::Node::ExprField(field)) = self.tcx.hir().find_parent(expr.hir_id) {
+            // `expr` is a literal field for a struct, only suggest if appropriate
+            if field.is_shorthand {
+                // This is a field literal
+                sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
+            } else {
+                // Likely a field was meant, but this field wasn't found. Do not suggest anything.
+                return false;
+            }
+        };
+
+        if let hir::ExprKind::Call(path, args) = &expr.kind
+            && let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
+                (&path.kind, args.len())
+            // `expr` is a conversion like `u32::from(val)`, do not suggest anything (#63697).
+            && let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
+                (&base_ty.kind, path_segment.ident.name)
+        {
+            if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
+                match ident.name {
+                    sym::i128
+                    | sym::i64
+                    | sym::i32
+                    | sym::i16
+                    | sym::i8
+                    | sym::u128
+                    | sym::u64
+                    | sym::u32
+                    | sym::u16
+                    | sym::u8
+                    | sym::isize
+                    | sym::usize
+                        if base_ty_path.segments.len() == 1 =>
+                    {
+                        return false;
+                    }
+                    _ => {}
+                }
+            }
+        }
+
+        let msg = format!(
+            "you can convert {} `{}` to {} `{}`",
+            checked_ty.kind().article(),
+            checked_ty,
+            expected_ty.kind().article(),
+            expected_ty,
+        );
+        let cast_msg = format!(
+            "you can cast {} `{}` to {} `{}`",
+            checked_ty.kind().article(),
+            checked_ty,
+            expected_ty.kind().article(),
+            expected_ty,
+        );
+        let lit_msg = format!(
+            "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
+        );
+
+        let close_paren = if expr.precedence().order() < PREC_POSTFIX {
+            sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
+            ")"
+        } else {
+            ""
+        };
+
+        let mut cast_suggestion = sugg.clone();
+        cast_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren} as {expected_ty}")));
+        let mut into_suggestion = sugg.clone();
+        into_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren}.into()")));
+        let mut suffix_suggestion = sugg.clone();
+        suffix_suggestion.push((
+            if matches!(
+                (&expected_ty.kind(), &checked_ty.kind()),
+                (ty::Int(_) | ty::Uint(_), ty::Float(_))
+            ) {
+                // Remove fractional part from literal, for example `42.0f32` into `42`
+                let src = src.trim_end_matches(&checked_ty.to_string());
+                let len = src.split('.').next().unwrap().len();
+                expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
+            } else {
+                let len = src.trim_end_matches(&checked_ty.to_string()).len();
+                expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
+            },
+            if expr.precedence().order() < PREC_POSTFIX {
+                // Readd `)`
+                format!("{expected_ty})")
+            } else {
+                expected_ty.to_string()
+            },
+        ));
+        let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
+            if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
+        };
+        let is_negative_int =
+            |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..));
+        let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
+
+        let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);
+
+        let suggest_fallible_into_or_lhs_from =
+            |err: &mut Diagnostic, exp_to_found_is_fallible: bool| {
+                // If we know the expression the expected type is derived from, we might be able
+                // to suggest a widening conversion rather than a narrowing one (which may
+                // panic). For example, given x: u8 and y: u32, if we know the span of "x",
+                //   x > y
+                // can be given the suggestion "u32::from(x) > y" rather than
+                // "x > y.try_into().unwrap()".
+                let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
+                    self.tcx
+                        .sess
+                        .source_map()
+                        .span_to_snippet(expr.span)
+                        .ok()
+                        .map(|src| (expr, src))
+                });
+                let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
+                    (lhs_expr_and_src, exp_to_found_is_fallible)
+                {
+                    let msg = format!(
+                        "you can convert `{lhs_src}` from `{expected_ty}` to `{checked_ty}`, matching the type of `{src}`",
+                    );
+                    let suggestion = vec![
+                        (lhs_expr.span.shrink_to_lo(), format!("{checked_ty}::from(")),
+                        (lhs_expr.span.shrink_to_hi(), ")".to_string()),
+                    ];
+                    (msg, suggestion)
+                } else {
+                    let msg =
+                        format!("{} and panic if the converted value doesn't fit", msg.clone());
+                    let mut suggestion = sugg.clone();
+                    suggestion.push((
+                        expr.span.shrink_to_hi(),
+                        format!("{close_paren}.try_into().unwrap()"),
+                    ));
+                    (msg, suggestion)
+                };
+                err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
+            };
+
+        let suggest_to_change_suffix_or_into =
+            |err: &mut Diagnostic,
+             found_to_exp_is_fallible: bool,
+             exp_to_found_is_fallible: bool| {
+                let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir().is_lhs(e.hir_id));
+
+                if exp_is_lhs {
+                    return;
+                }
+
+                let always_fallible = found_to_exp_is_fallible
+                    && (exp_to_found_is_fallible || expected_ty_expr.is_none());
+                let msg = if literal_is_ty_suffixed(expr) {
+                    lit_msg.clone()
+                } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
+                    // We now know that converting either the lhs or rhs is fallible. Before we
+                    // suggest a fallible conversion, check if the value can never fit in the
+                    // expected type.
+                    let msg = format!("`{src}` cannot fit into type `{expected_ty}`");
+                    err.note(msg);
+                    return;
+                } else if in_const_context {
+                    // Do not recommend `into` or `try_into` in const contexts.
+                    return;
+                } else if found_to_exp_is_fallible {
+                    return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
+                } else {
+                    msg.clone()
+                };
+                let suggestion = if literal_is_ty_suffixed(expr) {
+                    suffix_suggestion.clone()
+                } else {
+                    into_suggestion.clone()
+                };
+                err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
+            };
+
+        match (&expected_ty.kind(), &checked_ty.kind()) {
+            (ty::Int(exp), ty::Int(found)) => {
+                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
+                {
+                    (Some(exp), Some(found)) if exp < found => (true, false),
+                    (Some(exp), Some(found)) if exp > found => (false, true),
+                    (None, Some(8 | 16)) => (false, true),
+                    (Some(8 | 16), None) => (true, false),
+                    (None, _) | (_, None) => (true, true),
+                    _ => (false, false),
+                };
+                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
+                true
+            }
+            (ty::Uint(exp), ty::Uint(found)) => {
+                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
+                {
+                    (Some(exp), Some(found)) if exp < found => (true, false),
+                    (Some(exp), Some(found)) if exp > found => (false, true),
+                    (None, Some(8 | 16)) => (false, true),
+                    (Some(8 | 16), None) => (true, false),
+                    (None, _) | (_, None) => (true, true),
+                    _ => (false, false),
+                };
+                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
+                true
+            }
+            (&ty::Int(exp), &ty::Uint(found)) => {
+                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
+                {
+                    (Some(exp), Some(found)) if found < exp => (false, true),
+                    (None, Some(8)) => (false, true),
+                    _ => (true, true),
+                };
+                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
+                true
+            }
+            (&ty::Uint(exp), &ty::Int(found)) => {
+                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
+                {
+                    (Some(exp), Some(found)) if found > exp => (true, false),
+                    (Some(8), None) => (true, false),
+                    _ => (true, true),
+                };
+                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
+                true
+            }
+            (ty::Float(exp), ty::Float(found)) => {
+                if found.bit_width() < exp.bit_width() {
+                    suggest_to_change_suffix_or_into(err, false, true);
+                } else if literal_is_ty_suffixed(expr) {
+                    err.multipart_suggestion_verbose(
+                        lit_msg,
+                        suffix_suggestion,
+                        Applicability::MachineApplicable,
+                    );
+                } else if can_cast {
+                    // Missing try_into implementation for `f64` to `f32`
+                    err.multipart_suggestion_verbose(
+                        format!("{cast_msg}, producing the closest possible value"),
+                        cast_suggestion,
+                        Applicability::MaybeIncorrect, // lossy conversion
+                    );
+                }
+                true
+            }
+            (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
+                if literal_is_ty_suffixed(expr) {
+                    err.multipart_suggestion_verbose(
+                        lit_msg,
+                        suffix_suggestion,
+                        Applicability::MachineApplicable,
+                    );
+                } else if can_cast {
+                    // Missing try_into implementation for `{float}` to `{integer}`
+                    err.multipart_suggestion_verbose(
+                        format!("{msg}, rounding the float towards zero"),
+                        cast_suggestion,
+                        Applicability::MaybeIncorrect, // lossy conversion
+                    );
+                }
+                true
+            }
+            (ty::Float(exp), ty::Uint(found)) => {
+                // if `found` is `None` (meaning found is `usize`), don't suggest `.into()`
+                if exp.bit_width() > found.bit_width().unwrap_or(256) {
+                    err.multipart_suggestion_verbose(
+                        format!(
+                            "{msg}, producing the floating point representation of the integer",
+                        ),
+                        into_suggestion,
+                        Applicability::MachineApplicable,
+                    );
+                } else if literal_is_ty_suffixed(expr) {
+                    err.multipart_suggestion_verbose(
+                        lit_msg,
+                        suffix_suggestion,
+                        Applicability::MachineApplicable,
+                    );
+                } else {
+                    // Missing try_into implementation for `{integer}` to `{float}`
+                    err.multipart_suggestion_verbose(
+                        format!(
+                            "{cast_msg}, producing the floating point representation of the integer, \
+                                 rounded if necessary",
+                        ),
+                        cast_suggestion,
+                        Applicability::MaybeIncorrect, // lossy conversion
+                    );
+                }
+                true
+            }
+            (ty::Float(exp), ty::Int(found)) => {
+                // if `found` is `None` (meaning found is `isize`), don't suggest `.into()`
+                if exp.bit_width() > found.bit_width().unwrap_or(256) {
+                    err.multipart_suggestion_verbose(
+                        format!(
+                            "{}, producing the floating point representation of the integer",
+                            msg.clone(),
+                        ),
+                        into_suggestion,
+                        Applicability::MachineApplicable,
+                    );
+                } else if literal_is_ty_suffixed(expr) {
+                    err.multipart_suggestion_verbose(
+                        lit_msg,
+                        suffix_suggestion,
+                        Applicability::MachineApplicable,
+                    );
+                } else {
+                    // Missing try_into implementation for `{integer}` to `{float}`
+                    err.multipart_suggestion_verbose(
+                        format!(
+                            "{}, producing the floating point representation of the integer, \
+                                rounded if necessary",
+                            &msg,
+                        ),
+                        cast_suggestion,
+                        Applicability::MaybeIncorrect, // lossy conversion
+                    );
+                }
+                true
+            }
+            (
+                &ty::Uint(ty::UintTy::U32 | ty::UintTy::U64 | ty::UintTy::U128)
+                | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128),
+                &ty::Char,
+            ) => {
+                err.multipart_suggestion_verbose(
+                    format!("{cast_msg}, since a `char` always occupies 4 bytes"),
+                    cast_suggestion,
+                    Applicability::MachineApplicable,
+                );
+                true
+            }
+            _ => false,
+        }
+    }
+
+    /// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
+    pub(crate) fn suggest_method_call_on_range_literal(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'tcx>,
+        checked_ty: Ty<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) {
+        if !hir::is_range_literal(expr) {
+            return;
+        }
+        let hir::ExprKind::Struct(hir::QPath::LangItem(LangItem::Range, ..), [start, end], _) =
+            expr.kind
+        else {
+            return;
+        };
+        let parent = self.tcx.hir().parent_id(expr.hir_id);
+        if let Some(hir::Node::ExprField(_)) = self.tcx.hir().find(parent) {
+            // Ignore `Foo { field: a..Default::default() }`
+            return;
+        }
+        let mut expr = end.expr;
+        let mut expectation = Some(expected_ty);
+        while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
+            // Getting to the root receiver and asserting it is a fn call let's us ignore cases in
+            // `tests/ui/methods/issues/issue-90315.stderr`.
+            expr = rcvr;
+            // If we have more than one layer of calls, then the expected ty
+            // cannot guide the method probe.
+            expectation = None;
+        }
+        let hir::ExprKind::Call(method_name, _) = expr.kind else {
+            return;
+        };
+        let ty::Adt(adt, _) = checked_ty.kind() else {
+            return;
+        };
+        if self.tcx.lang_items().range_struct() != Some(adt.did()) {
+            return;
+        }
+        if let ty::Adt(adt, _) = expected_ty.kind()
+            && self.tcx.lang_items().range_struct() == Some(adt.did())
+        {
+            return;
+        }
+        // Check if start has method named end.
+        let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else {
+            return;
+        };
+        let [hir::PathSegment { ident, .. }] = p.segments else {
+            return;
+        };
+        let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
+        let Ok(_pick) = self.lookup_probe_for_diagnostic(
+            *ident,
+            self_ty,
+            expr,
+            probe::ProbeScope::AllTraits,
+            expectation,
+        ) else {
+            return;
+        };
+        let mut sugg = ".";
+        let mut span = start.expr.span.between(end.expr.span);
+        if span.lo() + BytePos(2) == span.hi() {
+            // There's no space between the start, the range op and the end, suggest removal which
+            // will be more noticeable than the replacement of `..` with `.`.
+            span = span.with_lo(span.lo() + BytePos(1));
+            sugg = "";
+        }
+        err.span_suggestion_verbose(
+            span,
+            "you likely meant to write a method call instead of a range",
+            sugg,
+            Applicability::MachineApplicable,
+        );
+    }
+
+    /// Identify when the type error is because `()` is found in a binding that was assigned a
+    /// block without a tail expression.
+    pub(crate) fn suggest_return_binding_for_missing_tail_expr(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        checked_ty: Ty<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) {
+        if !checked_ty.is_unit() {
+            return;
+        }
+        let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {
+            return;
+        };
+        let hir::def::Res::Local(hir_id) = path.res else {
+            return;
+        };
+        let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else {
+            return;
+        };
+        let Some(hir::Node::Local(hir::Local { ty: None, init: Some(init), .. })) =
+            self.tcx.hir().find_parent(pat.hir_id)
+        else {
+            return;
+        };
+        let hir::ExprKind::Block(block, None) = init.kind else {
+            return;
+        };
+        if block.expr.is_some() {
+            return;
+        }
+        let [.., stmt] = block.stmts else {
+            err.span_label(block.span, "this empty block is missing a tail expression");
+            return;
+        };
+        let hir::StmtKind::Semi(tail_expr) = stmt.kind else {
+            return;
+        };
+        let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else {
+            return;
+        };
+        if self.can_eq(self.param_env, expected_ty, ty) {
+            err.span_suggestion_short(
+                stmt.span.with_lo(tail_expr.span.hi()),
+                "remove this semicolon",
+                "",
+                Applicability::MachineApplicable,
+            );
+        } else {
+            err.span_label(block.span, "this block is missing a tail expression");
+        }
+    }
 }
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 26034dbaf99..ffdc05526f2 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -18,10 +18,9 @@ use rustc_query_system::query::print_query_stack;
 use rustc_session::config::{
     self, Cfg, CheckCfg, ExpectedValues, Input, OutFileName, OutputFilenames,
 };
+use rustc_session::filesearch::sysroot_candidates;
 use rustc_session::parse::ParseSess;
-use rustc_session::CompilerIO;
-use rustc_session::Session;
-use rustc_session::{lint, EarlyErrorHandler};
+use rustc_session::{lint, CompilerIO, EarlyErrorHandler, Session};
 use rustc_span::source_map::{FileLoader, FileName};
 use rustc_span::symbol::sym;
 use std::path::PathBuf;
@@ -398,32 +397,71 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
         || {
             crate::callbacks::setup_callbacks();
 
-            let registry = &config.registry;
-
             let handler = EarlyErrorHandler::new(config.opts.error_format);
 
+            let codegen_backend = if let Some(make_codegen_backend) = config.make_codegen_backend {
+                make_codegen_backend(&config.opts)
+            } else {
+                util::get_codegen_backend(
+                    &handler,
+                    &config.opts.maybe_sysroot,
+                    config.opts.unstable_opts.codegen_backend.as_deref(),
+                )
+            };
+
             let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
-            let (mut sess, codegen_backend) = util::create_session(
+
+            let bundle = match rustc_errors::fluent_bundle(
+                config.opts.maybe_sysroot.clone(),
+                sysroot_candidates().to_vec(),
+                config.opts.unstable_opts.translate_lang.clone(),
+                config.opts.unstable_opts.translate_additional_ftl.as_deref(),
+                config.opts.unstable_opts.translate_directionality_markers,
+            ) {
+                Ok(bundle) => bundle,
+                Err(e) => {
+                    handler.early_error(format!("failed to load fluent bundle: {e}"));
+                }
+            };
+
+            let mut locale_resources = Vec::from(config.locale_resources);
+            locale_resources.push(codegen_backend.locale_resource());
+
+            // target_override is documented to be called before init(), so this is okay
+            let target_override = codegen_backend.target_override(&config.opts);
+
+            let mut sess = rustc_session::build_session(
                 &handler,
                 config.opts,
-                parse_cfg(&handler, config.crate_cfg),
-                parse_check_cfg(&handler, config.crate_check_cfg),
-                config.locale_resources,
-                config.file_loader,
                 CompilerIO {
                     input: config.input,
                     output_dir: config.output_dir,
                     output_file: config.output_file,
                     temps_dir,
                 },
+                bundle,
+                config.registry.clone(),
+                locale_resources,
                 config.lint_caps,
-                config.make_codegen_backend,
-                registry.clone(),
+                config.file_loader,
+                target_override,
+                util::rustc_version_str().unwrap_or("unknown"),
                 config.ice_file,
                 config.using_internal_features,
                 config.expanded_args,
             );
 
+            codegen_backend.init(&sess);
+
+            let cfg = parse_cfg(&handler, config.crate_cfg);
+            let mut cfg = config::build_configuration(&sess, cfg);
+            util::add_configuration(&mut cfg, &mut sess, &*codegen_backend);
+            sess.parse_sess.config = cfg;
+
+            let mut check_cfg = parse_check_cfg(&handler, config.crate_check_cfg);
+            check_cfg.fill_well_known(&sess.target);
+            sess.parse_sess.check_config = check_cfg;
+
             if let Some(parse_sess_created) = config.parse_sess_created {
                 parse_sess_created(&mut sess.parse_sess);
             }
@@ -444,7 +482,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
             rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || {
                 let r = {
                     let _sess_abort_error = defer(|| {
-                        compiler.sess.finish_diagnostics(registry);
+                        compiler.sess.finish_diagnostics(&config.registry);
                     });
 
                     f(&compiler)
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 5fde98c7c06..22d12793464 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -3,29 +3,24 @@ use info;
 use libloading::Library;
 use rustc_ast as ast;
 use rustc_codegen_ssa::traits::CodegenBackend;
-use rustc_data_structures::fx::FxHashMap;
 #[cfg(parallel_compiler)]
 use rustc_data_structures::sync;
-use rustc_errors::registry::Registry;
 use rustc_parse::validate_attr;
 use rustc_session as session;
-use rustc_session::config::{
-    self, Cfg, CheckCfg, CrateType, OutFileName, OutputFilenames, OutputTypes,
-};
+use rustc_session::config::{self, Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes};
 use rustc_session::filesearch::sysroot_candidates;
 use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
 use rustc_session::{filesearch, output, Session};
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::edition::Edition;
-use rustc_span::source_map::FileLoader;
 use rustc_span::symbol::{sym, Symbol};
-use session::{CompilerIO, EarlyErrorHandler};
+use session::EarlyErrorHandler;
 use std::env;
 use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
 use std::mem;
 use std::path::{Path, PathBuf};
 use std::sync::atomic::{AtomicBool, Ordering};
-use std::sync::{Arc, OnceLock};
+use std::sync::OnceLock;
 use std::thread;
 
 /// Function pointer type that constructs a new CodegenBackend.
@@ -52,82 +47,6 @@ pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dy
     }
 }
 
-pub fn create_session(
-    handler: &EarlyErrorHandler,
-    sopts: config::Options,
-    cfg: Cfg,
-    mut check_cfg: CheckCfg,
-    locale_resources: &'static [&'static str],
-    file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
-    io: CompilerIO,
-    lint_caps: FxHashMap<lint::LintId, lint::Level>,
-    make_codegen_backend: Option<
-        Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
-    >,
-    descriptions: Registry,
-    ice_file: Option<PathBuf>,
-    using_internal_features: Arc<AtomicBool>,
-    expanded_args: Vec<String>,
-) -> (Session, Box<dyn CodegenBackend>) {
-    let codegen_backend = if let Some(make_codegen_backend) = make_codegen_backend {
-        make_codegen_backend(&sopts)
-    } else {
-        get_codegen_backend(
-            handler,
-            &sopts.maybe_sysroot,
-            sopts.unstable_opts.codegen_backend.as_deref(),
-        )
-    };
-
-    // target_override is documented to be called before init(), so this is okay
-    let target_override = codegen_backend.target_override(&sopts);
-
-    let bundle = match rustc_errors::fluent_bundle(
-        sopts.maybe_sysroot.clone(),
-        sysroot_candidates().to_vec(),
-        sopts.unstable_opts.translate_lang.clone(),
-        sopts.unstable_opts.translate_additional_ftl.as_deref(),
-        sopts.unstable_opts.translate_directionality_markers,
-    ) {
-        Ok(bundle) => bundle,
-        Err(e) => {
-            handler.early_error(format!("failed to load fluent bundle: {e}"));
-        }
-    };
-
-    let mut locale_resources = Vec::from(locale_resources);
-    locale_resources.push(codegen_backend.locale_resource());
-
-    let mut sess = session::build_session(
-        handler,
-        sopts,
-        io,
-        bundle,
-        descriptions,
-        locale_resources,
-        lint_caps,
-        file_loader,
-        target_override,
-        rustc_version_str().unwrap_or("unknown"),
-        ice_file,
-        using_internal_features,
-        expanded_args,
-    );
-
-    codegen_backend.init(&sess);
-
-    let mut cfg = config::build_configuration(&sess, cfg);
-    add_configuration(&mut cfg, &mut sess, &*codegen_backend);
-
-    check_cfg.fill_well_known(&sess.target);
-
-    // These configs use symbols, rather than strings.
-    sess.parse_sess.config = cfg;
-    sess.parse_sess.check_config = check_cfg;
-
-    (sess, codegen_backend)
-}
-
 const STACK_SIZE: usize = 8 * 1024 * 1024;
 
 fn get_stack_size() -> Option<usize> {
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 78df4a8f6e0..de643fb333e 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -134,7 +134,6 @@
 #![feature(const_hash)]
 #![feature(const_heap)]
 #![feature(const_index_range_slice_index)]
-#![feature(const_inherent_unchecked_arith)]
 #![feature(const_int_unchecked_arith)]
 #![feature(const_intrinsic_forget)]
 #![feature(const_ipv4)]
@@ -188,6 +187,8 @@
 #![feature(str_split_inclusive_remainder)]
 #![feature(str_split_remainder)]
 #![feature(strict_provenance)]
+#![feature(unchecked_math)]
+#![feature(unchecked_shifts)]
 #![feature(utf16_extra)]
 #![feature(utf16_extra_const)]
 #![feature(variant_count)]
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index 3cbb55af3bc..fd01f1b2610 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -471,7 +471,7 @@ macro_rules! int_impl {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
+        #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")]
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_add(self, rhs: Self) -> Self {
@@ -539,7 +539,7 @@ macro_rules! int_impl {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
+        #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")]
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self {
@@ -607,7 +607,7 @@ macro_rules! int_impl {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
+        #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")]
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self {
@@ -740,6 +740,31 @@ macro_rules! int_impl {
             if unlikely!(b) {None} else {Some(a)}
         }
 
+        /// Unchecked negation. Computes `-self`, assuming overflow cannot occur.
+        ///
+        /// # Safety
+        ///
+        /// This results in undefined behavior when
+        #[doc = concat!("`self == ", stringify!($SelfT), "::MIN`,")]
+        /// i.e. when [`checked_neg`] would return `None`.
+        ///
+        #[doc = concat!("[`checked_neg`]: ", stringify!($SelfT), "::checked_neg")]
+        #[unstable(
+            feature = "unchecked_neg",
+            reason = "niche optimization path",
+            issue = "85122",
+        )]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[rustc_const_unstable(feature = "unchecked_neg", issue = "85122")]
+        #[inline(always)]
+        #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
+        pub const unsafe fn unchecked_neg(self) -> Self {
+            // SAFETY: the caller must uphold the safety contract for
+            // `unchecked_neg`.
+            unsafe { intrinsics::unchecked_sub(0, self) }
+        }
+
         /// Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger
         /// than or equal to the number of bits in `self`.
         ///
@@ -772,13 +797,13 @@ macro_rules! int_impl {
         ///
         #[doc = concat!("[`checked_shl`]: ", stringify!($SelfT), "::checked_shl")]
         #[unstable(
-            feature = "unchecked_math",
+            feature = "unchecked_shifts",
             reason = "niche optimization path",
             issue = "85122",
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
+        #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")]
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self {
@@ -820,13 +845,13 @@ macro_rules! int_impl {
         ///
         #[doc = concat!("[`checked_shr`]: ", stringify!($SelfT), "::checked_shr")]
         #[unstable(
-            feature = "unchecked_math",
+            feature = "unchecked_shifts",
             reason = "niche optimization path",
             issue = "85122",
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
+        #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")]
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self {
@@ -1404,7 +1429,7 @@ macro_rules! int_impl {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline(always)]
-        #[rustc_allow_const_fn_unstable(const_inherent_unchecked_arith)]
+        #[rustc_allow_const_fn_unstable(unchecked_shifts)]
         pub const fn wrapping_shl(self, rhs: u32) -> Self {
             // SAFETY: the masking by the bitsize of the type ensures that we do not shift
             // out of bounds
@@ -1434,7 +1459,7 @@ macro_rules! int_impl {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline(always)]
-        #[rustc_allow_const_fn_unstable(const_inherent_unchecked_arith)]
+        #[rustc_allow_const_fn_unstable(unchecked_shifts)]
         pub const fn wrapping_shr(self, rhs: u32) -> Self {
             // SAFETY: the masking by the bitsize of the type ensures that we do not shift
             // out of bounds
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index a9c5312a1c0..11a53aaf122 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -479,7 +479,7 @@ macro_rules! uint_impl {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
+        #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")]
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_add(self, rhs: Self) -> Self {
@@ -548,7 +548,7 @@ macro_rules! uint_impl {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
+        #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")]
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self {
@@ -595,7 +595,7 @@ macro_rules! uint_impl {
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
+        #[rustc_const_unstable(feature = "unchecked_math", issue = "85122")]
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self {
@@ -926,13 +926,13 @@ macro_rules! uint_impl {
         ///
         #[doc = concat!("[`checked_shl`]: ", stringify!($SelfT), "::checked_shl")]
         #[unstable(
-            feature = "unchecked_math",
+            feature = "unchecked_shifts",
             reason = "niche optimization path",
             issue = "85122",
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
+        #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")]
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self {
@@ -974,13 +974,13 @@ macro_rules! uint_impl {
         ///
         #[doc = concat!("[`checked_shr`]: ", stringify!($SelfT), "::checked_shr")]
         #[unstable(
-            feature = "unchecked_math",
+            feature = "unchecked_shifts",
             reason = "niche optimization path",
             issue = "85122",
         )]
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
-        #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")]
+        #[rustc_const_unstable(feature = "unchecked_shifts", issue = "85122")]
         #[inline(always)]
         #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
         pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self {
@@ -1418,7 +1418,7 @@ macro_rules! uint_impl {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline(always)]
-        #[rustc_allow_const_fn_unstable(const_inherent_unchecked_arith)]
+        #[rustc_allow_const_fn_unstable(unchecked_shifts)]
         pub const fn wrapping_shl(self, rhs: u32) -> Self {
             // SAFETY: the masking by the bitsize of the type ensures that we do not shift
             // out of bounds
@@ -1451,7 +1451,7 @@ macro_rules! uint_impl {
         #[must_use = "this returns the result of the operation, \
                       without modifying the original"]
         #[inline(always)]
-        #[rustc_allow_const_fn_unstable(const_inherent_unchecked_arith)]
+        #[rustc_allow_const_fn_unstable(unchecked_shifts)]
         pub const fn wrapping_shr(self, rhs: u32) -> Self {
             // SAFETY: the masking by the bitsize of the type ensures that we do not shift
             // out of bounds
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_shl.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_shl.rs
index 4554d0cb82b..abb345938fa 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_shl.rs
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_shl.rs
@@ -1,4 +1,4 @@
-#![feature(unchecked_math)]
+#![feature(unchecked_shifts)]
 
 fn main() {
     unsafe {
diff --git a/src/tools/miri/tests/fail/intrinsics/unchecked_shr.rs b/src/tools/miri/tests/fail/intrinsics/unchecked_shr.rs
index fe2e85be698..cdc10185e47 100644
--- a/src/tools/miri/tests/fail/intrinsics/unchecked_shr.rs
+++ b/src/tools/miri/tests/fail/intrinsics/unchecked_shr.rs
@@ -1,4 +1,4 @@
-#![feature(unchecked_math)]
+#![feature(unchecked_shifts)]
 
 fn main() {
     unsafe {
diff --git a/tests/codegen/unchecked_shifts.rs b/tests/codegen/unchecked_shifts.rs
index d5f53bedd54..aca9bec77df 100644
--- a/tests/codegen/unchecked_shifts.rs
+++ b/tests/codegen/unchecked_shifts.rs
@@ -2,7 +2,7 @@
 // ignore-debug (because unchecked is checked in debug)
 
 #![crate_type = "lib"]
-#![feature(unchecked_math)]
+#![feature(unchecked_shifts)]
 
 // CHECK-LABEL: @unchecked_shl_unsigned_same
 #[no_mangle]
diff --git a/tests/mir-opt/inline/unchecked_shifts.rs b/tests/mir-opt/inline/unchecked_shifts.rs
index 67666f2f713..c559a056ca7 100644
--- a/tests/mir-opt/inline/unchecked_shifts.rs
+++ b/tests/mir-opt/inline/unchecked_shifts.rs
@@ -1,7 +1,7 @@
 // skip-filecheck
 // EMIT_MIR_FOR_EACH_PANIC_STRATEGY
 #![crate_type = "lib"]
-#![feature(unchecked_math)]
+#![feature(unchecked_shifts)]
 
 // ignore-debug: the debug assertions prevent the inlining we are testing for
 // compile-flags: -Zmir-opt-level=2 -Zinline-mir