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 receiver_method_ident = expr.method_ident();
385                    if let Some(method_ident) = receiver_method_ident
386                        && method_ident.name == conversion_method.name
387                    {
388                        return None; // do not suggest code that is already there (#53348)
389                    }
390
391                    let method_call_list = [sym::to_vec, sym::to_string];
392                    let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
393                        && receiver_method.ident.name == sym::clone
394                        && method_call_list.contains(&conversion_method.name)
395                    // If receiver is `.clone()` and found type has one of those methods,
396                    // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
397                    // to an owned type (`Vec` or `String`). These conversions clone internally,
398                    // so we remove the user's `clone` call.
399                    {
400                        vec![(receiver_method.ident.span, conversion_method.name.to_string())]
401                    } else if expr.precedence() < ExprPrecedence::Unambiguous {
402                        vec![
403                            (expr.span.shrink_to_lo(), "(".to_string()),
404                            (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
405                        ]
406                    } else {
407                        vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
408                    };
409                    let struct_pat_shorthand_field =
410                        self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr);
411                    if let Some(name) = struct_pat_shorthand_field {
412                        sugg.insert(0, (expr.span.shrink_to_lo(), format!("{name}: ")));
413                    }
414                    Some(sugg)
415                })
416                .peekable();
417            if suggestions.peek().is_some() {
418                err.multipart_suggestions(
419                    "try using a conversion method",
420                    suggestions,
421                    Applicability::MaybeIncorrect,
422                );
423                return true;
424            }
425        }
426
427        if let Some((found_ty_inner, expected_ty_inner, error_tys)) =
428            self.deconstruct_option_or_result(found, expected)
429            && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind()
430        {
431            // Suggest removing any stray borrows (unless there's macro shenanigans involved).
432            let inner_expr = expr.peel_borrows();
433            if !inner_expr.span.eq_ctxt(expr.span) {
434                return false;
435            }
436            let borrow_removal_span = if inner_expr.hir_id == expr.hir_id {
437                None
438            } else {
439                Some(expr.span.shrink_to_lo().until(inner_expr.span))
440            };
441            // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for
442            // `as_ref` and `as_deref` compatibility.
443            let error_tys_equate_as_ref = error_tys.is_none_or(|(found, expected)| {
444                self.can_eq(
445                    self.param_env,
446                    Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, found),
447                    expected,
448                )
449            });
450
451            let prefix_wrap = |sugg: &str| {
452                if let Some(name) = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
453                    format!(": {}{}", name, sugg)
454                } else {
455                    sugg.to_string()
456                }
457            };
458
459            // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
460            // but those checks need to be a bit more delicate and the benefit is diminishing.
461            if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
462                let sugg = prefix_wrap(".as_ref()");
463                err.subdiagnostic(errors::SuggestConvertViaMethod {
464                    span: expr.span.shrink_to_hi(),
465                    sugg,
466                    expected,
467                    found,
468                    borrow_removal_span,
469                });
470                return true;
471            } else if let ty::Ref(_, peeled_found_ty, _) = found_ty_inner.kind()
472                && let ty::Adt(adt, _) = peeled_found_ty.peel_refs().kind()
473                && self.tcx.is_lang_item(adt.did(), LangItem::String)
474                && peeled.is_str()
475                // `Result::map`, conversely, does not take ref of the error type.
476                && error_tys.is_none_or(|(found, expected)| {
477                    self.can_eq(self.param_env, found, expected)
478                })
479            {
480                let sugg = prefix_wrap(".map(|x| x.as_str())");
481                err.span_suggestion_verbose(
482                    expr.span.shrink_to_hi(),
483                    fluent::hir_typeck_convert_to_str,
484                    sugg,
485                    Applicability::MachineApplicable,
486                );
487                return true;
488            } else {
489                if !error_tys_equate_as_ref {
490                    return false;
491                }
492                let mut steps = self.autoderef(expr.span, found_ty_inner).silence_errors();
493                if let Some((deref_ty, _)) = steps.nth(1)
494                    && self.can_eq(self.param_env, deref_ty, peeled)
495                {
496                    let sugg = prefix_wrap(".as_deref()");
497                    err.subdiagnostic(errors::SuggestConvertViaMethod {
498                        span: expr.span.shrink_to_hi(),
499                        sugg,
500                        expected,
501                        found,
502                        borrow_removal_span,
503                    });
504                    return true;
505                }
506                for (deref_ty, n_step) in steps {
507                    if self.can_eq(self.param_env, deref_ty, peeled) {
508                        let explicit_deref = "*".repeat(n_step);
509                        let sugg = prefix_wrap(&format!(".map(|v| &{explicit_deref}v)"));
510                        err.subdiagnostic(errors::SuggestConvertViaMethod {
511                            span: expr.span.shrink_to_hi(),
512                            sugg,
513                            expected,
514                            found,
515                            borrow_removal_span,
516                        });
517                        return true;
518                    }
519                }
520            }
521        }
522
523        false
524    }
525
526    /// If `ty` is `Option<T>`, returns `T, T, None`.
527    /// If `ty` is `Result<T, E>`, returns `T, T, Some(E, E)`.
528    /// Otherwise, returns `None`.
529    fn deconstruct_option_or_result(
530        &self,
531        found_ty: Ty<'tcx>,
532        expected_ty: Ty<'tcx>,
533    ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> {
534        let ty::Adt(found_adt, found_args) = found_ty.peel_refs().kind() else {
535            return None;
536        };
537        let ty::Adt(expected_adt, expected_args) = expected_ty.kind() else {
538            return None;
539        };
540        if self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
541            && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
542        {
543            Some((found_args.type_at(0), expected_args.type_at(0), None))
544        } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did())
545            && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did())
546        {
547            Some((
548                found_args.type_at(0),
549                expected_args.type_at(0),
550                Some((found_args.type_at(1), expected_args.type_at(1))),
551            ))
552        } else {
553            None
554        }
555    }
556
557    /// When encountering the expected boxed value allocated in the stack, suggest allocating it
558    /// in the heap by calling `Box::new()`.
559    pub(in super::super) fn suggest_boxing_when_appropriate(
560        &self,
561        err: &mut Diag<'_>,
562        span: Span,
563        hir_id: HirId,
564        expected: Ty<'tcx>,
565        found: Ty<'tcx>,
566    ) -> bool {
567        // Do not suggest `Box::new` in const context.
568        if self.tcx.hir_is_inside_const_context(hir_id) || !expected.is_box() || found.is_box() {
569            return false;
570        }
571        if self.may_coerce(Ty::new_box(self.tcx, found), expected) {
572            let suggest_boxing = match found.kind() {
573                ty::Tuple(tuple) if tuple.is_empty() => {
574                    errors::SuggestBoxing::Unit { start: span.shrink_to_lo(), end: span }
575                }
576                ty::Coroutine(def_id, ..)
577                    if matches!(
578                        self.tcx.coroutine_kind(def_id),
579                        Some(CoroutineKind::Desugared(
580                            CoroutineDesugaring::Async,
581                            CoroutineSource::Closure
582                        ))
583                    ) =>
584                {
585                    errors::SuggestBoxing::AsyncBody
586                }
587                _ => errors::SuggestBoxing::Other {
588                    start: span.shrink_to_lo(),
589                    end: span.shrink_to_hi(),
590                },
591            };
592            err.subdiagnostic(suggest_boxing);
593
594            true
595        } else {
596            false
597        }
598    }
599
600    /// When encountering a closure that captures variables, where a FnPtr is expected,
601    /// suggest a non-capturing closure
602    pub(in super::super) fn suggest_no_capture_closure(
603        &self,
604        err: &mut Diag<'_>,
605        expected: Ty<'tcx>,
606        found: Ty<'tcx>,
607    ) -> bool {
608        if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
609            if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
610                // Report upto four upvars being captured to reduce the amount error messages
611                // reported back to the user.
612                let spans_and_labels = upvars
613                    .iter()
614                    .take(4)
615                    .map(|(var_hir_id, upvar)| {
616                        let var_name = self.tcx.hir_name(*var_hir_id).to_string();
617                        let msg = format!("`{var_name}` captured here");
618                        (upvar.span, msg)
619                    })
620                    .collect::<Vec<_>>();
621
622                let mut multi_span: MultiSpan =
623                    spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
624                for (sp, label) in spans_and_labels {
625                    multi_span.push_span_label(sp, label);
626                }
627                err.span_note(
628                    multi_span,
629                    "closures can only be coerced to `fn` types if they do not capture any variables"
630                );
631                return true;
632            }
633        }
634        false
635    }
636
637    /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
638    #[instrument(skip(self, err))]
639    pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
640        &self,
641        err: &mut Diag<'_>,
642        expr: &hir::Expr<'_>,
643        expected: Ty<'tcx>,
644        found: Ty<'tcx>,
645    ) -> bool {
646        // Handle #68197.
647
648        if self.tcx.hir_is_inside_const_context(expr.hir_id) {
649            // Do not suggest `Box::new` in const context.
650            return false;
651        }
652        let pin_did = self.tcx.lang_items().pin_type();
653        // This guards the `new_box` below.
654        if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() {
655            return false;
656        }
657        let box_found = Ty::new_box(self.tcx, found);
658        let Some(pin_box_found) = Ty::new_lang_item(self.tcx, box_found, LangItem::Pin) else {
659            return false;
660        };
661        let Some(pin_found) = Ty::new_lang_item(self.tcx, found, LangItem::Pin) else {
662            return false;
663        };
664        match expected.kind() {
665            ty::Adt(def, _) if Some(def.did()) == pin_did => {
666                if self.may_coerce(pin_box_found, expected) {
667                    debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected);
668                    match found.kind() {
669                        ty::Adt(def, _) if def.is_box() => {
670                            err.help("use `Box::pin`");
671                        }
672                        _ => {
673                            let prefix = if let Some(name) =
674                                self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr)
675                            {
676                                format!("{}: ", name)
677                            } else {
678                                String::new()
679                            };
680                            let suggestion = vec![
681                                (expr.span.shrink_to_lo(), format!("{prefix}Box::pin(")),
682                                (expr.span.shrink_to_hi(), ")".to_string()),
683                            ];
684                            err.multipart_suggestion(
685                                "you need to pin and box this expression",
686                                suggestion,
687                                Applicability::MaybeIncorrect,
688                            );
689                        }
690                    }
691                    true
692                } else if self.may_coerce(pin_found, expected) {
693                    match found.kind() {
694                        ty::Adt(def, _) if def.is_box() => {
695                            err.help("use `Box::pin`");
696                            true
697                        }
698                        _ => false,
699                    }
700                } else {
701                    false
702                }
703            }
704            ty::Adt(def, _) if def.is_box() && self.may_coerce(box_found, expected) => {
705                // Check if the parent expression is a call to Pin::new. If it
706                // is and we were expecting a Box, ergo Pin<Box<expected>>, we
707                // can suggest Box::pin.
708                let Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. }) =
709                    self.tcx.parent_hir_node(expr.hir_id)
710                else {
711                    return false;
712                };
713                match fn_name.kind {
714                    ExprKind::Path(QPath::TypeRelative(
715                        hir::Ty {
716                            kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })),
717                            ..
718                        },
719                        method,
720                    )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => {
721                        err.span_suggestion(
722                            fn_name.span,
723                            "use `Box::pin` to pin and box this expression",
724                            "Box::pin",
725                            Applicability::MachineApplicable,
726                        );
727                        true
728                    }
729                    _ => false,
730                }
731            }
732            _ => false,
733        }
734    }
735
736    /// A common error is to forget to add a semicolon at the end of a block, e.g.,
737    ///
738    /// ```compile_fail,E0308
739    /// # fn bar_that_returns_u32() -> u32 { 4 }
740    /// fn foo() {
741    ///     bar_that_returns_u32()
742    /// }
743    /// ```
744    ///
745    /// This routine checks if the return expression in a block would make sense on its own as a
746    /// statement and the return type has been left as default or has been specified as `()`. If so,
747    /// it suggests adding a semicolon.
748    ///
749    /// If the expression is the expression of a closure without block (`|| expr`), a
750    /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`.
751    pub(crate) fn suggest_missing_semicolon(
752        &self,
753        err: &mut Diag<'_>,
754        expression: &'tcx hir::Expr<'tcx>,
755        expected: Ty<'tcx>,
756        needs_block: bool,
757    ) {
758        if expected.is_unit() {
759            // `BlockTailExpression` only relevant if the tail expr would be
760            // useful on its own.
761            match expression.kind {
762                ExprKind::Call(..)
763                | ExprKind::MethodCall(..)
764                | ExprKind::Loop(..)
765                | ExprKind::If(..)
766                | ExprKind::Match(..)
767                | ExprKind::Block(..)
768                    if expression.can_have_side_effects()
769                        // If the expression is from an external macro, then do not suggest
770                        // adding a semicolon, because there's nowhere to put it.
771                        // See issue #81943.
772                        && !expression.span.in_external_macro(self.tcx.sess.source_map()) =>
773                {
774                    if needs_block {
775                        err.multipart_suggestion(
776                            "consider using a semicolon here",
777                            vec![
778                                (expression.span.shrink_to_lo(), "{ ".to_owned()),
779                                (expression.span.shrink_to_hi(), "; }".to_owned()),
780                            ],
781                            Applicability::MachineApplicable,
782                        );
783                    } else {
784                        err.span_suggestion(
785                            expression.span.shrink_to_hi(),
786                            "consider using a semicolon here",
787                            ";",
788                            Applicability::MachineApplicable,
789                        );
790                    }
791                }
792                _ => (),
793            }
794        }
795    }
796
797    /// A possible error is to forget to add a return type that is needed:
798    ///
799    /// ```compile_fail,E0308
800    /// # fn bar_that_returns_u32() -> u32 { 4 }
801    /// fn foo() {
802    ///     bar_that_returns_u32()
803    /// }
804    /// ```
805    ///
806    /// This routine checks if the return type is left as default, the method is not part of an
807    /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
808    /// type.
809    #[instrument(level = "trace", skip(self, err))]
810    pub(in super::super) fn suggest_missing_return_type(
811        &self,
812        err: &mut Diag<'_>,
813        fn_decl: &hir::FnDecl<'tcx>,
814        expected: Ty<'tcx>,
815        found: Ty<'tcx>,
816        fn_id: LocalDefId,
817    ) -> bool {
818        // Can't suggest `->` on a block-like coroutine
819        if let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Block)) =
820            self.tcx.coroutine_kind(fn_id)
821        {
822            return false;
823        }
824
825        let found =
826            self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
827        // Only suggest changing the return type for methods that
828        // haven't set a return type at all (and aren't `fn main()`, impl or closure).
829        match &fn_decl.output {
830            // For closure with default returns, don't suggest adding return type
831            &hir::FnRetTy::DefaultReturn(_) if self.tcx.is_closure_like(fn_id.to_def_id()) => {}
832            &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
833                if !self.can_add_return_type(fn_id) {
834                    err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span });
835                } else if let Some(found) = found.make_suggestable(self.tcx, false, None) {
836                    err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
837                        span,
838                        found: found.to_string(),
839                    });
840                } else if let Some(sugg) = suggest_impl_trait(self, self.param_env, found) {
841                    err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: sugg });
842                } else {
843                    // FIXME: if `found` could be `impl Iterator` we should suggest that.
844                    err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span });
845                }
846
847                return true;
848            }
849            hir::FnRetTy::Return(hir_ty) => {
850                if let hir::TyKind::OpaqueDef(op_ty, ..) = hir_ty.kind
851                    // FIXME: account for RPITIT.
852                    && let [hir::GenericBound::Trait(trait_ref)] = op_ty.bounds
853                    && let Some(hir::PathSegment { args: Some(generic_args), .. }) =
854                        trait_ref.trait_ref.path.segments.last()
855                    && let [constraint] = generic_args.constraints
856                    && let Some(ty) = constraint.ty()
857                {
858                    // Check if async function's return type was omitted.
859                    // Don't emit suggestions if the found type is `impl Future<...>`.
860                    debug!(?found);
861                    if found.is_suggestable(self.tcx, false) {
862                        if ty.span.is_empty() {
863                            err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
864                                span: ty.span,
865                                found: found.to_string(),
866                            });
867                            return true;
868                        } else {
869                            err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
870                                span: ty.span,
871                                expected,
872                            });
873                        }
874                    }
875                } else {
876                    // Only point to return type if the expected type is the return type, as if they
877                    // are not, the expectation must have been caused by something else.
878                    debug!(?hir_ty, "return type");
879                    let ty = self.lowerer().lower_ty(hir_ty);
880                    debug!(?ty, "return type (lowered)");
881                    debug!(?expected, "expected type");
882                    let bound_vars =
883                        self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
884                    let ty = Binder::bind_with_vars(ty, bound_vars);
885                    let ty = self.normalize(hir_ty.span, ty);
886                    let ty = self.tcx.instantiate_bound_regions_with_erased(ty);
887                    if self.may_coerce(expected, ty) {
888                        err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
889                            span: hir_ty.span,
890                            expected,
891                        });
892                        self.try_suggest_return_impl_trait(err, expected, found, fn_id);
893                        self.try_note_caller_chooses_ty_for_ty_param(err, expected, found);
894                        return true;
895                    }
896                }
897            }
898            _ => {}
899        }
900        false
901    }
902
903    /// Checks whether we can add a return type to a function.
904    /// Assumes given function doesn't have a explicit return type.
905    fn can_add_return_type(&self, fn_id: LocalDefId) -> bool {
906        match self.tcx.hir_node_by_def_id(fn_id) {
907            Node::Item(item) => {
908                let (ident, _, _, _) = item.expect_fn();
909                // This is less than ideal, it will not suggest a return type span on any
910                // method called `main`, regardless of whether it is actually the entry point,
911                // but it will still present it as the reason for the expected type.
912                ident.name != sym::main
913            }
914            Node::ImplItem(item) => {
915                // If it doesn't impl a trait, we can add a return type
916                let Node::Item(&hir::Item {
917                    kind: hir::ItemKind::Impl(&hir::Impl { of_trait, .. }),
918                    ..
919                }) = self.tcx.parent_hir_node(item.hir_id())
920                else {
921                    unreachable!();
922                };
923
924                of_trait.is_none()
925            }
926            _ => true,
927        }
928    }
929
930    fn try_note_caller_chooses_ty_for_ty_param(
931        &self,
932        diag: &mut Diag<'_>,
933        expected: Ty<'tcx>,
934        found: Ty<'tcx>,
935    ) {
936        // Only show the note if:
937        // 1. `expected` ty is a type parameter;
938        // 2. The `expected` type parameter does *not* occur in the return expression type. This can
939        //    happen for e.g. `fn foo<T>(t: &T) -> T { t }`, where `expected` is `T` but `found` is
940        //    `&T`. Saying "the caller chooses a type for `T` which can be different from `&T`" is
941        //    "well duh" and is only confusing and not helpful.
942        let ty::Param(expected_ty_as_param) = expected.kind() else {
943            return;
944        };
945
946        if found.contains(expected) {
947            return;
948        }
949
950        diag.subdiagnostic(errors::NoteCallerChoosesTyForTyParam {
951            ty_param_name: expected_ty_as_param.name,
952            found_ty: found,
953        });
954    }
955
956    /// check whether the return type is a generic type with a trait bound
957    /// only suggest this if the generic param is not present in the arguments
958    /// if this is true, hint them towards changing the return type to `impl Trait`
959    /// ```compile_fail,E0308
960    /// fn cant_name_it<T: Fn() -> u32>() -> T {
961    ///     || 3
962    /// }
963    /// ```
964    fn try_suggest_return_impl_trait(
965        &self,
966        err: &mut Diag<'_>,
967        expected: Ty<'tcx>,
968        found: Ty<'tcx>,
969        fn_id: LocalDefId,
970    ) {
971        // Only apply the suggestion if:
972        //  - the return type is a generic parameter
973        //  - the generic param is not used as a fn param
974        //  - the generic param has at least one bound
975        //  - the generic param doesn't appear in any other bounds where it's not the Self type
976        // Suggest:
977        //  - Changing the return type to be `impl <all bounds>`
978
979        debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found);
980
981        let ty::Param(expected_ty_as_param) = expected.kind() else { return };
982
983        let fn_node = self.tcx.hir_node_by_def_id(fn_id);
984
985        let hir::Node::Item(hir::Item {
986            kind:
987                hir::ItemKind::Fn {
988                    sig:
989                        hir::FnSig {
990                            decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. },
991                            ..
992                        },
993                    generics: hir::Generics { params, predicates, .. },
994                    ..
995                },
996            ..
997        }) = fn_node
998        else {
999            return;
1000        };
1001
1002        if params.get(expected_ty_as_param.index as usize).is_none() {
1003            return;
1004        };
1005
1006        // get all where BoundPredicates here, because they are used in two cases below
1007        let where_predicates = predicates
1008            .iter()
1009            .filter_map(|p| match p.kind {
1010                WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
1011                    bounds,
1012                    bounded_ty,
1013                    ..
1014                }) => {
1015                    // FIXME: Maybe these calls to `lower_ty` can be removed (and the ones below)
1016                    let ty = self.lowerer().lower_ty(bounded_ty);
1017                    Some((ty, bounds))
1018                }
1019                _ => None,
1020            })
1021            .map(|(ty, bounds)| match ty.kind() {
1022                ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
1023                // check whether there is any predicate that contains our `T`, like `Option<T>: Send`
1024                _ => match ty.contains(expected) {
1025                    true => Err(()),
1026                    false => Ok(None),
1027                },
1028            })
1029            .collect::<Result<Vec<_>, _>>();
1030
1031        let Ok(where_predicates) = where_predicates else { return };
1032
1033        // now get all predicates in the same types as the where bounds, so we can chain them
1034        let predicates_from_where =
1035            where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
1036
1037        // extract all bounds from the source code using their spans
1038        let all_matching_bounds_strs = predicates_from_where
1039            .filter_map(|bound| match bound {
1040                GenericBound::Trait(_) => {
1041                    self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
1042                }
1043                _ => None,
1044            })
1045            .collect::<Vec<String>>();
1046
1047        if all_matching_bounds_strs.len() == 0 {
1048            return;
1049        }
1050
1051        let all_bounds_str = all_matching_bounds_strs.join(" + ");
1052
1053        let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
1054                let ty = self.lowerer().lower_ty( param);
1055                matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
1056            });
1057
1058        if ty_param_used_in_fn_params {
1059            return;
1060        }
1061
1062        err.span_suggestion(
1063            fn_return.span(),
1064            "consider using an impl return type",
1065            format!("impl {all_bounds_str}"),
1066            Applicability::MaybeIncorrect,
1067        );
1068    }
1069
1070    pub(in super::super) fn suggest_missing_break_or_return_expr(
1071        &self,
1072        err: &mut Diag<'_>,
1073        expr: &'tcx hir::Expr<'tcx>,
1074        fn_decl: &hir::FnDecl<'tcx>,
1075        expected: Ty<'tcx>,
1076        found: Ty<'tcx>,
1077        id: HirId,
1078        fn_id: LocalDefId,
1079    ) {
1080        if !expected.is_unit() {
1081            return;
1082        }
1083        let found = self.resolve_vars_if_possible(found);
1084
1085        let in_loop = self.is_loop(id)
1086            || self
1087                .tcx
1088                .hir_parent_iter(id)
1089                .take_while(|(_, node)| {
1090                    // look at parents until we find the first body owner
1091                    node.body_id().is_none()
1092                })
1093                .any(|(parent_id, _)| self.is_loop(parent_id));
1094
1095        let in_local_statement = self.is_local_statement(id)
1096            || self
1097                .tcx
1098                .hir_parent_iter(id)
1099                .any(|(parent_id, _)| self.is_local_statement(parent_id));
1100
1101        if in_loop && in_local_statement {
1102            err.multipart_suggestion(
1103                "you might have meant to break the loop with this value",
1104                vec![
1105                    (expr.span.shrink_to_lo(), "break ".to_string()),
1106                    (expr.span.shrink_to_hi(), ";".to_string()),
1107                ],
1108                Applicability::MaybeIncorrect,
1109            );
1110            return;
1111        }
1112
1113        let scope = self.tcx.hir_parent_iter(id).find(|(_, node)| {
1114            matches!(
1115                node,
1116                Node::Expr(Expr { kind: ExprKind::Closure(..), .. })
1117                    | Node::Item(_)
1118                    | Node::TraitItem(_)
1119                    | Node::ImplItem(_)
1120            )
1121        });
1122        let in_closure =
1123            matches!(scope, Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. }))));
1124
1125        let can_return = match fn_decl.output {
1126            hir::FnRetTy::Return(ty) => {
1127                let ty = self.lowerer().lower_ty(ty);
1128                let bound_vars = self.tcx.late_bound_vars(self.tcx.local_def_id_to_hir_id(fn_id));
1129                let ty = self
1130                    .tcx
1131                    .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
1132                let ty = match self.tcx.asyncness(fn_id) {
1133                    ty::Asyncness::Yes => {
1134                        self.err_ctxt().get_impl_future_output_ty(ty).unwrap_or_else(|| {
1135                            span_bug!(
1136                                fn_decl.output.span(),
1137                                "failed to get output type of async function"
1138                            )
1139                        })
1140                    }
1141                    ty::Asyncness::No => ty,
1142                };
1143                let ty = self.normalize(expr.span, ty);
1144                self.may_coerce(found, ty)
1145            }
1146            hir::FnRetTy::DefaultReturn(_) if in_closure => {
1147                self.ret_coercion.as_ref().is_some_and(|ret| {
1148                    let ret_ty = ret.borrow().expected_ty();
1149                    self.may_coerce(found, ret_ty)
1150                })
1151            }
1152            _ => false,
1153        };
1154        if can_return
1155            && let Some(span) = expr.span.find_ancestor_inside(
1156                self.tcx.hir().span_with_body(self.tcx.local_def_id_to_hir_id(fn_id)),
1157            )
1158        {
1159            // When the expr is in a match arm's body, we shouldn't add semicolon ';' at the end.
1160            // For example:
1161            // fn mismatch_types() -> i32 {
1162            //     match 1 {
1163            //         x => dbg!(x),
1164            //     }
1165            //     todo!()
1166            // }
1167            // -------------^^^^^^^-
1168            // Don't add semicolon `;` at the end of `dbg!(x)` expr
1169            fn is_in_arm<'tcx>(expr: &'tcx hir::Expr<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
1170                for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
1171                    match node {
1172                        hir::Node::Block(block) => {
1173                            if let Some(ret) = block.expr
1174                                && ret.hir_id == expr.hir_id
1175                            {
1176                                continue;
1177                            }
1178                        }
1179                        hir::Node::Arm(arm) => {
1180                            if let hir::ExprKind::Block(block, _) = arm.body.kind
1181                                && let Some(ret) = block.expr
1182                                && ret.hir_id == expr.hir_id
1183                            {
1184                                return true;
1185                            }
1186                        }
1187                        hir::Node::Expr(e) if let hir::ExprKind::Block(block, _) = e.kind => {
1188                            if let Some(ret) = block.expr
1189                                && ret.hir_id == expr.hir_id
1190                            {
1191                                continue;
1192                            }
1193                        }
1194                        _ => {
1195                            return false;
1196                        }
1197                    }
1198                }
1199
1200                false
1201            }
1202            let mut suggs = vec![(span.shrink_to_lo(), "return ".to_string())];
1203            if !is_in_arm(expr, self.tcx) {
1204                suggs.push((span.shrink_to_hi(), ";".to_string()));
1205            }
1206            err.multipart_suggestion_verbose(
1207                "you might have meant to return this value",
1208                suggs,
1209                Applicability::MaybeIncorrect,
1210            );
1211        }
1212    }
1213
1214    pub(in super::super) fn suggest_missing_parentheses(
1215        &self,
1216        err: &mut Diag<'_>,
1217        expr: &hir::Expr<'_>,
1218    ) -> bool {
1219        let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None);
1220        if let Some(sp) = self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
1221            // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
1222            err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
1223            true
1224        } else {
1225            false
1226        }
1227    }
1228
1229    /// Given an expression type mismatch, peel any `&` expressions until we get to
1230    /// a block expression, and then suggest replacing the braces with square braces
1231    /// if it was possibly mistaken array syntax.
1232    pub(crate) fn suggest_block_to_brackets_peeling_refs(
1233        &self,
1234        diag: &mut Diag<'_>,
1235        mut expr: &hir::Expr<'_>,
1236        mut expr_ty: Ty<'tcx>,
1237        mut expected_ty: Ty<'tcx>,
1238    ) -> bool {
1239        loop {
1240            match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
1241                (
1242                    hir::ExprKind::AddrOf(_, _, inner_expr),
1243                    ty::Ref(_, inner_expr_ty, _),
1244                    ty::Ref(_, inner_expected_ty, _),
1245                ) => {
1246                    expr = *inner_expr;
1247                    expr_ty = *inner_expr_ty;
1248                    expected_ty = *inner_expected_ty;
1249                }
1250                (hir::ExprKind::Block(blk, _), _, _) => {
1251                    self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
1252                    break true;
1253                }
1254                _ => break false,
1255            }
1256        }
1257    }
1258
1259    pub(crate) fn suggest_clone_for_ref(
1260        &self,
1261        diag: &mut Diag<'_>,
1262        expr: &hir::Expr<'_>,
1263        expr_ty: Ty<'tcx>,
1264        expected_ty: Ty<'tcx>,
1265    ) -> bool {
1266        if let ty::Ref(_, inner_ty, hir::Mutability::Not) = expr_ty.kind()
1267            && let Some(clone_trait_def) = self.tcx.lang_items().clone_trait()
1268            && expected_ty == *inner_ty
1269            && self
1270                .infcx
1271                .type_implements_trait(
1272                    clone_trait_def,
1273                    [self.tcx.erase_regions(expected_ty)],
1274                    self.param_env,
1275                )
1276                .must_apply_modulo_regions()
1277        {
1278            let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
1279                Some(ident) => format!(": {ident}.clone()"),
1280                None => ".clone()".to_string(),
1281            };
1282
1283            diag.span_suggestion_verbose(
1284                expr.span.shrink_to_hi(),
1285                "consider using clone here",
1286                suggestion,
1287                Applicability::MachineApplicable,
1288            );
1289            return true;
1290        }
1291        false
1292    }
1293
1294    pub(crate) fn suggest_copied_cloned_or_as_ref(
1295        &self,
1296        diag: &mut Diag<'_>,
1297        expr: &hir::Expr<'_>,
1298        expr_ty: Ty<'tcx>,
1299        expected_ty: Ty<'tcx>,
1300    ) -> bool {
1301        let ty::Adt(adt_def, args) = expr_ty.kind() else {
1302            return false;
1303        };
1304        let ty::Adt(expected_adt_def, expected_args) = expected_ty.kind() else {
1305            return false;
1306        };
1307        if adt_def != expected_adt_def {
1308            return false;
1309        }
1310
1311        if Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Result)
1312            && self.can_eq(self.param_env, args.type_at(1), expected_args.type_at(1))
1313            || Some(adt_def.did()) == self.tcx.get_diagnostic_item(sym::Option)
1314        {
1315            let expr_inner_ty = args.type_at(0);
1316            let expected_inner_ty = expected_args.type_at(0);
1317            if let &ty::Ref(_, ty, _mutability) = expr_inner_ty.kind()
1318                && self.can_eq(self.param_env, ty, expected_inner_ty)
1319            {
1320                let def_path = self.tcx.def_path_str(adt_def.did());
1321                let span = expr.span.shrink_to_hi();
1322                let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
1323                    errors::OptionResultRefMismatch::Copied { span, def_path }
1324                } else if self.type_is_clone_modulo_regions(self.param_env, ty) {
1325                    errors::OptionResultRefMismatch::Cloned { span, def_path }
1326                } else {
1327                    return false;
1328                };
1329                diag.subdiagnostic(subdiag);
1330                return true;
1331            }
1332        }
1333
1334        false
1335    }
1336
1337    pub(crate) fn suggest_into(
1338        &self,
1339        diag: &mut Diag<'_>,
1340        expr: &hir::Expr<'_>,
1341        expr_ty: Ty<'tcx>,
1342        expected_ty: Ty<'tcx>,
1343    ) -> bool {
1344        let expr = expr.peel_blocks();
1345
1346        // We have better suggestions for scalar interconversions...
1347        if expr_ty.is_scalar() && expected_ty.is_scalar() {
1348            return false;
1349        }
1350
1351        // Don't suggest turning a block into another type (e.g. `{}.into()`)
1352        if matches!(expr.kind, hir::ExprKind::Block(..)) {
1353            return false;
1354        }
1355
1356        // We'll later suggest `.as_ref` when noting the type error,
1357        // so skip if we will suggest that instead.
1358        if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() {
1359            return false;
1360        }
1361
1362        if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
1363            && self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
1364                self.tcx,
1365                self.misc(expr.span),
1366                self.param_env,
1367                ty::TraitRef::new(self.tcx, into_def_id, [expr_ty, expected_ty]),
1368            ))
1369            && !expr
1370                .span
1371                .macro_backtrace()
1372                .any(|x| matches!(x.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..)))
1373        {
1374            let span = expr.span.find_oldest_ancestor_in_same_ctxt();
1375
1376            let mut sugg = if expr.precedence() >= ExprPrecedence::Unambiguous {
1377                vec![(span.shrink_to_hi(), ".into()".to_owned())]
1378            } else {
1379                vec![
1380                    (span.shrink_to_lo(), "(".to_owned()),
1381                    (span.shrink_to_hi(), ").into()".to_owned()),
1382                ]
1383            };
1384            if let Some(name) = self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
1385                sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
1386            }
1387            diag.multipart_suggestion(
1388                    format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
1389                    sugg,
1390                    Applicability::MaybeIncorrect
1391                );
1392            return true;
1393        }
1394
1395        false
1396    }
1397
1398    /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()`
1399    pub(crate) fn suggest_option_to_bool(
1400        &self,
1401        diag: &mut Diag<'_>,
1402        expr: &hir::Expr<'_>,
1403        expr_ty: Ty<'tcx>,
1404        expected_ty: Ty<'tcx>,
1405    ) -> bool {
1406        if !expected_ty.is_bool() {
1407            return false;
1408        }
1409
1410        let ty::Adt(def, _) = expr_ty.peel_refs().kind() else {
1411            return false;
1412        };
1413        if !self.tcx.is_diagnostic_item(sym::Option, def.did()) {
1414            return false;
1415        }
1416
1417        let cond_parent = self.tcx.hir_parent_iter(expr.hir_id).find(|(_, node)| {
1418            !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
1419        });
1420        // Don't suggest:
1421        //     `let Some(_) = a.is_some() && b`
1422        //                     ++++++++++
1423        // since the user probably just misunderstood how `let else`
1424        // and `&&` work together.
1425        if let Some((_, hir::Node::LetStmt(local))) = cond_parent
1426            && let hir::PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
1427            | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind
1428            && let hir::QPath::Resolved(None, path) = qpath
1429            && let Some(did) = path
1430                .res
1431                .opt_def_id()
1432                .and_then(|did| self.tcx.opt_parent(did))
1433                .and_then(|did| self.tcx.opt_parent(did))
1434            && self.tcx.is_diagnostic_item(sym::Option, did)
1435        {
1436            return false;
1437        }
1438
1439        let suggestion = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
1440            Some(ident) => format!(": {ident}.is_some()"),
1441            None => ".is_some()".to_string(),
1442        };
1443
1444        diag.span_suggestion_verbose(
1445            expr.span.shrink_to_hi(),
1446            "use `Option::is_some` to test if the `Option` has a value",
1447            suggestion,
1448            Applicability::MachineApplicable,
1449        );
1450        true
1451    }
1452
1453    // Suggest to change `Option<&Vec<T>>::unwrap_or(&[])` to `Option::map_or(&[], |v| v)`.
1454    #[instrument(level = "trace", skip(self, err, provided_expr))]
1455    pub(crate) fn suggest_deref_unwrap_or(
1456        &self,
1457        err: &mut Diag<'_>,
1458        callee_ty: Option<Ty<'tcx>>,
1459        call_ident: Option<Ident>,
1460        expected_ty: Ty<'tcx>,
1461        provided_ty: Ty<'tcx>,
1462        provided_expr: &Expr<'tcx>,
1463        is_method: bool,
1464    ) {
1465        if !is_method {
1466            return;
1467        }
1468        let Some(callee_ty) = callee_ty else {
1469            return;
1470        };
1471        let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
1472            return;
1473        };
1474        let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
1475            "Option"
1476        } else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) {
1477            "Result"
1478        } else {
1479            return;
1480        };
1481
1482        let Some(call_ident) = call_ident else {
1483            return;
1484        };
1485        if call_ident.name != sym::unwrap_or {
1486            return;
1487        }
1488
1489        let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
1490            return;
1491        };
1492
1493        // NOTE: Can we reuse `suggest_deref_or_ref`?
1494
1495        // Create an dummy type `&[_]` so that both &[] and `&Vec<T>` can coerce to it.
1496        let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
1497            && let ty::Infer(_) = elem_ty.kind()
1498            && self
1499                .try_structurally_resolve_const(provided_expr.span, *size)
1500                .try_to_target_usize(self.tcx)
1501                == Some(0)
1502        {
1503            let slice = Ty::new_slice(self.tcx, *elem_ty);
1504            Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
1505        } else {
1506            provided_ty
1507        };
1508
1509        if !self.may_coerce(expected_ty, dummy_ty) {
1510            return;
1511        }
1512        let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
1513        err.multipart_suggestion_verbose(
1514            msg,
1515            vec![
1516                (call_ident.span, "map_or".to_owned()),
1517                (provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()),
1518            ],
1519            Applicability::MachineApplicable,
1520        );
1521    }
1522
1523    /// Suggest wrapping the block in square brackets instead of curly braces
1524    /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
1525    pub(crate) fn suggest_block_to_brackets(
1526        &self,
1527        diag: &mut Diag<'_>,
1528        blk: &hir::Block<'_>,
1529        blk_ty: Ty<'tcx>,
1530        expected_ty: Ty<'tcx>,
1531    ) {
1532        if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() {
1533            if self.may_coerce(blk_ty, *elem_ty)
1534                && blk.stmts.is_empty()
1535                && blk.rules == hir::BlockCheckMode::DefaultBlock
1536                && let source_map = self.tcx.sess.source_map()
1537                && let Ok(snippet) = source_map.span_to_snippet(blk.span)
1538                && snippet.starts_with('{')
1539                && snippet.ends_with('}')
1540            {
1541                diag.multipart_suggestion_verbose(
1542                    "to create an array, use square brackets instead of curly braces",
1543                    vec![
1544                        (
1545                            blk.span
1546                                .shrink_to_lo()
1547                                .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)),
1548                            "[".to_string(),
1549                        ),
1550                        (
1551                            blk.span
1552                                .shrink_to_hi()
1553                                .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)),
1554                            "]".to_string(),
1555                        ),
1556                    ],
1557                    Applicability::MachineApplicable,
1558                );
1559            }
1560        }
1561    }
1562
1563    #[instrument(skip(self, err))]
1564    pub(crate) fn suggest_floating_point_literal(
1565        &self,
1566        err: &mut Diag<'_>,
1567        expr: &hir::Expr<'_>,
1568        expected_ty: Ty<'tcx>,
1569    ) -> bool {
1570        if !expected_ty.is_floating_point() {
1571            return false;
1572        }
1573        match expr.kind {
1574            ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [start, end], _) => {
1575                err.span_suggestion_verbose(
1576                    start.span.shrink_to_hi().with_hi(end.span.lo()),
1577                    "remove the unnecessary `.` operator for a floating point literal",
1578                    '.',
1579                    Applicability::MaybeIncorrect,
1580                );
1581                true
1582            }
1583            ExprKind::Struct(QPath::LangItem(LangItem::RangeFrom, ..), [start], _) => {
1584                err.span_suggestion_verbose(
1585                    expr.span.with_lo(start.span.hi()),
1586                    "remove the unnecessary `.` operator for a floating point literal",
1587                    '.',
1588                    Applicability::MaybeIncorrect,
1589                );
1590                true
1591            }
1592            ExprKind::Struct(QPath::LangItem(LangItem::RangeTo, ..), [end], _) => {
1593                err.span_suggestion_verbose(
1594                    expr.span.until(end.span),
1595                    "remove the unnecessary `.` operator and add an integer part for a floating point literal",
1596                    "0.",
1597                    Applicability::MaybeIncorrect,
1598                );
1599                true
1600            }
1601            ExprKind::Lit(Spanned {
1602                node: rustc_ast::LitKind::Int(lit, rustc_ast::LitIntType::Unsuffixed),
1603                span,
1604            }) => {
1605                let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(*span) else {
1606                    return false;
1607                };
1608                if !(snippet.starts_with("0x") || snippet.starts_with("0X")) {
1609                    return false;
1610                }
1611                if snippet.len() <= 5 || !snippet.is_char_boundary(snippet.len() - 3) {
1612                    return false;
1613                }
1614                let (_, suffix) = snippet.split_at(snippet.len() - 3);
1615                let value = match suffix {
1616                    "f32" => (lit.get() - 0xf32) / (16 * 16 * 16),
1617                    "f64" => (lit.get() - 0xf64) / (16 * 16 * 16),
1618                    _ => return false,
1619                };
1620                err.span_suggestions(
1621                    expr.span,
1622                    "rewrite this as a decimal floating point literal, or use `as` to turn a hex literal into a float",
1623                    [format!("0x{value:X} as {suffix}"), format!("{value}_{suffix}")],
1624                    Applicability::MaybeIncorrect,
1625                );
1626                true
1627            }
1628            _ => false,
1629        }
1630    }
1631
1632    /// Suggest providing `std::ptr::null()` or `std::ptr::null_mut()` if they
1633    /// pass in a literal 0 to an raw pointer.
1634    #[instrument(skip(self, err))]
1635    pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg(
1636        &self,
1637        err: &mut Diag<'_>,
1638        expr: &hir::Expr<'_>,
1639        expected_ty: Ty<'tcx>,
1640    ) -> bool {
1641        // Expected type needs to be a raw pointer.
1642        let ty::RawPtr(_, mutbl) = expected_ty.kind() else {
1643            return false;
1644        };
1645
1646        // Provided expression needs to be a literal `0`.
1647        let ExprKind::Lit(Spanned { node: rustc_ast::LitKind::Int(Pu128(0), _), span }) = expr.kind
1648        else {
1649            return false;
1650        };
1651
1652        // We need to find a null pointer symbol to suggest
1653        let null_sym = match mutbl {
1654            hir::Mutability::Not => sym::ptr_null,
1655            hir::Mutability::Mut => sym::ptr_null_mut,
1656        };
1657        let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else {
1658            return false;
1659        };
1660        let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did));
1661
1662        // We have satisfied all requirements to provide a suggestion. Emit it.
1663        err.span_suggestion(
1664            *span,
1665            format!("if you meant to create a null pointer, use `{null_path_str}()`"),
1666            null_path_str + "()",
1667            Applicability::MachineApplicable,
1668        );
1669
1670        true
1671    }
1672
1673    pub(crate) fn suggest_associated_const(
1674        &self,
1675        err: &mut Diag<'_>,
1676        expr: &hir::Expr<'tcx>,
1677        expected_ty: Ty<'tcx>,
1678    ) -> bool {
1679        let Some((DefKind::AssocFn, old_def_id)) =
1680            self.typeck_results.borrow().type_dependent_def(expr.hir_id)
1681        else {
1682            return false;
1683        };
1684        let old_item_name = self.tcx.item_name(old_def_id);
1685        let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
1686        if old_item_name == capitalized_name {
1687            return false;
1688        }
1689        let (item, segment) = match expr.kind {
1690            hir::ExprKind::Path(QPath::Resolved(
1691                Some(ty),
1692                hir::Path { segments: [segment], .. },
1693            ))
1694            | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => {
1695                if let Some(self_ty) = self.typeck_results.borrow().node_type_opt(ty.hir_id)
1696                    && let Ok(pick) = self.probe_for_name(
1697                        Mode::Path,
1698                        Ident::new(capitalized_name, segment.ident.span),
1699                        Some(expected_ty),
1700                        IsSuggestion(true),
1701                        self_ty,
1702                        expr.hir_id,
1703                        ProbeScope::TraitsInScope,
1704                    )
1705                {
1706                    (pick.item, segment)
1707                } else {
1708                    return false;
1709                }
1710            }
1711            hir::ExprKind::Path(QPath::Resolved(
1712                None,
1713                hir::Path { segments: [.., segment], .. },
1714            )) => {
1715                // we resolved through some path that doesn't end in the item name,
1716                // better not do a bad suggestion by accident.
1717                if old_item_name != segment.ident.name {
1718                    return false;
1719                }
1720                if let Some(item) = self
1721                    .tcx
1722                    .associated_items(self.tcx.parent(old_def_id))
1723                    .filter_by_name_unhygienic(capitalized_name)
1724                    .next()
1725                {
1726                    (*item, segment)
1727                } else {
1728                    return false;
1729                }
1730            }
1731            _ => return false,
1732        };
1733        if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst {
1734            // Same item
1735            return false;
1736        }
1737        let item_ty = self.tcx.type_of(item.def_id).instantiate_identity();
1738        // FIXME(compiler-errors): This check is *so* rudimentary
1739        if item_ty.has_param() {
1740            return false;
1741        }
1742        if self.may_coerce(item_ty, expected_ty) {
1743            err.span_suggestion_verbose(
1744                segment.ident.span,
1745                format!("try referring to the associated const `{capitalized_name}` instead",),
1746                capitalized_name,
1747                Applicability::MachineApplicable,
1748            );
1749            true
1750        } else {
1751            false
1752        }
1753    }
1754
1755    fn is_loop(&self, id: HirId) -> bool {
1756        let node = self.tcx.hir_node(id);
1757        matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
1758    }
1759
1760    fn is_local_statement(&self, id: HirId) -> bool {
1761        let node = self.tcx.hir_node(id);
1762        matches!(node, Node::Stmt(Stmt { kind: StmtKind::Let(..), .. }))
1763    }
1764
1765    /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`,
1766    /// which is a side-effect of autoref.
1767    pub(crate) fn note_type_is_not_clone(
1768        &self,
1769        diag: &mut Diag<'_>,
1770        expected_ty: Ty<'tcx>,
1771        found_ty: Ty<'tcx>,
1772        expr: &hir::Expr<'_>,
1773    ) {
1774        // When `expr` is `x` in something like `let x = foo.clone(); x`, need to recurse up to get
1775        // `foo` and `clone`.
1776        let expr = self.note_type_is_not_clone_inner_expr(expr);
1777
1778        // If we've recursed to an `expr` of `foo.clone()`, get `foo` and `clone`.
1779        let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else {
1780            return;
1781        };
1782
1783        let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else {
1784            return;
1785        };
1786        let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
1787        let results = self.typeck_results.borrow();
1788        // First, look for a `Clone::clone` call
1789        if segment.ident.name == sym::clone
1790            && results.type_dependent_def_id(expr.hir_id).is_some_and(|did| {
1791                    let assoc_item = self.tcx.associated_item(did);
1792                    assoc_item.container == ty::AssocItemContainer::Trait
1793                        && assoc_item.container_id(self.tcx) == clone_trait_did
1794                })
1795            // If that clone call hasn't already dereferenced the self type (i.e. don't give this
1796            // diagnostic in cases where we have `(&&T).clone()` and we expect `T`).
1797            && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..)))
1798            // Check that we're in fact trying to clone into the expected type
1799            && self.may_coerce(*pointee_ty, expected_ty)
1800            && let trait_ref = ty::TraitRef::new(self.tcx, clone_trait_did, [expected_ty])
1801            // And the expected type doesn't implement `Clone`
1802            && !self.predicate_must_hold_considering_regions(&traits::Obligation::new(
1803                self.tcx,
1804                traits::ObligationCause::dummy(),
1805                self.param_env,
1806                trait_ref,
1807            ))
1808        {
1809            diag.span_note(
1810                callee_expr.span,
1811                format!(
1812                    "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead"
1813                ),
1814            );
1815            let owner = self.tcx.hir_enclosing_body_owner(expr.hir_id);
1816            if let ty::Param(param) = expected_ty.kind()
1817                && let Some(generics) = self.tcx.hir_get_generics(owner)
1818            {
1819                suggest_constraining_type_params(
1820                    self.tcx,
1821                    generics,
1822                    diag,
1823                    vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
1824                    None,
1825                );
1826            } else {
1827                if let Some(errors) =
1828                    self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env)
1829                {
1830                    match &errors[..] {
1831                        [] => {}
1832                        [error] => {
1833                            diag.help(format!(
1834                                "`Clone` is not implemented because the trait bound `{}` is \
1835                                 not satisfied",
1836                                error.obligation.predicate,
1837                            ));
1838                        }
1839                        _ => {
1840                            diag.help(format!(
1841                                "`Clone` is not implemented because the following trait bounds \
1842                                 could not be satisfied: {}",
1843                                listify(&errors, |e| format!("`{}`", e.obligation.predicate))
1844                                    .unwrap(),
1845                            ));
1846                        }
1847                    }
1848                    for error in errors {
1849                        if let traits::FulfillmentErrorCode::Select(
1850                            traits::SelectionError::Unimplemented,
1851                        ) = error.code
1852                            && let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
1853                                error.obligation.predicate.kind().skip_binder()
1854                        {
1855                            self.infcx.err_ctxt().suggest_derive(
1856                                &error.obligation,
1857                                diag,
1858                                error.obligation.predicate.kind().rebind(pred),
1859                            );
1860                        }
1861                    }
1862                }
1863                self.suggest_derive(diag, &[(trait_ref.upcast(self.tcx), None, None)]);
1864            }
1865        }
1866    }
1867
1868    /// Given a type mismatch error caused by `&T` being cloned instead of `T`, and
1869    /// the `expr` as the source of this type mismatch, try to find the method call
1870    /// as the source of this error and return that instead. Otherwise, return the
1871    /// original expression.
1872    fn note_type_is_not_clone_inner_expr<'b>(
1873        &'b self,
1874        expr: &'b hir::Expr<'b>,
1875    ) -> &'b hir::Expr<'b> {
1876        match expr.peel_blocks().kind {
1877            hir::ExprKind::Path(hir::QPath::Resolved(
1878                None,
1879                hir::Path { segments: [_], res: crate::Res::Local(binding), .. },
1880            )) => {
1881                let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding) else {
1882                    return expr;
1883                };
1884
1885                match self.tcx.parent_hir_node(*hir_id) {
1886                    // foo.clone()
1887                    hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => {
1888                        self.note_type_is_not_clone_inner_expr(init)
1889                    }
1890                    // When `expr` is more complex like a tuple
1891                    hir::Node::Pat(hir::Pat {
1892                        hir_id: pat_hir_id,
1893                        kind: hir::PatKind::Tuple(pats, ..),
1894                        ..
1895                    }) => {
1896                        let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
1897                            self.tcx.parent_hir_node(*pat_hir_id)
1898                        else {
1899                            return expr;
1900                        };
1901
1902                        match init.peel_blocks().kind {
1903                            ExprKind::Tup(init_tup) => {
1904                                if let Some(init) = pats
1905                                    .iter()
1906                                    .enumerate()
1907                                    .filter(|x| x.1.hir_id == *hir_id)
1908                                    .find_map(|(i, _)| init_tup.get(i))
1909                                {
1910                                    self.note_type_is_not_clone_inner_expr(init)
1911                                } else {
1912                                    expr
1913                                }
1914                            }
1915                            _ => expr,
1916                        }
1917                    }
1918                    _ => expr,
1919                }
1920            }
1921            // If we're calling into a closure that may not be typed recurse into that call. no need
1922            // to worry if it's a call to a typed function or closure as this would ne handled
1923            // previously.
1924            hir::ExprKind::Call(Expr { kind: call_expr_kind, .. }, _) => {
1925                if let hir::ExprKind::Path(hir::QPath::Resolved(None, call_expr_path)) =
1926                    call_expr_kind
1927                    && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } =
1928                        call_expr_path
1929                    && let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding)
1930                    && let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) =
1931                        self.tcx.parent_hir_node(*hir_id)
1932                    && let Expr {
1933                        kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }),
1934                        ..
1935                    } = init
1936                {
1937                    let hir::Body { value: body_expr, .. } = self.tcx.hir_body(*body_id);
1938                    self.note_type_is_not_clone_inner_expr(body_expr)
1939                } else {
1940                    expr
1941                }
1942            }
1943            _ => expr,
1944        }
1945    }
1946
1947    pub(crate) fn is_field_suggestable(
1948        &self,
1949        field: &ty::FieldDef,
1950        hir_id: HirId,
1951        span: Span,
1952    ) -> bool {
1953        // The field must be visible in the containing module.
1954        field.vis.is_accessible_from(self.tcx.parent_module(hir_id), self.tcx)
1955            // The field must not be unstable.
1956            && !matches!(
1957                self.tcx.eval_stability(field.did, None, rustc_span::DUMMY_SP, None),
1958                rustc_middle::middle::stability::EvalResult::Deny { .. }
1959            )
1960            // If the field is from an external crate it must not be `doc(hidden)`.
1961            && (field.did.is_local() || !self.tcx.is_doc_hidden(field.did))
1962            // If the field is hygienic it must come from the same syntax context.
1963            && self.tcx.def_ident_span(field.did).unwrap().normalize_to_macros_2_0().eq_ctxt(span)
1964    }
1965
1966    pub(crate) fn suggest_missing_unwrap_expect(
1967        &self,
1968        err: &mut Diag<'_>,
1969        expr: &hir::Expr<'tcx>,
1970        expected: Ty<'tcx>,
1971        found: Ty<'tcx>,
1972    ) -> bool {
1973        let ty::Adt(adt, args) = found.kind() else {
1974            return false;
1975        };
1976        let ret_ty_matches = |diagnostic_item| {
1977            let Some(sig) = self.body_fn_sig() else {
1978                return false;
1979            };
1980            let ty::Adt(kind, _) = sig.output().kind() else {
1981                return false;
1982            };
1983            self.tcx.is_diagnostic_item(diagnostic_item, kind.did())
1984        };
1985
1986        // don't suggest anything like `Ok(ok_val).unwrap()` , `Some(some_val).unwrap()`,
1987        // `None.unwrap()` etc.
1988        let is_ctor = matches!(
1989            expr.kind,
1990            hir::ExprKind::Call(
1991                hir::Expr {
1992                    kind: hir::ExprKind::Path(hir::QPath::Resolved(
1993                        None,
1994                        hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
1995                    )),
1996                    ..
1997                },
1998                ..,
1999            ) | hir::ExprKind::Path(hir::QPath::Resolved(
2000                None,
2001                hir::Path { res: Res::Def(hir::def::DefKind::Ctor(_, _), _), .. },
2002            )),
2003        );
2004
2005        let (article, kind, variant, sugg_operator) =
2006            if self.tcx.is_diagnostic_item(sym::Result, adt.did()) {
2007                ("a", "Result", "Err", ret_ty_matches(sym::Result))
2008            } else if self.tcx.is_diagnostic_item(sym::Option, adt.did()) {
2009                ("an", "Option", "None", ret_ty_matches(sym::Option))
2010            } else {
2011                return false;
2012            };
2013        if is_ctor || !self.may_coerce(args.type_at(0), expected) {
2014            return false;
2015        }
2016
2017        let (msg, sugg) = if sugg_operator {
2018            (
2019                format!(
2020                    "use the `?` operator to extract the `{found}` value, propagating \
2021                            {article} `{kind}::{variant}` value to the caller"
2022                ),
2023                "?",
2024            )
2025        } else {
2026            (
2027                format!(
2028                    "consider using `{kind}::expect` to unwrap the `{found}` value, \
2029                                panicking if the value is {article} `{kind}::{variant}`"
2030                ),
2031                ".expect(\"REASON\")",
2032            )
2033        };
2034
2035        let sugg = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
2036            Some(ident) => format!(": {ident}{sugg}"),
2037            None => sugg.to_string(),
2038        };
2039
2040        let span = expr.span.find_oldest_ancestor_in_same_ctxt();
2041        err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders);
2042        true
2043    }
2044
2045    pub(crate) fn suggest_coercing_result_via_try_operator(
2046        &self,
2047        err: &mut Diag<'_>,
2048        expr: &hir::Expr<'tcx>,
2049        expected: Ty<'tcx>,
2050        found: Ty<'tcx>,
2051    ) -> bool {
2052        let returned = matches!(
2053            self.tcx.parent_hir_node(expr.hir_id),
2054            hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })
2055        ) || self.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_some();
2056        if returned
2057            && let ty::Adt(e, args_e) = expected.kind()
2058            && let ty::Adt(f, args_f) = found.kind()
2059            && e.did() == f.did()
2060            && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result)
2061            && let e_ok = args_e.type_at(0)
2062            && let f_ok = args_f.type_at(0)
2063            && self.infcx.can_eq(self.param_env, f_ok, e_ok)
2064            && let e_err = args_e.type_at(1)
2065            && let f_err = args_f.type_at(1)
2066            && self
2067                .infcx
2068                .type_implements_trait(
2069                    self.tcx.get_diagnostic_item(sym::Into).unwrap(),
2070                    [f_err, e_err],
2071                    self.param_env,
2072                )
2073                .must_apply_modulo_regions()
2074        {
2075            err.multipart_suggestion(
2076                "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
2077                 in `Ok` so the expression remains of type `Result`",
2078                vec![
2079                    (expr.span.shrink_to_lo(), "Ok(".to_string()),
2080                    (expr.span.shrink_to_hi(), "?)".to_string()),
2081                ],
2082                Applicability::MaybeIncorrect,
2083            );
2084            return true;
2085        }
2086        false
2087    }
2088
2089    // If the expr is a while or for loop and is the tail expr of its
2090    // enclosing body suggest returning a value right after it
2091    pub(crate) fn suggest_returning_value_after_loop(
2092        &self,
2093        err: &mut Diag<'_>,
2094        expr: &hir::Expr<'tcx>,
2095        expected: Ty<'tcx>,
2096    ) -> bool {
2097        let tcx = self.tcx;
2098        let enclosing_scope =
2099            tcx.hir_get_enclosing_scope(expr.hir_id).map(|hir_id| tcx.hir_node(hir_id));
2100
2101        // Get tail expr of the enclosing block or body
2102        let tail_expr = if let Some(Node::Block(hir::Block { expr, .. })) = enclosing_scope
2103            && expr.is_some()
2104        {
2105            *expr
2106        } else {
2107            let body_def_id = tcx.hir_enclosing_body_owner(expr.hir_id);
2108            let body = tcx.hir_body_owned_by(body_def_id);
2109
2110            // Get tail expr of the body
2111            match body.value.kind {
2112                // Regular function body etc.
2113                hir::ExprKind::Block(block, _) => block.expr,
2114                // Anon const body (there's no block in this case)
2115                hir::ExprKind::DropTemps(expr) => Some(expr),
2116                _ => None,
2117            }
2118        };
2119
2120        let Some(tail_expr) = tail_expr else {
2121            return false; // Body doesn't have a tail expr we can compare with
2122        };
2123
2124        // Get the loop expr within the tail expr
2125        let loop_expr_in_tail = match expr.kind {
2126            hir::ExprKind::Loop(_, _, hir::LoopSource::While, _) => tail_expr,
2127            hir::ExprKind::Loop(_, _, hir::LoopSource::ForLoop, _) => {
2128                match tail_expr.peel_drop_temps() {
2129                    Expr { kind: ExprKind::Match(_, [Arm { body, .. }], _), .. } => body,
2130                    _ => return false, // Not really a for loop
2131                }
2132            }
2133            _ => return false, // Not a while or a for loop
2134        };
2135
2136        // If the expr is the loop expr in the tail
2137        // then make the suggestion
2138        if expr.hir_id == loop_expr_in_tail.hir_id {
2139            let span = expr.span;
2140
2141            let (msg, suggestion) = if expected.is_never() {
2142                (
2143                    "consider adding a diverging expression here",
2144                    "`loop {}` or `panic!(\"...\")`".to_string(),
2145                )
2146            } else {
2147                ("consider returning a value here", format!("`{expected}` value"))
2148            };
2149
2150            let src_map = tcx.sess.source_map();
2151            let suggestion = if src_map.is_multiline(expr.span) {
2152                let indentation = src_map.indentation_before(span).unwrap_or_else(String::new);
2153                format!("\n{indentation}/* {suggestion} */")
2154            } else {
2155                // If the entire expr is on a single line
2156                // put the suggestion also on the same line
2157                format!(" /* {suggestion} */")
2158            };
2159
2160            err.span_suggestion_verbose(
2161                span.shrink_to_hi(),
2162                msg,
2163                suggestion,
2164                Applicability::MaybeIncorrect,
2165            );
2166
2167            true
2168        } else {
2169            false
2170        }
2171    }
2172
2173    /// Suggest replacing comma with semicolon in incorrect repeat expressions
2174    /// like `["_", 10]` or `vec![String::new(), 10]`.
2175    pub(crate) fn suggest_semicolon_in_repeat_expr(
2176        &self,
2177        err: &mut Diag<'_>,
2178        expr: &hir::Expr<'_>,
2179        expr_ty: Ty<'tcx>,
2180    ) -> bool {
2181        // Check if `expr` is contained in array of two elements
2182        if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id)
2183            && let hir::ExprKind::Array(elements) = array_expr.kind
2184            && let [first, second] = &elements[..]
2185            && second.hir_id == expr.hir_id
2186        {
2187            // Span between the two elements of the array
2188            let comma_span = first.span.between(second.span);
2189
2190            // Check if `expr` is a constant value of type `usize`.
2191            // This can only detect const variable declarations and
2192            // calls to const functions.
2193
2194            // Checking this here instead of rustc_hir::hir because
2195            // this check needs access to `self.tcx` but rustc_hir
2196            // has no access to `TyCtxt`.
2197            let expr_is_const_usize = expr_ty.is_usize()
2198                && match expr.kind {
2199                    ExprKind::Path(QPath::Resolved(
2200                        None,
2201                        Path { res: Res::Def(DefKind::Const, _), .. },
2202                    )) => true,
2203                    ExprKind::Call(
2204                        Expr {
2205                            kind:
2206                                ExprKind::Path(QPath::Resolved(
2207                                    None,
2208                                    Path { res: Res::Def(DefKind::Fn, fn_def_id), .. },
2209                                )),
2210                            ..
2211                        },
2212                        _,
2213                    ) => self.tcx.is_const_fn(*fn_def_id),
2214                    _ => false,
2215                };
2216
2217            // Type of the first element is guaranteed to be checked
2218            // when execution reaches here because `mismatched types`
2219            // error occurs only when type of second element of array
2220            // is not the same as type of first element.
2221            let first_ty = self.typeck_results.borrow().expr_ty(first);
2222
2223            // `array_expr` is from a macro `vec!["a", 10]` if
2224            // 1. array expression's span is imported from a macro
2225            // 2. first element of array implements `Clone` trait
2226            // 3. second element is an integer literal or is an expression of `usize` like type
2227            if self.tcx.sess.source_map().is_imported(array_expr.span)
2228                && self.type_is_clone_modulo_regions(self.param_env, first_ty)
2229                && (expr.is_size_lit() || expr_ty.is_usize_like())
2230            {
2231                err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2232                    comma_span,
2233                    descr: "a vector",
2234                });
2235                return true;
2236            }
2237
2238            // `array_expr` is from an array `["a", 10]` if
2239            // 1. first element of array implements `Copy` trait
2240            // 2. second element is an integer literal or is a const value of type `usize`
2241            if self.type_is_copy_modulo_regions(self.param_env, first_ty)
2242                && (expr.is_size_lit() || expr_is_const_usize)
2243            {
2244                err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2245                    comma_span,
2246                    descr: "an array",
2247                });
2248                return true;
2249            }
2250        }
2251        false
2252    }
2253
2254    /// If the expected type is an enum (Issue #55250) with any variants whose
2255    /// sole field is of the found type, suggest such variants. (Issue #42764)
2256    pub(crate) fn suggest_compatible_variants(
2257        &self,
2258        err: &mut Diag<'_>,
2259        expr: &hir::Expr<'_>,
2260        expected: Ty<'tcx>,
2261        expr_ty: Ty<'tcx>,
2262    ) -> bool {
2263        if expr.span.in_external_macro(self.tcx.sess.source_map()) {
2264            return false;
2265        }
2266        if let ty::Adt(expected_adt, args) = expected.kind() {
2267            if let hir::ExprKind::Field(base, ident) = expr.kind {
2268                let base_ty = self.typeck_results.borrow().expr_ty(base);
2269                if self.can_eq(self.param_env, base_ty, expected)
2270                    && let Some(base_span) = base.span.find_ancestor_inside(expr.span)
2271                {
2272                    err.span_suggestion_verbose(
2273                        expr.span.with_lo(base_span.hi()),
2274                        format!("consider removing the tuple struct field `{ident}`"),
2275                        "",
2276                        Applicability::MaybeIncorrect,
2277                    );
2278                    return true;
2279                }
2280            }
2281
2282            // If the expression is of type () and it's the return expression of a block,
2283            // we suggest adding a separate return expression instead.
2284            // (To avoid things like suggesting `Ok(while .. { .. })`.)
2285            if expr_ty.is_unit() {
2286                let mut id = expr.hir_id;
2287                let mut parent;
2288
2289                // Unroll desugaring, to make sure this works for `for` loops etc.
2290                loop {
2291                    parent = self.tcx.parent_hir_id(id);
2292                    let parent_span = self.tcx.hir().span(parent);
2293                    if parent_span.find_ancestor_inside(expr.span).is_some() {
2294                        // The parent node is part of the same span, so is the result of the
2295                        // same expansion/desugaring and not the 'real' parent node.
2296                        id = parent;
2297                        continue;
2298                    }
2299                    break;
2300                }
2301
2302                if let hir::Node::Block(&hir::Block { span: block_span, expr: Some(e), .. }) =
2303                    self.tcx.hir_node(parent)
2304                {
2305                    if e.hir_id == id {
2306                        if let Some(span) = expr.span.find_ancestor_inside(block_span) {
2307                            let return_suggestions = if self
2308                                .tcx
2309                                .is_diagnostic_item(sym::Result, expected_adt.did())
2310                            {
2311                                vec!["Ok(())"]
2312                            } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
2313                                vec!["None", "Some(())"]
2314                            } else {
2315                                return false;
2316                            };
2317                            if let Some(indent) =
2318                                self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
2319                            {
2320                                // Add a semicolon, except after `}`.
2321                                let semicolon =
2322                                    match self.tcx.sess.source_map().span_to_snippet(span) {
2323                                        Ok(s) if s.ends_with('}') => "",
2324                                        _ => ";",
2325                                    };
2326                                err.span_suggestions(
2327                                    span.shrink_to_hi(),
2328                                    "try adding an expression at the end of the block",
2329                                    return_suggestions
2330                                        .into_iter()
2331                                        .map(|r| format!("{semicolon}\n{indent}{r}")),
2332                                    Applicability::MaybeIncorrect,
2333                                );
2334                            }
2335                            return true;
2336                        }
2337                    }
2338                }
2339            }
2340
2341            let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
2342                .variants()
2343                .iter()
2344                .filter(|variant| {
2345                    variant.fields.len() == 1
2346                })
2347                .filter_map(|variant| {
2348                    let sole_field = &variant.single_field();
2349
2350                    let field_is_local = sole_field.did.is_local();
2351                    let field_is_accessible =
2352                        sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
2353                        // Skip suggestions for unstable public fields (for example `Pin::__pointer`)
2354                        && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
2355
2356                    if !field_is_local && !field_is_accessible {
2357                        return None;
2358                    }
2359
2360                    let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
2361                        .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
2362
2363                    let sole_field_ty = sole_field.ty(self.tcx, args);
2364                    if self.may_coerce(expr_ty, sole_field_ty) {
2365                        let variant_path =
2366                            with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
2367                        // FIXME #56861: DRYer prelude filtering
2368                        if let Some(path) = variant_path.strip_prefix("std::prelude::")
2369                            && let Some((_, path)) = path.split_once("::")
2370                        {
2371                            return Some((path.to_string(), variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy));
2372                        }
2373                        Some((variant_path, variant.ctor_kind(), sole_field.name, note_about_variant_field_privacy))
2374                    } else {
2375                        None
2376                    }
2377                })
2378                .collect();
2379
2380            let suggestions_for = |variant: &_, ctor_kind, field_name| {
2381                let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
2382                    Some(ident) => format!("{ident}: "),
2383                    None => String::new(),
2384                };
2385
2386                let (open, close) = match ctor_kind {
2387                    Some(CtorKind::Fn) => ("(".to_owned(), ")"),
2388                    None => (format!(" {{ {field_name}: "), " }"),
2389
2390                    Some(CtorKind::Const) => unreachable!("unit variants don't have fields"),
2391                };
2392
2393                // Suggest constructor as deep into the block tree as possible.
2394                // This fixes https://github.com/rust-lang/rust/issues/101065,
2395                // and also just helps make the most minimal suggestions.
2396                let mut expr = expr;
2397                while let hir::ExprKind::Block(block, _) = &expr.kind
2398                    && let Some(expr_) = &block.expr
2399                {
2400                    expr = expr_
2401                }
2402
2403                vec![
2404                    (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
2405                    (expr.span.shrink_to_hi(), close.to_owned()),
2406                ]
2407            };
2408
2409            match &compatible_variants[..] {
2410                [] => { /* No variants to format */ }
2411                [(variant, ctor_kind, field_name, note)] => {
2412                    // Just a single matching variant.
2413                    err.multipart_suggestion_verbose(
2414                        format!(
2415                            "try wrapping the expression in `{variant}`{note}",
2416                            note = note.as_deref().unwrap_or("")
2417                        ),
2418                        suggestions_for(&**variant, *ctor_kind, *field_name),
2419                        Applicability::MaybeIncorrect,
2420                    );
2421                    return true;
2422                }
2423                _ => {
2424                    // More than one matching variant.
2425                    err.multipart_suggestions(
2426                        format!(
2427                            "try wrapping the expression in a variant of `{}`",
2428                            self.tcx.def_path_str(expected_adt.did())
2429                        ),
2430                        compatible_variants.into_iter().map(
2431                            |(variant, ctor_kind, field_name, _)| {
2432                                suggestions_for(&variant, ctor_kind, field_name)
2433                            },
2434                        ),
2435                        Applicability::MaybeIncorrect,
2436                    );
2437                    return true;
2438                }
2439            }
2440        }
2441
2442        false
2443    }
2444
2445    pub(crate) fn suggest_non_zero_new_unwrap(
2446        &self,
2447        err: &mut Diag<'_>,
2448        expr: &hir::Expr<'_>,
2449        expected: Ty<'tcx>,
2450        expr_ty: Ty<'tcx>,
2451    ) -> bool {
2452        let tcx = self.tcx;
2453        let (adt, args, unwrap) = match expected.kind() {
2454            // In case `Option<NonZero<T>>` is wanted, but `T` is provided, suggest calling `new`.
2455            ty::Adt(adt, args) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
2456                let nonzero_type = args.type_at(0); // Unwrap option type.
2457                let ty::Adt(adt, args) = nonzero_type.kind() else {
2458                    return false;
2459                };
2460                (adt, args, "")
2461            }
2462            // In case `NonZero<T>` is wanted but `T` is provided, also add `.unwrap()` to satisfy types.
2463            ty::Adt(adt, args) => (adt, args, ".unwrap()"),
2464            _ => return false,
2465        };
2466
2467        if !self.tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
2468            return false;
2469        }
2470
2471        let int_type = args.type_at(0);
2472        if !self.may_coerce(expr_ty, int_type) {
2473            return false;
2474        }
2475
2476        err.multipart_suggestion(
2477            format!("consider calling `{}::new`", sym::NonZero),
2478            vec![
2479                (expr.span.shrink_to_lo(), format!("{}::new(", sym::NonZero)),
2480                (expr.span.shrink_to_hi(), format!("){unwrap}")),
2481            ],
2482            Applicability::MaybeIncorrect,
2483        );
2484
2485        true
2486    }
2487
2488    /// Identify some cases where `as_ref()` would be appropriate and suggest it.
2489    ///
2490    /// Given the following code:
2491    /// ```compile_fail,E0308
2492    /// struct Foo;
2493    /// fn takes_ref(_: &Foo) {}
2494    /// let ref opt = Some(Foo);
2495    ///
2496    /// opt.map(|param| takes_ref(param));
2497    /// ```
2498    /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead.
2499    ///
2500    /// It only checks for `Option` and `Result` and won't work with
2501    /// ```ignore (illustrative)
2502    /// opt.map(|param| { takes_ref(param) });
2503    /// ```
2504    fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Vec<(Span, String)>, &'static str)> {
2505        let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind else {
2506            return None;
2507        };
2508
2509        let hir::def::Res::Local(local_id) = path.res else {
2510            return None;
2511        };
2512
2513        let Node::Param(hir::Param { hir_id: param_hir_id, .. }) =
2514            self.tcx.parent_hir_node(local_id)
2515        else {
2516            return None;
2517        };
2518
2519        let Node::Expr(hir::Expr {
2520            hir_id: expr_hir_id,
2521            kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
2522            ..
2523        }) = self.tcx.parent_hir_node(*param_hir_id)
2524        else {
2525            return None;
2526        };
2527
2528        let hir = self.tcx.parent_hir_node(*expr_hir_id);
2529        let closure_params_len = closure_fn_decl.inputs.len();
2530        let (
2531            Node::Expr(hir::Expr {
2532                kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
2533                ..
2534            }),
2535            1,
2536        ) = (hir, closure_params_len)
2537        else {
2538            return None;
2539        };
2540
2541        let self_ty = self.typeck_results.borrow().expr_ty(receiver);
2542        let name = method_path.ident.name;
2543        let is_as_ref_able = match self_ty.peel_refs().kind() {
2544            ty::Adt(def, _) => {
2545                (self.tcx.is_diagnostic_item(sym::Option, def.did())
2546                    || self.tcx.is_diagnostic_item(sym::Result, def.did()))
2547                    && (name == sym::map || name == sym::and_then)
2548            }
2549            _ => false,
2550        };
2551        if is_as_ref_able {
2552            Some((
2553                vec![(method_path.ident.span.shrink_to_lo(), "as_ref().".to_string())],
2554                "consider using `as_ref` instead",
2555            ))
2556        } else {
2557            None
2558        }
2559    }
2560
2561    /// This function is used to determine potential "simple" improvements or users' errors and
2562    /// provide them useful help. For example:
2563    ///
2564    /// ```compile_fail,E0308
2565    /// fn some_fn(s: &str) {}
2566    ///
2567    /// let x = "hey!".to_owned();
2568    /// some_fn(x); // error
2569    /// ```
2570    ///
2571    /// No need to find every potential function which could make a coercion to transform a
2572    /// `String` into a `&str` since a `&` would do the trick!
2573    ///
2574    /// In addition of this check, it also checks between references mutability state. If the
2575    /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
2576    /// `&mut`!".
2577    pub(crate) fn suggest_deref_or_ref(
2578        &self,
2579        expr: &hir::Expr<'tcx>,
2580        checked_ty: Ty<'tcx>,
2581        expected: Ty<'tcx>,
2582    ) -> Option<(
2583        Vec<(Span, String)>,
2584        String,
2585        Applicability,
2586        bool, /* verbose */
2587        bool, /* suggest `&` or `&mut` type annotation */
2588    )> {
2589        let sess = self.sess();
2590        let sp = expr.span;
2591        let sm = sess.source_map();
2592
2593        // If the span is from an external macro, there's no suggestion we can make.
2594        if sp.in_external_macro(sm) {
2595            return None;
2596        }
2597
2598        let replace_prefix = |s: &str, old: &str, new: &str| {
2599            s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
2600        };
2601
2602        // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
2603        let expr = expr.peel_drop_temps();
2604
2605        match (&expr.kind, expected.kind(), checked_ty.kind()) {
2606            (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
2607                (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
2608                    if let hir::ExprKind::Lit(_) = expr.kind
2609                        && let Ok(src) = sm.span_to_snippet(sp)
2610                        && replace_prefix(&src, "b\"", "\"").is_some()
2611                    {
2612                        let pos = sp.lo() + BytePos(1);
2613                        return Some((
2614                            vec![(sp.with_hi(pos), String::new())],
2615                            "consider removing the leading `b`".to_string(),
2616                            Applicability::MachineApplicable,
2617                            true,
2618                            false,
2619                        ));
2620                    }
2621                }
2622                (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
2623                    if let hir::ExprKind::Lit(_) = expr.kind
2624                        && let Ok(src) = sm.span_to_snippet(sp)
2625                        && replace_prefix(&src, "\"", "b\"").is_some()
2626                    {
2627                        return Some((
2628                            vec![(sp.shrink_to_lo(), "b".to_string())],
2629                            "consider adding a leading `b`".to_string(),
2630                            Applicability::MachineApplicable,
2631                            true,
2632                            false,
2633                        ));
2634                    }
2635                }
2636                _ => {}
2637            },
2638            (_, &ty::Ref(_, _, mutability), _) => {
2639                // Check if it can work when put into a ref. For example:
2640                //
2641                // ```
2642                // fn bar(x: &mut i32) {}
2643                //
2644                // let x = 0u32;
2645                // bar(&x); // error, expected &mut
2646                // ```
2647                let ref_ty = match mutability {
2648                    hir::Mutability::Mut => {
2649                        Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2650                    }
2651                    hir::Mutability::Not => {
2652                        Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, checked_ty)
2653                    }
2654                };
2655                if self.may_coerce(ref_ty, expected) {
2656                    let mut sugg_sp = sp;
2657                    if let hir::ExprKind::MethodCall(segment, receiver, args, _) = expr.kind {
2658                        let clone_trait =
2659                            self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span));
2660                        if args.is_empty()
2661                            && self
2662                                .typeck_results
2663                                .borrow()
2664                                .type_dependent_def_id(expr.hir_id)
2665                                .is_some_and(|did| {
2666                                    let ai = self.tcx.associated_item(did);
2667                                    ai.trait_container(self.tcx) == Some(clone_trait)
2668                                })
2669                            && segment.ident.name == sym::clone
2670                        {
2671                            // If this expression had a clone call when suggesting borrowing
2672                            // we want to suggest removing it because it'd now be unnecessary.
2673                            sugg_sp = receiver.span;
2674                        }
2675                    }
2676
2677                    if let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
2678                        && let Some(1) = self.deref_steps_for_suggestion(expected, checked_ty)
2679                        && self.typeck_results.borrow().expr_ty(inner).is_ref()
2680                    {
2681                        // We have `*&T`, check if what was expected was `&T`.
2682                        // If so, we may want to suggest removing a `*`.
2683                        sugg_sp = sugg_sp.with_hi(inner.span.lo());
2684                        return Some((
2685                            vec![(sugg_sp, String::new())],
2686                            "consider removing deref here".to_string(),
2687                            Applicability::MachineApplicable,
2688                            true,
2689                            false,
2690                        ));
2691                    }
2692
2693                    if let Some((sugg, msg)) = self.can_use_as_ref(expr) {
2694                        return Some((
2695                            sugg,
2696                            msg.to_string(),
2697                            Applicability::MachineApplicable,
2698                            true,
2699                            false,
2700                        ));
2701                    }
2702
2703                    let prefix = match self.tcx.hir().maybe_get_struct_pattern_shorthand_field(expr)
2704                    {
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        op: hir::BinOp,
3482    ) {
3483        match op.node {
3484            hir::BinOpKind::Eq => {
3485                if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
3486                    && self
3487                        .infcx
3488                        .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
3489                        .must_apply_modulo_regions()
3490                {
3491                    let sm = self.tcx.sess.source_map();
3492                    if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span)
3493                        && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
3494                    {
3495                        err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
3496                        err.multipart_suggestion(
3497                            "consider swapping the equality",
3498                            vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
3499                            Applicability::MaybeIncorrect,
3500                        );
3501                    }
3502                }
3503            }
3504            _ => {}
3505        }
3506    }
3507}