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