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;
6 use rustc_ast::util::parser::PREC_POSTFIX;
7 use rustc_errors::{Applicability, DiagnosticBuilder};
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::print::with_no_trimmed_paths;
14 use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut};
15 use rustc_span::symbol::sym;
16 use rustc_span::{BytePos, Span};
18 use super::method::probe;
22 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23 pub fn emit_coerce_suggestions(
25 err: &mut DiagnosticBuilder<'_>,
29 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
31 self.annotate_expected_due_to_let_ty(err, expr);
32 self.suggest_compatible_variants(err, expr, expected, expr_ty);
33 self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
34 if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
37 self.suggest_no_capture_closure(err, expected, expr_ty);
38 self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
39 self.suggest_missing_parentheses(err, expr);
40 self.note_need_for_fn_pointer(err, expected, expr_ty);
41 self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
42 self.report_closure_inferred_return_type(err, expected);
45 // Requires that the two types unify, and prints an error message if
47 pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
48 if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) {
53 pub fn demand_suptype_diag(
58 ) -> Option<DiagnosticBuilder<'tcx>> {
59 self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
62 pub fn demand_suptype_with_origin(
64 cause: &ObligationCause<'tcx>,
67 ) -> Option<DiagnosticBuilder<'tcx>> {
68 match self.at(cause, self.param_env).sup(expected, actual) {
69 Ok(InferOk { obligations, value: () }) => {
70 self.register_predicates(obligations);
73 Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)),
77 pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
78 if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) {
83 pub fn demand_eqtype_diag(
88 ) -> Option<DiagnosticBuilder<'tcx>> {
89 self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
92 pub fn demand_eqtype_with_origin(
94 cause: &ObligationCause<'tcx>,
97 ) -> Option<DiagnosticBuilder<'tcx>> {
98 match self.at(cause, self.param_env).eq(expected, actual) {
99 Ok(InferOk { obligations, value: () }) => {
100 self.register_predicates(obligations);
103 Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)),
107 pub fn demand_coerce(
109 expr: &hir::Expr<'_>,
110 checked_ty: Ty<'tcx>,
112 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
113 allow_two_phase: AllowTwoPhase,
116 self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase);
117 if let Some(mut err) = err {
123 /// Checks that the type of `expr` can be coerced to `expected`.
125 /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!`
126 /// will be permitted if the diverges flag is currently "always".
127 pub fn demand_coerce_diag(
129 expr: &hir::Expr<'_>,
130 checked_ty: Ty<'tcx>,
132 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
133 allow_two_phase: AllowTwoPhase,
134 ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) {
135 let expected = self.resolve_vars_with_obligations(expected);
137 let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase, None) {
138 Ok(ty) => return (ty, None),
142 let expr = expr.peel_drop_temps();
143 let cause = self.misc(expr.span);
144 let expr_ty = self.resolve_vars_with_obligations(checked_ty);
145 let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
147 if self.is_assign_to_bool(expr, expected) {
148 // Error reported in `check_assign` so avoid emitting error again.
150 return (expected, None);
153 self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr);
155 (expected, Some(err))
158 fn annotate_expected_due_to_let_ty(
160 err: &mut DiagnosticBuilder<'_>,
161 expr: &hir::Expr<'_>,
163 let parent = self.tcx.hir().get_parent_node(expr.hir_id);
164 if let Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })) =
165 self.tcx.hir().find(parent)
167 if init.hir_id == expr.hir_id {
168 // Point at `let` assignment type.
169 err.span_label(ty.span, "expected due to this");
174 /// Returns whether the expected type is `bool` and the expression is `x = y`.
175 pub fn is_assign_to_bool(&self, expr: &hir::Expr<'_>, expected: Ty<'tcx>) -> bool {
176 if let hir::ExprKind::Assign(..) = expr.kind {
177 return expected == self.tcx.types.bool;
182 /// If the expected type is an enum (Issue #55250) with any variants whose
183 /// sole field is of the found type, suggest such variants. (Issue #42764)
184 fn suggest_compatible_variants(
186 err: &mut DiagnosticBuilder<'_>,
187 expr: &hir::Expr<'_>,
191 if let ty::Adt(expected_adt, substs) = expected.kind() {
192 if !expected_adt.is_enum() {
196 let mut compatible_variants = expected_adt
199 .filter(|variant| variant.fields.len() == 1)
200 .filter_map(|variant| {
201 let sole_field = &variant.fields[0];
202 let sole_field_ty = sole_field.ty(self.tcx, substs);
203 if self.can_coerce(expr_ty, sole_field_ty) {
205 with_no_trimmed_paths(|| self.tcx.def_path_str(variant.def_id));
206 // FIXME #56861: DRYer prelude filtering
207 if let Some(path) = variant_path.strip_prefix("std::prelude::") {
208 if let Some((_, path)) = path.split_once("::") {
209 return Some(path.to_string());
219 if compatible_variants.peek().is_some() {
220 if let Ok(expr_text) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
221 let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text));
222 let msg = "try using a variant of the expected enum";
223 err.span_suggestions(
227 Applicability::MaybeIncorrect,
234 pub fn get_conversion_methods(
238 checked_ty: Ty<'tcx>,
240 ) -> Vec<AssocItem> {
242 self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id);
244 self.has_only_self_parameter(m)
249 // This special internal attribute is used to permit
250 // "identity-like" conversion methods to be suggested here.
252 // FIXME (#46459 and #46460): ideally
253 // `std::convert::Into::into` and `std::borrow:ToOwned` would
254 // also be `#[rustc_conversion_suggestion]`, if not for
255 // method-probing false-positives and -negatives (respectively).
257 // FIXME? Other potential candidate methods: `as_ref` and
259 .any(|a| a.has_name(sym::rustc_conversion_suggestion))
265 /// This function checks whether the method is not static and does not accept other parameters than `self`.
266 fn has_only_self_parameter(&self, method: &AssocItem) -> bool {
268 ty::AssocKind::Fn => {
269 method.fn_has_self_parameter
270 && self.tcx.fn_sig(method.def_id).inputs().skip_binder().len() == 1
276 /// Identify some cases where `as_ref()` would be appropriate and suggest it.
278 /// Given the following code:
281 /// fn takes_ref(_: &Foo) {}
282 /// let ref opt = Some(Foo);
284 /// opt.map(|param| takes_ref(param));
286 /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead.
288 /// It only checks for `Option` and `Result` and won't work with
290 /// opt.map(|param| { takes_ref(param) });
292 fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Span, &'static str, String)> {
293 let path = match expr.kind {
294 hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) => path,
298 let local_id = match path.res {
299 hir::def::Res::Local(id) => id,
303 let local_parent = self.tcx.hir().get_parent_node(local_id);
304 let param_hir_id = match self.tcx.hir().find(local_parent) {
305 Some(Node::Param(hir::Param { hir_id, .. })) => hir_id,
309 let param_parent = self.tcx.hir().get_parent_node(*param_hir_id);
310 let (expr_hir_id, closure_fn_decl) = match self.tcx.hir().find(param_parent) {
311 Some(Node::Expr(hir::Expr {
313 kind: hir::ExprKind::Closure(_, decl, ..),
315 })) => (hir_id, decl),
319 let expr_parent = self.tcx.hir().get_parent_node(*expr_hir_id);
320 let hir = self.tcx.hir().find(expr_parent);
321 let closure_params_len = closure_fn_decl.inputs.len();
322 let (method_path, method_span, method_expr) = match (hir, closure_params_len) {
324 Some(Node::Expr(hir::Expr {
325 kind: hir::ExprKind::MethodCall(path, span, expr, _),
329 ) => (path, span, expr),
333 let self_ty = self.typeck_results.borrow().node_type(method_expr[0].hir_id);
334 let self_ty = format!("{:?}", self_ty);
335 let name = method_path.ident.name;
336 let is_as_ref_able = (self_ty.starts_with("&std::option::Option")
337 || self_ty.starts_with("&std::result::Result")
338 || self_ty.starts_with("std::option::Option")
339 || self_ty.starts_with("std::result::Result"))
340 && (name == sym::map || name == sym::and_then);
341 match (is_as_ref_able, self.sess().source_map().span_to_snippet(*method_span)) {
343 let suggestion = format!("as_ref().{}", src);
344 Some((*method_span, "consider using `as_ref` instead", suggestion))
350 crate fn is_hir_id_from_struct_pattern_shorthand_field(
355 let sm = self.sess().source_map();
356 let parent_id = self.tcx.hir().get_parent_node(hir_id);
357 if let Some(parent) = self.tcx.hir().find(parent_id) {
358 // Account for fields
359 if let Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) = parent
361 if let Ok(src) = sm.span_to_snippet(sp) {
362 for field in *fields {
363 if field.ident.as_str() == src && field.is_shorthand {
373 /// If the given `HirId` corresponds to a block with a trailing expression, return that expression
374 crate fn maybe_get_block_expr(&self, hir_id: hir::HirId) -> Option<&'tcx hir::Expr<'tcx>> {
375 match self.tcx.hir().find(hir_id)? {
376 Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, ..), .. }) => block.expr,
381 /// Returns whether the given expression is an `else if`.
382 crate fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool {
383 if let hir::ExprKind::If(..) = expr.kind {
384 let parent_id = self.tcx.hir().get_parent_node(expr.hir_id);
385 if let Some(Node::Expr(hir::Expr {
386 kind: hir::ExprKind::If(_, _, Some(else_expr)),
388 })) = self.tcx.hir().find(parent_id)
390 return else_expr.hir_id == expr.hir_id;
396 /// This function is used to determine potential "simple" improvements or users' errors and
397 /// provide them useful help. For example:
400 /// fn some_fn(s: &str) {}
402 /// let x = "hey!".to_owned();
403 /// some_fn(x); // error
406 /// No need to find every potential function which could make a coercion to transform a
407 /// `String` into a `&str` since a `&` would do the trick!
409 /// In addition of this check, it also checks between references mutability state. If the
410 /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
414 expr: &hir::Expr<'_>,
415 checked_ty: Ty<'tcx>,
417 ) -> Option<(Span, &'static str, String, Applicability, bool /* verbose */)> {
418 let sess = self.sess();
421 // If the span is from an external macro, there's no suggestion we can make.
422 if in_external_macro(sess, sp) {
426 let sm = sess.source_map();
428 let replace_prefix = |s: &str, old: &str, new: &str| {
429 s.strip_prefix(old).map(|stripped| new.to_string() + stripped)
432 let is_struct_pat_shorthand_field =
433 self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, sp);
435 // `ExprKind::DropTemps` is semantically irrelevant for these suggestions.
436 let expr = expr.peel_drop_temps();
438 match (&expr.kind, expected.kind(), checked_ty.kind()) {
439 (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) {
440 (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
441 if let hir::ExprKind::Lit(_) = expr.kind {
442 if let Ok(src) = sm.span_to_snippet(sp) {
443 if let Some(_) = replace_prefix(&src, "b\"", "\"") {
444 let pos = sp.lo() + BytePos(1);
447 "consider removing the leading `b`",
449 Applicability::MachineApplicable,
456 (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => {
457 if let hir::ExprKind::Lit(_) = expr.kind {
458 if let Ok(src) = sm.span_to_snippet(sp) {
459 if let Some(_) = replace_prefix(&src, "\"", "b\"") {
462 "consider adding a leading `b`",
464 Applicability::MachineApplicable,
473 (_, &ty::Ref(_, _, mutability), _) => {
474 // Check if it can work when put into a ref. For example:
477 // fn bar(x: &mut i32) {}
480 // bar(&x); // error, expected &mut
482 let ref_ty = match mutability {
483 hir::Mutability::Mut => {
484 self.tcx.mk_mut_ref(self.tcx.mk_region(ty::ReStatic), checked_ty)
486 hir::Mutability::Not => {
487 self.tcx.mk_imm_ref(self.tcx.mk_region(ty::ReStatic), checked_ty)
490 if self.can_coerce(ref_ty, expected) {
491 let mut sugg_sp = sp;
492 if let hir::ExprKind::MethodCall(ref segment, sp, ref args, _) = expr.kind {
493 let clone_trait = self.tcx.require_lang_item(LangItem::Clone, Some(sp));
494 if let ([arg], Some(true), sym::clone) = (
496 self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
498 let ai = self.tcx.associated_item(did);
499 ai.container == ty::TraitContainer(clone_trait)
504 // If this expression had a clone call when suggesting borrowing
505 // we want to suggest removing it because it'd now be unnecessary.
509 if let Ok(src) = sm.span_to_snippet(sugg_sp) {
510 let needs_parens = match expr.kind {
511 // parenthesize if needed (Issue #46756)
512 hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true,
513 // parenthesize borrows of range literals (Issue #54505)
514 _ if is_range_literal(expr) => true,
517 let sugg_expr = if needs_parens { format!("({})", src) } else { src };
519 if let Some(sugg) = self.can_use_as_ref(expr) {
524 Applicability::MachineApplicable,
528 let field_name = if is_struct_pat_shorthand_field {
529 format!("{}: ", sugg_expr)
533 if let Some(hir::Node::Expr(hir::Expr {
534 kind: hir::ExprKind::Assign(left_expr, ..),
536 })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
538 if mutability == hir::Mutability::Mut {
539 // Found the following case:
540 // fn foo(opt: &mut Option<String>){ opt = None }
543 // consider dereferencing here: `*opt` |
544 // expected mutable reference, found enum `Option`
545 if sm.span_to_snippet(left_expr.span).is_ok() {
547 left_expr.span.shrink_to_lo(),
548 "consider dereferencing here to assign to the mutable \
549 borrowed piece of memory",
551 Applicability::MachineApplicable,
558 return Some(match mutability {
559 hir::Mutability::Mut => (
561 "consider mutably borrowing here",
562 format!("{}&mut {}", field_name, sugg_expr),
563 Applicability::MachineApplicable,
566 hir::Mutability::Not => (
568 "consider borrowing here",
569 format!("{}&{}", field_name, sugg_expr),
570 Applicability::MachineApplicable,
578 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr),
580 &ty::Ref(_, checked, _),
581 ) if self.infcx.can_sub(self.param_env, checked, &expected).is_ok() => {
582 // We have `&T`, check if what was expected was `T`. If so,
583 // we may want to suggest removing a `&`.
584 if sm.is_imported(expr.span) {
585 // Go through the spans from which this span was expanded,
586 // and find the one that's pointing inside `sp`.
588 // E.g. for `&format!("")`, where we want the span to the
589 // `format!()` invocation instead of its expansion.
590 if let Some(call_span) =
591 iter::successors(Some(expr.span), |s| s.parent_callsite())
592 .find(|&s| sp.contains(s))
594 if sm.span_to_snippet(call_span).is_ok() {
596 sp.with_hi(call_span.lo()),
597 "consider removing the borrow",
599 Applicability::MachineApplicable,
606 if sp.contains(expr.span) {
607 if sm.span_to_snippet(expr.span).is_ok() {
609 sp.with_hi(expr.span.lo()),
610 "consider removing the borrow",
612 Applicability::MachineApplicable,
620 &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }),
621 &ty::Ref(_, ty_a, mutbl_a),
623 if let Some(steps) = self.deref_steps(ty_a, ty_b) {
624 // Only suggest valid if dereferencing needed.
626 // The pointer type implements `Copy` trait so the suggestion is always valid.
627 if let Ok(src) = sm.span_to_snippet(sp) {
628 let derefs = "*".repeat(steps);
629 if let Some((span, src, applicability)) = match mutbl_b {
630 hir::Mutability::Mut => {
631 let new_prefix = "&mut ".to_owned() + &derefs;
633 hir::Mutability::Mut => {
634 replace_prefix(&src, "&mut ", &new_prefix).map(|_| {
635 let pos = sp.lo() + BytePos(5);
636 let sp = sp.with_lo(pos).with_hi(pos);
637 (sp, derefs, Applicability::MachineApplicable)
640 hir::Mutability::Not => {
641 replace_prefix(&src, "&", &new_prefix).map(|_| {
642 let pos = sp.lo() + BytePos(1);
643 let sp = sp.with_lo(pos).with_hi(pos);
646 format!("mut {}", derefs),
647 Applicability::Unspecified,
653 hir::Mutability::Not => {
654 let new_prefix = "&".to_owned() + &derefs;
656 hir::Mutability::Mut => {
657 replace_prefix(&src, "&mut ", &new_prefix).map(|_| {
658 let lo = sp.lo() + BytePos(1);
659 let hi = sp.lo() + BytePos(5);
660 let sp = sp.with_lo(lo).with_hi(hi);
661 (sp, derefs, Applicability::MachineApplicable)
664 hir::Mutability::Not => {
665 replace_prefix(&src, "&", &new_prefix).map(|_| {
666 let pos = sp.lo() + BytePos(1);
667 let sp = sp.with_lo(pos).with_hi(pos);
668 (sp, derefs, Applicability::MachineApplicable)
676 "consider dereferencing",
686 _ if sp == expr.span => {
687 if let Some(steps) = self.deref_steps(checked_ty, expected) {
688 let expr = expr.peel_blocks();
691 if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind {
692 // If the expression has `&`, removing it would fix the error
693 let prefix_span = expr.span.with_hi(inner.span.lo());
694 let message = match mutbl {
695 hir::Mutability::Not => "consider removing the `&`",
696 hir::Mutability::Mut => "consider removing the `&mut`",
698 let suggestion = String::new();
703 Applicability::MachineApplicable,
706 } else if self.infcx.type_is_copy_modulo_regions(
711 // For this suggestion to make sense, the type would need to be `Copy`.
712 if let Ok(code) = sm.span_to_snippet(expr.span) {
713 let message = if checked_ty.is_region_ptr() {
714 "consider dereferencing the borrow"
716 "consider dereferencing the type"
718 let (span, suggestion) = if is_struct_pat_shorthand_field {
719 (expr.span, format!("{}: *{}", code, code))
720 } else if self.is_else_if_block(expr) {
721 // Don't suggest nonsense like `else *if`
723 } else if let Some(expr) = self.maybe_get_block_expr(expr.hir_id) {
724 (expr.span.shrink_to_lo(), "*".to_string())
726 (expr.span.shrink_to_lo(), "*".to_string())
732 Applicability::MachineApplicable,
745 pub fn check_for_cast(
747 err: &mut DiagnosticBuilder<'_>,
748 expr: &hir::Expr<'_>,
749 checked_ty: Ty<'tcx>,
750 expected_ty: Ty<'tcx>,
751 expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
753 if self.tcx.sess.source_map().is_imported(expr.span) {
754 // Ignore if span is from within a macro.
758 let src = if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
764 // If casting this expression to a given numeric type would be appropriate in case of a type
767 // We want to minimize the amount of casting operations that are suggested, as it can be a
768 // lossy operation with potentially bad side effects, so we only suggest when encountering
769 // an expression that indicates that the original type couldn't be directly changed.
771 // For now, don't suggest casting with `as`.
772 let can_cast = false;
774 let mut sugg = vec![];
776 if let Some(hir::Node::Expr(hir::Expr {
777 kind: hir::ExprKind::Struct(_, fields, _), ..
778 })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
780 // `expr` is a literal field for a struct, only suggest if appropriate
783 .find(|field| field.expr.hir_id == expr.hir_id && field.is_shorthand)
785 // This is a field literal
787 sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident)));
789 // Likely a field was meant, but this field wasn't found. Do not suggest anything.
790 None => return false,
794 if let hir::ExprKind::Call(path, args) = &expr.kind {
795 if let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) =
796 (&path.kind, args.len())
798 // `expr` is a conversion like `u32::from(val)`, do not suggest anything (#63697).
799 if let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) =
800 (&base_ty.kind, path_segment.ident.name)
802 if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() {
816 if base_ty_path.segments.len() == 1 =>
828 "you can convert {} `{}` to {} `{}`",
829 checked_ty.kind().article(),
831 expected_ty.kind().article(),
834 let cast_msg = format!(
835 "you can cast {} `{}` to {} `{}`",
836 checked_ty.kind().article(),
838 expected_ty.kind().article(),
841 let lit_msg = format!(
842 "change the type of the numeric literal from `{}` to `{}`",
843 checked_ty, expected_ty,
846 let close_paren = if expr.precedence().order() < PREC_POSTFIX {
847 sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
853 let mut cast_suggestion = sugg.clone();
855 .push((expr.span.shrink_to_hi(), format!("{} as {}", close_paren, expected_ty)));
856 let mut into_suggestion = sugg.clone();
857 into_suggestion.push((expr.span.shrink_to_hi(), format!("{}.into()", close_paren)));
858 let mut suffix_suggestion = sugg.clone();
859 suffix_suggestion.push((
861 (&expected_ty.kind(), &checked_ty.kind()),
862 (ty::Int(_) | ty::Uint(_), ty::Float(_))
864 // Remove fractional part from literal, for example `42.0f32` into `42`
865 let src = src.trim_end_matches(&checked_ty.to_string());
866 let len = src.split('.').next().unwrap().len();
867 expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
869 let len = src.trim_end_matches(&checked_ty.to_string()).len();
870 expr.span.with_lo(expr.span.lo() + BytePos(len as u32))
872 if expr.precedence().order() < PREC_POSTFIX {
874 format!("{})", expected_ty)
876 expected_ty.to_string()
879 let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| {
880 if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false }
882 let is_negative_int =
883 |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..));
884 let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..));
886 let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);
888 let suggest_fallible_into_or_lhs_from =
889 |err: &mut DiagnosticBuilder<'_>, exp_to_found_is_fallible: bool| {
890 // If we know the expression the expected type is derived from, we might be able
891 // to suggest a widening conversion rather than a narrowing one (which may
892 // panic). For example, given x: u8 and y: u32, if we know the span of "x",
894 // can be given the suggestion "u32::from(x) > y" rather than
895 // "x > y.try_into().unwrap()".
896 let lhs_expr_and_src = expected_ty_expr.and_then(|expr| {
900 .span_to_snippet(expr.span)
902 .map(|src| (expr, src))
904 let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) =
905 (lhs_expr_and_src, exp_to_found_is_fallible)
908 "you can convert `{}` from `{}` to `{}`, matching the type of `{}`",
909 lhs_src, expected_ty, checked_ty, src
911 let suggestion = vec![
912 (lhs_expr.span.shrink_to_lo(), format!("{}::from(", checked_ty)),
913 (lhs_expr.span.shrink_to_hi(), ")".to_string()),
917 let msg = format!("{} and panic if the converted value doesn't fit", msg);
918 let mut suggestion = sugg.clone();
920 expr.span.shrink_to_hi(),
921 format!("{}.try_into().unwrap()", close_paren),
925 err.multipart_suggestion_verbose(
928 Applicability::MachineApplicable,
932 let suggest_to_change_suffix_or_into =
933 |err: &mut DiagnosticBuilder<'_>,
934 found_to_exp_is_fallible: bool,
935 exp_to_found_is_fallible: bool| {
937 expected_ty_expr.map(|e| self.tcx.hir().is_lhs(e.hir_id)).unwrap_or(false);
943 let always_fallible = found_to_exp_is_fallible
944 && (exp_to_found_is_fallible || expected_ty_expr.is_none());
945 let msg = if literal_is_ty_suffixed(expr) {
947 } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) {
948 // We now know that converting either the lhs or rhs is fallible. Before we
949 // suggest a fallible conversion, check if the value can never fit in the
951 let msg = format!("`{}` cannot fit into type `{}`", src, expected_ty);
954 } else if in_const_context {
955 // Do not recommend `into` or `try_into` in const contexts.
957 } else if found_to_exp_is_fallible {
958 return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible);
962 let suggestion = if literal_is_ty_suffixed(expr) {
963 suffix_suggestion.clone()
965 into_suggestion.clone()
967 err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable);
970 match (&expected_ty.kind(), &checked_ty.kind()) {
971 (&ty::Int(ref exp), &ty::Int(ref found)) => {
972 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
974 (Some(exp), Some(found)) if exp < found => (true, false),
975 (Some(exp), Some(found)) if exp > found => (false, true),
976 (None, Some(8 | 16)) => (false, true),
977 (Some(8 | 16), None) => (true, false),
978 (None, _) | (_, None) => (true, true),
981 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
984 (&ty::Uint(ref exp), &ty::Uint(ref found)) => {
985 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
987 (Some(exp), Some(found)) if exp < found => (true, false),
988 (Some(exp), Some(found)) if exp > found => (false, true),
989 (None, Some(8 | 16)) => (false, true),
990 (Some(8 | 16), None) => (true, false),
991 (None, _) | (_, None) => (true, true),
994 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
997 (&ty::Int(exp), &ty::Uint(found)) => {
998 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
1000 (Some(exp), Some(found)) if found < exp => (false, true),
1001 (None, Some(8)) => (false, true),
1004 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
1007 (&ty::Uint(exp), &ty::Int(found)) => {
1008 let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
1010 (Some(exp), Some(found)) if found > exp => (true, false),
1011 (Some(8), None) => (true, false),
1014 suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
1017 (&ty::Float(ref exp), &ty::Float(ref found)) => {
1018 if found.bit_width() < exp.bit_width() {
1019 suggest_to_change_suffix_or_into(err, false, true);
1020 } else if literal_is_ty_suffixed(expr) {
1021 err.multipart_suggestion_verbose(
1024 Applicability::MachineApplicable,
1026 } else if can_cast {
1027 // Missing try_into implementation for `f64` to `f32`
1028 err.multipart_suggestion_verbose(
1029 &format!("{}, producing the closest possible value", cast_msg),
1031 Applicability::MaybeIncorrect, // lossy conversion
1036 (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => {
1037 if literal_is_ty_suffixed(expr) {
1038 err.multipart_suggestion_verbose(
1041 Applicability::MachineApplicable,
1043 } else if can_cast {
1044 // Missing try_into implementation for `{float}` to `{integer}`
1045 err.multipart_suggestion_verbose(
1046 &format!("{}, rounding the float towards zero", msg),
1048 Applicability::MaybeIncorrect, // lossy conversion
1053 (&ty::Float(ref exp), &ty::Uint(ref found)) => {
1054 // if `found` is `None` (meaning found is `usize`), don't suggest `.into()`
1055 if exp.bit_width() > found.bit_width().unwrap_or(256) {
1056 err.multipart_suggestion_verbose(
1058 "{}, producing the floating point representation of the integer",
1062 Applicability::MachineApplicable,
1064 } else if literal_is_ty_suffixed(expr) {
1065 err.multipart_suggestion_verbose(
1068 Applicability::MachineApplicable,
1071 // Missing try_into implementation for `{integer}` to `{float}`
1072 err.multipart_suggestion_verbose(
1074 "{}, producing the floating point representation of the integer,
1075 rounded if necessary",
1079 Applicability::MaybeIncorrect, // lossy conversion
1084 (&ty::Float(ref exp), &ty::Int(ref found)) => {
1085 // if `found` is `None` (meaning found is `isize`), don't suggest `.into()`
1086 if exp.bit_width() > found.bit_width().unwrap_or(256) {
1087 err.multipart_suggestion_verbose(
1089 "{}, producing the floating point representation of the integer",
1093 Applicability::MachineApplicable,
1095 } else if literal_is_ty_suffixed(expr) {
1096 err.multipart_suggestion_verbose(
1099 Applicability::MachineApplicable,
1102 // Missing try_into implementation for `{integer}` to `{float}`
1103 err.multipart_suggestion_verbose(
1105 "{}, producing the floating point representation of the integer, \
1106 rounded if necessary",
1110 Applicability::MaybeIncorrect, // lossy conversion
1119 // Report the type inferred by the return statement.
1120 fn report_closure_inferred_return_type(
1122 err: &mut DiagnosticBuilder<'_>,
1125 if let Some(sp) = self.ret_coercion_span.get() {
1126 // If the closure has an explicit return type annotation, or if
1127 // the closure's return type has been inferred from outside
1128 // requirements (such as an Fn* trait bound), then a type error
1129 // may occur at the first return expression we see in the closure
1130 // (if it conflicts with the declared return type). Skip adding a
1131 // note in this case, since it would be incorrect.
1132 if !self.return_type_pre_known {
1136 "return type inferred to be `{}` here",
1137 self.resolve_vars_if_possible(expected)