]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_typeck/src/check/demand.rs
c7ce5008c335405ba45d50e0c18ec5b4b53c2305
[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, DiagnosticBuilder};
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::ty::adjustment::AllowTwoPhase;
12 use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
13 use rustc_span::symbol::sym;
14 use rustc_span::Span;
15
16 use super::method::probe;
17
18 use std::fmt;
19
20 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21     pub fn emit_coerce_suggestions(
22         &self,
23         err: &mut DiagnosticBuilder<'_>,
24         expr: &hir::Expr<'_>,
25         expr_ty: Ty<'tcx>,
26         expected: Ty<'tcx>,
27         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
28     ) {
29         self.annotate_expected_due_to_let_ty(err, expr);
30         self.suggest_compatible_variants(err, expr, expected, expr_ty);
31         self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
32         if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
33             return;
34         }
35         self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
36         self.suggest_missing_await(err, expr, expected, expr_ty);
37         self.suggest_missing_parentheses(err, expr);
38         self.note_need_for_fn_pointer(err, expected, expr_ty);
39         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
40     }
41
42     // Requires that the two types unify, and prints an error message if
43     // they don't.
44     pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
45         if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) {
46             e.emit();
47         }
48     }
49
50     pub fn demand_suptype_diag(
51         &self,
52         sp: Span,
53         expected: Ty<'tcx>,
54         actual: Ty<'tcx>,
55     ) -> Option<DiagnosticBuilder<'tcx>> {
56         self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
57     }
58
59     pub fn demand_suptype_with_origin(
60         &self,
61         cause: &ObligationCause<'tcx>,
62         expected: Ty<'tcx>,
63         actual: Ty<'tcx>,
64     ) -> Option<DiagnosticBuilder<'tcx>> {
65         match self.at(cause, self.param_env).sup(expected, actual) {
66             Ok(InferOk { obligations, value: () }) => {
67                 self.register_predicates(obligations);
68                 None
69             }
70             Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)),
71         }
72     }
73
74     pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
75         if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) {
76             err.emit();
77         }
78     }
79
80     pub fn demand_eqtype_diag(
81         &self,
82         sp: Span,
83         expected: Ty<'tcx>,
84         actual: Ty<'tcx>,
85     ) -> Option<DiagnosticBuilder<'tcx>> {
86         self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
87     }
88
89     pub fn demand_eqtype_with_origin(
90         &self,
91         cause: &ObligationCause<'tcx>,
92         expected: Ty<'tcx>,
93         actual: Ty<'tcx>,
94     ) -> Option<DiagnosticBuilder<'tcx>> {
95         match self.at(cause, self.param_env).eq(expected, actual) {
96             Ok(InferOk { obligations, value: () }) => {
97                 self.register_predicates(obligations);
98                 None
99             }
100             Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)),
101         }
102     }
103
104     pub fn demand_coerce(
105         &self,
106         expr: &hir::Expr<'_>,
107         checked_ty: Ty<'tcx>,
108         expected: Ty<'tcx>,
109         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
110         allow_two_phase: AllowTwoPhase,
111     ) -> Ty<'tcx> {
112         let (ty, err) =
113             self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase);
114         if let Some(mut err) = err {
115             err.emit();
116         }
117         ty
118     }
119
120     // Checks that the type of `expr` can be coerced to `expected`.
121     //
122     // N.B., this code relies on `self.diverges` to be accurate. In
123     // particular, assignments to `!` will be permitted if the
124     // diverges flag is currently "always".
125     pub fn demand_coerce_diag(
126         &self,
127         expr: &hir::Expr<'_>,
128         checked_ty: Ty<'tcx>,
129         expected: Ty<'tcx>,
130         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
131         allow_two_phase: AllowTwoPhase,
132     ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) {
133         let expected = self.resolve_vars_with_obligations(expected);
134
135         let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase) {
136             Ok(ty) => return (ty, None),
137             Err(e) => e,
138         };
139
140         let expr = expr.peel_drop_temps();
141         let cause = self.misc(expr.span);
142         let expr_ty = self.resolve_vars_with_obligations(checked_ty);
143         let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
144
145         if self.is_assign_to_bool(expr, expected) {
146             // Error reported in `check_assign` so avoid emitting error again.
147             err.delay_as_bug();
148             return (expected, None);
149         }
150
151         self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr);
152
153         (expected, Some(err))
154     }
155
156     fn annotate_expected_due_to_let_ty(
157         &self,
158         err: &mut DiagnosticBuilder<'_>,
159         expr: &hir::Expr<'_>,
160     ) {
161         let parent = self.tcx.hir().get_parent_node(expr.hir_id);
162         if let Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })) =
163             self.tcx.hir().find(parent)
164         {
165             if init.hir_id == expr.hir_id {
166                 // Point at `let` assignment type.
167                 err.span_label(ty.span, "expected due to this");
168             }
169         }
170     }
171
172     /// Returns whether the expected type is `bool` and the expression is `x = y`.
173     pub fn is_assign_to_bool(&self, expr: &hir::Expr<'_>, expected: Ty<'tcx>) -> bool {
174         if let hir::ExprKind::Assign(..) = expr.kind {
175             return expected == self.tcx.types.bool;
176         }
177         false
178     }
179
180     /// If the expected type is an enum (Issue #55250) with any variants whose
181     /// sole field is of the found type, suggest such variants. (Issue #42764)
182     fn suggest_compatible_variants(
183         &self,
184         err: &mut DiagnosticBuilder<'_>,
185         expr: &hir::Expr<'_>,
186         expected: Ty<'tcx>,
187         expr_ty: Ty<'tcx>,
188     ) {
189         if let ty::Adt(expected_adt, substs) = expected.kind() {
190             if !expected_adt.is_enum() {
191                 return;
192             }
193
194             let mut compatible_variants = expected_adt
195                 .variants
196                 .iter()
197                 .filter(|variant| variant.fields.len() == 1)
198                 .filter_map(|variant| {
199                     let sole_field = &variant.fields[0];
200                     let sole_field_ty = sole_field.ty(self.tcx, substs);
201                     if self.can_coerce(expr_ty, sole_field_ty) {
202                         let variant_path = self.tcx.def_path_str(variant.def_id);
203                         // FIXME #56861: DRYer prelude filtering
204                         Some(variant_path.trim_start_matches("std::prelude::v1::").to_string())
205                     } else {
206                         None
207                     }
208                 })
209                 .peekable();
210
211             if compatible_variants.peek().is_some() {
212                 if let Ok(expr_text) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
213                     let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text));
214                     let msg = "try using a variant of the expected enum";
215                     err.span_suggestions(
216                         expr.span,
217                         msg,
218                         suggestions,
219                         Applicability::MaybeIncorrect,
220                     );
221                 }
222             }
223         }
224     }
225
226     pub fn get_conversion_methods(
227         &self,
228         span: Span,
229         expected: Ty<'tcx>,
230         checked_ty: Ty<'tcx>,
231         hir_id: hir::HirId,
232     ) -> Vec<AssocItem> {
233         let mut methods =
234             self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id);
235         methods.retain(|m| {
236             self.has_only_self_parameter(m)
237                 && self
238                     .tcx
239                     .get_attrs(m.def_id)
240                     .iter()
241                     // This special internal attribute is used to permit
242                     // "identity-like" conversion methods to be suggested here.
243                     //
244                     // FIXME (#46459 and #46460): ideally
245                     // `std::convert::Into::into` and `std::borrow:ToOwned` would
246                     // also be `#[rustc_conversion_suggestion]`, if not for
247                     // method-probing false-positives and -negatives (respectively).
248                     //
249                     // FIXME? Other potential candidate methods: `as_ref` and
250                     // `as_mut`?
251                     .any(|a| self.sess().check_name(a, sym::rustc_conversion_suggestion))
252         });
253
254         methods
255     }
256
257     /// This function checks whether the method is not static and does not accept other parameters than `self`.
258     fn has_only_self_parameter(&self, method: &AssocItem) -> bool {
259         match method.kind {
260             ty::AssocKind::Fn => {
261                 method.fn_has_self_parameter
262                     && self.tcx.fn_sig(method.def_id).inputs().skip_binder().len() == 1
263             }
264             _ => false,
265         }
266     }
267
268     /// Identify some cases where `as_ref()` would be appropriate and suggest it.
269     ///
270     /// Given the following code:
271     /// ```
272     /// struct Foo;
273     /// fn takes_ref(_: &Foo) {}
274     /// let ref opt = Some(Foo);
275     ///
276     /// opt.map(|param| takes_ref(param));
277     /// ```
278     /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead.
279     ///
280     /// It only checks for `Option` and `Result` and won't work with
281     /// ```
282     /// opt.map(|param| { takes_ref(param) });
283     /// ```
284     fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Span, &'static str, String)> {
285         let path = match expr.kind {
286             hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => path,
287             _ => return None,
288         };
289
290         let local_id = match path.res {
291             hir::def::Res::Local(id) => id,
292             _ => return None,
293         };
294
295         let local_parent = self.tcx.hir().get_parent_node(local_id);
296         let param_hir_id = match self.tcx.hir().find(local_parent) {
297             Some(Node::Param(hir::Param { hir_id, .. })) => hir_id,
298             _ => return None,
299         };
300
301         let param_parent = self.tcx.hir().get_parent_node(*param_hir_id);
302         let (expr_hir_id, closure_fn_decl) = match self.tcx.hir().find(param_parent) {
303             Some(Node::Expr(hir::Expr {
304                 hir_id,
305                 kind: hir::ExprKind::Closure(_, decl, ..),
306                 ..
307             })) => (hir_id, decl),
308             _ => return None,
309         };
310
311         let expr_parent = self.tcx.hir().get_parent_node(*expr_hir_id);
312         let hir = self.tcx.hir().find(expr_parent);
313         let closure_params_len = closure_fn_decl.inputs.len();
314         let (method_path, method_span, method_expr) = match (hir, closure_params_len) {
315             (
316                 Some(Node::Expr(hir::Expr {
317                     kind: hir::ExprKind::MethodCall(path, span, expr, _),
318                     ..
319                 })),
320                 1,
321             ) => (path, span, expr),
322             _ => return None,
323         };
324
325         let self_ty = self.typeck_results.borrow().node_type(method_expr[0].hir_id);
326         let self_ty = format!("{:?}", self_ty);
327         let name = method_path.ident.name;
328         let is_as_ref_able = (self_ty.starts_with("&std::option::Option")
329             || self_ty.starts_with("&std::result::Result")
330             || self_ty.starts_with("std::option::Option")
331             || self_ty.starts_with("std::result::Result"))
332             && (name == sym::map || name == sym::and_then);
333         match (is_as_ref_able, self.sess().source_map().span_to_snippet(*method_span)) {
334             (true, Ok(src)) => {
335                 let suggestion = format!("as_ref().{}", src);
336                 Some((*method_span, "consider using `as_ref` instead", suggestion))
337             }
338             _ => None,
339         }
340     }
341
342     crate fn is_hir_id_from_struct_pattern_shorthand_field(
343         &self,
344         hir_id: hir::HirId,
345         sp: Span,
346     ) -> bool {
347         let sm = self.sess().source_map();
348         let parent_id = self.tcx.hir().get_parent_node(hir_id);
349         if let Some(parent) = self.tcx.hir().find(parent_id) {
350             // Account for fields
351             if let Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) = parent
352             {
353                 if let Ok(src) = sm.span_to_snippet(sp) {
354                     for field in *fields {
355                         if field.ident.as_str() == src && field.is_shorthand {
356                             return true;
357                         }
358                     }
359                 }
360             }
361         }
362         false
363     }
364
365     fn replace_prefix<A, B, C>(&self, s: A, old: B, new: C) -> Option<String>
366     where
367         A: AsRef<str>,
368         B: AsRef<str>,
369         C: AsRef<str>,
370     {
371         let s = s.as_ref();
372         let old = old.as_ref();
373         if let Some(stripped) = s.strip_prefix(old) {
374             Some(new.as_ref().to_owned() + stripped)
375         } else {
376             None
377         }
378     }
379
380     /// This function is used to determine potential "simple" improvements or users' errors and
381     /// provide them useful help. For example:
382     ///
383     /// ```
384     /// fn some_fn(s: &str) {}
385     ///
386     /// let x = "hey!".to_owned();
387     /// some_fn(x); // error
388     /// ```
389     ///
390     /// No need to find every potential function which could make a coercion to transform a
391     /// `String` into a `&str` since a `&` would do the trick!
392     ///
393     /// In addition of this check, it also checks between references mutability state. If the
394     /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
395     /// `&mut`!".
396     pub fn check_ref(
397         &self,
398         expr: &hir::Expr<'_>,
399         checked_ty: Ty<'tcx>,
400         expected: Ty<'tcx>,
401     ) -> Option<(Span, &'static str, String, Applicability)> {
402         let sm = self.sess().source_map();
403         let sp = expr.span;
404         if sm.is_imported(sp) {
405             // Ignore if span is from within a macro #41858, #58298. We previously used the macro
406             // call span, but that breaks down when the type error comes from multiple calls down.
407             return None;
408         }
409
410         let is_struct_pat_shorthand_field =
411             self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, sp);
412
413         // If the span is from a macro, then it's hard to extract the text
414         // and make a good suggestion, so don't bother.
415         let is_macro = sp.from_expansion() && sp.desugaring_kind().is_none();
416
417         // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
418         let expr = expr.peel_drop_temps();
419
420         match (&expr.kind, expected.kind(), checked_ty.kind()) {
421             (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
422                 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
423                     if let hir::ExprKind::Lit(_) = expr.kind {
424                         if let Ok(src) = sm.span_to_snippet(sp) {
425                             if let Some(src) = self.replace_prefix(src, "b\"", "\"") {
426                                 return Some((
427                                     sp,
428                                     "consider removing the leading `b`",
429                                     src,
430                                     Applicability::MachineApplicable,
431                                 ));
432                             }
433                         }
434                     }
435                 }
436                 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
437                     if let hir::ExprKind::Lit(_) = expr.kind {
438                         if let Ok(src) = sm.span_to_snippet(sp) {
439                             if let Some(src) = self.replace_prefix(src, "\"", "b\"") {
440                                 return Some((
441                                     sp,
442                                     "consider adding a leading `b`",
443                                     src,
444                                     Applicability::MachineApplicable,
445                                 ));
446                             }
447                         }
448                     }
449                 }
450                 _ => {}
451             },
452             (_, &ty::Ref(_, _, mutability), _) => {
453                 // Check if it can work when put into a ref. For example:
454                 //
455                 // ```
456                 // fn bar(x: &mut i32) {}
457                 //
458                 // let x = 0u32;
459                 // bar(&x); // error, expected &mut
460                 // ```
461                 let ref_ty = match mutability {
462                     hir::Mutability::Mut => {
463                         self.tcx.mk_mut_ref(self.tcx.mk_region(ty::ReStatic), checked_ty)
464                     }
465                     hir::Mutability::Not => {
466                         self.tcx.mk_imm_ref(self.tcx.mk_region(ty::ReStatic), checked_ty)
467                     }
468                 };
469                 if self.can_coerce(ref_ty, expected) {
470                     let mut sugg_sp = sp;
471                     if let hir::ExprKind::MethodCall(ref segment, sp, ref args, _) = expr.kind {
472                         let clone_trait = self.tcx.require_lang_item(LangItem::Clone, Some(sp));
473                         if let ([arg], Some(true), sym::clone) = (
474                             &args[..],
475                             self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
476                                 |did| {
477                                     let ai = self.tcx.associated_item(did);
478                                     ai.container == ty::TraitContainer(clone_trait)
479                                 },
480                             ),
481                             segment.ident.name,
482                         ) {
483                             // If this expression had a clone call when suggesting borrowing
484                             // we want to suggest removing it because it'd now be unnecessary.
485                             sugg_sp = arg.span;
486                         }
487                     }
488                     if let Ok(src) = sm.span_to_snippet(sugg_sp) {
489                         let needs_parens = match expr.kind {
490                             // parenthesize if needed (Issue #46756)
491                             hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
492                             // parenthesize borrows of range literals (Issue #54505)
493                             _ if is_range_literal(expr) => true,
494                             _ => false,
495                         };
496                         let sugg_expr = if needs_parens { format!("({})", src) } else { src };
497
498                         if let Some(sugg) = self.can_use_as_ref(expr) {
499                             return Some((
500                                 sugg.0,
501                                 sugg.1,
502                                 sugg.2,
503                                 Applicability::MachineApplicable,
504                             ));
505                         }
506                         let field_name = if is_struct_pat_shorthand_field {
507                             format!("{}: ", sugg_expr)
508                         } else {
509                             String::new()
510                         };
511                         if let Some(hir::Node::Expr(hir::Expr {
512                             kind: hir::ExprKind::Assign(left_expr, ..),
513                             ..
514                         })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
515                         {
516                             if mutability == hir::Mutability::Mut {
517                                 // Found the following case:
518                                 // fn foo(opt: &mut Option<String>){ opt = None }
519                                 //                                   ---   ^^^^
520                                 //                                   |     |
521                                 //    consider dereferencing here: `*opt`  |
522                                 // expected mutable reference, found enum `Option`
523                                 if let Ok(src) = sm.span_to_snippet(left_expr.span) {
524                                     return Some((
525                                         left_expr.span,
526                                         "consider dereferencing here to assign to the mutable \
527                                          borrowed piece of memory",
528                                         format!("*{}", src),
529                                         Applicability::MachineApplicable,
530                                     ));
531                                 }
532                             }
533                         }
534
535                         return Some(match mutability {
536                             hir::Mutability::Mut => (
537                                 sp,
538                                 "consider mutably borrowing here",
539                                 format!("{}&mut {}", field_name, sugg_expr),
540                                 Applicability::MachineApplicable,
541                             ),
542                             hir::Mutability::Not => (
543                                 sp,
544                                 "consider borrowing here",
545                                 format!("{}&{}", field_name, sugg_expr),
546                                 Applicability::MachineApplicable,
547                             ),
548                         });
549                     }
550                 }
551             }
552             (
553                 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr),
554                 _,
555                 &ty::Ref(_, checked, _),
556             ) if {
557                 self.infcx.can_sub(self.param_env, checked, &expected).is_ok() && !is_macro
558             } =>
559             {
560                 // We have `&T`, check if what was expected was `T`. If so,
561                 // we may want to suggest removing a `&`.
562                 if sm.is_imported(expr.span) {
563                     if let Ok(src) = sm.span_to_snippet(sp) {
564                         if let Some(src) = self.replace_prefix(src, "&", "") {
565                             return Some((
566                                 sp,
567                                 "consider removing the borrow",
568                                 src,
569                                 Applicability::MachineApplicable,
570                             ));
571                         }
572                     }
573                     return None;
574                 }
575                 if let Ok(code) = sm.span_to_snippet(expr.span) {
576                     return Some((
577                         sp,
578                         "consider removing the borrow",
579                         code,
580                         Applicability::MachineApplicable,
581                     ));
582                 }
583             }
584             (
585                 _,
586                 &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }),
587                 &ty::Ref(_, ty_a, mutbl_a),
588             ) => {
589                 if let Some(steps) = self.deref_steps(ty_a, ty_b) {
590                     // Only suggest valid if dereferencing needed.
591                     if steps > 0 {
592                         // The pointer type implements `Copy` trait so the suggestion is always valid.
593                         if let Ok(src) = sm.span_to_snippet(sp) {
594                             let derefs = &"*".repeat(steps);
595                             if let Some((src, applicability)) = match mutbl_b {
596                                 hir::Mutability::Mut => {
597                                     let new_prefix = "&mut ".to_owned() + derefs;
598                                     match mutbl_a {
599                                         hir::Mutability::Mut => {
600                                             if let Some(s) =
601                                                 self.replace_prefix(src, "&mut ", new_prefix)
602                                             {
603                                                 Some((s, Applicability::MachineApplicable))
604                                             } else {
605                                                 None
606                                             }
607                                         }
608                                         hir::Mutability::Not => {
609                                             if let Some(s) =
610                                                 self.replace_prefix(src, "&", new_prefix)
611                                             {
612                                                 Some((s, Applicability::Unspecified))
613                                             } else {
614                                                 None
615                                             }
616                                         }
617                                     }
618                                 }
619                                 hir::Mutability::Not => {
620                                     let new_prefix = "&".to_owned() + derefs;
621                                     match mutbl_a {
622                                         hir::Mutability::Mut => {
623                                             if let Some(s) =
624                                                 self.replace_prefix(src, "&mut ", new_prefix)
625                                             {
626                                                 Some((s, Applicability::MachineApplicable))
627                                             } else {
628                                                 None
629                                             }
630                                         }
631                                         hir::Mutability::Not => {
632                                             if let Some(s) =
633                                                 self.replace_prefix(src, "&", new_prefix)
634                                             {
635                                                 Some((s, Applicability::MachineApplicable))
636                                             } else {
637                                                 None
638                                             }
639                                         }
640                                     }
641                                 }
642                             } {
643                                 return Some((sp, "consider dereferencing", src, applicability));
644                             }
645                         }
646                     }
647                 }
648             }
649             _ if sp == expr.span && !is_macro => {
650                 if let Some(steps) = self.deref_steps(checked_ty, expected) {
651                     if steps == 1 {
652                         // For a suggestion to make sense, the type would need to be `Copy`.
653                         if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) {
654                             if let Ok(code) = sm.span_to_snippet(sp) {
655                                 let message = if checked_ty.is_region_ptr() {
656                                     "consider dereferencing the borrow"
657                                 } else {
658                                     "consider dereferencing the type"
659                                 };
660                                 let suggestion = if is_struct_pat_shorthand_field {
661                                     format!("{}: *{}", code, code)
662                                 } else {
663                                     format!("*{}", code)
664                                 };
665                                 return Some((
666                                     sp,
667                                     message,
668                                     suggestion,
669                                     Applicability::MachineApplicable,
670                                 ));
671                             }
672                         }
673                     }
674                 }
675             }
676             _ => {}
677         }
678         None
679     }
680
681     pub fn check_for_cast(
682         &self,
683         err: &mut DiagnosticBuilder<'_>,
684         expr: &hir::Expr<'_>,
685         checked_ty: Ty<'tcx>,
686         expected_ty: Ty<'tcx>,
687         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
688     ) -> bool {
689         if self.tcx.sess.source_map().is_imported(expr.span) {
690             // Ignore if span is from within a macro.
691             return false;
692         }
693
694         let src = if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
695             src
696         } else {
697             return false;
698         };
699
700         // If casting this expression to a given numeric type would be appropriate in case of a type
701         // mismatch.
702         //
703         // We want to minimize the amount of casting operations that are suggested, as it can be a
704         // lossy operation with potentially bad side effects, so we only suggest when encountering
705         // an expression that indicates that the original type couldn't be directly changed.
706         //
707         // For now, don't suggest casting with `as`.
708         let can_cast = false;
709
710         let prefix = if let Some(hir::Node::Expr(hir::Expr {
711             kind: hir::ExprKind::Struct(_, fields, _),
712             ..
713         })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
714         {
715             // `expr` is a literal field for a struct, only suggest if appropriate
716             match (*fields)
717                 .iter()
718                 .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand)
719             {
720                 // This is a field literal
721                 Some(field) => format!("{}: ", field.ident),
722                 // Likely a field was meant, but this field wasn't found. Do not suggest anything.
723                 None => return false,
724             }
725         } else {
726             String::new()
727         };
728
729         if let hir::ExprKind::Call(path, args) = &expr.kind {
730             if let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
731                 (&path.kind, args.len())
732             {
733                 // `expr` is a conversion like `u32::from(val)`, do not suggest anything (#63697).
734                 if let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
735                     (&base_ty.kind, path_segment.ident.name)
736                 {
737                     if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
738                         match ident.name {
739                             sym::i128
740                             | sym::i64
741                             | sym::i32
742                             | sym::i16
743                             | sym::i8
744                             | sym::u128
745                             | sym::u64
746                             | sym::u32
747                             | sym::u16
748                             | sym::u8
749                             | sym::isize
750                             | sym::usize
751                                 if base_ty_path.segments.len() == 1 =>
752                             {
753                                 return false;
754                             }
755                             _ => {}
756                         }
757                     }
758                 }
759             }
760         }
761
762         let msg = format!("you can convert an `{}` to `{}`", checked_ty, expected_ty);
763         let cast_msg = format!("you can cast an `{} to `{}`", checked_ty, expected_ty);
764         let lit_msg = format!(
765             "change the type of the numeric literal from `{}` to `{}`",
766             checked_ty, expected_ty,
767         );
768
769         let with_opt_paren: fn(&dyn fmt::Display) -> String =
770             if expr.precedence().order() < PREC_POSTFIX {
771                 |s| format!("({})", s)
772             } else {
773                 |s| s.to_string()
774             };
775
776         let cast_suggestion = format!("{}{} as {}", prefix, with_opt_paren(&src), expected_ty);
777         let into_suggestion = format!("{}{}.into()", prefix, with_opt_paren(&src));
778         let suffix_suggestion = with_opt_paren(&format_args!(
779             "{}{}",
780             if matches!(
781                 (&expected_ty.kind(), &checked_ty.kind()),
782                 (ty::Int(_) | ty::Uint(_), ty::Float(_))
783             ) {
784                 // Remove fractional part from literal, for example `42.0f32` into `42`
785                 let src = src.trim_end_matches(&checked_ty.to_string());
786                 src.split('.').next().unwrap()
787             } else {
788                 src.trim_end_matches(&checked_ty.to_string())
789             },
790             expected_ty,
791         ));
792         let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
793             if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
794         };
795         let is_negative_int =
796             |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::UnNeg, ..));
797         let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
798
799         let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);
800
801         let suggest_fallible_into_or_lhs_from =
802             |err: &mut DiagnosticBuilder<'_>, exp_to_found_is_fallible: bool| {
803                 // If we know the expression the expected type is derived from, we might be able
804                 // to suggest a widening conversion rather than a narrowing one (which may
805                 // panic). For example, given x: u8 and y: u32, if we know the span of "x",
806                 //   x > y
807                 // can be given the suggestion "u32::from(x) > y" rather than
808                 // "x > y.try_into().unwrap()".
809                 let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
810                     match self.tcx.sess.source_map().span_to_snippet(expr.span).ok() {
811                         Some(src) => Some((expr, src)),
812                         None => None,
813                     }
814                 });
815                 let (span, msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
816                     (lhs_expr_and_src, exp_to_found_is_fallible)
817                 {
818                     let msg = format!(
819                         "you can convert `{}` from `{}` to `{}`, matching the type of `{}`",
820                         lhs_src, expected_ty, checked_ty, src
821                     );
822                     let suggestion = format!("{}::from({})", checked_ty, lhs_src);
823                     (lhs_expr.span, msg, suggestion)
824                 } else {
825                     let msg = format!("{} and panic if the converted value wouldn't fit", msg);
826                     let suggestion =
827                         format!("{}{}.try_into().unwrap()", prefix, with_opt_paren(&src));
828                     (expr.span, msg, suggestion)
829                 };
830                 err.span_suggestion(span, &msg, suggestion, Applicability::MachineApplicable);
831             };
832
833         let suggest_to_change_suffix_or_into =
834             |err: &mut DiagnosticBuilder<'_>,
835              found_to_exp_is_fallible: bool,
836              exp_to_found_is_fallible: bool| {
837                 let always_fallible = found_to_exp_is_fallible
838                     && (exp_to_found_is_fallible || expected_ty_expr.is_none());
839                 let msg = if literal_is_ty_suffixed(expr) {
840                     &lit_msg
841                 } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
842                     // We now know that converting either the lhs or rhs is fallible. Before we
843                     // suggest a fallible conversion, check if the value can never fit in the
844                     // expected type.
845                     let msg = format!("`{}` cannot fit into type `{}`", src, expected_ty);
846                     err.note(&msg);
847                     return;
848                 } else if in_const_context {
849                     // Do not recommend `into` or `try_into` in const contexts.
850                     return;
851                 } else if found_to_exp_is_fallible {
852                     return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
853                 } else {
854                     &msg
855                 };
856                 let suggestion = if literal_is_ty_suffixed(expr) {
857                     suffix_suggestion.clone()
858                 } else {
859                     into_suggestion.clone()
860                 };
861                 err.span_suggestion(expr.span, msg, suggestion, Applicability::MachineApplicable);
862             };
863
864         match (&expected_ty.kind(), &checked_ty.kind()) {
865             (&ty::Int(ref exp), &ty::Int(ref found)) => {
866                 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
867                 {
868                     (Some(exp), Some(found)) if exp < found => (true, false),
869                     (Some(exp), Some(found)) if exp > found => (false, true),
870                     (None, Some(8 | 16)) => (false, true),
871                     (Some(8 | 16), None) => (true, false),
872                     (None, _) | (_, None) => (true, true),
873                     _ => (false, false),
874                 };
875                 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
876                 true
877             }
878             (&ty::Uint(ref exp), &ty::Uint(ref found)) => {
879                 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
880                 {
881                     (Some(exp), Some(found)) if exp < found => (true, false),
882                     (Some(exp), Some(found)) if exp > found => (false, true),
883                     (None, Some(8 | 16)) => (false, true),
884                     (Some(8 | 16), None) => (true, false),
885                     (None, _) | (_, None) => (true, true),
886                     _ => (false, false),
887                 };
888                 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
889                 true
890             }
891             (&ty::Int(exp), &ty::Uint(found)) => {
892                 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
893                 {
894                     (Some(exp), Some(found)) if found < exp => (false, true),
895                     (None, Some(8)) => (false, true),
896                     _ => (true, true),
897                 };
898                 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
899                 true
900             }
901             (&ty::Uint(exp), &ty::Int(found)) => {
902                 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
903                 {
904                     (Some(exp), Some(found)) if found > exp => (true, false),
905                     (Some(8), None) => (true, false),
906                     _ => (true, true),
907                 };
908                 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
909                 true
910             }
911             (&ty::Float(ref exp), &ty::Float(ref found)) => {
912                 if found.bit_width() < exp.bit_width() {
913                     suggest_to_change_suffix_or_into(err, false, true);
914                 } else if literal_is_ty_suffixed(expr) {
915                     err.span_suggestion(
916                         expr.span,
917                         &lit_msg,
918                         suffix_suggestion,
919                         Applicability::MachineApplicable,
920                     );
921                 } else if can_cast {
922                     // Missing try_into implementation for `f64` to `f32`
923                     err.span_suggestion(
924                         expr.span,
925                         &format!("{}, producing the closest possible value", cast_msg),
926                         cast_suggestion,
927                         Applicability::MaybeIncorrect, // lossy conversion
928                     );
929                 }
930                 true
931             }
932             (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
933                 if literal_is_ty_suffixed(expr) {
934                     err.span_suggestion(
935                         expr.span,
936                         &lit_msg,
937                         suffix_suggestion,
938                         Applicability::MachineApplicable,
939                     );
940                 } else if can_cast {
941                     // Missing try_into implementation for `{float}` to `{integer}`
942                     err.span_suggestion(
943                         expr.span,
944                         &format!("{}, rounding the float towards zero", msg),
945                         cast_suggestion,
946                         Applicability::MaybeIncorrect, // lossy conversion
947                     );
948                 }
949                 true
950             }
951             (&ty::Float(ref exp), &ty::Uint(ref found)) => {
952                 // if `found` is `None` (meaning found is `usize`), don't suggest `.into()`
953                 if exp.bit_width() > found.bit_width().unwrap_or(256) {
954                     err.span_suggestion(
955                         expr.span,
956                         &format!(
957                             "{}, producing the floating point representation of the integer",
958                             msg,
959                         ),
960                         into_suggestion,
961                         Applicability::MachineApplicable,
962                     );
963                 } else if literal_is_ty_suffixed(expr) {
964                     err.span_suggestion(
965                         expr.span,
966                         &lit_msg,
967                         suffix_suggestion,
968                         Applicability::MachineApplicable,
969                     );
970                 } else {
971                     // Missing try_into implementation for `{integer}` to `{float}`
972                     err.span_suggestion(
973                         expr.span,
974                         &format!(
975                             "{}, producing the floating point representation of the integer,
976                                  rounded if necessary",
977                             cast_msg,
978                         ),
979                         cast_suggestion,
980                         Applicability::MaybeIncorrect, // lossy conversion
981                     );
982                 }
983                 true
984             }
985             (&ty::Float(ref exp), &ty::Int(ref found)) => {
986                 // if `found` is `None` (meaning found is `isize`), don't suggest `.into()`
987                 if exp.bit_width() > found.bit_width().unwrap_or(256) {
988                     err.span_suggestion(
989                         expr.span,
990                         &format!(
991                             "{}, producing the floating point representation of the integer",
992                             &msg,
993                         ),
994                         into_suggestion,
995                         Applicability::MachineApplicable,
996                     );
997                 } else if literal_is_ty_suffixed(expr) {
998                     err.span_suggestion(
999                         expr.span,
1000                         &lit_msg,
1001                         suffix_suggestion,
1002                         Applicability::MachineApplicable,
1003                     );
1004                 } else {
1005                     // Missing try_into implementation for `{integer}` to `{float}`
1006                     err.span_suggestion(
1007                         expr.span,
1008                         &format!(
1009                             "{}, producing the floating point representation of the integer, \
1010                                 rounded if necessary",
1011                             &msg,
1012                         ),
1013                         cast_suggestion,
1014                         Applicability::MaybeIncorrect, // lossy conversion
1015                     );
1016                 }
1017                 true
1018             }
1019             _ => false,
1020         }
1021     }
1022 }