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