rustc_hir_typeck/fn_ctxt/
suggestions.rs

1use core::cmp::min;
2use core::iter;
3
4use hir::def_id::LocalDefId;
5use rustc_ast::util::parser::ExprPrecedence;
6use rustc_data_structures::packed::Pu128;
7use rustc_errors::{Applicability, Diag, MultiSpan, listify};
8use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
9use rustc_hir::lang_items::LangItem;
10use rustc_hir::{
11    self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind,
12    GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind,
13    WherePredicateKind, expr_needs_parens,
14};
15use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
16use rustc_hir_analysis::suggest_impl_trait;
17use rustc_middle::middle::stability::EvalResult;
18use rustc_middle::span_bug;
19use rustc_middle::ty::print::with_no_trimmed_paths;
20use rustc_middle::ty::{
21    self, Article, Binder, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Upcast,
22    suggest_constraining_type_params,
23};
24use rustc_session::errors::ExprParenthesesNeeded;
25use rustc_span::source_map::Spanned;
26use rustc_span::{ExpnKind, Ident, MacroKind, Span, Symbol, sym};
27use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
28use rustc_trait_selection::error_reporting::traits::DefIdOrName;
29use rustc_trait_selection::infer::InferCtxtExt;
30use rustc_trait_selection::traits;
31use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
32use tracing::{debug, instrument};
33
34use super::FnCtxt;
35use crate::fn_ctxt::rustc_span::BytePos;
36use crate::method::probe;
37use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
38use crate::{errors, fluent_generated as fluent};
39
40impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
41    pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
42        self.typeck_results
43            .borrow()
44            .liberated_fn_sigs()
45            .get(self.tcx.local_def_id_to_hir_id(self.body_id))
46            .copied()
47    }
48
49    pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diag<'_>) {
50        // This suggestion is incorrect for
51        // fn foo() -> bool { match () { () => true } || match () { () => true } }
52        err.span_suggestion_short(
53            span.shrink_to_hi(),
54            "consider using a semicolon here",
55            ";",
56            Applicability::MaybeIncorrect,
57        );
58    }
59
60    /// On implicit return expressions with mismatched types, provides the following suggestions:
61    ///
62    /// - Points out the method's return type as the reason for the expected type.
63    /// - Possible missing semicolon.
64    /// - Possible missing return type if the return type is the default, and not `fn main()`.
65    pub(crate) fn suggest_mismatched_types_on_tail(
66        &self,
67        err: &mut Diag<'_>,
68        expr: &'tcx hir::Expr<'tcx>,
69        expected: Ty<'tcx>,
70        found: Ty<'tcx>,
71        blk_id: HirId,
72    ) -> bool {
73        let expr = expr.peel_drop_temps();
74        let mut pointing_at_return_type = false;
75        if let hir::ExprKind::Break(..) = expr.kind {
76            // `break` type mismatches provide better context for tail `loop` expressions.
77            return false;
78        }
79        if let Some((fn_id, fn_decl)) = self.get_fn_decl(blk_id) {
80            pointing_at_return_type =
81                self.suggest_missing_return_type(err, fn_decl, expected, found, fn_id);
82            self.suggest_missing_break_or_return_expr(
83                err, expr, fn_decl, expected, found, blk_id, fn_id,
84            );
85        }
86        pointing_at_return_type
87    }
88
89    /// When encountering an fn-like type, try accessing the output of the type
90    /// and suggesting calling it if it satisfies a predicate (i.e. if the
91    /// output has a method or a field):
92    /// ```compile_fail,E0308
93    /// fn foo(x: usize) -> usize { x }
94    /// let x: usize = foo;  // suggest calling the `foo` function: `foo(42)`
95    /// ```
96    pub(crate) fn suggest_fn_call(
97        &self,
98        err: &mut Diag<'_>,
99        expr: &hir::Expr<'_>,
100        found: Ty<'tcx>,
101        can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
102    ) -> bool {
103        let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(found) else {
104            return false;
105        };
106        if can_satisfy(output) {
107            let (sugg_call, mut applicability) = match inputs.len() {
108                0 => ("".to_string(), Applicability::MachineApplicable),
109                1..=4 => (
110                    inputs
111                        .iter()
112                        .map(|ty| {
113                            if ty.is_suggestable(self.tcx, false) {
114                                format!("/* {ty} */")
115                            } else {
116                                "/* value */".to_string()
117                            }
118                        })
119                        .collect::<Vec<_>>()
120                        .join(", "),
121                    Applicability::HasPlaceholders,
122                ),
123                _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
124            };
125
126            let msg = match def_id_or_name {
127                DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
128                    DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(),
129                    DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(),
130                    kind => format!("call this {}", self.tcx.def_kind_descr(kind, def_id)),
131                },
132                DefIdOrName::Name(name) => format!("call this {name}"),
133            };
134
135            let sugg = match expr.kind {
136                hir::ExprKind::Call(..)
137                | hir::ExprKind::Path(..)
138                | hir::ExprKind::Index(..)
139                | hir::ExprKind::Lit(..) => {
140                    vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))]
141                }
142                hir::ExprKind::Closure { .. } => {
143                    // Might be `{ expr } || { bool }`
144                    applicability = Applicability::MaybeIncorrect;
145                    vec![
146                        (expr.span.shrink_to_lo(), "(".to_string()),
147                        (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
148                    ]
149                }
150                _ => {
151                    vec![
152                        (expr.span.shrink_to_lo(), "(".to_string()),
153                        (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
154                    ]
155                }
156            };
157
158            err.multipart_suggestion_verbose(
159                format!("use parentheses to {msg}"),
160                sugg,
161                applicability,
162            );
163            return true;
164        }
165        false
166    }
167
168    /// Extracts information about a callable type for diagnostics. This is a
169    /// heuristic -- it doesn't necessarily mean that a type is always callable,
170    /// because the callable type must also be well-formed to be called.
171    pub(in super::super) fn extract_callable_info(
172        &self,
173        ty: Ty<'tcx>,
174    ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
175        self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty)
176    }
177
178    pub(crate) fn suggest_two_fn_call(
179        &self,
180        err: &mut Diag<'_>,
181        lhs_expr: &'tcx hir::Expr<'tcx>,
182        lhs_ty: Ty<'tcx>,
183        rhs_expr: &'tcx hir::Expr<'tcx>,
184        rhs_ty: Ty<'tcx>,
185        can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
186    ) -> bool {
187        if lhs_expr.span.in_derive_expansion() || rhs_expr.span.in_derive_expansion() {
188            return false;
189        }
190        let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty) else {
191            return false;
192        };
193        let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_ty) else {
194            return false;
195        };
196
197        if can_satisfy(lhs_output_ty, rhs_output_ty) {
198            let mut sugg = vec![];
199            let mut applicability = Applicability::MachineApplicable;
200
201            for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
202                let (sugg_call, this_applicability) = match inputs.len() {
203                    0 => ("".to_string(), Applicability::MachineApplicable),
204                    1..=4 => (
205                        inputs
206                            .iter()
207                            .map(|ty| {
208                                if ty.is_suggestable(self.tcx, false) {
209                                    format!("/* {ty} */")
210                                } else {
211                                    "/* value */".to_string()
212                                }
213                            })
214                            .collect::<Vec<_>>()
215                            .join(", "),
216                        Applicability::HasPlaceholders,
217                    ),
218                    _ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
219                };
220
221                applicability = applicability.max(this_applicability);
222
223                match expr.kind {
224                    hir::ExprKind::Call(..)
225                    | hir::ExprKind::Path(..)
226                    | hir::ExprKind::Index(..)
227                    | hir::ExprKind::Lit(..) => {
228                        sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
229                    }
230                    hir::ExprKind::Closure { .. } => {
231                        // Might be `{ expr } || { bool }`
232                        applicability = Applicability::MaybeIncorrect;
233                        sugg.extend([
234                            (expr.span.shrink_to_lo(), "(".to_string()),
235                            (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
236                        ]);
237                    }
238                    _ => {
239                        sugg.extend([
240                            (expr.span.shrink_to_lo(), "(".to_string()),
241                            (expr.span.shrink_to_hi(), format!(")({sugg_call})")),
242                        ]);
243                    }
244                }
245            }
246
247            err.multipart_suggestion_verbose("use parentheses to call these", sugg, applicability);
248
249            true
250        } else {
251            false
252        }
253    }
254
255    pub(crate) fn suggest_remove_last_method_call(
256        &self,
257        err: &mut Diag<'_>,
258        expr: &hir::Expr<'tcx>,
259        expected: Ty<'tcx>,
260    ) -> bool {
261        if let hir::ExprKind::MethodCall(hir::PathSegment { ident: method, .. }, recv_expr, &[], _) =
262            expr.kind
263            && let Some(recv_ty) = self.typeck_results.borrow().expr_ty_opt(recv_expr)
264            && self.may_coerce(recv_ty, expected)
265            && let name = method.name.as_str()
266            && (name.starts_with("to_") || name.starts_with("as_") || name == "into")
267        {
268            let span = if let Some(recv_span) = recv_expr.span.find_ancestor_inside(expr.span) {
269                expr.span.with_lo(recv_span.hi())
270            } else {
271                expr.span.with_lo(method.span.lo() - rustc_span::BytePos(1))
272            };
273            err.span_suggestion_verbose(
274                span,
275                "try removing the method call",
276                "",
277                Applicability::MachineApplicable,
278            );
279            return true;
280        }
281        false
282    }
283
284    pub(crate) fn suggest_deref_ref_or_into(
285        &self,
286        err: &mut Diag<'_>,
287        expr: &hir::Expr<'tcx>,
288        expected: Ty<'tcx>,
289        found: Ty<'tcx>,
290        expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
291    ) -> bool {
292        let expr = expr.peel_blocks();
293        let methods =
294            self.get_conversion_methods_for_diagnostic(expr.span, expected, found, expr.hir_id);
295
296        if let Some((suggestion, msg, applicability, verbose, annotation)) =
297            self.suggest_deref_or_ref(expr, found, expected)
298        {
299            if verbose {
300                err.multipart_suggestion_verbose(msg, suggestion, applicability);
301            } else {
302                err.multipart_suggestion(msg, suggestion, applicability);
303            }
304            if annotation {
305                let suggest_annotation = match expr.peel_drop_temps().kind {
306                    hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(),
307                    _ => return true,
308                };
309                let mut tuple_indexes = Vec::new();
310                let mut expr_id = expr.hir_id;
311                for (parent_id, node) in self.tcx.hir_parent_iter(expr.hir_id) {
312                    match node {
313                        Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => {
314                            tuple_indexes.push(
315                                subs.iter()
316                                    .enumerate()
317                                    .find(|(_, sub_expr)| sub_expr.hir_id == expr_id)
318                                    .unwrap()
319                                    .0,
320                            );
321                            expr_id = parent_id;
322                        }
323                        Node::LetStmt(local) => {
324                            if let Some(mut ty) = local.ty {
325                                while let Some(index) = tuple_indexes.pop() {
326                                    match ty.kind {
327                                        TyKind::Tup(tys) => ty = &tys[index],
328                                        _ => return true,
329                                    }
330                                }
331                                let annotation_span = ty.span;
332                                err.span_suggestion(
333                                    annotation_span.with_hi(annotation_span.lo()),
334                                    "alternatively, consider changing the type annotation",
335                                    suggest_annotation,
336                                    Applicability::MaybeIncorrect,
337                                );
338                            }
339                            break;
340                        }
341                        _ => break,
342                    }
343                }
344            }
345            return true;
346        }
347
348        if self.suggest_else_fn_with_closure(err, expr, found, expected) {
349            return true;
350        }
351
352        if self.suggest_fn_call(err, expr, found, |output| self.may_coerce(output, expected))
353            && let ty::FnDef(def_id, ..) = *found.kind()
354            && let Some(sp) = self.tcx.hir_span_if_local(def_id)
355        {
356            let name = self.tcx.item_name(def_id);
357            let kind = self.tcx.def_kind(def_id);
358            if let DefKind::Ctor(of, CtorKind::Fn) = kind {
359                err.span_label(
360                    sp,
361                    format!(
362                        "`{name}` defines {} constructor here, which should be called",
363                        match of {
364                            CtorOf::Struct => "a struct",
365                            CtorOf::Variant => "an enum variant",
366                        }
367                    ),
368                );
369            } else {
370                let descr = self.tcx.def_kind_descr(kind, def_id);
371                err.span_label(sp, format!("{descr} `{name}` defined here"));
372            }
373            return true;
374        }
375
376        if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
377            return true;
378        }
379
380        if !methods.is_empty() {
381            let mut suggestions = methods
382                .iter()
383                .filter_map(|conversion_method| {
384                    let conversion_method_name = conversion_method.name();
385                    let receiver_method_ident = expr.method_ident();
386                    if let Some(method_ident) = receiver_method_ident
387                        && method_ident.name == conversion_method_name
388                    {
389                        return None; // do not suggest code that is already there (#53348)
390                    }
391
392                    let method_call_list = [sym::to_vec, sym::to_string];
393                    let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
394                        && receiver_method.ident.name == sym::clone
395                        && method_call_list.contains(&conversion_method_name)
396                    // If receiver is `.clone()` and found type has one of those methods,
397                    // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
398                    // to an owned type (`Vec` or `String`). These conversions clone internally,
399                    // so we remove the user's `clone` call.
400                    {
401                        vec![(receiver_method.ident.span, conversion_method_name.to_string())]
402                    } else if self.precedence(expr) < ExprPrecedence::Unambiguous {
403                        vec![
404                            (expr.span.shrink_to_lo(), "(".to_string()),
405                            (expr.span.shrink_to_hi(), format!(").{}()", conversion_method_name)),
406                        ]
407                    } else {
408                        vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method_name))]
409                    };
410                    let struct_pat_shorthand_field =
411                        self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr);
412                    if let Some(name) = struct_pat_shorthand_field {
413                        sugg.insert(0, (expr.span.shrink_to_lo(), format!("{name}: ")));
414                    }
415                    Some(sugg)
416                })
417                .peekable();
418            if suggestions.peek().is_some() {
419                err.multipart_suggestions(
420                    "try using a conversion method",
421                    suggestions,
422                    Applicability::MaybeIncorrect,
423                );
424                return true;
425            }
426        }
427
428        if let Some((found_ty_inner, expected_ty_inner, error_tys)) =
429            self.deconstruct_option_or_result(found, expected)
430            && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind()
431        {
432            // Suggest removing any stray borrows (unless there's macro shenanigans involved).
433            let inner_expr = expr.peel_borrows();
434            if !inner_expr.span.eq_ctxt(expr.span) {
435                return false;
436            }
437            let borrow_removal_span = if inner_expr.hir_id == expr.hir_id {
438                None
439            } else {
440                Some(expr.span.shrink_to_lo().until(inner_expr.span))
441            };
442            // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for
443            // `as_ref` and `as_deref` compatibility.
444            let error_tys_equate_as_ref = error_tys.is_none_or(|(found, expected)| {
445                self.can_eq(
446                    self.param_env,
447                    Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, found),
448                    expected,
449                )
450            });
451
452            let prefix_wrap = |sugg: &str| {
453                if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
454                    format!(": {}{}", name, sugg)
455                } else {
456                    sugg.to_string()
457                }
458            };
459
460            // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
461            // but those checks need to be a bit more delicate and the benefit is diminishing.
462            if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
463                let sugg = prefix_wrap(".as_ref()");
464                err.subdiagnostic(errors::SuggestConvertViaMethod {
465                    span: expr.span.shrink_to_hi(),
466                    sugg,
467                    expected,
468                    found,
469                    borrow_removal_span,
470                });
471                return true;
472            } else if let ty::Ref(_, peeled_found_ty, _) = found_ty_inner.kind()
473                && let ty::Adt(adt, _) = peeled_found_ty.peel_refs().kind()
474                && self.tcx.is_lang_item(adt.did(), LangItem::String)
475                && peeled.is_str()
476                // `Result::map`, conversely, does not take ref of the error type.
477                && error_tys.is_none_or(|(found, expected)| {
478                    self.can_eq(self.param_env, found, expected)
479                })
480            {
481                let sugg = prefix_wrap(".map(|x| x.as_str())");
482                err.span_suggestion_verbose(
483                    expr.span.shrink_to_hi(),
484                    fluent::hir_typeck_convert_to_str,
485                    sugg,
486                    Applicability::MachineApplicable,
487                );
488                return true;
489            } else {
490                if !error_tys_equate_as_ref {
491                    return false;
492                }
493                let mut steps = self.autoderef(expr.span, found_ty_inner).silence_errors();
494                if let Some((deref_ty, _)) = steps.nth(1)
495                    && self.can_eq(self.param_env, deref_ty, peeled)
496                {
497                    let sugg = prefix_wrap(".as_deref()");
498                    err.subdiagnostic(errors::SuggestConvertViaMethod {
499                        span: expr.span.shrink_to_hi(),
500                        sugg,
501                        expected,
502                        found,
503                        borrow_removal_span,
504                    });
505                    return true;
506                }
507                for (deref_ty, n_step) in steps {
508                    if self.can_eq(self.param_env, deref_ty, peeled) {
509                        let explicit_deref = "*".repeat(n_step);
510                        let sugg = prefix_wrap(&format!(".map(|v| &{explicit_deref}v)"));
511                        err.subdiagnostic(errors::SuggestConvertViaMethod {
512                            span: expr.span.shrink_to_hi(),
513                            sugg,
514                            expected,
515                            found,
516                            borrow_removal_span,
517                        });
518                        return true;
519                    }
520                }
521            }
522        }
523
524        false
525    }
526
527    /// If `ty` is `Option<T>`, returns `T, T, None`.
528    /// If `ty` is `Result<T, E>`, returns `T, T, Some(E, E)`.
529    /// Otherwise, returns `None`.
530    fn deconstruct_option_or_result(
531        &self,
532        found_ty: Ty<'tcx>,
533        expected_ty: Ty<'tcx>,
534    ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> {
535        let ty::Adt(found_adt, found_args) = found_ty.peel_refs().kind() else {
536            return None;
537        };
538        let ty::Adt(expected_adt, expected_args) = expected_ty.kind() else {
539            return None;
540        };
541        if self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
542            && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
543        {
544            Some((found_args.type_at(0), expected_args.type_at(0), None))
545        } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did())
546            && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did())
547        {
548            Some((
549                found_args.type_at(0),
550                expected_args.type_at(0),
551                Some((found_args.type_at(1), expected_args.type_at(1))),
552            ))
553        } else {
554            None
555        }
556    }
557
558    /// When encountering the expected boxed value allocated in the stack, suggest allocating it
559    /// in the heap by calling `Box::new()`.
560    pub(in super::super) fn suggest_boxing_when_appropriate(
561        &self,
562        err: &mut Diag<'_>,
563        span: Span,
564        hir_id: HirId,
565        expected: Ty<'tcx>,
566        found: Ty<'tcx>,
567    ) -> bool {
568        // Do not suggest `Box::new` in const context.
569        if self.tcx.hir_is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
570            return false;
571        }
572        if self.may_coerce(Ty::new_box(self.tcx, found), expected) {
573            let suggest_boxing = match found.kind() {
574                ty::Tuple(tuple) if tuple.is_empty() => {
575                    errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
576                }
577                ty::Coroutine(def_id, ..)
578                    if matches!(
579                        self.tcx.coroutine_kind(def_id),
580                        Some(CoroutineKind::Desugared(
581                            CoroutineDesugaring::Async,
582                            CoroutineSource::Closure
583                        ))
584                    ) =>
585                {
586                    errors::SuggestBoxing::AsyncBody
587                }
588                _ if let Node::ExprField(expr_field) = self.tcx.parent_hir_node(hir_id)
589                    && expr_field.is_shorthand =>
590                {
591                    errors::SuggestBoxing::ExprFieldShorthand {
592                        start: span.shrink_to_lo(),
593                        end: span.shrink_to_hi(),
594                        ident: expr_field.ident,
595                    }
596                }
597                _ => errors::SuggestBoxing::Other {
598                    start: span.shrink_to_lo(),
599                    end: span.shrink_to_hi(),
600                },
601            };
602            err.subdiagnostic(suggest_boxing);
603
604            true
605        } else {
606            false
607        }
608    }
609
610    /// When encountering a closure that captures variables, where a FnPtr is expected,
611    /// suggest a non-capturing closure
612    pub(in super::super) fn suggest_no_capture_closure(
613        &self,
614        err: &mut Diag<'_>,
615        expected: Ty<'tcx>,
616        found: Ty<'tcx>,
617    ) -> bool {
618        if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind())
619            && let Some(upvars) = self.tcx.upvars_mentioned(*def_id)
620        {
621            // Report upto four upvars being captured to reduce the amount error messages
622            // reported back to the user.
623            let spans_and_labels = upvars
624                .iter()
625                .take(4)
626                .map(|(var_hir_id, upvar)| {
627                    let var_name = self.tcx.hir_name(*var_hir_id).to_string();
628                    let msg = format!("`{var_name}` captured here");
629                    (upvar.span, msg)
630                })
631                .collect::<Vec<_>>();
632
633            let mut multi_span: MultiSpan =
634                spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
635            for (sp, label) in spans_and_labels {
636                multi_span.push_span_label(sp, label);
637            }
638            err.span_note(
639                multi_span,
640                "closures can only be coerced to `fn` types if they do not capture any variables",
641            );
642            return true;
643        }
644        false
645    }
646
647    /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
648    #[instrument(skip(self, err))]
649    pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
650        &self,
651        err: &mut Diag<'_>,
652        expr: &hir::Expr<'_>,
653        expected: Ty<'tcx>,
654        found: Ty<'tcx>,
655    ) -> bool {
656        // Handle #68197.
657
658        if self.tcx.hir_is_inside_const_context(expr.hir_id) {
659            // Do not suggest `Box::new` in const context.
660            return false;
661        }
662        let pin_did = self.tcx.lang_items().pin_type();
663        // This guards the `new_box` below.
664        if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
665            return false;
666        }
667        let box_found = Ty::new_box(self.tcx, found);
668        let Some(pin_box_found) = Ty::new_lang_item(self.tcx, box_found, LangItem::Pin) else {
669            return false;
670        };
671        let Some(pin_found) = Ty::new_lang_item(self.tcx, found, LangItem::Pin) else {
672            return false;
673        };
674        match expected.kind() {
675            ty::Adt(def, _) if Some(def.did()) == pin_did => {
676                if self.may_coerce(pin_box_found, expected) {
677                    debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
678                    match found.kind() {
679                        ty::Adt(def, _) if def.is_box() => {
680                            err.help("use `Box::pin`");
681                        }
682                        _ => {
683                            let prefix = if let Some(name) =
684                                self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr)
685                            {
686                                format!("{}: ", name)
687                            } else {
688                                String::new()
689                            };
690                            let suggestion = vec![
691                                (expr.span.shrink_to_lo(), format!("{prefix}Box::pin(")),
692                                (expr.span.shrink_to_hi(), ")".to_string()),
693                            ];
694                            err.multipart_suggestion(
695                                "you need to pin and box this expression",
696                                suggestion,
697                                Applicability::MaybeIncorrect,
698                            );
699                        }
700                    }
701                    true
702                } else if self.may_coerce(pin_found, expected) {
703                    match found.kind() {
704                        ty::Adt(def, _) if def.is_box() => {
705                            err.help("use `Box::pin`");
706                            true
707                        }
708                        _ => false,
709                    }
710                } else {
711                    false
712                }
713            }
714            ty::Adt(def, _) if def.is_box() && self.may_coerce(box_found, expected) => {
715                // Check if the parent expression is a call to Pin::new. If it
716                // is and we were expecting a Box, ergo Pin<Box<expected>>, we
717                // can suggest Box::pin.
718                let Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. }) =
719                    self.tcx.parent_hir_node(expr.hir_id)
720                else {
721                    return false;
722                };
723                match fn_name.kind {
724                    ExprKind::Path(QPath::TypeRelative(
725                        hir::Ty {
726                            kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })),
727                            ..
728                        },
729                        method,
730                    )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => {
731                        err.span_suggestion(
732                            fn_name.span,
733                            "use `Box::pin` to pin and box this expression",
734                            "Box::pin",
735                            Applicability::MachineApplicable,
736                        );
737                        true
738                    }
739                    _ => false,
740                }
741            }
742            _ => false,
743        }
744    }
745
746    /// A common error is to forget to add a semicolon at the end of a block, e.g.,
747    ///
748    /// ```compile_fail,E0308
749    /// # fn bar_that_returns_u32() -> u32 { 4 }
750    /// fn foo() {
751    ///     bar_that_returns_u32()
752    /// }
753    /// ```
754    ///
755    /// This routine checks if the return expression in a block would make sense on its own as a
756    /// statement and the return type has been left as default or has been specified as `()`. If so,
757    /// it suggests adding a semicolon.
758    ///
759    /// If the expression is the expression of a closure without block (`|| expr`), a
760    /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`.
761    pub(crate) fn suggest_missing_semicolon(
762        &self,
763        err: &mut Diag<'_>,
764        expression: &'tcx hir::Expr<'tcx>,
765        expected: Ty<'tcx>,
766        needs_block: bool,
767        parent_is_closure: bool,
768    ) {
769        if expected.is_unit() {
770            // `BlockTailExpression` only relevant if the tail expr would be
771            // useful on its own.
772            match expression.kind {
773                ExprKind::Call(..)
774                | ExprKind::MethodCall(..)
775                | ExprKind::Loop(..)
776                | ExprKind::If(..)
777                | ExprKind::Match(..)
778                | ExprKind::Block(..)
779                    if expression.can_have_side_effects()
780                        // If the expression is from an external macro, then do not suggest
781                        // adding a semicolon, because there's nowhere to put it.
782                        // See issue #81943.
783                        && !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
784                {
785                    if needs_block {
786                        err.multipart_suggestion(
787                            "consider using a semicolon here",
788                            vec![
789                                (expression.span.shrink_to_lo(), "{ ".to_owned()),
790                                (expression.span.shrink_to_hi(), "; }".to_owned()),
791                            ],
792                            Applicability::MachineApplicable,
793                        );
794                    } else {
795                        err.span_suggestion(
796                            expression.span.shrink_to_hi(),
797                            "consider using a semicolon here",
798                            ";",
799                            Applicability::MachineApplicable,
800                        );
801                    }
802                }
803                ExprKind::Path(..) | ExprKind::Lit(_)
804                    if parent_is_closure
805                        && !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
806                {
807                    err.span_suggestion_verbose(
808                        expression.span.shrink_to_lo(),
809                        "consider ignoring the value",
810                        "_ = ",
811                        Applicability::MachineApplicable,
812                    );
813                }
814                _ => (),
815            }
816        }
817    }
818
819    /// A possible error is to forget to add a return type that is needed:
820    ///
821    /// ```compile_fail,E0308
822    /// # fn bar_that_returns_u32() -> u32 { 4 }
823    /// fn foo() {
824    ///     bar_that_returns_u32()
825    /// }
826    /// ```
827    ///
828    /// This routine checks if the return type is left as default, the method is not part of an
829    /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
830    /// type.
831    #[instrument(level = "trace", skip(self, err))]
832    pub(in super::super) fn suggest_missing_return_type(
833        &self,
834        err: &mut Diag<'_>,
835        fn_decl: &hir::FnDecl<'tcx>,
836        expected: Ty<'tcx>,
837        found: Ty<'tcx>,
838        fn_id: LocalDefId,
839    ) -> bool {
840        // Can't suggest `->` on a block-like coroutine
841        if let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Block)) =
842            self.tcx.coroutine_kind(fn_id)
843        {
844            return false;
845        }
846
847        let found =
848            self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
849        // Only suggest changing the return type for methods that
850        // haven't set a return type at all (and aren't `fn main()`, impl or closure).
851        match &fn_decl.output {
852            // For closure with default returns, don't suggest adding return type
853            &hir::FnRetTy::DefaultReturn(_) if self.tcx.is_closure_like(fn_id.to_def_id()) => {}
854            &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
855                if !self.can_add_return_type(fn_id) {
856                    err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span });
857                } else if let Some(found) = found.make_suggestable(self.tcx, false, None) {
858                    err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
859                        span,
860                        found: found.to_string(),
861                    });
862                } else if let Some(sugg) = suggest_impl_trait(self, self.param_env, found) {
863                    err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: sugg });
864                } else {
865                    // FIXME: if `found` could be `impl Iterator` we should suggest that.
866                    err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span });
867                }
868
869                return true;
870            }
871            hir::FnRetTy::Return(hir_ty) => {
872                if let hir::TyKind::OpaqueDef(op_ty, ..) = hir_ty.kind
873                    // FIXME: account for RPITIT.
874                    && let [hir::GenericBound::Trait(trait_ref)] = op_ty.bounds
875                    && let Some(hir::PathSegment { args: Some(generic_args), .. }) =
876                        trait_ref.trait_ref.path.segments.last()
877                    && let [constraint] = generic_args.constraints
878                    && let Some(ty) = constraint.ty()
879                {
880                    // Check if async function's return type was omitted.
881                    // Don't emit suggestions if the found type is `impl Future<...>`.
882                    debug!(?found);
883                    if found.is_suggestable(self.tcx, false) {
884                        if ty.span.is_empty() {
885                            err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
886                                span: ty.span,
887                                found: found.to_string(),
888                            });
889                            return true;
890                        } else {
891                            err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
892                                span: ty.span,
893                                expected,
894                            });
895                        }
896                    }
897                } else {
898                    // Only point to return type if the expected type is the return type, as if they
899                    // are not, the expectation must have been caused by something else.
900                    debug!(?hir_ty, "return type");
901                    let ty = self.lowerer().lower_ty(hir_ty);
902                    debug!(?ty, "return type (lowered)");
903                    debug!(?expected, "expected type");
904                    let bound_vars =
905                        self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
906                    let ty = Binder::bind_with_vars(ty, bound_vars);
907                    let ty = self.normalize(hir_ty.span, ty);
908                    let ty = self.tcx.instantiate_bound_regions_with_erased(ty);
909                    if self.may_coerce(expected, ty) {
910                        err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
911                            span: hir_ty.span,
912                            expected,
913                        });
914                        self.try_suggest_return_impl_trait(err, expected, found, fn_id);
915                        self.try_note_caller_chooses_ty_for_ty_param(err, expected, found);
916                        return true;
917                    }
918                }
919            }
920            _ => {}
921        }
922        false
923    }
924
925    /// Checks whether we can add a return type to a function.
926    /// Assumes given function doesn't have a explicit return type.
927    fn can_add_return_type(&self, fn_id: LocalDefId) -> bool {
928        match self.tcx.hir_node_by_def_id(fn_id) {
929            Node::Item(item) => {
930                let (ident, _, _, _) = item.expect_fn();
931                // This is less than ideal, it will not suggest a return type span on any
932                // method called `main`, regardless of whether it is actually the entry point,
933                // but it will still present it as the reason for the expected type.
934                ident.name != sym::main
935            }
936            Node::ImplItem(item) => {
937                // If it doesn't impl a trait, we can add a return type
938                let Node::Item(&hir::Item {
939                    kind: hir::ItemKind::Impl(&hir::Impl { of_trait, .. }),
940                    ..
941                }) = self.tcx.parent_hir_node(item.hir_id())
942                else {
943                    unreachable!();
944                };
945
946                of_trait.is_none()
947            }
948            _ => true,
949        }
950    }
951
952    fn try_note_caller_chooses_ty_for_ty_param(
953        &self,
954        diag: &mut Diag<'_>,
955        expected: Ty<'tcx>,
956        found: Ty<'tcx>,
957    ) {
958        // Only show the note if:
959        // 1. `expected` ty is a type parameter;
960        // 2. The `expected` type parameter does *not* occur in the return expression type. This can
961        //    happen for e.g. `fn foo<T>(t: &T) -> T { t }`, where `expected` is `T` but `found` is
962        //    `&T`. Saying "the caller chooses a type for `T` which can be different from `&T`" is
963        //    "well duh" and is only confusing and not helpful.
964        let ty::Param(expected_ty_as_param) = expected.kind() else {
965            return;
966        };
967
968        if found.contains(expected) {
969            return;
970        }
971
972        diag.subdiagnostic(errors::NoteCallerChoosesTyForTyParam {
973            ty_param_name: expected_ty_as_param.name,
974            found_ty: found,
975        });
976    }
977
978    /// check whether the return type is a generic type with a trait bound
979    /// only suggest this if the generic param is not present in the arguments
980    /// if this is true, hint them towards changing the return type to `impl Trait`
981    /// ```compile_fail,E0308
982    /// fn cant_name_it<T: Fn() -> u32>() -> T {
983    ///     || 3
984    /// }
985    /// ```
986    fn try_suggest_return_impl_trait(
987        &self,
988        err: &mut Diag<'_>,
989        expected: Ty<'tcx>,
990        found: Ty<'tcx>,
991        fn_id: LocalDefId,
992    ) {
993        // Only apply the suggestion if:
994        //  - the return type is a generic parameter
995        //  - the generic param is not used as a fn param
996        //  - the generic param has at least one bound
997        //  - the generic param doesn't appear in any other bounds where it's not the Self type
998        // Suggest:
999        //  - Changing the return type to be `impl <all bounds>`
1000
1001        debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found);
1002
1003        let ty::Param(expected_ty_as_param) = expected.kind() else { return };
1004
1005        let fn_node = self.tcx.hir_node_by_def_id(fn_id);
1006
1007        let hir::Node::Item(hir::Item {
1008            kind:
1009                hir::ItemKind::Fn {
1010                    sig:
1011                        hir::FnSig {
1012                            decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. },
1013                            ..
1014                        },
1015                    generics: hir::Generics { params, predicates, .. },
1016                    ..
1017                },
1018            ..
1019        }) = fn_node
1020        else {
1021            return;
1022        };
1023
1024        if params.get(expected_ty_as_param.index as usize).is_none() {
1025            return;
1026        };
1027
1028        // get all where BoundPredicates here, because they are used in two cases below
1029        let where_predicates = predicates
1030            .iter()
1031            .filter_map(|p| match p.kind {
1032                WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
1033                    bounds,
1034                    bounded_ty,
1035                    ..
1036                }) => {
1037                    // FIXME: Maybe these calls to `lower_ty` can be removed (and the ones below)
1038                    let ty = self.lowerer().lower_ty(bounded_ty);
1039                    Some((ty, bounds))
1040                }
1041                _ => None,
1042            })
1043            .map(|(ty, bounds)| match ty.kind() {
1044                ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
1045                // check whether there is any predicate that contains our `T`, like `Option<T>: Send`
1046                _ => match ty.contains(expected) {
1047                    true => Err(()),
1048                    false => Ok(None),
1049                },
1050            })
1051            .collect::<Result<Vec<_>, _>>();
1052
1053        let Ok(where_predicates) = where_predicates else { return };
1054
1055        // now get all predicates in the same types as the where bounds, so we can chain them
1056        let predicates_from_where =
1057            where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
1058
1059        // extract all bounds from the source code using their spans
1060        let all_matching_bounds_strs = predicates_from_where
1061            .filter_map(|bound| match bound {
1062                GenericBound::Trait(_) => {
1063                    self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
1064                }
1065                _ => None,
1066            })
1067            .collect::<Vec<String>>();
1068
1069        if all_matching_bounds_strs.len() == 0 {
1070            return;
1071        }
1072
1073        let all_bounds_str = all_matching_bounds_strs.join(" + ");
1074
1075        let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
1076                let ty = self.lowerer().lower_ty( param);
1077                matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
1078            });
1079
1080        if ty_param_used_in_fn_params {
1081            return;
1082        }
1083
1084        err.span_suggestion(
1085            fn_return.span(),
1086            "consider using an impl return type",
1087            format!("impl {all_bounds_str}"),
1088            Applicability::MaybeIncorrect,
1089        );
1090    }
1091
1092    pub(in super::super) fn suggest_missing_break_or_return_expr(
1093        &self,
1094        err: &mut Diag<'_>,
1095        expr: &'tcx hir::Expr<'tcx>,
1096        fn_decl: &hir::FnDecl<'tcx>,
1097        expected: Ty<'tcx>,
1098        found: Ty<'tcx>,
1099        id: HirId,
1100        fn_id: LocalDefId,
1101    ) {
1102        if !expected.is_unit() {
1103            return;
1104        }
1105        let found = self.resolve_vars_if_possible(found);
1106
1107        let in_loop = self.is_loop(id)
1108            || self
1109                .tcx
1110                .hir_parent_iter(id)
1111                .take_while(|(_, node)| {
1112                    // look at parents until we find the first body owner
1113                    node.body_id().is_none()
1114                })
1115                .any(|(parent_id, _)| self.is_loop(parent_id));
1116
1117        let in_local_statement = self.is_local_statement(id)
1118            || self
1119                .tcx
1120                .hir_parent_iter(id)
1121                .any(|(parent_id, _)| self.is_local_statement(parent_id));
1122
1123        if in_loop && in_local_statement {
1124            err.multipart_suggestion(
1125                "you might have meant to break the loop with this value",
1126                vec![
1127                    (expr.span.shrink_to_lo(), "break ".to_string()),
1128                    (expr.span.shrink_to_hi(), ";".to_string()),
1129                ],
1130                Applicability::MaybeIncorrect,
1131            );
1132            return;
1133        }
1134
1135        let scope = self.tcx.hir_parent_iter(id).find(|(_, node)| {
1136            matches!(
1137                node,
1138                Node::Expr(Expr { kind: ExprKind::Closure(..), .. })
1139                    | Node::Item(_)
1140                    | Node::TraitItem(_)
1141                    | Node::ImplItem(_)
1142            )
1143        });
1144        let in_closure =
1145            matches!(scope, Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. }))));
1146
1147        let can_return = match fn_decl.output {
1148            hir::FnRetTy::Return(ty) => {
1149                let ty = self.lowerer().lower_ty(ty);
1150                let bound_vars = self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
1151                let ty = self
1152                    .tcx
1153                    .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
1154                let ty = match self.tcx.asyncness(fn_id) {
1155                    ty::Asyncness::Yes => {
1156                        self.err_ctxt().get_impl_future_output_ty(ty).unwrap_or_else(|| {
1157                            span_bug!(
1158                                fn_decl.output.span(),
1159                                "failed to get output type of async function"
1160                            )
1161                        })
1162                    }
1163                    ty::Asyncness::No => ty,
1164                };
1165                let ty = self.normalize(expr.span, ty);
1166                self.may_coerce(found, ty)
1167            }
1168            hir::FnRetTy::DefaultReturn(_) if in_closure => {
1169                self.ret_coercion.as_ref().is_some_and(|ret| {
1170                    let ret_ty = ret.borrow().expected_ty();
1171                    self.may_coerce(found, ret_ty)
1172                })
1173            }
1174            _ => false,
1175        };
1176        if can_return
1177            && let Some(span) = expr.span.find_ancestor_inside(
1178                self.tcx.hir_span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)),
1179            )
1180        {
1181            // When the expr is in a match arm's body, we shouldn't add semicolon ';' at the end.
1182            // For example:
1183            // fn mismatch_types() -> i32 {
1184            //     match 1 {
1185            //         x => dbg!(x),
1186            //     }
1187            //     todo!()
1188            // }
1189            // -------------^^^^^^^-
1190            // Don't add semicolon `;` at the end of `dbg!(x)` expr
1191            fn is_in_arm<'tcx>(expr: &'tcx hir::Expr<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
1192                for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
1193                    match node {
1194                        hir::Node::Block(block) => {
1195                            if let Some(ret) = block.expr
1196                                && ret.hir_id == expr.hir_id
1197                            {
1198                                continue;
1199                            }
1200                        }
1201                        hir::Node::Arm(arm) => {
1202                            if let hir::ExprKind::Block(block, _) = arm.body.kind
1203                                && let Some(ret) = block.expr
1204                                && ret.hir_id == expr.hir_id
1205                            {
1206                                return true;
1207                            }
1208                        }
1209                        hir::Node::Expr(e) if let hir::ExprKind::Block(block, _) = e.kind => {
1210                            if let Some(ret) = block.expr
1211                                && ret.hir_id == expr.hir_id
1212                            {
1213                                continue;
1214                            }
1215                        }
1216                        _ => {
1217                            return false;
1218                        }
1219                    }
1220                }
1221
1222                false
1223            }
1224            let mut suggs = vec![(span.shrink_to_lo(), "return ".to_string())];
1225            if !is_in_arm(expr, self.tcx) {
1226                suggs.push((span.shrink_to_hi(), ";".to_string()));
1227            }
1228            err.multipart_suggestion_verbose(
1229                "you might have meant to return this value",
1230                suggs,
1231                Applicability::MaybeIncorrect,
1232            );
1233        }
1234    }
1235
1236    pub(in super::super) fn suggest_missing_parentheses(
1237        &self,
1238        err: &mut Diag<'_>,
1239        expr: &hir::Expr<'_>,
1240    ) -> bool {
1241        let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None);
1242        if let Some(sp) = self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
1243            // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
1244            err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
1245            true
1246        } else {
1247            false
1248        }
1249    }
1250
1251    /// Given an expression type mismatch, peel any `&` expressions until we get to
1252    /// a block expression, and then suggest replacing the braces with square braces
1253    /// if it was possibly mistaken array syntax.
1254    pub(crate) fn suggest_block_to_brackets_peeling_refs(
1255        &self,
1256        diag: &mut Diag<'_>,
1257        mut expr: &hir::Expr<'_>,
1258        mut expr_ty: Ty<'tcx>,
1259        mut expected_ty: Ty<'tcx>,
1260    ) -> bool {
1261        loop {
1262            match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
1263                (
1264                    hir::ExprKind::AddrOf(_, _, inner_expr),
1265                    ty::Ref(_, inner_expr_ty, _),
1266                    ty::Ref(_, inner_expected_ty, _),
1267                ) => {
1268                    expr = *inner_expr;
1269                    expr_ty = *inner_expr_ty;
1270                    expected_ty = *inner_expected_ty;
1271                }
1272                (hir::ExprKind::Block(blk, _), _, _) => {
1273                    self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
1274                    break true;
1275                }
1276                _ => break false,
1277            }
1278        }
1279    }
1280
1281    pub(crate) fn suggest_clone_for_ref(
1282        &self,
1283        diag: &mut Diag<'_>,
1284        expr: &hir::Expr<'_>,
1285        expr_ty: Ty<'tcx>,
1286        expected_ty: Ty<'tcx>,
1287    ) -> bool {
1288        if let ty::Ref(_, inner_ty, hir::Mutability::Not) = expr_ty.kind()
1289            && let Some(clone_trait_def) = self.tcx.lang_items().clone_trait()
1290            && expected_ty == *inner_ty
1291            && self
1292                .infcx
1293                .type_implements_trait(
1294                    clone_trait_def,
1295                    [self.tcx.erase_regions(expected_ty)],
1296                    self.param_env,
1297                )
1298                .must_apply_modulo_regions()
1299        {
1300            let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1301                Some(ident) => format!(": {ident}.clone()"),
1302                None => ".clone()".to_string(),
1303            };
1304
1305            let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span).shrink_to_hi();
1306
1307            diag.span_suggestion_verbose(
1308                span,
1309                "consider using clone here",
1310                suggestion,
1311                Applicability::MachineApplicable,
1312            );
1313            return true;
1314        }
1315        false
1316    }
1317
1318    pub(crate) fn suggest_copied_cloned_or_as_ref(
1319        &self,
1320        diag: &mut Diag<'_>,
1321        expr: &hir::Expr<'_>,
1322        expr_ty: Ty<'tcx>,
1323        expected_ty: Ty<'tcx>,
1324    ) -> bool {
1325        let ty::Adt(adt_def, args) = expr_ty.kind() else {
1326            return false;
1327        };
1328        let ty::Adt(expected_adt_def, expected_args) = expected_ty.kind() else {
1329            return false;
1330        };
1331        if adt_def != expected_adt_def {
1332            return false;
1333        }
1334
1335        if Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Result)
1336            && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
1337            || Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Option)
1338        {
1339            let expr_inner_ty = args.type_at(0);
1340            let expected_inner_ty = expected_args.type_at(0);
1341            if let &ty::Ref(_, ty, _mutability) = expr_inner_ty.kind()
1342                && self.can_eq(self.param_env, ty, expected_inner_ty)
1343            {
1344                let def_path = self.tcx.def_path_str(adt_def.did());
1345                let span = expr.span.shrink_to_hi();
1346                let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
1347                    errors::OptionResultRefMismatch::Copied { span, def_path }
1348                } else if self.type_is_clone_modulo_regions(self.param_env, ty) {
1349                    errors::OptionResultRefMismatch::Cloned { span, def_path }
1350                } else {
1351                    return false;
1352                };
1353                diag.subdiagnostic(subdiag);
1354                return true;
1355            }
1356        }
1357
1358        false
1359    }
1360
1361    pub(crate) fn suggest_into(
1362        &self,
1363        diag: &mut Diag<'_>,
1364        expr: &hir::Expr<'_>,
1365        expr_ty: Ty<'tcx>,
1366        expected_ty: Ty<'tcx>,
1367    ) -> bool {
1368        let expr = expr.peel_blocks();
1369
1370        // We have better suggestions for scalar interconversions...
1371        if expr_ty.is_scalar() && expected_ty.is_scalar() {
1372            return false;
1373        }
1374
1375        // Don't suggest turning a block into another type (e.g. `{}.into()`)
1376        if matches!(expr.kind, hir::ExprKind::Block(..)) {
1377            return false;
1378        }
1379
1380        // We'll later suggest `.as_ref` when noting the type error,
1381        // so skip if we will suggest that instead.
1382        if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() {
1383            return false;
1384        }
1385
1386        if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
1387            && self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
1388                self.tcx,
1389                self.misc(expr.span),
1390                self.param_env,
1391                ty::TraitRef::new(self.tcx, into_def_id, [expr_ty, expected_ty]),
1392            ))
1393            && !expr
1394                .span
1395                .macro_backtrace()
1396                .any(|x| matches!(x.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..)))
1397        {
1398            let span = expr
1399                .span
1400                .find_ancestor_not_from_extern_macro(&self.tcx.sess.source_map())
1401                .unwrap_or(expr.span);
1402
1403            let mut sugg = if self.precedence(expr) >= ExprPrecedence::Unambiguous {
1404                vec![(span.shrink_to_hi(), ".into()".to_owned())]
1405            } else {
1406                vec![
1407                    (span.shrink_to_lo(), "(".to_owned()),
1408                    (span.shrink_to_hi(), ").into()".to_owned()),
1409                ]
1410            };
1411            if let Some(name) = self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1412                sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
1413            }
1414            diag.multipart_suggestion(
1415                    format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
1416                    sugg,
1417                    Applicability::MaybeIncorrect
1418                );
1419            return true;
1420        }
1421
1422        false
1423    }
1424
1425    /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()`
1426    pub(crate) fn suggest_option_to_bool(
1427        &self,
1428        diag: &mut Diag<'_>,
1429        expr: &hir::Expr<'_>,
1430        expr_ty: Ty<'tcx>,
1431        expected_ty: Ty<'tcx>,
1432    ) -> bool {
1433        if !expected_ty.is_bool() {
1434            return false;
1435        }
1436
1437        let ty::Adt(def, _) = expr_ty.peel_refs().kind() else {
1438            return false;
1439        };
1440        if !self.tcx.is_diagnostic_item(sym::Option, def.did()) {
1441            return false;
1442        }
1443
1444        let cond_parent = self.tcx.hir_parent_iter(expr.hir_id).find(|(_, node)| {
1445            !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
1446        });
1447        // Don't suggest:
1448        //     `let Some(_) = a.is_some() && b`
1449        //                     ++++++++++
1450        // since the user probably just misunderstood how `let else`
1451        // and `&&` work together.
1452        if let Some((_, hir::Node::LetStmt(local))) = cond_parent
1453            && let hir::PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
1454            | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind
1455            && let hir::QPath::Resolved(None, path) = qpath
1456            && let Some(did) = path
1457                .res
1458                .opt_def_id()
1459                .and_then(|did| self.tcx.opt_parent(did))
1460                .and_then(|did| self.tcx.opt_parent(did))
1461            && self.tcx.is_diagnostic_item(sym::Option, did)
1462        {
1463            return false;
1464        }
1465
1466        let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
1467            Some(ident) => format!(": {ident}.is_some()"),
1468            None => ".is_some()".to_string(),
1469        };
1470
1471        diag.span_suggestion_verbose(
1472            expr.span.shrink_to_hi(),
1473            "use `Option::is_some` to test if the `Option` has a value",
1474            suggestion,
1475            Applicability::MachineApplicable,
1476        );
1477        true
1478    }
1479
1480    // Suggest to change `Option<&Vec<T>>::unwrap_or(&[])` to `Option::map_or(&[], |v| v)`.
1481    #[instrument(level = "trace", skip(self, err, provided_expr))]
1482    pub(crate) fn suggest_deref_unwrap_or(
1483        &self,
1484        err: &mut Diag<'_>,
1485        callee_ty: Option<Ty<'tcx>>,
1486        call_ident: Option<Ident>,
1487        expected_ty: Ty<'tcx>,
1488        provided_ty: Ty<'tcx>,
1489        provided_expr: &Expr<'tcx>,
1490        is_method: bool,
1491    ) {
1492        if !is_method {
1493            return;
1494        }
1495        let Some(callee_ty) = callee_ty else {
1496            return;
1497        };
1498        let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
1499            return;
1500        };
1501        let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
1502            "Option"
1503        } else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) {
1504            "Result"
1505        } else {
1506            return;
1507        };
1508
1509        let Some(call_ident) = call_ident else {
1510            return;
1511        };
1512        if call_ident.name != sym::unwrap_or {
1513            return;
1514        }
1515
1516        let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
1517            return;
1518        };
1519
1520        // NOTE: Can we reuse `suggest_deref_or_ref`?
1521
1522        // Create an dummy type `&[_]` so that both &[] and `&Vec<T>` can coerce to it.
1523        let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
1524            && let ty::Infer(_) = elem_ty.kind()
1525            && self
1526                .try_structurally_resolve_const(provided_expr.span, *size)
1527                .try_to_target_usize(self.tcx)
1528                == Some(0)
1529        {
1530            let slice = Ty::new_slice(self.tcx, *elem_ty);
1531            Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
1532        } else {
1533            provided_ty
1534        };
1535
1536        if !self.may_coerce(expected_ty, dummy_ty) {
1537            return;
1538        }
1539        let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
1540        err.multipart_suggestion_verbose(
1541            msg,
1542            vec![
1543                (call_ident.span, "map_or".to_owned()),
1544                (provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()),
1545            ],
1546            Applicability::MachineApplicable,
1547        );
1548    }
1549
1550    /// Suggest wrapping the block in square brackets instead of curly braces
1551    /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
1552    pub(crate) fn suggest_block_to_brackets(
1553        &self,
1554        diag: &mut Diag<'_>,
1555        blk: &hir::Block<'_>,
1556        blk_ty: Ty<'tcx>,
1557        expected_ty: Ty<'tcx>,
1558    ) {
1559        if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
1560            if self.may_coerce(blk_ty, *elem_ty)
1561                && blk.stmts.is_empty()
1562                && blk.rules == hir::BlockCheckMode::DefaultBlock
1563                && let source_map = self.tcx.sess.source_map()
1564                && let Ok(snippet) = source_map.span_to_snippet(blk.span)
1565                && snippet.starts_with('{')
1566                && snippet.ends_with('}')
1567            {
1568                diag.multipart_suggestion_verbose(
1569                    "to create an array, use square brackets instead of curly braces",
1570                    vec![
1571                        (
1572                            blk.span
1573                                .shrink_to_lo()
1574                                .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
1575                            "[".to_string(),
1576                        ),
1577                        (
1578                            blk.span
1579                                .shrink_to_hi()
1580                                .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
1581                            "]".to_string(),
1582                        ),
1583                    ],
1584                    Applicability::MachineApplicable,
1585                );
1586            }
1587        }
1588    }
1589
1590    #[instrument(skip(self, err))]
1591    pub(crate) fn suggest_floating_point_literal(
1592        &self,
1593        err: &mut Diag<'_>,
1594        expr: &hir::Expr<'_>,
1595        expected_ty: Ty<'tcx>,
1596    ) -> bool {
1597        if !expected_ty.is_floating_point() {
1598            return false;
1599        }
1600        match expr.kind {
1601            ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => {
1602                err.span_suggestion_verbose(
1603                    start.span.shrink_to_hi().with_hi(end.span.lo()),
1604                    "remove the unnecessary `.` operator for a floating point literal",
1605                    '.',
1606                    Applicability::MaybeIncorrect,
1607                );
1608                true
1609            }
1610            ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => {
1611                err.span_suggestion_verbose(
1612                    expr.span.with_lo(start.span.hi()),
1613                    "remove the unnecessary `.` operator for a floating point literal",
1614                    '.',
1615                    Applicability::MaybeIncorrect,
1616                );
1617                true
1618            }
1619            ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => {
1620                err.span_suggestion_verbose(
1621                    expr.span.until(end.span),
1622                    "remove the unnecessary `.` operator and add an integer part for a floating point literal",
1623                    "0.",
1624                    Applicability::MaybeIncorrect,
1625                );
1626                true
1627            }
1628            ExprKind::Lit(Spanned {
1629                node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed),
1630                span,
1631            }) => {
1632                let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) else {
1633                    return false;
1634                };
1635                if !(snippet.starts_with("0x") || snippet.starts_with("0X")) {
1636                    return false;
1637                }
1638                if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) {
1639                    return false;
1640                }
1641                let (_, suffix) = snippet.split_at(snippet.len() - 3);
1642                let value = match suffix {
1643                    "f32" => (lit.get() - 0xf32) / (16 * 16 * 16),
1644                    "f64" => (lit.get() - 0xf64) / (16 * 16 * 16),
1645                    _ => return false,
1646                };
1647                err.span_suggestions(
1648                    expr.span,
1649                    "rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float",
1650                    [format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")],
1651                    Applicability::MaybeIncorrect,
1652                );
1653                true
1654            }
1655            _ => false,
1656        }
1657    }
1658
1659    /// Suggest providing `std::ptr::null()` or `std::ptr::null_mut()` if they
1660    /// pass in a literal 0 to an raw pointer.
1661    #[instrument(skip(self, err))]
1662    pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg(
1663        &self,
1664        err: &mut Diag<'_>,
1665        expr: &hir::Expr<'_>,
1666        expected_ty: Ty<'tcx>,
1667    ) -> bool {
1668        // Expected type needs to be a raw pointer.
1669        let ty::RawPtr(_, mutbl) = expected_ty.kind() else {
1670            return false;
1671        };
1672
1673        // Provided expression needs to be a literal `0`.
1674        let ExprKind::Lit(Spanned { node: rustc_ast::LitKind::Int(Pu128(0), _), span }) = expr.kind
1675        else {
1676            return false;
1677        };
1678
1679        // We need to find a null pointer symbol to suggest
1680        let null_sym = match mutbl {
1681            hir::Mutability::Not => sym::ptr_null,
1682            hir::Mutability::Mut => sym::ptr_null_mut,
1683        };
1684        let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else {
1685            return false;
1686        };
1687        let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did));
1688
1689        // We have satisfied all requirements to provide a suggestion. Emit it.
1690        err.span_suggestion(
1691            span,
1692            format!("if you meant to create a null pointer, use `{null_path_str}()`"),
1693            null_path_str + "()",
1694            Applicability::MachineApplicable,
1695        );
1696
1697        true
1698    }
1699
1700    pub(crate) fn suggest_associated_const(
1701        &self,
1702        err: &mut Diag<'_>,
1703        expr: &hir::Expr<'tcx>,
1704        expected_ty: Ty<'tcx>,
1705    ) -> bool {
1706        let Some((DefKind::AssocFn, old_def_id)) =
1707            self.typeck_results.borrow().type_dependent_def(expr.hir_id)
1708        else {
1709            return false;
1710        };
1711        let old_item_name = self.tcx.item_name(old_def_id);
1712        let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
1713        if old_item_name == capitalized_name {
1714            return false;
1715        }
1716        let (item, segment) = match expr.kind {
1717            hir::ExprKind::Path(QPath::Resolved(
1718                Some(ty),
1719                hir::Path { segments: [segment], .. },
1720            ))
1721            | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => {
1722                if let Some(self_ty) = self.typeck_results.borrow().node_type_opt(ty.hir_id)
1723                    && let Ok(pick) = self.probe_for_name(
1724                        Mode::Path,
1725                        Ident::new(capitalized_name, segment.ident.span),
1726                        Some(expected_ty),
1727                        IsSuggestion(true),
1728                        self_ty,
1729                        expr.hir_id,
1730                        ProbeScope::TraitsInScope,
1731                    )
1732                {
1733                    (pick.item, segment)
1734                } else {
1735                    return false;
1736                }
1737            }
1738            hir::ExprKind::Path(QPath::Resolved(
1739                None,
1740                hir::Path { segments: [.., segment], .. },
1741            )) => {
1742                // we resolved through some path that doesn't end in the item name,
1743                // better not do a bad suggestion by accident.
1744                if old_item_name != segment.ident.name {
1745                    return false;
1746                }
1747                if let Some(item) = self
1748                    .tcx
1749                    .associated_items(self.tcx.parent(old_def_id))
1750                    .filter_by_name_unhygienic(capitalized_name)
1751                    .next()
1752                {
1753                    (*item, segment)
1754                } else {
1755                    return false;
1756                }
1757            }
1758            _ => return false,
1759        };
1760        if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst {
1761            // Same item
1762            return false;
1763        }
1764        let item_ty = self.tcx.type_of(item.def_id).instantiate_identity();
1765        // FIXME(compiler-errors): This check is *so* rudimentary
1766        if item_ty.has_param() {
1767            return false;
1768        }
1769        if self.may_coerce(item_ty, expected_ty) {
1770            err.span_suggestion_verbose(
1771                segment.ident.span,
1772                format!("try referring to the associated const `{capitalized_name}` instead",),
1773                capitalized_name,
1774                Applicability::MachineApplicable,
1775            );
1776            true
1777        } else {
1778            false
1779        }
1780    }
1781
1782    fn is_loop(&self, id: HirId) -> bool {
1783        let node = self.tcx.hir_node(id);
1784        matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
1785    }
1786
1787    fn is_local_statement(&self, id: HirId) -> bool {
1788        let node = self.tcx.hir_node(id);
1789        matches!(node, Node::Stmt(Stmt { kind: StmtKind::Let(..), .. }))
1790    }
1791
1792    /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
1793    /// which is a side-effect of autoref.
1794    pub(crate) fn note_type_is_not_clone(
1795        &self,
1796        diag: &mut Diag<'_>,
1797        expected_ty: Ty<'tcx>,
1798        found_ty: Ty<'tcx>,
1799        expr: &hir::Expr<'_>,
1800    ) {
1801        // When `expr` is `x` in something like `let x = foo.clone(); x`, need to recurse up to get
1802        // `foo` and `clone`.
1803        let expr = self.note_type_is_not_clone_inner_expr(expr);
1804
1805        // If we've recursed to an `expr` of `foo.clone()`, get `foo` and `clone`.
1806        let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else {
1807            return;
1808        };
1809
1810        let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else {
1811            return;
1812        };
1813        let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
1814        let results = self.typeck_results.borrow();
1815        // First, look for a `Clone::clone` call
1816        if segment.ident.name == sym::clone
1817            && results.type_dependent_def_id(expr.hir_id).is_some_and(|did| {
1818                    let assoc_item = self.tcx.associated_item(did);
1819                    assoc_item.container == ty::AssocItemContainer::Trait
1820                        && assoc_item.container_id(self.tcx) == clone_trait_did
1821                })
1822            // If that clone call hasn't already dereferenced the self type (i.e. don't give this
1823            // diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
1824            && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
1825            // Check that we're in fact trying to clone into the expected type
1826            && self.may_coerce(*pointee_ty, expected_ty)
1827            && let trait_ref = ty::TraitRef::new(self.tcx, clone_trait_did, [expected_ty])
1828            // And the expected type doesn't implement `Clone`
1829            && !self.predicate_must_hold_considering_regions(&traits::Obligation::new(
1830                self.tcx,
1831                traits::ObligationCause::dummy(),
1832                self.param_env,
1833                trait_ref,
1834            ))
1835        {
1836            diag.span_note(
1837                callee_expr.span,
1838                format!(
1839                    "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
1840                ),
1841            );
1842            let owner = self.tcx.hir_enclosing_body_owner(expr.hir_id);
1843            if let ty::Param(param) = expected_ty.kind()
1844                && let Some(generics) = self.tcx.hir_get_generics(owner)
1845            {
1846                suggest_constraining_type_params(
1847                    self.tcx,
1848                    generics,
1849                    diag,
1850                    vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
1851                    None,
1852                );
1853            } else {
1854                if let Some(errors) =
1855                    self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env)
1856                {
1857                    match &errors[..] {
1858                        [] => {}
1859                        [error] => {
1860                            diag.help(format!(
1861                                "`Clone` is not implemented because the trait bound `{}` is \
1862                                 not satisfied",
1863                                error.obligation.predicate,
1864                            ));
1865                        }
1866                        _ => {
1867                            diag.help(format!(
1868                                "`Clone` is not implemented because the following trait bounds \
1869                                 could not be satisfied: {}",
1870                                listify(&errors, |e| format!("`{}`", e.obligation.predicate))
1871                                    .unwrap(),
1872                            ));
1873                        }
1874                    }
1875                    for error in errors {
1876                        if let traits::FulfillmentErrorCode::Select(
1877                            traits::SelectionError::Unimplemented,
1878                        ) = error.code
1879                            && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1880                                error.obligation.predicate.kind().skip_binder()
1881                        {
1882                            self.infcx.err_ctxt().suggest_derive(
1883                                &error.obligation,
1884                                diag,
1885                                error.obligation.predicate.kind().rebind(pred),
1886                            );
1887                        }
1888                    }
1889                }
1890                self.suggest_derive(diag, &[(trait_ref.upcast(self.tcx), None, None)]);
1891            }
1892        }
1893    }
1894
1895    /// Given a type mismatch error caused by `&T` being cloned instead of `T`, and
1896    /// the `expr` as the source of this type mismatch, try to find the method call
1897    /// as the source of this error and return that instead. Otherwise, return the
1898    /// original expression.
1899    fn note_type_is_not_clone_inner_expr<'b>(
1900        &'b self,
1901        expr: &'b hir::Expr<'b>,
1902    ) -> &'b hir::Expr<'b> {
1903        match expr.peel_blocks().kind {
1904            hir::ExprKind::Path(hir::QPath::Resolved(
1905                None,
1906                hir::Path { segments: [_], res: crate::Res::Local(binding), .. },
1907            )) => {
1908                let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding) else {
1909                    return expr;
1910                };
1911
1912                match self.tcx.parent_hir_node(*hir_id) {
1913                    // foo.clone()
1914                    hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => {
1915                        self.note_type_is_not_clone_inner_expr(init)
1916                    }
1917                    // When `expr` is more complex like a tuple
1918                    hir::Node::Pat(hir::Pat {
1919                        hir_id: pat_hir_id,
1920                        kind: hir::PatKind::Tuple(pats, ..),
1921                        ..
1922                    }) => {
1923                        let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
1924                            self.tcx.parent_hir_node(*pat_hir_id)
1925                        else {
1926                            return expr;
1927                        };
1928
1929                        match init.peel_blocks().kind {
1930                            ExprKind::Tup(init_tup) => {
1931                                if let Some(init) = pats
1932                                    .iter()
1933                                    .enumerate()
1934                                    .filter(|x| x.1.hir_id == *hir_id)
1935                                    .find_map(|(i, _)| init_tup.get(i))
1936                                {
1937                                    self.note_type_is_not_clone_inner_expr(init)
1938                                } else {
1939                                    expr
1940                                }
1941                            }
1942                            _ => expr,
1943                        }
1944                    }
1945                    _ => expr,
1946                }
1947            }
1948            // If we're calling into a closure that may not be typed recurse into that call. no need
1949            // to worry if it's a call to a typed function or closure as this would ne handled
1950            // previously.
1951            hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => {
1952                if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) =
1953                    call_expr_kind
1954                    && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } =
1955                        call_expr_path
1956                    && let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding)
1957                    && let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
1958                        self.tcx.parent_hir_node(*hir_id)
1959                    && let Expr {
1960                        kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }),
1961                        ..
1962                    } = init
1963                {
1964                    let hir::Body { value: body_expr, .. } = self.tcx.hir_body(*body_id);
1965                    self.note_type_is_not_clone_inner_expr(body_expr)
1966                } else {
1967                    expr
1968                }
1969            }
1970            _ => expr,
1971        }
1972    }
1973
1974    pub(crate) fn is_field_suggestable(
1975        &self,
1976        field: &ty::FieldDef,
1977        hir_id: HirId,
1978        span: Span,
1979    ) -> bool {
1980        // The field must be visible in the containing module.
1981        field.vis.is_accessible_from(self.tcx.parent_module(hir_id), self.tcx)
1982            // The field must not be unstable.
1983            && !matches!(
1984                self.tcx.eval_stability(field.did, None, rustc_span::DUMMY_SP, None),
1985                rustc_middle::middle::stability::EvalResult::Deny { .. }
1986            )
1987            // If the field is from an external crate it must not be `doc(hidden)`.
1988            && (field.did.is_local() || !self.tcx.is_doc_hidden(field.did))
1989            // If the field is hygienic it must come from the same syntax context.
1990            && self.tcx.def_ident_span(field.did).unwrap().normalize_to_macros_2_0().eq_ctxt(span)
1991    }
1992
1993    pub(crate) fn suggest_missing_unwrap_expect(
1994        &self,
1995        err: &mut Diag<'_>,
1996        expr: &hir::Expr<'tcx>,
1997        expected: Ty<'tcx>,
1998        found: Ty<'tcx>,
1999    ) -> bool {
2000        let ty::Adt(adt, args) = found.kind() else {
2001            return false;
2002        };
2003        let ret_ty_matches = |diagnostic_item| {
2004            let Some(sig) = self.body_fn_sig() else {
2005                return false;
2006            };
2007            let ty::Adt(kind, _) = sig.output().kind() else {
2008                return false;
2009            };
2010            self.tcx.is_diagnostic_item(diagnostic_item, kind.did())
2011        };
2012
2013        // don't suggest anything like `Ok(ok_val).unwrap()` , `Some(some_val).unwrap()`,
2014        // `None.unwrap()` etc.
2015        let is_ctor = matches!(
2016            expr.kind,
2017            hir::ExprKind::Call(
2018                hir::Expr {
2019                    kind: hir::ExprKind::Path(hir::QPath::Resolved(
2020                        None,
2021                        hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
2022                    )),
2023                    ..
2024                },
2025                ..,
2026            ) | hir::ExprKind::Path(hir::QPath::Resolved(
2027                None,
2028                hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
2029            )),
2030        );
2031
2032        let (article, kind, variant, sugg_operator) =
2033            if self.tcx.is_diagnostic_item(sym::Result, adt.did()) {
2034                ("a", "Result", "Err", ret_ty_matches(sym::Result))
2035            } else if self.tcx.is_diagnostic_item(sym::Option, adt.did()) {
2036                ("an", "Option", "None", ret_ty_matches(sym::Option))
2037            } else {
2038                return false;
2039            };
2040        if is_ctor || !self.may_coerce(args.type_at(0), expected) {
2041            return false;
2042        }
2043
2044        let (msg, sugg) = if sugg_operator {
2045            (
2046                format!(
2047                    "use the `?` operator to extract the `{found}` value, propagating \
2048                            {article} `{kind}::{variant}` value to the caller"
2049                ),
2050                "?",
2051            )
2052        } else {
2053            (
2054                format!(
2055                    "consider using `{kind}::expect` to unwrap the `{found}` value, \
2056                                panicking if the value is {article} `{kind}::{variant}`"
2057                ),
2058                ".expect(\"REASON\")",
2059            )
2060        };
2061
2062        let sugg = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2063            Some(_) if expr.span.from_expansion() => return false,
2064            Some(ident) => format!(": {ident}{sugg}"),
2065            None => sugg.to_string(),
2066        };
2067
2068        let span = expr
2069            .span
2070            .find_ancestor_not_from_extern_macro(&self.tcx.sess.source_map())
2071            .unwrap_or(expr.span);
2072        err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders);
2073        true
2074    }
2075
2076    pub(crate) fn suggest_coercing_result_via_try_operator(
2077        &self,
2078        err: &mut Diag<'_>,
2079        expr: &hir::Expr<'tcx>,
2080        expected: Ty<'tcx>,
2081        found: Ty<'tcx>,
2082    ) -> bool {
2083        let returned = matches!(
2084            self.tcx.parent_hir_node(expr.hir_id),
2085            hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })
2086        ) || self.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_some();
2087        if returned
2088            && let ty::Adt(e, args_e) = expected.kind()
2089            && let ty::Adt(f, args_f) = found.kind()
2090            && e.did() == f.did()
2091            && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
2092            && let e_ok = args_e.type_at(0)
2093            && let f_ok = args_f.type_at(0)
2094            && self.infcx.can_eq(self.param_env, f_ok, e_ok)
2095            && let e_err = args_e.type_at(1)
2096            && let f_err = args_f.type_at(1)
2097            && self
2098                .infcx
2099                .type_implements_trait(
2100                    self.tcx.get_diagnostic_item(sym::Into).unwrap(),
2101                    [f_err, e_err],
2102                    self.param_env,
2103                )
2104                .must_apply_modulo_regions()
2105        {
2106            err.multipart_suggestion(
2107                "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
2108                 in `Ok` so the expression remains of type `Result`",
2109                vec![
2110                    (expr.span.shrink_to_lo(), "Ok(".to_string()),
2111                    (expr.span.shrink_to_hi(), "?)".to_string()),
2112                ],
2113                Applicability::MaybeIncorrect,
2114            );
2115            return true;
2116        }
2117        false
2118    }
2119
2120    // If the expr is a while or for loop and is the tail expr of its
2121    // enclosing body suggest returning a value right after it
2122    pub(crate) fn suggest_returning_value_after_loop(
2123        &self,
2124        err: &mut Diag<'_>,
2125        expr: &hir::Expr<'tcx>,
2126        expected: Ty<'tcx>,
2127    ) -> bool {
2128        let tcx = self.tcx;
2129        let enclosing_scope =
2130            tcx.hir_get_enclosing_scope(expr.hir_id).map(|hir_id| tcx.hir_node(hir_id));
2131
2132        // Get tail expr of the enclosing block or body
2133        let tail_expr = if let Some(Node::Block(hir::Block { expr, .. })) = enclosing_scope
2134            && expr.is_some()
2135        {
2136            *expr
2137        } else {
2138            let body_def_id = tcx.hir_enclosing_body_owner(expr.hir_id);
2139            let body = tcx.hir_body_owned_by(body_def_id);
2140
2141            // Get tail expr of the body
2142            match body.value.kind {
2143                // Regular function body etc.
2144                hir::ExprKind::Block(block, _) => block.expr,
2145                // Anon const body (there's no block in this case)
2146                hir::ExprKind::DropTemps(expr) => Some(expr),
2147                _ => None,
2148            }
2149        };
2150
2151        let Some(tail_expr) = tail_expr else {
2152            return false; // Body doesn't have a tail expr we can compare with
2153        };
2154
2155        // Get the loop expr within the tail expr
2156        let loop_expr_in_tail = match expr.kind {
2157            hir::ExprKind::Loop(_, _, hir::LoopSource::While, _) => tail_expr,
2158            hir::ExprKind::Loop(_, _, hir::LoopSource::ForLoop, _) => {
2159                match tail_expr.peel_drop_temps() {
2160                    Expr { kind: ExprKind::Match(_, [Arm { body, .. }], _), .. } => body,
2161                    _ => return false, // Not really a for loop
2162                }
2163            }
2164            _ => return false, // Not a while or a for loop
2165        };
2166
2167        // If the expr is the loop expr in the tail
2168        // then make the suggestion
2169        if expr.hir_id == loop_expr_in_tail.hir_id {
2170            let span = expr.span;
2171
2172            let (msg, suggestion) = if expected.is_never() {
2173                (
2174                    "consider adding a diverging expression here",
2175                    "`loop {}` or `panic!(\"...\")`".to_string(),
2176                )
2177            } else {
2178                ("consider returning a value here", format!("`{expected}` value"))
2179            };
2180
2181            let src_map = tcx.sess.source_map();
2182            let suggestion = if src_map.is_multiline(expr.span) {
2183                let indentation = src_map.indentation_before(span).unwrap_or_else(String::new);
2184                format!("\n{indentation}/* {suggestion} */")
2185            } else {
2186                // If the entire expr is on a single line
2187                // put the suggestion also on the same line
2188                format!(" /* {suggestion} */")
2189            };
2190
2191            err.span_suggestion_verbose(
2192                span.shrink_to_hi(),
2193                msg,
2194                suggestion,
2195                Applicability::MaybeIncorrect,
2196            );
2197
2198            true
2199        } else {
2200            false
2201        }
2202    }
2203
2204    /// Suggest replacing comma with semicolon in incorrect repeat expressions
2205    /// like `["_", 10]` or `vec![String::new(), 10]`.
2206    pub(crate) fn suggest_semicolon_in_repeat_expr(
2207        &self,
2208        err: &mut Diag<'_>,
2209        expr: &hir::Expr<'_>,
2210        expr_ty: Ty<'tcx>,
2211    ) -> bool {
2212        // Check if `expr` is contained in array of two elements
2213        if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id)
2214            && let hir::ExprKind::Array(elements) = array_expr.kind
2215            && let [first, second] = &elements[..]
2216            && second.hir_id == expr.hir_id
2217        {
2218            // Span between the two elements of the array
2219            let comma_span = first.span.between(second.span);
2220
2221            // Check if `expr` is a constant value of type `usize`.
2222            // This can only detect const variable declarations and
2223            // calls to const functions.
2224
2225            // Checking this here instead of rustc_hir::hir because
2226            // this check needs access to `self.tcx` but rustc_hir
2227            // has no access to `TyCtxt`.
2228            let expr_is_const_usize = expr_ty.is_usize()
2229                && match expr.kind {
2230                    ExprKind::Path(QPath::Resolved(
2231                        None,
2232                        Path { res: Res::Def(DefKind::Const, _), .. },
2233                    )) => true,
2234                    ExprKind::Call(
2235                        Expr {
2236                            kind:
2237                                ExprKind::Path(QPath::Resolved(
2238                                    None,
2239                                    Path { res: Res::Def(DefKind::Fn, fn_def_id), .. },
2240                                )),
2241                            ..
2242                        },
2243                        _,
2244                    ) => self.tcx.is_const_fn(*fn_def_id),
2245                    _ => false,
2246                };
2247
2248            // Type of the first element is guaranteed to be checked
2249            // when execution reaches here because `mismatched types`
2250            // error occurs only when type of second element of array
2251            // is not the same as type of first element.
2252            let first_ty = self.typeck_results.borrow().expr_ty(first);
2253
2254            // `array_expr` is from a macro `vec!["a", 10]` if
2255            // 1. array expression's span is imported from a macro
2256            // 2. first element of array implements `Clone` trait
2257            // 3. second element is an integer literal or is an expression of `usize` like type
2258            if self.tcx.sess.source_map().is_imported(array_expr.span)
2259                && self.type_is_clone_modulo_regions(self.param_env, first_ty)
2260                && (expr.is_size_lit() || expr_ty.is_usize_like())
2261            {
2262                err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2263                    comma_span,
2264                    descr: "a vector",
2265                });
2266                return true;
2267            }
2268
2269            // `array_expr` is from an array `["a", 10]` if
2270            // 1. first element of array implements `Copy` trait
2271            // 2. second element is an integer literal or is a const value of type `usize`
2272            if self.type_is_copy_modulo_regions(self.param_env, first_ty)
2273                && (expr.is_size_lit() || expr_is_const_usize)
2274            {
2275                err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2276                    comma_span,
2277                    descr: "an array",
2278                });
2279                return true;
2280            }
2281        }
2282        false
2283    }
2284
2285    /// If the expected type is an enum (Issue #55250) with any variants whose
2286    /// sole field is of the found type, suggest such variants. (Issue #42764)
2287    pub(crate) fn suggest_compatible_variants(
2288        &self,
2289        err: &mut Diag<'_>,
2290        expr: &hir::Expr<'_>,
2291        expected: Ty<'tcx>,
2292        expr_ty: Ty<'tcx>,
2293    ) -> bool {
2294        if expr.span.in_external_macro(self.tcx.sess.source_map()) {
2295            return false;
2296        }
2297        if let ty::Adt(expected_adt, args) = expected.kind() {
2298            if let hir::ExprKind::Field(base, ident) = expr.kind {
2299                let base_ty = self.typeck_results.borrow().expr_ty(base);
2300                if self.can_eq(self.param_env, base_ty, expected)
2301                    && let Some(base_span) = base.span.find_ancestor_inside(expr.span)
2302                {
2303                    err.span_suggestion_verbose(
2304                        expr.span.with_lo(base_span.hi()),
2305                        format!("consider removing the tuple struct field `{ident}`"),
2306                        "",
2307                        Applicability::MaybeIncorrect,
2308                    );
2309                    return true;
2310                }
2311            }
2312
2313            // If the expression is of type () and it's the return expression of a block,
2314            // we suggest adding a separate return expression instead.
2315            // (To avoid things like suggesting `Ok(while .. { .. })`.)
2316            if expr_ty.is_unit() {
2317                let mut id = expr.hir_id;
2318                let mut parent;
2319
2320                // Unroll desugaring, to make sure this works for `for` loops etc.
2321                loop {
2322                    parent = self.tcx.parent_hir_id(id);
2323                    let parent_span = self.tcx.hir_span(parent);
2324                    if parent_span.find_ancestor_inside(expr.span).is_some() {
2325                        // The parent node is part of the same span, so is the result of the
2326                        // same expansion/desugaring and not the 'real' parent node.
2327                        id = parent;
2328                        continue;
2329                    }
2330                    break;
2331                }
2332
2333                if let hir::Node::Block(&hir::Block { span: block_span, expr: Some(e), .. }) =
2334                    self.tcx.hir_node(parent)
2335                {
2336                    if e.hir_id == id {
2337                        if let Some(span) = expr.span.find_ancestor_inside(block_span) {
2338                            let return_suggestions = if self
2339                                .tcx
2340                                .is_diagnostic_item(sym::Result, expected_adt.did())
2341                            {
2342                                vec!["Ok(())"]
2343                            } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
2344                                vec!["None", "Some(())"]
2345                            } else {
2346                                return false;
2347                            };
2348                            if let Some(indent) =
2349                                self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
2350                            {
2351                                // Add a semicolon, except after `}`.
2352                                let semicolon =
2353                                    match self.tcx.sess.source_map().span_to_snippet(span) {
2354                                        Ok(s) if s.ends_with('}') => "",
2355                                        _ => ";",
2356                                    };
2357                                err.span_suggestions(
2358                                    span.shrink_to_hi(),
2359                                    "try adding an expression at the end of the block",
2360                                    return_suggestions
2361                                        .into_iter()
2362                                        .map(|r| format!("{semicolon}\n{indent}{r}")),
2363                                    Applicability::MaybeIncorrect,
2364                                );
2365                            }
2366                            return true;
2367                        }
2368                    }
2369                }
2370            }
2371
2372            let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
2373                .variants()
2374                .iter()
2375                .filter(|variant| {
2376                    variant.fields.len() == 1
2377                })
2378                .filter_map(|variant| {
2379                    let sole_field = &variant.single_field();
2380
2381                    let field_is_local = sole_field.did.is_local();
2382                    let field_is_accessible =
2383                        sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
2384                        // Skip suggestions for unstable public fields (for example `Pin::__pointer`)
2385                        && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
2386
2387                    if !field_is_local && !field_is_accessible {
2388                        return None;
2389                    }
2390
2391                    let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
2392                        .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
2393
2394                    let sole_field_ty = sole_field.ty(self.tcx, args);
2395                    if self.may_coerce(expr_ty, sole_field_ty) {
2396                        let variant_path =
2397                            with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
2398                        // FIXME #56861: DRYer prelude filtering
2399                        if let Some(path) = variant_path.strip_prefix("std::prelude::")
2400                            && let Some((_, path)) = path.split_once("::")
2401                        {
2402                            return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy));
2403                        }
2404                        Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy))
2405                    } else {
2406                        None
2407                    }
2408                })
2409                .collect();
2410
2411            let suggestions_for = |variant: &_, ctor_kind, field_name| {
2412                let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2413                    Some(ident) => format!("{ident}: "),
2414                    None => String::new(),
2415                };
2416
2417                let (open, close) = match ctor_kind {
2418                    Some(CtorKind::Fn) => ("(".to_owned(), ")"),
2419                    None => (format!(" {{ {field_name}: "), " }"),
2420
2421                    Some(CtorKind::Const) => unreachable!("unit variants don't have fields"),
2422                };
2423
2424                // Suggest constructor as deep into the block tree as possible.
2425                // This fixes https://github.com/rust-lang/rust/issues/101065,
2426                // and also just helps make the most minimal suggestions.
2427                let mut expr = expr;
2428                while let hir::ExprKind::Block(block, _) = &expr.kind
2429                    && let Some(expr_) = &block.expr
2430                {
2431                    expr = expr_
2432                }
2433
2434                vec![
2435                    (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
2436                    (expr.span.shrink_to_hi(), close.to_owned()),
2437                ]
2438            };
2439
2440            match &compatible_variants[..] {
2441                [] => { /* No variants to format */ }
2442                [(variant, ctor_kind, field_name, note)] => {
2443                    // Just a single matching variant.
2444                    err.multipart_suggestion_verbose(
2445                        format!(
2446                            "try wrapping the expression in `{variant}`{note}",
2447                            note = note.as_deref().unwrap_or("")
2448                        ),
2449                        suggestions_for(&**variant, *ctor_kind, *field_name),
2450                        Applicability::MaybeIncorrect,
2451                    );
2452                    return true;
2453                }
2454                _ => {
2455                    // More than one matching variant.
2456                    err.multipart_suggestions(
2457                        format!(
2458                            "try wrapping the expression in a variant of `{}`",
2459                            self.tcx.def_path_str(expected_adt.did())
2460                        ),
2461                        compatible_variants.into_iter().map(
2462                            |(variant, ctor_kind, field_name, _)| {
2463                                suggestions_for(&variant, ctor_kind, field_name)
2464                            },
2465                        ),
2466                        Applicability::MaybeIncorrect,
2467                    );
2468                    return true;
2469                }
2470            }
2471        }
2472
2473        false
2474    }
2475
2476    pub(crate) fn suggest_non_zero_new_unwrap(
2477        &self,
2478        err: &mut Diag<'_>,
2479        expr: &hir::Expr<'_>,
2480        expected: Ty<'tcx>,
2481        expr_ty: Ty<'tcx>,
2482    ) -> bool {
2483        let tcx = self.tcx;
2484        let (adt, args, unwrap) = match expected.kind() {
2485            // In case `Option<NonZero<T>>` is wanted, but `T` is provided, suggest calling `new`.
2486            ty::Adt(adt, args) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
2487                let nonzero_type = args.type_at(0); // Unwrap option type.
2488                let ty::Adt(adt, args) = nonzero_type.kind() else {
2489                    return false;
2490                };
2491                (adt, args, "")
2492            }
2493            // In case `NonZero<T>` is wanted but `T` is provided, also add `.unwrap()` to satisfy types.
2494            ty::Adt(adt, args) => (adt, args, ".unwrap()"),
2495            _ => return false,
2496        };
2497
2498        if !self.tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
2499            return false;
2500        }
2501
2502        let int_type = args.type_at(0);
2503        if !self.may_coerce(expr_ty, int_type) {
2504            return false;
2505        }
2506
2507        err.multipart_suggestion(
2508            format!("consider calling `{}::new`", sym::NonZero),
2509            vec![
2510                (expr.span.shrink_to_lo(), format!("{}::new(", sym::NonZero)),
2511                (expr.span.shrink_to_hi(), format!("){unwrap}")),
2512            ],
2513            Applicability::MaybeIncorrect,
2514        );
2515
2516        true
2517    }
2518
2519    /// Identify some cases where `as_ref()` would be appropriate and suggest it.
2520    ///
2521    /// Given the following code:
2522    /// ```compile_fail,E0308
2523    /// struct Foo;
2524    /// fn takes_ref(_: &Foo) {}
2525    /// let ref opt = Some(Foo);
2526    ///
2527    /// opt.map(|param| takes_ref(param));
2528    /// ```
2529    /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead.
2530    ///
2531    /// It only checks for `Option` and `Result` and won't work with
2532    /// ```ignore (illustrative)
2533    /// opt.map(|param| { takes_ref(param) });
2534    /// ```
2535    fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> {
2536        let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind else {
2537            return None;
2538        };
2539
2540        let hir::def::Res::Local(local_id) = path.res else {
2541            return None;
2542        };
2543
2544        let Node::Param(hir::Param { hir_id: param_hir_id, .. }) =
2545            self.tcx.parent_hir_node(local_id)
2546        else {
2547            return None;
2548        };
2549
2550        let Node::Expr(hir::Expr {
2551            hir_id: expr_hir_id,
2552            kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
2553            ..
2554        }) = self.tcx.parent_hir_node(*param_hir_id)
2555        else {
2556            return None;
2557        };
2558
2559        let hir = self.tcx.parent_hir_node(*expr_hir_id);
2560        let closure_params_len = closure_fn_decl.inputs.len();
2561        let (
2562            Node::Expr(hir::Expr {
2563                kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
2564                ..
2565            }),
2566            1,
2567        ) = (hir, closure_params_len)
2568        else {
2569            return None;
2570        };
2571
2572        let self_ty = self.typeck_results.borrow().expr_ty(receiver);
2573        let name = method_path.ident.name;
2574        let is_as_ref_able = match self_ty.peel_refs().kind() {
2575            ty::Adt(def, _) => {
2576                (self.tcx.is_diagnostic_item(sym::Option, def.did())
2577                    || self.tcx.is_diagnostic_item(sym::Result, def.did()))
2578                    && (name == sym::map || name == sym::and_then)
2579            }
2580            _ => false,
2581        };
2582        if is_as_ref_able {
2583            Some((
2584                vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())],
2585                "consider using `as_ref` instead",
2586            ))
2587        } else {
2588            None
2589        }
2590    }
2591
2592    /// This function is used to determine potential "simple" improvements or users' errors and
2593    /// provide them useful help. For example:
2594    ///
2595    /// ```compile_fail,E0308
2596    /// fn some_fn(s: &str) {}
2597    ///
2598    /// let x = "hey!".to_owned();
2599    /// some_fn(x); // error
2600    /// ```
2601    ///
2602    /// No need to find every potential function which could make a coercion to transform a
2603    /// `String` into a `&str` since a `&` would do the trick!
2604    ///
2605    /// In addition of this check, it also checks between references mutability state. If the
2606    /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
2607    /// `&mut`!".
2608    pub(crate) fn suggest_deref_or_ref(
2609        &self,
2610        expr: &hir::Expr<'tcx>,
2611        checked_ty: Ty<'tcx>,
2612        expected: Ty<'tcx>,
2613    ) -> Option<(
2614        Vec<(Span, String)>,
2615        String,
2616        Applicability,
2617        bool, /* verbose */
2618        bool, /* suggest `&` or `&mut` type annotation */
2619    )> {
2620        let sess = self.sess();
2621        let sp = expr.span;
2622        let sm = sess.source_map();
2623
2624        // If the span is from an external macro, there's no suggestion we can make.
2625        if sp.in_external_macro(sm) {
2626            return None;
2627        }
2628
2629        let replace_prefix = |s: &str, old: &str, new: &str| {
2630            s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
2631        };
2632
2633        // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
2634        let expr = expr.peel_drop_temps();
2635
2636        match (&expr.kind, expected.kind(), checked_ty.kind()) {
2637            (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
2638                (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
2639                    if let hir::ExprKind::Lit(_) = expr.kind
2640                        && let Ok(src) = sm.span_to_snippet(sp)
2641                        && replace_prefix(&src, "b\"", "\"").is_some()
2642                    {
2643                        let pos = sp.lo() + BytePos(1);
2644                        return Some((
2645                            vec![(sp.with_hi(pos), String::new())],
2646                            "consider removing the leading `b`".to_string(),
2647                            Applicability::MachineApplicable,
2648                            true,
2649                            false,
2650                        ));
2651                    }
2652                }
2653                (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
2654                    if let hir::ExprKind::Lit(_) = expr.kind
2655                        && let Ok(src) = sm.span_to_snippet(sp)
2656                        && replace_prefix(&src, "\"", "b\"").is_some()
2657                    {
2658                        return Some((
2659                            vec![(sp.shrink_to_lo(), "b".to_string())],
2660                            "consider adding a leading `b`".to_string(),
2661                            Applicability::MachineApplicable,
2662                            true,
2663                            false,
2664                        ));
2665                    }
2666                }
2667                _ => {}
2668            },
2669            (_, &ty::Ref(_, _, mutability), _) => {
2670                // Check if it can work when put into a ref. For example:
2671                //
2672                // ```
2673                // fn bar(x: &mut i32) {}
2674                //
2675                // let x = 0u32;
2676                // bar(&x); // error, expected &mut
2677                // ```
2678                let ref_ty = match mutability {
2679                    hir::Mutability::Mut => {
2680                        Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2681                    }
2682                    hir::Mutability::Not => {
2683                        Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2684                    }
2685                };
2686                if self.may_coerce(ref_ty, expected) {
2687                    let mut sugg_sp = sp;
2688                    if let hir::ExprKind::MethodCall(segment, receiver, args, _) = expr.kind {
2689                        let clone_trait =
2690                            self.tcx.require_lang_item(LangItem::Clone, segment.ident.span);
2691                        if args.is_empty()
2692                            && self
2693                                .typeck_results
2694                                .borrow()
2695                                .type_dependent_def_id(expr.hir_id)
2696                                .is_some_and(|did| {
2697                                    let ai = self.tcx.associated_item(did);
2698                                    ai.trait_container(self.tcx) == Some(clone_trait)
2699                                })
2700                            && segment.ident.name == sym::clone
2701                        {
2702                            // If this expression had a clone call when suggesting borrowing
2703                            // we want to suggest removing it because it'd now be unnecessary.
2704                            sugg_sp = receiver.span;
2705                        }
2706                    }
2707
2708                    if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
2709                        && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty)
2710                        && self.typeck_results.borrow().expr_ty(inner).is_ref()
2711                    {
2712                        // We have `*&T`, check if what was expected was `&T`.
2713                        // If so, we may want to suggest removing a `*`.
2714                        sugg_sp = sugg_sp.with_hi(inner.span.lo());
2715                        return Some((
2716                            vec![(sugg_sp, String::new())],
2717                            "consider removing deref here".to_string(),
2718                            Applicability::MachineApplicable,
2719                            true,
2720                            false,
2721                        ));
2722                    }
2723
2724                    // Don't try to suggest ref/deref on an `if` expression, because:
2725                    // - The `if` could be part of a desugared `if else` statement,
2726                    //   which would create impossible suggestions such as `if ... { ... } else &if { ... } else { ... }`.
2727                    // - In general the suggestions it creates such as `&if ... { ... } else { ... }` are not very helpful.
2728                    // We try to generate a suggestion such as `if ... { &... } else { &... }` instead.
2729                    if let hir::ExprKind::If(_c, then, els) = expr.kind {
2730                        // The `then` of a `Expr::If` always contains a block, and that block may have a final expression that we can borrow
2731                        // If the block does not have a final expression, it will return () and we do not make a suggestion to borrow that.
2732                        let ExprKind::Block(then, _) = then.kind else { return None };
2733                        let Some(then) = then.expr else { return None };
2734                        let (mut suggs, help, app, verbose, mutref) =
2735                            self.suggest_deref_or_ref(then, checked_ty, expected)?;
2736
2737                        // If there is no `else`, the return type of this `if` will be (), so suggesting to change the `then` block is useless
2738                        let els_expr = match els?.kind {
2739                            ExprKind::Block(block, _) => block.expr?,
2740                            _ => els?,
2741                        };
2742                        let (else_suggs, ..) =
2743                            self.suggest_deref_or_ref(els_expr, checked_ty, expected)?;
2744                        suggs.extend(else_suggs);
2745
2746                        return Some((suggs, help, app, verbose, mutref));
2747                    }
2748
2749                    if let Some((sugg, msg)) = self.can_use_as_ref(expr) {
2750                        return Some((
2751                            sugg,
2752                            msg.to_string(),
2753                            Applicability::MachineApplicable,
2754                            true,
2755                            false,
2756                        ));
2757                    }
2758
2759                    let prefix = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2760                        Some(ident) => format!("{ident}: "),
2761                        None => String::new(),
2762                    };
2763
2764                    if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(..), .. }) =
2765                        self.tcx.parent_hir_node(expr.hir_id)
2766                    {
2767                        if mutability.is_mut() {
2768                            // Suppressing this diagnostic, we'll properly print it in `check_expr_assign`
2769                            return None;
2770                        }
2771                    }
2772
2773                    let make_sugg = |expr: &Expr<'_>, span: Span, sugg: &str| {
2774                        if expr_needs_parens(expr) {
2775                            (
2776                                vec![
2777                                    (span.shrink_to_lo(), format!("{prefix}{sugg}(")),
2778                                    (span.shrink_to_hi(), ")".to_string()),
2779                                ],
2780                                false,
2781                            )
2782                        } else {
2783                            (vec![(span.shrink_to_lo(), format!("{prefix}{sugg}"))], true)
2784                        }
2785                    };
2786
2787                    // Suggest dereferencing the lhs for expressions such as `&T <= T`
2788                    if let hir::Node::Expr(hir::Expr {
2789                        kind: hir::ExprKind::Binary(_, lhs, ..),
2790                        ..
2791                    }) = self.tcx.parent_hir_node(expr.hir_id)
2792                        && let &ty::Ref(..) = self.check_expr(lhs).kind()
2793                    {
2794                        let (sugg, verbose) = make_sugg(lhs, lhs.span, "*");
2795
2796                        return Some((
2797                            sugg,
2798                            "consider dereferencing the borrow".to_string(),
2799                            Applicability::MachineApplicable,
2800                            verbose,
2801                            false,
2802                        ));
2803                    }
2804
2805                    let sugg = mutability.ref_prefix_str();
2806                    let (sugg, verbose) = make_sugg(expr, sp, sugg);
2807                    return Some((
2808                        sugg,
2809                        format!("consider {}borrowing here", mutability.mutably_str()),
2810                        Applicability::MachineApplicable,
2811                        verbose,
2812                        false,
2813                    ));
2814                }
2815            }
2816            (hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr), _, &ty::Ref(_, checked, _))
2817                if self.can_eq(self.param_env, checked, expected) =>
2818            {
2819                let make_sugg = |start: Span, end: BytePos| {
2820                    // skip `(` for tuples such as `(c) = (&123)`.
2821                    // make sure we won't suggest like `(c) = 123)` which is incorrect.
2822                    let sp = sm
2823                        .span_extend_while(start.shrink_to_lo(), |c| c == '(' || c.is_whitespace())
2824                        .map_or(start, |s| s.shrink_to_hi());
2825                    Some((
2826                        vec![(sp.with_hi(end), String::new())],
2827                        "consider removing the borrow".to_string(),
2828                        Applicability::MachineApplicable,
2829                        true,
2830                        true,
2831                    ))
2832                };
2833
2834                // We have `&T`, check if what was expected was `T`. If so,
2835                // we may want to suggest removing a `&`.
2836                if sm.is_imported(expr.span) {
2837                    // Go through the spans from which this span was expanded,
2838                    // and find the one that's pointing inside `sp`.
2839                    //
2840                    // E.g. for `&format!("")`, where we want the span to the
2841                    // `format!()` invocation instead of its expansion.
2842                    if let Some(call_span) =
2843                        iter::successors(Some(expr.span), |s| s.parent_callsite())
2844                            .find(|&s| sp.contains(s))
2845                        && sm.is_span_accessible(call_span)
2846                    {
2847                        return make_sugg(sp, call_span.lo());
2848                    }
2849                    return None;
2850                }
2851                if sp.contains(expr.span) && sm.is_span_accessible(expr.span) {
2852                    return make_sugg(sp, expr.span.lo());
2853                }
2854            }
2855            (_, &ty::RawPtr(ty_b, mutbl_b), &ty::Ref(_, ty_a, mutbl_a)) => {
2856                if let Some(steps) = self.deref_steps_for_suggestion(ty_a, ty_b)
2857                    // Only suggest valid if dereferencing needed.
2858                    && steps > 0
2859                    // The pointer type implements `Copy` trait so the suggestion is always valid.
2860                    && let Ok(src) = sm.span_to_snippet(sp)
2861                {
2862                    let derefs = "*".repeat(steps);
2863                    let old_prefix = mutbl_a.ref_prefix_str();
2864                    let new_prefix = mutbl_b.ref_prefix_str().to_owned() + &derefs;
2865
2866                    let suggestion = replace_prefix(&src, old_prefix, &new_prefix).map(|_| {
2867                        // skip `&` or `&mut ` if both mutabilities are mutable
2868                        let lo = sp.lo()
2869                            + BytePos(min(old_prefix.len(), mutbl_b.ref_prefix_str().len()) as _);
2870                        // skip `&` or `&mut `
2871                        let hi = sp.lo() + BytePos(old_prefix.len() as _);
2872                        let sp = sp.with_lo(lo).with_hi(hi);
2873
2874                        (
2875                            sp,
2876                            format!(
2877                                "{}{derefs}",
2878                                if mutbl_a != mutbl_b { mutbl_b.prefix_str() } else { "" }
2879                            ),
2880                            if mutbl_b <= mutbl_a {
2881                                Applicability::MachineApplicable
2882                            } else {
2883                                Applicability::MaybeIncorrect
2884                            },
2885                        )
2886                    });
2887
2888                    if let Some((span, src, applicability)) = suggestion {
2889                        return Some((
2890                            vec![(span, src)],
2891                            "consider dereferencing".to_string(),
2892                            applicability,
2893                            true,
2894                            false,
2895                        ));
2896                    }
2897                }
2898            }
2899            _ if sp == expr.span => {
2900                if let Some(mut steps) = self.deref_steps_for_suggestion(checked_ty, expected) {
2901                    let mut expr = expr.peel_blocks();
2902                    let mut prefix_span = expr.span.shrink_to_lo();
2903                    let mut remove = String::new();
2904
2905                    // Try peeling off any existing `&` and `&mut` to reach our target type
2906                    while steps > 0 {
2907                        if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
2908                            // If the expression has `&`, removing it would fix the error
2909                            prefix_span = prefix_span.with_hi(inner.span.lo());
2910                            expr = inner;
2911                            remove.push_str(mutbl.ref_prefix_str());
2912                            steps -= 1;
2913                        } else {
2914                            break;
2915                        }
2916                    }
2917                    // If we've reached our target type with just removing `&`, then just print now.
2918                    if steps == 0 && !remove.trim().is_empty() {
2919                        return Some((
2920                            vec![(prefix_span, String::new())],
2921                            format!("consider removing the `{}`", remove.trim()),
2922                            // Do not remove `&&` to get to bool, because it might be something like
2923                            // { a } && b, which we have a separate fixup suggestion that is more
2924                            // likely correct...
2925                            if remove.trim() == "&&" && expected == self.tcx.types.bool {
2926                                Applicability::MaybeIncorrect
2927                            } else {
2928                                Applicability::MachineApplicable
2929                            },
2930                            true,
2931                            false,
2932                        ));
2933                    }
2934
2935                    // For this suggestion to make sense, the type would need to be `Copy`,
2936                    // or we have to be moving out of a `Box<T>`
2937                    if self.type_is_copy_modulo_regions(self.param_env, expected)
2938                        // FIXME(compiler-errors): We can actually do this if the checked_ty is
2939                        // `steps` layers of boxes, not just one, but this is easier and most likely.
2940                        || (checked_ty.is_box() && steps == 1)
2941                        // We can always deref a binop that takes its arguments by ref.
2942                        || matches!(
2943                            self.tcx.parent_hir_node(expr.hir_id),
2944                            hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, ..), .. })
2945                                if !op.node.is_by_value()
2946                        )
2947                    {
2948                        let deref_kind = if checked_ty.is_box() {
2949                            "unboxing the value"
2950                        } else if checked_ty.is_ref() {
2951                            "dereferencing the borrow"
2952                        } else {
2953                            "dereferencing the type"
2954                        };
2955
2956                        // Suggest removing `&` if we have removed any, otherwise suggest just
2957                        // dereferencing the remaining number of steps.
2958                        let message = if remove.is_empty() {
2959                            format!("consider {deref_kind}")
2960                        } else {
2961                            format!(
2962                                "consider removing the `{}` and {} instead",
2963                                remove.trim(),
2964                                deref_kind
2965                            )
2966                        };
2967
2968                        let prefix =
2969                            match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {
2970                                Some(ident) => format!("{ident}: "),
2971                                None => String::new(),
2972                            };
2973
2974                        let (span, suggestion) = if self.is_else_if_block(expr) {
2975                            // Don't suggest nonsense like `else *if`
2976                            return None;
2977                        } else if let Some(expr) = self.maybe_get_block_expr(expr) {
2978                            // prefix should be empty here..
2979                            (expr.span.shrink_to_lo(), "*".to_string())
2980                        } else {
2981                            (prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
2982                        };
2983                        if suggestion.trim().is_empty() {
2984                            return None;
2985                        }
2986
2987                        if expr_needs_parens(expr) {
2988                            return Some((
2989                                vec![
2990                                    (span, format!("{suggestion}(")),
2991                                    (expr.span.shrink_to_hi(), ")".to_string()),
2992                                ],
2993                                message,
2994                                Applicability::MachineApplicable,
2995                                true,
2996                                false,
2997                            ));
2998                        }
2999
3000                        return Some((
3001                            vec![(span, suggestion)],
3002                            message,
3003                            Applicability::MachineApplicable,
3004                            true,
3005                            false,
3006                        ));
3007                    }
3008                }
3009            }
3010            _ => {}
3011        }
3012        None
3013    }
3014
3015    /// Returns whether the given expression is an `else if`.
3016    fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
3017        if let hir::ExprKind::If(..) = expr.kind
3018            && let Node::Expr(hir::Expr { kind: hir::ExprKind::If(_, _, Some(else_expr)), .. }) =
3019                self.tcx.parent_hir_node(expr.hir_id)
3020        {
3021            return else_expr.hir_id == expr.hir_id;
3022        }
3023        false
3024    }
3025
3026    pub(crate) fn suggest_cast(
3027        &self,
3028        err: &mut Diag<'_>,
3029        expr: &hir::Expr<'_>,
3030        checked_ty: Ty<'tcx>,
3031        expected_ty: Ty<'tcx>,
3032        expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
3033    ) -> bool {
3034        if self.tcx.sess.source_map().is_imported(expr.span) {
3035            // Ignore if span is from within a macro.
3036            return false;
3037        }
3038
3039        let span = if let hir::ExprKind::Lit(lit) = &expr.kind { lit.span } else { expr.span };
3040        let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) else {
3041            return false;
3042        };
3043
3044        // If casting this expression to a given numeric type would be appropriate in case of a type
3045        // mismatch.
3046        //
3047        // We want to minimize the amount of casting operations that are suggested, as it can be a
3048        // lossy operation with potentially bad side effects, so we only suggest when encountering
3049        // an expression that indicates that the original type couldn't be directly changed.
3050        //
3051        // For now, don't suggest casting with `as`.
3052        let can_cast = false;
3053
3054        let mut sugg = vec![];
3055
3056        if let hir::Node::ExprField(field) = self.tcx.parent_hir_node(expr.hir_id) {
3057            // `expr` is a literal field for a struct, only suggest if appropriate
3058            if field.is_shorthand {
3059                // This is a field literal
3060                sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
3061            } else {
3062                // Likely a field was meant, but this field wasn't found. Do not suggest anything.
3063                return false;
3064            }
3065        };
3066
3067        if let hir::ExprKind::Call(path, args) = &expr.kind
3068            && let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
3069                (&path.kind, args.len())
3070            // `expr` is a conversion like `u32::from(val)`, do not suggest anything (#63697).
3071            && let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
3072                (&base_ty.kind, path_segment.ident.name)
3073        {
3074            if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
3075                match ident.name {
3076                    sym::i128
3077                    | sym::i64
3078                    | sym::i32
3079                    | sym::i16
3080                    | sym::i8
3081                    | sym::u128
3082                    | sym::u64
3083                    | sym::u32
3084                    | sym::u16
3085                    | sym::u8
3086                    | sym::isize
3087                    | sym::usize
3088                        if base_ty_path.segments.len() == 1 =>
3089                    {
3090                        return false;
3091                    }
3092                    _ => {}
3093                }
3094            }
3095        }
3096
3097        let msg = format!(
3098            "you can convert {} `{}` to {} `{}`",
3099            checked_ty.kind().article(),
3100            checked_ty,
3101            expected_ty.kind().article(),
3102            expected_ty,
3103        );
3104        let cast_msg = format!(
3105            "you can cast {} `{}` to {} `{}`",
3106            checked_ty.kind().article(),
3107            checked_ty,
3108            expected_ty.kind().article(),
3109            expected_ty,
3110        );
3111        let lit_msg = format!(
3112            "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
3113        );
3114
3115        let close_paren = if self.precedence(expr) < ExprPrecedence::Unambiguous {
3116            sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
3117            ")"
3118        } else {
3119            ""
3120        };
3121
3122        let mut cast_suggestion = sugg.clone();
3123        cast_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren} as {expected_ty}")));
3124        let mut into_suggestion = sugg.clone();
3125        into_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren}.into()")));
3126        let mut suffix_suggestion = sugg.clone();
3127        suffix_suggestion.push((
3128            if matches!(
3129                (expected_ty.kind(), checked_ty.kind()),
3130                (ty::Int(_) | ty::Uint(_), ty::Float(_))
3131            ) {
3132                // Remove fractional part from literal, for example `42.0f32` into `42`
3133                let src = src.trim_end_matches(&checked_ty.to_string());
3134                let len = src.split('.').next().unwrap().len();
3135                span.with_lo(span.lo() + BytePos(len as u32))
3136            } else {
3137                let len = src.trim_end_matches(&checked_ty.to_string()).len();
3138                span.with_lo(span.lo() + BytePos(len as u32))
3139            },
3140            if self.precedence(expr) < ExprPrecedence::Unambiguous {
3141                // Readd `)`
3142                format!("{expected_ty})")
3143            } else {
3144                expected_ty.to_string()
3145            },
3146        ));
3147        let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
3148            if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
3149        };
3150        let is_negative_int =
3151            |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..));
3152        let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
3153
3154        let in_const_context = self.tcx.hir_is_inside_const_context(expr.hir_id);
3155
3156        let suggest_fallible_into_or_lhs_from =
3157            |err: &mut Diag<'_>, exp_to_found_is_fallible: bool| {
3158                // If we know the expression the expected type is derived from, we might be able
3159                // to suggest a widening conversion rather than a narrowing one (which may
3160                // panic). For example, given x: u8 and y: u32, if we know the span of "x",
3161                //   x > y
3162                // can be given the suggestion "u32::from(x) > y" rather than
3163                // "x > y.try_into().unwrap()".
3164                let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
3165                    self.tcx
3166                        .sess
3167                        .source_map()
3168                        .span_to_snippet(expr.span)
3169                        .ok()
3170                        .map(|src| (expr, src))
3171                });
3172                let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
3173                    (lhs_expr_and_src, exp_to_found_is_fallible)
3174                {
3175                    let msg = format!(
3176                        "you can convert `{lhs_src}` from `{expected_ty}` to `{checked_ty}`, matching the type of `{src}`",
3177                    );
3178                    let suggestion = vec![
3179                        (lhs_expr.span.shrink_to_lo(), format!("{checked_ty}::from(")),
3180                        (lhs_expr.span.shrink_to_hi(), ")".to_string()),
3181                    ];
3182                    (msg, suggestion)
3183                } else {
3184                    let msg =
3185                        format!("{} and panic if the converted value doesn't fit", msg.clone());
3186                    let mut suggestion = sugg.clone();
3187                    suggestion.push((
3188                        expr.span.shrink_to_hi(),
3189                        format!("{close_paren}.try_into().unwrap()"),
3190                    ));
3191                    (msg, suggestion)
3192                };
3193                err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
3194            };
3195
3196        let suggest_to_change_suffix_or_into =
3197            |err: &mut Diag<'_>, found_to_exp_is_fallible: bool, exp_to_found_is_fallible: bool| {
3198                let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir_is_lhs(e.hir_id));
3199
3200                if exp_is_lhs {
3201                    return;
3202                }
3203
3204                let always_fallible = found_to_exp_is_fallible
3205                    && (exp_to_found_is_fallible || expected_ty_expr.is_none());
3206                let msg = if literal_is_ty_suffixed(expr) {
3207                    lit_msg.clone()
3208                } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
3209                    // We now know that converting either the lhs or rhs is fallible. Before we
3210                    // suggest a fallible conversion, check if the value can never fit in the
3211                    // expected type.
3212                    let msg = format!("`{src}` cannot fit into type `{expected_ty}`");
3213                    err.note(msg);
3214                    return;
3215                } else if in_const_context {
3216                    // Do not recommend `into` or `try_into` in const contexts.
3217                    return;
3218                } else if found_to_exp_is_fallible {
3219                    return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
3220                } else {
3221                    msg.clone()
3222                };
3223                let suggestion = if literal_is_ty_suffixed(expr) {
3224                    suffix_suggestion.clone()
3225                } else {
3226                    into_suggestion.clone()
3227                };
3228                err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
3229            };
3230
3231        match (expected_ty.kind(), checked_ty.kind()) {
3232            (ty::Int(exp), ty::Int(found)) => {
3233                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3234                {
3235                    (Some(exp), Some(found)) if exp < found => (true, false),
3236                    (Some(exp), Some(found)) if exp > found => (false, true),
3237                    (None, Some(8 | 16)) => (false, true),
3238                    (Some(8 | 16), None) => (true, false),
3239                    (None, _) | (_, None) => (true, true),
3240                    _ => (false, false),
3241                };
3242                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3243                true
3244            }
3245            (ty::Uint(exp), ty::Uint(found)) => {
3246                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3247                {
3248                    (Some(exp), Some(found)) if exp < found => (true, false),
3249                    (Some(exp), Some(found)) if exp > found => (false, true),
3250                    (None, Some(8 | 16)) => (false, true),
3251                    (Some(8 | 16), None) => (true, false),
3252                    (None, _) | (_, None) => (true, true),
3253                    _ => (false, false),
3254                };
3255                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3256                true
3257            }
3258            (&ty::Int(exp), &ty::Uint(found)) => {
3259                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3260                {
3261                    (Some(exp), Some(found)) if found < exp => (false, true),
3262                    (None, Some(8)) => (false, true),
3263                    _ => (true, true),
3264                };
3265                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3266                true
3267            }
3268            (&ty::Uint(exp), &ty::Int(found)) => {
3269                let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
3270                {
3271                    (Some(exp), Some(found)) if found > exp => (true, false),
3272                    (Some(8), None) => (true, false),
3273                    _ => (true, true),
3274                };
3275                suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
3276                true
3277            }
3278            (ty::Float(exp), ty::Float(found)) => {
3279                if found.bit_width() < exp.bit_width() {
3280                    suggest_to_change_suffix_or_into(err, false, true);
3281                } else if literal_is_ty_suffixed(expr) {
3282                    err.multipart_suggestion_verbose(
3283                        lit_msg,
3284                        suffix_suggestion,
3285                        Applicability::MachineApplicable,
3286                    );
3287                } else if can_cast {
3288                    // Missing try_into implementation for `f64` to `f32`
3289                    err.multipart_suggestion_verbose(
3290                        format!("{cast_msg}, producing the closest possible value"),
3291                        cast_suggestion,
3292                        Applicability::MaybeIncorrect, // lossy conversion
3293                    );
3294                }
3295                true
3296            }
3297            (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
3298                if literal_is_ty_suffixed(expr) {
3299                    err.multipart_suggestion_verbose(
3300                        lit_msg,
3301                        suffix_suggestion,
3302                        Applicability::MachineApplicable,
3303                    );
3304                } else if can_cast {
3305                    // Missing try_into implementation for `{float}` to `{integer}`
3306                    err.multipart_suggestion_verbose(
3307                        format!("{msg}, rounding the float towards zero"),
3308                        cast_suggestion,
3309                        Applicability::MaybeIncorrect, // lossy conversion
3310                    );
3311                }
3312                true
3313            }
3314            (ty::Float(exp), ty::Uint(found)) => {
3315                // if `found` is `None` (meaning found is `usize`), don't suggest `.into()`
3316                if exp.bit_width() > found.bit_width().unwrap_or(256) {
3317                    err.multipart_suggestion_verbose(
3318                        format!(
3319                            "{msg}, producing the floating point representation of the integer",
3320                        ),
3321                        into_suggestion,
3322                        Applicability::MachineApplicable,
3323                    );
3324                } else if literal_is_ty_suffixed(expr) {
3325                    err.multipart_suggestion_verbose(
3326                        lit_msg,
3327                        suffix_suggestion,
3328                        Applicability::MachineApplicable,
3329                    );
3330                } else {
3331                    // Missing try_into implementation for `{integer}` to `{float}`
3332                    err.multipart_suggestion_verbose(
3333                        format!(
3334                            "{cast_msg}, producing the floating point representation of the integer, \
3335                                 rounded if necessary",
3336                        ),
3337                        cast_suggestion,
3338                        Applicability::MaybeIncorrect, // lossy conversion
3339                    );
3340                }
3341                true
3342            }
3343            (ty::Float(exp), ty::Int(found)) => {
3344                // if `found` is `None` (meaning found is `isize`), don't suggest `.into()`
3345                if exp.bit_width() > found.bit_width().unwrap_or(256) {
3346                    err.multipart_suggestion_verbose(
3347                        format!(
3348                            "{}, producing the floating point representation of the integer",
3349                            msg.clone(),
3350                        ),
3351                        into_suggestion,
3352                        Applicability::MachineApplicable,
3353                    );
3354                } else if literal_is_ty_suffixed(expr) {
3355                    err.multipart_suggestion_verbose(
3356                        lit_msg,
3357                        suffix_suggestion,
3358                        Applicability::MachineApplicable,
3359                    );
3360                } else {
3361                    // Missing try_into implementation for `{integer}` to `{float}`
3362                    err.multipart_suggestion_verbose(
3363                        format!(
3364                            "{}, producing the floating point representation of the integer, \
3365                                rounded if necessary",
3366                            &msg,
3367                        ),
3368                        cast_suggestion,
3369                        Applicability::MaybeIncorrect, // lossy conversion
3370                    );
3371                }
3372                true
3373            }
3374            (
3375                &ty::Uint(ty::UintTy::U32 | ty::UintTy::U64 | ty::UintTy::U128)
3376                | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128),
3377                &ty::Char,
3378            ) => {
3379                err.multipart_suggestion_verbose(
3380                    format!("{cast_msg}, since a `char` always occupies 4 bytes"),
3381                    cast_suggestion,
3382                    Applicability::MachineApplicable,
3383                );
3384                true
3385            }
3386            _ => false,
3387        }
3388    }
3389
3390    /// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
3391    pub(crate) fn suggest_method_call_on_range_literal(
3392        &self,
3393        err: &mut Diag<'_>,
3394        expr: &hir::Expr<'tcx>,
3395        checked_ty: Ty<'tcx>,
3396        expected_ty: Ty<'tcx>,
3397    ) {
3398        if !hir::is_range_literal(expr) {
3399            return;
3400        }
3401        let hir::ExprKind::Struct(hir::QPath::LangItem(LangItem::Range, ..), [start, end], _) =
3402            expr.kind
3403        else {
3404            return;
3405        };
3406        if let hir::Node::ExprField(_) = self.tcx.parent_hir_node(expr.hir_id) {
3407            // Ignore `Foo { field: a..Default::default() }`
3408            return;
3409        }
3410        let mut expr = end.expr;
3411        let mut expectation = Some(expected_ty);
3412        while let hir::ExprKind::MethodCall(_, rcvr, ..) = expr.kind {
3413            // Getting to the root receiver and asserting it is a fn call let's us ignore cases in
3414            // `tests/ui/methods/issues/issue-90315.stderr`.
3415            expr = rcvr;
3416            // If we have more than one layer of calls, then the expected ty
3417            // cannot guide the method probe.
3418            expectation = None;
3419        }
3420        let hir::ExprKind::Call(method_name, _) = expr.kind else {
3421            return;
3422        };
3423        let ty::Adt(adt, _) = checked_ty.kind() else {
3424            return;
3425        };
3426        if self.tcx.lang_items().range_struct() != Some(adt.did()) {
3427            return;
3428        }
3429        if let ty::Adt(adt, _) = expected_ty.kind()
3430            && self.tcx.is_lang_item(adt.did(), LangItem::Range)
3431        {
3432            return;
3433        }
3434        // Check if start has method named end.
3435        let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = method_name.kind else {
3436            return;
3437        };
3438        let [hir::PathSegment { ident, .. }] = p.segments else {
3439            return;
3440        };
3441        let self_ty = self.typeck_results.borrow().expr_ty(start.expr);
3442        let Ok(_pick) = self.lookup_probe_for_diagnostic(
3443            *ident,
3444            self_ty,
3445            expr,
3446            probe::ProbeScope::AllTraits,
3447            expectation,
3448        ) else {
3449            return;
3450        };
3451        let mut sugg = ".";
3452        let mut span = start.expr.span.between(end.expr.span);
3453        if span.lo() + BytePos(2) == span.hi() {
3454            // There's no space between the start, the range op and the end, suggest removal which
3455            // will be more noticeable than the replacement of `..` with `.`.
3456            span = span.with_lo(span.lo() + BytePos(1));
3457            sugg = "";
3458        }
3459        err.span_suggestion_verbose(
3460            span,
3461            "you likely meant to write a method call instead of a range",
3462            sugg,
3463            Applicability::MachineApplicable,
3464        );
3465    }
3466
3467    /// Identify when the type error is because `()` is found in a binding that was assigned a
3468    /// block without a tail expression.
3469    pub(crate) fn suggest_return_binding_for_missing_tail_expr(
3470        &self,
3471        err: &mut Diag<'_>,
3472        expr: &hir::Expr<'_>,
3473        checked_ty: Ty<'tcx>,
3474        expected_ty: Ty<'tcx>,
3475    ) {
3476        if !checked_ty.is_unit() {
3477            return;
3478        }
3479        let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {
3480            return;
3481        };
3482        let hir::def::Res::Local(hir_id) = path.res else {
3483            return;
3484        };
3485        let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else {
3486            return;
3487        };
3488        let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) =
3489            self.tcx.parent_hir_node(pat.hir_id)
3490        else {
3491            return;
3492        };
3493        let hir::ExprKind::Block(block, None) = init.kind else {
3494            return;
3495        };
3496        if block.expr.is_some() {
3497            return;
3498        }
3499        let [.., stmt] = block.stmts else {
3500            err.span_label(block.span, "this empty block is missing a tail expression");
3501            return;
3502        };
3503        let hir::StmtKind::Semi(tail_expr) = stmt.kind else {
3504            return;
3505        };
3506        let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else {
3507            return;
3508        };
3509        if self.can_eq(self.param_env, expected_ty, ty)
3510            // FIXME: this happens with macro calls. Need to figure out why the stmt
3511            // `println!();` doesn't include the `;` in its `Span`. (#133845)
3512            // We filter these out to avoid ICEs with debug assertions on caused by
3513            // empty suggestions.
3514            && stmt.span.hi() != tail_expr.span.hi()
3515        {
3516            err.span_suggestion_short(
3517                stmt.span.with_lo(tail_expr.span.hi()),
3518                "remove this semicolon",
3519                "",
3520                Applicability::MachineApplicable,
3521            );
3522        } else {
3523            err.span_label(block.span, "this block is missing a tail expression");
3524        }
3525    }
3526
3527    pub(crate) fn suggest_swapping_lhs_and_rhs(
3528        &self,
3529        err: &mut Diag<'_>,
3530        rhs_ty: Ty<'tcx>,
3531        lhs_ty: Ty<'tcx>,
3532        rhs_expr: &'tcx hir::Expr<'tcx>,
3533        lhs_expr: &'tcx hir::Expr<'tcx>,
3534    ) {
3535        if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
3536            && self
3537                .infcx
3538                .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
3539                .must_apply_modulo_regions()
3540        {
3541            let sm = self.tcx.sess.source_map();
3542            if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
3543                && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
3544            {
3545                err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
3546                err.multipart_suggestion(
3547                    "consider swapping the equality",
3548                    vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
3549                    Applicability::MaybeIncorrect,
3550                );
3551            }
3552        }
3553    }
3554}