]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/demand.rs
Auto merge of #102189 - davidtwco:translation-derive-enums, r=compiler-errors
[rust.git] / compiler / rustc_typeck / src / check / demand.rs
1 use crate::check::FnCtxt;
2 use rustc_infer::infer::InferOk;
3 use rustc_middle::middle::stability::EvalResult;
4 use rustc_trait_selection::infer::InferCtxtExt as _;
5 use rustc_trait_selection::traits::ObligationCause;
6
7 use rustc_ast::util::parser::PREC_POSTFIX;
8 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
9 use rustc_hir as hir;
10 use rustc_hir::lang_items::LangItem;
11 use rustc_hir::{is_range_literal, Node};
12 use rustc_middle::lint::in_external_macro;
13 use rustc_middle::ty::adjustment::AllowTwoPhase;
14 use rustc_middle::ty::error::{ExpectedFound, TypeError};
15 use rustc_middle::ty::print::with_no_trimmed_paths;
16 use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut};
17 use rustc_span::symbol::{sym, Symbol};
18 use rustc_span::{BytePos, Span};
19
20 use super::method::probe;
21
22 use std::iter;
23
24 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
25     pub fn emit_coerce_suggestions(
26         &self,
27         err: &mut Diagnostic,
28         expr: &hir::Expr<'tcx>,
29         expr_ty: Ty<'tcx>,
30         expected: Ty<'tcx>,
31         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
32         error: Option<TypeError<'tcx>>,
33     ) {
34         self.annotate_expected_due_to_let_ty(err, expr, error);
35         self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
36         self.suggest_compatible_variants(err, expr, expected, expr_ty);
37         self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty);
38         if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
39             return;
40         }
41         self.suggest_no_capture_closure(err, expected, expr_ty);
42         self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
43         self.suggest_missing_parentheses(err, expr);
44         self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
45         self.suggest_copied_or_cloned(err, expr, expr_ty, expected);
46         self.note_type_is_not_clone(err, expected, expr_ty, expr);
47         self.note_need_for_fn_pointer(err, expected, expr_ty);
48         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
49     }
50
51     // Requires that the two types unify, and prints an error message if
52     // they don't.
53     pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
54         if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) {
55             e.emit();
56         }
57     }
58
59     pub fn demand_suptype_diag(
60         &self,
61         sp: Span,
62         expected: Ty<'tcx>,
63         actual: Ty<'tcx>,
64     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
65         self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
66     }
67
68     #[instrument(skip(self), level = "debug")]
69     pub fn demand_suptype_with_origin(
70         &self,
71         cause: &ObligationCause<'tcx>,
72         expected: Ty<'tcx>,
73         actual: Ty<'tcx>,
74     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
75         match self.at(cause, self.param_env).sup(expected, actual) {
76             Ok(InferOk { obligations, value: () }) => {
77                 self.register_predicates(obligations);
78                 None
79             }
80             Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)),
81         }
82     }
83
84     pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
85         if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) {
86             err.emit();
87         }
88     }
89
90     pub fn demand_eqtype_diag(
91         &self,
92         sp: Span,
93         expected: Ty<'tcx>,
94         actual: Ty<'tcx>,
95     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
96         self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
97     }
98
99     pub fn demand_eqtype_with_origin(
100         &self,
101         cause: &ObligationCause<'tcx>,
102         expected: Ty<'tcx>,
103         actual: Ty<'tcx>,
104     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
105         match self.at(cause, self.param_env).eq(expected, actual) {
106             Ok(InferOk { obligations, value: () }) => {
107                 self.register_predicates(obligations);
108                 None
109             }
110             Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)),
111         }
112     }
113
114     pub fn demand_coerce(
115         &self,
116         expr: &hir::Expr<'tcx>,
117         checked_ty: Ty<'tcx>,
118         expected: Ty<'tcx>,
119         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
120         allow_two_phase: AllowTwoPhase,
121     ) -> Ty<'tcx> {
122         let (ty, err) =
123             self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase);
124         if let Some(mut err) = err {
125             err.emit();
126         }
127         ty
128     }
129
130     /// Checks that the type of `expr` can be coerced to `expected`.
131     ///
132     /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!`
133     /// will be permitted if the diverges flag is currently "always".
134     #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
135     pub fn demand_coerce_diag(
136         &self,
137         expr: &hir::Expr<'tcx>,
138         checked_ty: Ty<'tcx>,
139         expected: Ty<'tcx>,
140         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
141         allow_two_phase: AllowTwoPhase,
142     ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>) {
143         let expected = self.resolve_vars_with_obligations(expected);
144
145         let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase, None) {
146             Ok(ty) => return (ty, None),
147             Err(e) => e,
148         };
149
150         self.set_tainted_by_errors();
151         let expr = expr.peel_drop_temps();
152         let cause = self.misc(expr.span);
153         let expr_ty = self.resolve_vars_with_obligations(checked_ty);
154         let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone());
155
156         let is_insufficiently_polymorphic =
157             matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..));
158
159         // FIXME(#73154): For now, we do leak check when coercing function
160         // pointers in typeck, instead of only during borrowck. This can lead
161         // to these `RegionsInsufficientlyPolymorphic` errors that aren't helpful.
162         if !is_insufficiently_polymorphic {
163             self.emit_coerce_suggestions(
164                 &mut err,
165                 expr,
166                 expr_ty,
167                 expected,
168                 expected_ty_expr,
169                 Some(e),
170             );
171         }
172
173         (expected, Some(err))
174     }
175
176     fn annotate_expected_due_to_let_ty(
177         &self,
178         err: &mut Diagnostic,
179         expr: &hir::Expr<'_>,
180         error: Option<TypeError<'_>>,
181     ) {
182         let parent = self.tcx.hir().get_parent_node(expr.hir_id);
183         match (self.tcx.hir().find(parent), error) {
184             (Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })), _)
185                 if init.hir_id == expr.hir_id =>
186             {
187                 // Point at `let` assignment type.
188                 err.span_label(ty.span, "expected due to this");
189             }
190             (
191                 Some(hir::Node::Expr(hir::Expr {
192                     kind: hir::ExprKind::Assign(lhs, rhs, _), ..
193                 })),
194                 Some(TypeError::Sorts(ExpectedFound { expected, .. })),
195             ) if rhs.hir_id == expr.hir_id && !expected.is_closure() => {
196                 // We ignore closures explicitly because we already point at them elsewhere.
197                 // Point at the assigned-to binding.
198                 let mut primary_span = lhs.span;
199                 let mut secondary_span = lhs.span;
200                 let mut post_message = "";
201                 match lhs.kind {
202                     hir::ExprKind::Path(hir::QPath::Resolved(
203                         None,
204                         hir::Path {
205                             res:
206                                 hir::def::Res::Def(
207                                     hir::def::DefKind::Static(_) | hir::def::DefKind::Const,
208                                     def_id,
209                                 ),
210                             ..
211                         },
212                     )) => {
213                         if let Some(hir::Node::Item(hir::Item {
214                             ident,
215                             kind: hir::ItemKind::Static(ty, ..) | hir::ItemKind::Const(ty, ..),
216                             ..
217                         })) = self.tcx.hir().get_if_local(*def_id)
218                         {
219                             primary_span = ty.span;
220                             secondary_span = ident.span;
221                             post_message = " type";
222                         }
223                     }
224                     hir::ExprKind::Path(hir::QPath::Resolved(
225                         None,
226                         hir::Path { res: hir::def::Res::Local(hir_id), .. },
227                     )) => {
228                         if let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(*hir_id) {
229                             let parent = self.tcx.hir().get_parent_node(pat.hir_id);
230                             primary_span = pat.span;
231                             secondary_span = pat.span;
232                             match self.tcx.hir().find(parent) {
233                                 Some(hir::Node::Local(hir::Local { ty: Some(ty), .. })) => {
234                                     primary_span = ty.span;
235                                     post_message = " type";
236                                 }
237                                 Some(hir::Node::Local(hir::Local { init: Some(init), .. })) => {
238                                     primary_span = init.span;
239                                     post_message = " value";
240                                 }
241                                 Some(hir::Node::Param(hir::Param { ty_span, .. })) => {
242                                     primary_span = *ty_span;
243                                     post_message = " parameter type";
244                                 }
245                                 _ => {}
246                             }
247                         }
248                     }
249                     _ => {}
250                 }
251
252                 if primary_span != secondary_span
253                     && self
254                         .tcx
255                         .sess
256                         .source_map()
257                         .is_multiline(secondary_span.shrink_to_hi().until(primary_span))
258                 {
259                     // We are pointing at the binding's type or initializer value, but it's pattern
260                     // is in a different line, so we point at both.
261                     err.span_label(secondary_span, "expected due to the type of this binding");
262                     err.span_label(primary_span, &format!("expected due to this{post_message}"));
263                 } else if post_message == "" {
264                     // We are pointing at either the assignment lhs or the binding def pattern.
265                     err.span_label(primary_span, "expected due to the type of this binding");
266                 } else {
267                     // We are pointing at the binding's type or initializer value.
268                     err.span_label(primary_span, &format!("expected due to this{post_message}"));
269                 }
270
271                 if !lhs.is_syntactic_place_expr() {
272                     // We already emitted E0070 "invalid left-hand side of assignment", so we
273                     // silence this.
274                     err.downgrade_to_delayed_bug();
275                 }
276             }
277             _ => {}
278         }
279     }
280
281     /// If the expected type is an enum (Issue #55250) with any variants whose
282     /// sole field is of the found type, suggest such variants. (Issue #42764)
283     fn suggest_compatible_variants(
284         &self,
285         err: &mut Diagnostic,
286         expr: &hir::Expr<'_>,
287         expected: Ty<'tcx>,
288         expr_ty: Ty<'tcx>,
289     ) {
290         if let ty::Adt(expected_adt, substs) = expected.kind() {
291             if let hir::ExprKind::Field(base, ident) = expr.kind {
292                 let base_ty = self.typeck_results.borrow().expr_ty(base);
293                 if self.can_eq(self.param_env, base_ty, expected).is_ok()
294                     && let Some(base_span) = base.span.find_ancestor_inside(expr.span)
295                 {
296                     err.span_suggestion_verbose(
297                         expr.span.with_lo(base_span.hi()),
298                         format!("consider removing the tuple struct field `{ident}`"),
299                         "",
300                         Applicability::MaybeIncorrect,
301                     );
302                     return
303                 }
304             }
305
306             // If the expression is of type () and it's the return expression of a block,
307             // we suggest adding a separate return expression instead.
308             // (To avoid things like suggesting `Ok(while .. { .. })`.)
309             if expr_ty.is_unit() {
310                 let mut id = expr.hir_id;
311                 let mut parent;
312
313                 // Unroll desugaring, to make sure this works for `for` loops etc.
314                 loop {
315                     parent = self.tcx.hir().get_parent_node(id);
316                     if let Some(parent_span) = self.tcx.hir().opt_span(parent) {
317                         if parent_span.find_ancestor_inside(expr.span).is_some() {
318                             // The parent node is part of the same span, so is the result of the
319                             // same expansion/desugaring and not the 'real' parent node.
320                             id = parent;
321                             continue;
322                         }
323                     }
324                     break;
325                 }
326
327                 if let Some(hir::Node::Block(&hir::Block {
328                     span: block_span, expr: Some(e), ..
329                 })) = self.tcx.hir().find(parent)
330                 {
331                     if e.hir_id == id {
332                         if let Some(span) = expr.span.find_ancestor_inside(block_span) {
333                             let return_suggestions = if self
334                                 .tcx
335                                 .is_diagnostic_item(sym::Result, expected_adt.did())
336                             {
337                                 vec!["Ok(())"]
338                             } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
339                                 vec!["None", "Some(())"]
340                             } else {
341                                 return;
342                             };
343                             if let Some(indent) =
344                                 self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
345                             {
346                                 // Add a semicolon, except after `}`.
347                                 let semicolon =
348                                     match self.tcx.sess.source_map().span_to_snippet(span) {
349                                         Ok(s) if s.ends_with('}') => "",
350                                         _ => ";",
351                                     };
352                                 err.span_suggestions(
353                                     span.shrink_to_hi(),
354                                     "try adding an expression at the end of the block",
355                                     return_suggestions
356                                         .into_iter()
357                                         .map(|r| format!("{semicolon}\n{indent}{r}")),
358                                     Applicability::MaybeIncorrect,
359                                 );
360                             }
361                             return;
362                         }
363                     }
364                 }
365             }
366
367             let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt
368                 .variants()
369                 .iter()
370                 .filter(|variant| {
371                     variant.fields.len() == 1
372                 })
373                 .filter_map(|variant| {
374                     let sole_field = &variant.fields[0];
375
376                     let field_is_local = sole_field.did.is_local();
377                     let field_is_accessible =
378                         sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx)
379                         // Skip suggestions for unstable public fields (for example `Pin::pointer`)
380                         && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked);
381
382                     if !field_is_local && !field_is_accessible {
383                         return None;
384                     }
385
386                     let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
387                         .then(|| format!(" (its field is private, but it's local to this crate and its privacy can be changed)"));
388
389                     let sole_field_ty = sole_field.ty(self.tcx, substs);
390                     if self.can_coerce(expr_ty, sole_field_ty) {
391                         let variant_path =
392                             with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
393                         // FIXME #56861: DRYer prelude filtering
394                         if let Some(path) = variant_path.strip_prefix("std::prelude::")
395                             && let Some((_, path)) = path.split_once("::")
396                         {
397                             return Some((path.to_string(), variant.ctor_kind, sole_field.name, note_about_variant_field_privacy));
398                         }
399                         Some((variant_path, variant.ctor_kind, sole_field.name, note_about_variant_field_privacy))
400                     } else {
401                         None
402                     }
403                 })
404                 .collect();
405
406             let suggestions_for = |variant: &_, ctor, field_name| {
407                 let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
408                     Some(ident) => format!("{ident}: "),
409                     None => String::new(),
410                 };
411
412                 let (open, close) = match ctor {
413                     hir::def::CtorKind::Fn => ("(".to_owned(), ")"),
414                     hir::def::CtorKind::Fictive => (format!(" {{ {field_name}: "), " }"),
415
416                     // unit variants don't have fields
417                     hir::def::CtorKind::Const => unreachable!(),
418                 };
419
420                 // Suggest constructor as deep into the block tree as possible.
421                 // This fixes https://github.com/rust-lang/rust/issues/101065,
422                 // and also just helps make the most minimal suggestions.
423                 let mut expr = expr;
424                 while let hir::ExprKind::Block(block, _) = &expr.kind
425                     && let Some(expr_) = &block.expr
426                 {
427                     expr = expr_
428                 }
429
430                 vec![
431                     (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")),
432                     (expr.span.shrink_to_hi(), close.to_owned()),
433                 ]
434             };
435
436             match &compatible_variants[..] {
437                 [] => { /* No variants to format */ }
438                 [(variant, ctor_kind, field_name, note)] => {
439                     // Just a single matching variant.
440                     err.multipart_suggestion_verbose(
441                         &format!(
442                             "try wrapping the expression in `{variant}`{note}",
443                             note = note.as_deref().unwrap_or("")
444                         ),
445                         suggestions_for(&**variant, *ctor_kind, *field_name),
446                         Applicability::MaybeIncorrect,
447                     );
448                 }
449                 _ => {
450                     // More than one matching variant.
451                     err.multipart_suggestions(
452                         &format!(
453                             "try wrapping the expression in a variant of `{}`",
454                             self.tcx.def_path_str(expected_adt.did())
455                         ),
456                         compatible_variants.into_iter().map(
457                             |(variant, ctor_kind, field_name, _)| {
458                                 suggestions_for(&variant, ctor_kind, field_name)
459                             },
460                         ),
461                         Applicability::MaybeIncorrect,
462                     );
463                 }
464             }
465         }
466     }
467
468     fn suggest_non_zero_new_unwrap(
469         &self,
470         err: &mut Diagnostic,
471         expr: &hir::Expr<'_>,
472         expected: Ty<'tcx>,
473         expr_ty: Ty<'tcx>,
474     ) {
475         let tcx = self.tcx;
476         let (adt, unwrap) = match expected.kind() {
477             // In case Option<NonZero*> is wanted, but * is provided, suggest calling new
478             ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
479                 // Unwrap option
480                 let ty::Adt(adt, _) = substs.type_at(0).kind() else { return };
481
482                 (adt, "")
483             }
484             // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types
485             ty::Adt(adt, _) => (adt, ".unwrap()"),
486             _ => return,
487         };
488
489         let map = [
490             (sym::NonZeroU8, tcx.types.u8),
491             (sym::NonZeroU16, tcx.types.u16),
492             (sym::NonZeroU32, tcx.types.u32),
493             (sym::NonZeroU64, tcx.types.u64),
494             (sym::NonZeroU128, tcx.types.u128),
495             (sym::NonZeroI8, tcx.types.i8),
496             (sym::NonZeroI16, tcx.types.i16),
497             (sym::NonZeroI32, tcx.types.i32),
498             (sym::NonZeroI64, tcx.types.i64),
499             (sym::NonZeroI128, tcx.types.i128),
500         ];
501
502         let Some((s, _)) = map
503             .iter()
504             .find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t))
505             else { return };
506
507         let path = self.tcx.def_path_str(adt.non_enum_variant().def_id);
508
509         err.multipart_suggestion(
510             format!("consider calling `{s}::new`"),
511             vec![
512                 (expr.span.shrink_to_lo(), format!("{path}::new(")),
513                 (expr.span.shrink_to_hi(), format!("){unwrap}")),
514             ],
515             Applicability::MaybeIncorrect,
516         );
517     }
518
519     pub fn get_conversion_methods(
520         &self,
521         span: Span,
522         expected: Ty<'tcx>,
523         checked_ty: Ty<'tcx>,
524         hir_id: hir::HirId,
525     ) -> Vec<AssocItem> {
526         let mut methods =
527             self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id);
528         methods.retain(|m| {
529             self.has_only_self_parameter(m)
530                 && self
531                     .tcx
532                     // This special internal attribute is used to permit
533                     // "identity-like" conversion methods to be suggested here.
534                     //
535                     // FIXME (#46459 and #46460): ideally
536                     // `std::convert::Into::into` and `std::borrow:ToOwned` would
537                     // also be `#[rustc_conversion_suggestion]`, if not for
538                     // method-probing false-positives and -negatives (respectively).
539                     //
540                     // FIXME? Other potential candidate methods: `as_ref` and
541                     // `as_mut`?
542                     .has_attr(m.def_id, sym::rustc_conversion_suggestion)
543         });
544
545         methods
546     }
547
548     /// This function checks whether the method is not static and does not accept other parameters than `self`.
549     fn has_only_self_parameter(&self, method: &AssocItem) -> bool {
550         match method.kind {
551             ty::AssocKind::Fn => {
552                 method.fn_has_self_parameter
553                     && self.tcx.fn_sig(method.def_id).inputs().skip_binder().len() == 1
554             }
555             _ => false,
556         }
557     }
558
559     /// Identify some cases where `as_ref()` would be appropriate and suggest it.
560     ///
561     /// Given the following code:
562     /// ```compile_fail,E0308
563     /// struct Foo;
564     /// fn takes_ref(_: &Foo) {}
565     /// let ref opt = Some(Foo);
566     ///
567     /// opt.map(|param| takes_ref(param));
568     /// ```
569     /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead.
570     ///
571     /// It only checks for `Option` and `Result` and won't work with
572     /// ```ignore (illustrative)
573     /// opt.map(|param| { takes_ref(param) });
574     /// ```
575     fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Span, &'static str, String)> {
576         let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind else {
577             return None;
578         };
579
580         let hir::def::Res::Local(local_id) = path.res else {
581             return None;
582         };
583
584         let local_parent = self.tcx.hir().get_parent_node(local_id);
585         let Some(Node::Param(hir::Param { hir_id: param_hir_id, .. })) = self.tcx.hir().find(local_parent) else {
586             return None;
587         };
588
589         let param_parent = self.tcx.hir().get_parent_node(*param_hir_id);
590         let Some(Node::Expr(hir::Expr {
591             hir_id: expr_hir_id,
592             kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }),
593             ..
594         })) = self.tcx.hir().find(param_parent) else {
595             return None;
596         };
597
598         let expr_parent = self.tcx.hir().get_parent_node(*expr_hir_id);
599         let hir = self.tcx.hir().find(expr_parent);
600         let closure_params_len = closure_fn_decl.inputs.len();
601         let (
602             Some(Node::Expr(hir::Expr {
603                 kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
604                 ..
605             })),
606             1,
607         ) = (hir, closure_params_len) else {
608             return None;
609         };
610
611         let self_ty = self.typeck_results.borrow().expr_ty(receiver);
612         let name = method_path.ident.name;
613         let is_as_ref_able = match self_ty.peel_refs().kind() {
614             ty::Adt(def, _) => {
615                 (self.tcx.is_diagnostic_item(sym::Option, def.did())
616                     || self.tcx.is_diagnostic_item(sym::Result, def.did()))
617                     && (name == sym::map || name == sym::and_then)
618             }
619             _ => false,
620         };
621         match (is_as_ref_able, self.sess().source_map().span_to_snippet(method_path.ident.span)) {
622             (true, Ok(src)) => {
623                 let suggestion = format!("as_ref().{}", src);
624                 Some((method_path.ident.span, "consider using `as_ref` instead", suggestion))
625             }
626             _ => None,
627         }
628     }
629
630     pub(crate) fn maybe_get_struct_pattern_shorthand_field(
631         &self,
632         expr: &hir::Expr<'_>,
633     ) -> Option<Symbol> {
634         let hir = self.tcx.hir();
635         let local = match expr {
636             hir::Expr {
637                 kind:
638                     hir::ExprKind::Path(hir::QPath::Resolved(
639                         None,
640                         hir::Path {
641                             res: hir::def::Res::Local(_),
642                             segments: [hir::PathSegment { ident, .. }],
643                             ..
644                         },
645                     )),
646                 ..
647             } => Some(ident),
648             _ => None,
649         }?;
650
651         match hir.find(hir.get_parent_node(expr.hir_id))? {
652             Node::ExprField(field) => {
653                 if field.ident.name == local.name && field.is_shorthand {
654                     return Some(local.name);
655                 }
656             }
657             _ => {}
658         }
659
660         None
661     }
662
663     /// If the given `HirId` corresponds to a block with a trailing expression, return that expression
664     pub(crate) fn maybe_get_block_expr(
665         &self,
666         expr: &hir::Expr<'tcx>,
667     ) -> Option<&'tcx hir::Expr<'tcx>> {
668         match expr {
669             hir::Expr { kind: hir::ExprKind::Block(block, ..), .. } => block.expr,
670             _ => None,
671         }
672     }
673
674     /// Returns whether the given expression is an `else if`.
675     pub(crate) fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
676         if let hir::ExprKind::If(..) = expr.kind {
677             let parent_id = self.tcx.hir().get_parent_node(expr.hir_id);
678             if let Some(Node::Expr(hir::Expr {
679                 kind: hir::ExprKind::If(_, _, Some(else_expr)),
680                 ..
681             })) = self.tcx.hir().find(parent_id)
682             {
683                 return else_expr.hir_id == expr.hir_id;
684             }
685         }
686         false
687     }
688
689     /// This function is used to determine potential "simple" improvements or users' errors and
690     /// provide them useful help. For example:
691     ///
692     /// ```compile_fail,E0308
693     /// fn some_fn(s: &str) {}
694     ///
695     /// let x = "hey!".to_owned();
696     /// some_fn(x); // error
697     /// ```
698     ///
699     /// No need to find every potential function which could make a coercion to transform a
700     /// `String` into a `&str` since a `&` would do the trick!
701     ///
702     /// In addition of this check, it also checks between references mutability state. If the
703     /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
704     /// `&mut`!".
705     pub fn check_ref(
706         &self,
707         expr: &hir::Expr<'tcx>,
708         checked_ty: Ty<'tcx>,
709         expected: Ty<'tcx>,
710     ) -> Option<(Span, String, String, Applicability, bool /* verbose */)> {
711         let sess = self.sess();
712         let sp = expr.span;
713
714         // If the span is from an external macro, there's no suggestion we can make.
715         if in_external_macro(sess, sp) {
716             return None;
717         }
718
719         let sm = sess.source_map();
720
721         let replace_prefix = |s: &str, old: &str, new: &str| {
722             s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
723         };
724
725         // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
726         let expr = expr.peel_drop_temps();
727
728         match (&expr.kind, expected.kind(), checked_ty.kind()) {
729             (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
730                 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
731                     if let hir::ExprKind::Lit(_) = expr.kind
732                         && let Ok(src) = sm.span_to_snippet(sp)
733                         && replace_prefix(&src, "b\"", "\"").is_some()
734                     {
735                                 let pos = sp.lo() + BytePos(1);
736                                 return Some((
737                                     sp.with_hi(pos),
738                                     "consider removing the leading `b`".to_string(),
739                                     String::new(),
740                                     Applicability::MachineApplicable,
741                                     true,
742                                 ));
743                             }
744                         }
745                 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
746                     if let hir::ExprKind::Lit(_) = expr.kind
747                         && let Ok(src) = sm.span_to_snippet(sp)
748                         && replace_prefix(&src, "\"", "b\"").is_some()
749                     {
750                                 return Some((
751                                     sp.shrink_to_lo(),
752                                     "consider adding a leading `b`".to_string(),
753                                     "b".to_string(),
754                                     Applicability::MachineApplicable,
755                                     true,
756                                 ));
757                     }
758                 }
759                 _ => {}
760             },
761             (_, &ty::Ref(_, _, mutability), _) => {
762                 // Check if it can work when put into a ref. For example:
763                 //
764                 // ```
765                 // fn bar(x: &mut i32) {}
766                 //
767                 // let x = 0u32;
768                 // bar(&x); // error, expected &mut
769                 // ```
770                 let ref_ty = match mutability {
771                     hir::Mutability::Mut => {
772                         self.tcx.mk_mut_ref(self.tcx.mk_region(ty::ReStatic), checked_ty)
773                     }
774                     hir::Mutability::Not => {
775                         self.tcx.mk_imm_ref(self.tcx.mk_region(ty::ReStatic), checked_ty)
776                     }
777                 };
778                 if self.can_coerce(ref_ty, expected) {
779                     let mut sugg_sp = sp;
780                     if let hir::ExprKind::MethodCall(ref segment, receiver, args, _) = expr.kind {
781                         let clone_trait =
782                             self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span));
783                         if args.is_empty()
784                             && self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
785                                 |did| {
786                                     let ai = self.tcx.associated_item(did);
787                                     ai.trait_container(self.tcx) == Some(clone_trait)
788                                 },
789                             ) == Some(true)
790                             && segment.ident.name == sym::clone
791                         {
792                             // If this expression had a clone call when suggesting borrowing
793                             // we want to suggest removing it because it'd now be unnecessary.
794                             sugg_sp = receiver.span;
795                         }
796                     }
797                     if let Ok(src) = sm.span_to_snippet(sugg_sp) {
798                         let needs_parens = match expr.kind {
799                             // parenthesize if needed (Issue #46756)
800                             hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
801                             // parenthesize borrows of range literals (Issue #54505)
802                             _ if is_range_literal(expr) => true,
803                             _ => false,
804                         };
805
806                         if let Some(sugg) = self.can_use_as_ref(expr) {
807                             return Some((
808                                 sugg.0,
809                                 sugg.1.to_string(),
810                                 sugg.2,
811                                 Applicability::MachineApplicable,
812                                 false,
813                             ));
814                         }
815
816                         let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
817                             Some(ident) => format!("{ident}: "),
818                             None => String::new(),
819                         };
820
821                         if let Some(hir::Node::Expr(hir::Expr {
822                             kind: hir::ExprKind::Assign(..),
823                             ..
824                         })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
825                         {
826                             if mutability == hir::Mutability::Mut {
827                                 // Suppressing this diagnostic, we'll properly print it in `check_expr_assign`
828                                 return None;
829                             }
830                         }
831
832                         let sugg_expr = if needs_parens { format!("({src})") } else { src };
833                         return Some(match mutability {
834                             hir::Mutability::Mut => (
835                                 sp,
836                                 "consider mutably borrowing here".to_string(),
837                                 format!("{prefix}&mut {sugg_expr}"),
838                                 Applicability::MachineApplicable,
839                                 false,
840                             ),
841                             hir::Mutability::Not => (
842                                 sp,
843                                 "consider borrowing here".to_string(),
844                                 format!("{prefix}&{sugg_expr}"),
845                                 Applicability::MachineApplicable,
846                                 false,
847                             ),
848                         });
849                     }
850                 }
851             }
852             (
853                 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr),
854                 _,
855                 &ty::Ref(_, checked, _),
856             ) if self.can_sub(self.param_env, checked, expected).is_ok() => {
857                 // We have `&T`, check if what was expected was `T`. If so,
858                 // we may want to suggest removing a `&`.
859                 if sm.is_imported(expr.span) {
860                     // Go through the spans from which this span was expanded,
861                     // and find the one that's pointing inside `sp`.
862                     //
863                     // E.g. for `&format!("")`, where we want the span to the
864                     // `format!()` invocation instead of its expansion.
865                     if let Some(call_span) =
866                         iter::successors(Some(expr.span), |s| s.parent_callsite())
867                             .find(|&s| sp.contains(s))
868                         && sm.is_span_accessible(call_span)
869                     {
870                         return Some((
871                             sp.with_hi(call_span.lo()),
872                             "consider removing the borrow".to_string(),
873                             String::new(),
874                             Applicability::MachineApplicable,
875                             true,
876                         ));
877                     }
878                     return None;
879                 }
880                 if sp.contains(expr.span)
881                     && sm.is_span_accessible(expr.span)
882                 {
883                     return Some((
884                         sp.with_hi(expr.span.lo()),
885                         "consider removing the borrow".to_string(),
886                         String::new(),
887                         Applicability::MachineApplicable,
888                         true,
889                     ));
890                 }
891             }
892             (
893                 _,
894                 &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }),
895                 &ty::Ref(_, ty_a, mutbl_a),
896             ) => {
897                 if let Some(steps) = self.deref_steps(ty_a, ty_b)
898                     // Only suggest valid if dereferencing needed.
899                     && steps > 0
900                     // The pointer type implements `Copy` trait so the suggestion is always valid.
901                     && let Ok(src) = sm.span_to_snippet(sp)
902                 {
903                     let derefs = "*".repeat(steps);
904                     if let Some((span, src, applicability)) = match mutbl_b {
905                         hir::Mutability::Mut => {
906                             let new_prefix = "&mut ".to_owned() + &derefs;
907                             match mutbl_a {
908                                 hir::Mutability::Mut => {
909                                     replace_prefix(&src, "&mut ", &new_prefix).map(|_| {
910                                         let pos = sp.lo() + BytePos(5);
911                                         let sp = sp.with_lo(pos).with_hi(pos);
912                                         (sp, derefs, Applicability::MachineApplicable)
913                                     })
914                                 }
915                                 hir::Mutability::Not => {
916                                     replace_prefix(&src, "&", &new_prefix).map(|_| {
917                                         let pos = sp.lo() + BytePos(1);
918                                         let sp = sp.with_lo(pos).with_hi(pos);
919                                         (
920                                             sp,
921                                             format!("mut {derefs}"),
922                                             Applicability::Unspecified,
923                                         )
924                                     })
925                                 }
926                             }
927                         }
928                         hir::Mutability::Not => {
929                             let new_prefix = "&".to_owned() + &derefs;
930                             match mutbl_a {
931                                 hir::Mutability::Mut => {
932                                     replace_prefix(&src, "&mut ", &new_prefix).map(|_| {
933                                         let lo = sp.lo() + BytePos(1);
934                                         let hi = sp.lo() + BytePos(5);
935                                         let sp = sp.with_lo(lo).with_hi(hi);
936                                         (sp, derefs, Applicability::MachineApplicable)
937                                     })
938                                 }
939                                 hir::Mutability::Not => {
940                                     replace_prefix(&src, "&", &new_prefix).map(|_| {
941                                         let pos = sp.lo() + BytePos(1);
942                                         let sp = sp.with_lo(pos).with_hi(pos);
943                                         (sp, derefs, Applicability::MachineApplicable)
944                                     })
945                                 }
946                             }
947                         }
948                     } {
949                         return Some((
950                             span,
951                             "consider dereferencing".to_string(),
952                             src,
953                             applicability,
954                             true,
955                         ));
956                     }
957                 }
958             }
959             _ if sp == expr.span => {
960                 if let Some(mut steps) = self.deref_steps(checked_ty, expected) {
961                     let mut expr = expr.peel_blocks();
962                     let mut prefix_span = expr.span.shrink_to_lo();
963                     let mut remove = String::new();
964
965                     // Try peeling off any existing `&` and `&mut` to reach our target type
966                     while steps > 0 {
967                         if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
968                             // If the expression has `&`, removing it would fix the error
969                             prefix_span = prefix_span.with_hi(inner.span.lo());
970                             expr = inner;
971                             remove += match mutbl {
972                                 hir::Mutability::Not => "&",
973                                 hir::Mutability::Mut => "&mut ",
974                             };
975                             steps -= 1;
976                         } else {
977                             break;
978                         }
979                     }
980                     // If we've reached our target type with just removing `&`, then just print now.
981                     if steps == 0 {
982                         return Some((
983                             prefix_span,
984                             format!("consider removing the `{}`", remove.trim()),
985                             String::new(),
986                             // Do not remove `&&` to get to bool, because it might be something like
987                             // { a } && b, which we have a separate fixup suggestion that is more
988                             // likely correct...
989                             if remove.trim() == "&&" && expected == self.tcx.types.bool {
990                                 Applicability::MaybeIncorrect
991                             } else {
992                                 Applicability::MachineApplicable
993                             },
994                             true,
995                         ));
996                     }
997
998                     // For this suggestion to make sense, the type would need to be `Copy`,
999                     // or we have to be moving out of a `Box<T>`
1000                     if self.type_is_copy_modulo_regions(self.param_env, expected, sp)
1001                         // FIXME(compiler-errors): We can actually do this if the checked_ty is
1002                         // `steps` layers of boxes, not just one, but this is easier and most likely.
1003                         || (checked_ty.is_box() && steps == 1)
1004                     {
1005                         let deref_kind = if checked_ty.is_box() {
1006                             "unboxing the value"
1007                         } else if checked_ty.is_region_ptr() {
1008                             "dereferencing the borrow"
1009                         } else {
1010                             "dereferencing the type"
1011                         };
1012
1013                         // Suggest removing `&` if we have removed any, otherwise suggest just
1014                         // dereferencing the remaining number of steps.
1015                         let message = if remove.is_empty() {
1016                             format!("consider {deref_kind}")
1017                         } else {
1018                             format!(
1019                                 "consider removing the `{}` and {} instead",
1020                                 remove.trim(),
1021                                 deref_kind
1022                             )
1023                         };
1024
1025                         let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) {
1026                             Some(ident) => format!("{ident}: "),
1027                             None => String::new(),
1028                         };
1029
1030                         let (span, suggestion) = if self.is_else_if_block(expr) {
1031                             // Don't suggest nonsense like `else *if`
1032                             return None;
1033                         } else if let Some(expr) = self.maybe_get_block_expr(expr) {
1034                             // prefix should be empty here..
1035                             (expr.span.shrink_to_lo(), "*".to_string())
1036                         } else {
1037                             (prefix_span, format!("{}{}", prefix, "*".repeat(steps)))
1038                         };
1039
1040                         return Some((
1041                             span,
1042                             message,
1043                             suggestion,
1044                             Applicability::MachineApplicable,
1045                             true,
1046                         ));
1047                     }
1048                 }
1049             }
1050             _ => {}
1051         }
1052         None
1053     }
1054
1055     pub fn check_for_cast(
1056         &self,
1057         err: &mut Diagnostic,
1058         expr: &hir::Expr<'_>,
1059         checked_ty: Ty<'tcx>,
1060         expected_ty: Ty<'tcx>,
1061         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
1062     ) -> bool {
1063         if self.tcx.sess.source_map().is_imported(expr.span) {
1064             // Ignore if span is from within a macro.
1065             return false;
1066         }
1067
1068         let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) else {
1069             return false;
1070         };
1071
1072         // If casting this expression to a given numeric type would be appropriate in case of a type
1073         // mismatch.
1074         //
1075         // We want to minimize the amount of casting operations that are suggested, as it can be a
1076         // lossy operation with potentially bad side effects, so we only suggest when encountering
1077         // an expression that indicates that the original type couldn't be directly changed.
1078         //
1079         // For now, don't suggest casting with `as`.
1080         let can_cast = false;
1081
1082         let mut sugg = vec![];
1083
1084         if let Some(hir::Node::ExprField(field)) =
1085             self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
1086         {
1087             // `expr` is a literal field for a struct, only suggest if appropriate
1088             if field.is_shorthand {
1089                 // This is a field literal
1090                 sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
1091             } else {
1092                 // Likely a field was meant, but this field wasn't found. Do not suggest anything.
1093                 return false;
1094             }
1095         };
1096
1097         if let hir::ExprKind::Call(path, args) = &expr.kind
1098             && let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
1099                 (&path.kind, args.len())
1100             // `expr` is a conversion like `u32::from(val)`, do not suggest anything (#63697).
1101             && let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
1102                 (&base_ty.kind, path_segment.ident.name)
1103         {
1104             if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
1105                 match ident.name {
1106                     sym::i128
1107                     | sym::i64
1108                     | sym::i32
1109                     | sym::i16
1110                     | sym::i8
1111                     | sym::u128
1112                     | sym::u64
1113                     | sym::u32
1114                     | sym::u16
1115                     | sym::u8
1116                     | sym::isize
1117                     | sym::usize
1118                         if base_ty_path.segments.len() == 1 =>
1119                     {
1120                         return false;
1121                     }
1122                     _ => {}
1123                 }
1124             }
1125         }
1126
1127         let msg = format!(
1128             "you can convert {} `{}` to {} `{}`",
1129             checked_ty.kind().article(),
1130             checked_ty,
1131             expected_ty.kind().article(),
1132             expected_ty,
1133         );
1134         let cast_msg = format!(
1135             "you can cast {} `{}` to {} `{}`",
1136             checked_ty.kind().article(),
1137             checked_ty,
1138             expected_ty.kind().article(),
1139             expected_ty,
1140         );
1141         let lit_msg = format!(
1142             "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`",
1143         );
1144
1145         let close_paren = if expr.precedence().order() < PREC_POSTFIX {
1146             sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
1147             ")"
1148         } else {
1149             ""
1150         };
1151
1152         let mut cast_suggestion = sugg.clone();
1153         cast_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren} as {expected_ty}")));
1154         let mut into_suggestion = sugg.clone();
1155         into_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren}.into()")));
1156         let mut suffix_suggestion = sugg.clone();
1157         suffix_suggestion.push((
1158             if matches!(
1159                 (&expected_ty.kind(), &checked_ty.kind()),
1160                 (ty::Int(_) | ty::Uint(_), ty::Float(_))
1161             ) {
1162                 // Remove fractional part from literal, for example `42.0f32` into `42`
1163                 let src = src.trim_end_matches(&checked_ty.to_string());
1164                 let len = src.split('.').next().unwrap().len();
1165                 expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
1166             } else {
1167                 let len = src.trim_end_matches(&checked_ty.to_string()).len();
1168                 expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
1169             },
1170             if expr.precedence().order() < PREC_POSTFIX {
1171                 // Readd `)`
1172                 format!("{expected_ty})")
1173             } else {
1174                 expected_ty.to_string()
1175             },
1176         ));
1177         let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
1178             if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
1179         };
1180         let is_negative_int =
1181             |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..));
1182         let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
1183
1184         let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);
1185
1186         let suggest_fallible_into_or_lhs_from =
1187             |err: &mut Diagnostic, exp_to_found_is_fallible: bool| {
1188                 // If we know the expression the expected type is derived from, we might be able
1189                 // to suggest a widening conversion rather than a narrowing one (which may
1190                 // panic). For example, given x: u8 and y: u32, if we know the span of "x",
1191                 //   x > y
1192                 // can be given the suggestion "u32::from(x) > y" rather than
1193                 // "x > y.try_into().unwrap()".
1194                 let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
1195                     self.tcx
1196                         .sess
1197                         .source_map()
1198                         .span_to_snippet(expr.span)
1199                         .ok()
1200                         .map(|src| (expr, src))
1201                 });
1202                 let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
1203                     (lhs_expr_and_src, exp_to_found_is_fallible)
1204                 {
1205                     let msg = format!(
1206                         "you can convert `{lhs_src}` from `{expected_ty}` to `{checked_ty}`, matching the type of `{src}`",
1207                     );
1208                     let suggestion = vec![
1209                         (lhs_expr.span.shrink_to_lo(), format!("{checked_ty}::from(")),
1210                         (lhs_expr.span.shrink_to_hi(), ")".to_string()),
1211                     ];
1212                     (msg, suggestion)
1213                 } else {
1214                     let msg = format!("{msg} and panic if the converted value doesn't fit");
1215                     let mut suggestion = sugg.clone();
1216                     suggestion.push((
1217                         expr.span.shrink_to_hi(),
1218                         format!("{close_paren}.try_into().unwrap()"),
1219                     ));
1220                     (msg, suggestion)
1221                 };
1222                 err.multipart_suggestion_verbose(
1223                     &msg,
1224                     suggestion,
1225                     Applicability::MachineApplicable,
1226                 );
1227             };
1228
1229         let suggest_to_change_suffix_or_into =
1230             |err: &mut Diagnostic,
1231              found_to_exp_is_fallible: bool,
1232              exp_to_found_is_fallible: bool| {
1233                 let exp_is_lhs =
1234                     expected_ty_expr.map(|e| self.tcx.hir().is_lhs(e.hir_id)).unwrap_or(false);
1235
1236                 if exp_is_lhs {
1237                     return;
1238                 }
1239
1240                 let always_fallible = found_to_exp_is_fallible
1241                     && (exp_to_found_is_fallible || expected_ty_expr.is_none());
1242                 let msg = if literal_is_ty_suffixed(expr) {
1243                     &lit_msg
1244                 } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
1245                     // We now know that converting either the lhs or rhs is fallible. Before we
1246                     // suggest a fallible conversion, check if the value can never fit in the
1247                     // expected type.
1248                     let msg = format!("`{src}` cannot fit into type `{expected_ty}`");
1249                     err.note(&msg);
1250                     return;
1251                 } else if in_const_context {
1252                     // Do not recommend `into` or `try_into` in const contexts.
1253                     return;
1254                 } else if found_to_exp_is_fallible {
1255                     return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
1256                 } else {
1257                     &msg
1258                 };
1259                 let suggestion = if literal_is_ty_suffixed(expr) {
1260                     suffix_suggestion.clone()
1261                 } else {
1262                     into_suggestion.clone()
1263                 };
1264                 err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
1265             };
1266
1267         match (&expected_ty.kind(), &checked_ty.kind()) {
1268             (&ty::Int(ref exp), &ty::Int(ref found)) => {
1269                 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
1270                 {
1271                     (Some(exp), Some(found)) if exp < found => (true, false),
1272                     (Some(exp), Some(found)) if exp > found => (false, true),
1273                     (None, Some(8 | 16)) => (false, true),
1274                     (Some(8 | 16), None) => (true, false),
1275                     (None, _) | (_, None) => (true, true),
1276                     _ => (false, false),
1277                 };
1278                 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
1279                 true
1280             }
1281             (&ty::Uint(ref exp), &ty::Uint(ref found)) => {
1282                 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
1283                 {
1284                     (Some(exp), Some(found)) if exp < found => (true, false),
1285                     (Some(exp), Some(found)) if exp > found => (false, true),
1286                     (None, Some(8 | 16)) => (false, true),
1287                     (Some(8 | 16), None) => (true, false),
1288                     (None, _) | (_, None) => (true, true),
1289                     _ => (false, false),
1290                 };
1291                 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
1292                 true
1293             }
1294             (&ty::Int(exp), &ty::Uint(found)) => {
1295                 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
1296                 {
1297                     (Some(exp), Some(found)) if found < exp => (false, true),
1298                     (None, Some(8)) => (false, true),
1299                     _ => (true, true),
1300                 };
1301                 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
1302                 true
1303             }
1304             (&ty::Uint(exp), &ty::Int(found)) => {
1305                 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
1306                 {
1307                     (Some(exp), Some(found)) if found > exp => (true, false),
1308                     (Some(8), None) => (true, false),
1309                     _ => (true, true),
1310                 };
1311                 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
1312                 true
1313             }
1314             (&ty::Float(ref exp), &ty::Float(ref found)) => {
1315                 if found.bit_width() < exp.bit_width() {
1316                     suggest_to_change_suffix_or_into(err, false, true);
1317                 } else if literal_is_ty_suffixed(expr) {
1318                     err.multipart_suggestion_verbose(
1319                         &lit_msg,
1320                         suffix_suggestion,
1321                         Applicability::MachineApplicable,
1322                     );
1323                 } else if can_cast {
1324                     // Missing try_into implementation for `f64` to `f32`
1325                     err.multipart_suggestion_verbose(
1326                         &format!("{cast_msg}, producing the closest possible value"),
1327                         cast_suggestion,
1328                         Applicability::MaybeIncorrect, // lossy conversion
1329                     );
1330                 }
1331                 true
1332             }
1333             (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
1334                 if literal_is_ty_suffixed(expr) {
1335                     err.multipart_suggestion_verbose(
1336                         &lit_msg,
1337                         suffix_suggestion,
1338                         Applicability::MachineApplicable,
1339                     );
1340                 } else if can_cast {
1341                     // Missing try_into implementation for `{float}` to `{integer}`
1342                     err.multipart_suggestion_verbose(
1343                         &format!("{msg}, rounding the float towards zero"),
1344                         cast_suggestion,
1345                         Applicability::MaybeIncorrect, // lossy conversion
1346                     );
1347                 }
1348                 true
1349             }
1350             (&ty::Float(ref exp), &ty::Uint(ref found)) => {
1351                 // if `found` is `None` (meaning found is `usize`), don't suggest `.into()`
1352                 if exp.bit_width() > found.bit_width().unwrap_or(256) {
1353                     err.multipart_suggestion_verbose(
1354                         &format!(
1355                             "{msg}, producing the floating point representation of the integer",
1356                         ),
1357                         into_suggestion,
1358                         Applicability::MachineApplicable,
1359                     );
1360                 } else if literal_is_ty_suffixed(expr) {
1361                     err.multipart_suggestion_verbose(
1362                         &lit_msg,
1363                         suffix_suggestion,
1364                         Applicability::MachineApplicable,
1365                     );
1366                 } else {
1367                     // Missing try_into implementation for `{integer}` to `{float}`
1368                     err.multipart_suggestion_verbose(
1369                         &format!(
1370                             "{cast_msg}, producing the floating point representation of the integer, \
1371                                  rounded if necessary",
1372                         ),
1373                         cast_suggestion,
1374                         Applicability::MaybeIncorrect, // lossy conversion
1375                     );
1376                 }
1377                 true
1378             }
1379             (&ty::Float(ref exp), &ty::Int(ref found)) => {
1380                 // if `found` is `None` (meaning found is `isize`), don't suggest `.into()`
1381                 if exp.bit_width() > found.bit_width().unwrap_or(256) {
1382                     err.multipart_suggestion_verbose(
1383                         &format!(
1384                             "{}, producing the floating point representation of the integer",
1385                             &msg,
1386                         ),
1387                         into_suggestion,
1388                         Applicability::MachineApplicable,
1389                     );
1390                 } else if literal_is_ty_suffixed(expr) {
1391                     err.multipart_suggestion_verbose(
1392                         &lit_msg,
1393                         suffix_suggestion,
1394                         Applicability::MachineApplicable,
1395                     );
1396                 } else {
1397                     // Missing try_into implementation for `{integer}` to `{float}`
1398                     err.multipart_suggestion_verbose(
1399                         &format!(
1400                             "{}, producing the floating point representation of the integer, \
1401                                 rounded if necessary",
1402                             &msg,
1403                         ),
1404                         cast_suggestion,
1405                         Applicability::MaybeIncorrect, // lossy conversion
1406                     );
1407                 }
1408                 true
1409             }
1410             (
1411                 &ty::Uint(ty::UintTy::U32 | ty::UintTy::U64 | ty::UintTy::U128)
1412                 | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128),
1413                 &ty::Char,
1414             ) => {
1415                 err.multipart_suggestion_verbose(
1416                     &format!("{cast_msg}, since a `char` always occupies 4 bytes"),
1417                     cast_suggestion,
1418                     Applicability::MachineApplicable,
1419                 );
1420                 true
1421             }
1422             _ => false,
1423         }
1424     }
1425 }