rustc_hir_typeck/fn_ctxt/
suggestions.rs

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