diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 48cd9c268a119..0a4f32c958502 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -671,68 +671,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { err } - fn ty_kind_suggestion(&self, ty: Ty<'tcx>) -> Option { - // Keep in sync with `rustc_hir_analysis/src/check/mod.rs:ty_kind_suggestion`. - // FIXME: deduplicate the above. - let tcx = self.infcx.tcx; - let implements_default = |ty| { - let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { - return false; - }; - self.infcx - .type_implements_trait(default_trait, [ty], self.param_env) - .must_apply_modulo_regions() - }; - - Some(match ty.kind() { - ty::Never | ty::Error(_) => return None, - ty::Bool => "false".to_string(), - ty::Char => "\'x\'".to_string(), - ty::Int(_) | ty::Uint(_) => "42".into(), - ty::Float(_) => "3.14159".into(), - ty::Slice(_) => "[]".to_string(), - ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => { - "vec![]".to_string() - } - ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::String) => { - "String::new()".to_string() - } - ty::Adt(def, args) if def.is_box() => { - format!("Box::new({})", self.ty_kind_suggestion(args[0].expect_ty())?) - } - ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Option) => { - "None".to_string() - } - ty::Adt(def, args) if Some(def.did()) == tcx.get_diagnostic_item(sym::Result) => { - format!("Ok({})", self.ty_kind_suggestion(args[0].expect_ty())?) - } - ty::Adt(_, _) if implements_default(ty) => "Default::default()".to_string(), - ty::Ref(_, ty, mutability) => { - if let (ty::Str, hir::Mutability::Not) = (ty.kind(), mutability) { - "\"\"".to_string() - } else { - let Some(ty) = self.ty_kind_suggestion(*ty) else { - return None; - }; - format!("&{}{ty}", mutability.prefix_str()) - } - } - ty::Array(ty, len) => format!( - "[{}; {}]", - self.ty_kind_suggestion(*ty)?, - len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()), - ), - ty::Tuple(tys) => format!( - "({})", - tys.iter() - .map(|ty| self.ty_kind_suggestion(ty)) - .collect::>>()? - .join(", ") - ), - _ => "value".to_string(), - }) - } - fn suggest_assign_value( &self, err: &mut Diag<'_>, @@ -742,7 +680,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let ty = moved_place.ty(self.body, self.infcx.tcx).ty; debug!("ty: {:?}, kind: {:?}", ty, ty.kind()); - let Some(assign_value) = self.ty_kind_suggestion(ty) else { + let Some(assign_value) = self.infcx.err_ctxt().ty_kind_suggestion(self.param_env, ty) + else { return; }; diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 024a0433b8cf6..eb0ffc19d4540 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -81,7 +81,6 @@ use rustc_errors::ErrorGuaranteed; use rustc_errors::{pluralize, struct_span_code_err, Diag}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::Mutability; use rustc_index::bit_set::BitSet; use rustc_infer::infer::error_reporting::ObligationCauseExt as _; use rustc_infer::infer::outlives::env::OutlivesEnvironment; @@ -96,8 +95,9 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{def_id::CRATE_DEF_ID, BytePos, Span, Symbol, DUMMY_SP}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; +use rustc_trait_selection::traits::error_reporting::suggestions::{ + ReturnsVisitor, TypeErrCtxtExt as _, +}; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::ObligationCtxt; @@ -467,67 +467,6 @@ fn fn_sig_suggestion<'tcx>( ) } -pub fn ty_kind_suggestion<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option { - // Keep in sync with `rustc_borrowck/src/diagnostics/conflict_errors.rs:ty_kind_suggestion`. - // FIXME: deduplicate the above. - let implements_default = |ty| { - let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { - return false; - }; - let infcx = tcx.infer_ctxt().build(); - infcx - .type_implements_trait(default_trait, [ty], ty::ParamEnv::reveal_all()) - .must_apply_modulo_regions() - }; - Some(match ty.kind() { - ty::Never | ty::Error(_) => return None, - ty::Bool => "false".to_string(), - ty::Char => "\'x\'".to_string(), - ty::Int(_) | ty::Uint(_) => "42".into(), - ty::Float(_) => "3.14159".into(), - ty::Slice(_) => "[]".to_string(), - ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => { - "vec![]".to_string() - } - ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::String) => { - "String::new()".to_string() - } - ty::Adt(def, args) if def.is_box() => { - format!("Box::new({})", ty_kind_suggestion(args[0].expect_ty(), tcx)?) - } - ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Option) => { - "None".to_string() - } - ty::Adt(def, args) if Some(def.did()) == tcx.get_diagnostic_item(sym::Result) => { - format!("Ok({})", ty_kind_suggestion(args[0].expect_ty(), tcx)?) - } - ty::Adt(_, _) if implements_default(ty) => "Default::default()".to_string(), - ty::Ref(_, ty, mutability) => { - if let (ty::Str, Mutability::Not) = (ty.kind(), mutability) { - "\"\"".to_string() - } else { - let Some(ty) = ty_kind_suggestion(*ty, tcx) else { - return None; - }; - format!("&{}{ty}", mutability.prefix_str()) - } - } - ty::Array(ty, len) => format!( - "[{}; {}]", - ty_kind_suggestion(*ty, tcx)?, - len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()), - ), - ty::Tuple(tys) => format!( - "({})", - tys.iter() - .map(|ty| ty_kind_suggestion(ty, tcx)) - .collect::>>()? - .join(", ") - ), - _ => "value".to_string(), - }) -} - /// Return placeholder code for the given associated item. /// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a /// structured suggestion. @@ -562,7 +501,12 @@ fn suggestion_signature<'tcx>( } ty::AssocKind::Const => { let ty = tcx.type_of(assoc.def_id).instantiate_identity(); - let val = ty_kind_suggestion(ty, tcx).unwrap_or_else(|| "value".to_string()); + let val = tcx + .infer_ctxt() + .build() + .err_ctxt() + .ty_kind_suggestion(tcx.param_env(assoc.def_id), ty) + .unwrap_or_else(|| "value".to_string()); format!("const {}: {} = {};", assoc.name, ty, val) } } diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 720f4927ea892..079cca8240895 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -63,6 +63,7 @@ use rustc_span::DesugaringKind; use rustc_span::{BytePos, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::TraitEngineExt as _; @@ -1616,6 +1617,15 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { E0069, "`return;` in a function whose return type is not `()`" ); + if let Some(value) = fcx.err_ctxt().ty_kind_suggestion(fcx.param_env, found) + { + err.span_suggestion_verbose( + cause.span.shrink_to_hi(), + "give the `return` a value of the expected type", + format!(" {value}"), + Applicability::HasPlaceholders, + ); + } err.span_label(cause.span, "return type is not `()`"); } ObligationCauseCode::BlockTailExpression(blk_id, ..) => { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 8f30c3fd37743..64590ca542d12 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -35,7 +35,6 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, HirId, QPath}; -use rustc_hir_analysis::check::ty_kind_suggestion; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -57,6 +56,7 @@ use rustc_span::Span; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; use rustc_target::spec::abi::Abi::RustIntrinsic; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCauseCode}; @@ -694,7 +694,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let error = Some(Sorts(ExpectedFound { expected: ty, found: e_ty })); self.annotate_loop_expected_due_to_inference(err, expr, error); - if let Some(val) = ty_kind_suggestion(ty, tcx) { + if let Some(val) = + self.err_ctxt().ty_kind_suggestion(self.param_env, ty) + { err.span_suggestion_verbose( expr.span.shrink_to_hi(), "give the `break` a value of the expected type", diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index b5fb710e4cc42..057d00aeae81e 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -22,6 +22,7 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(extract_if)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(option_take_if)] #![feature(never_type)] diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 2067956d0f5f0..48bb527c45d15 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -4540,6 +4540,61 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { Applicability::MachineApplicable, ); } + + fn ty_kind_suggestion(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option { + let tcx = self.infcx.tcx; + let implements_default = |ty| { + let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { + return false; + }; + self.type_implements_trait(default_trait, [ty], param_env).must_apply_modulo_regions() + }; + + Some(match ty.kind() { + ty::Never | ty::Error(_) => return None, + ty::Bool => "false".to_string(), + ty::Char => "\'x\'".to_string(), + ty::Int(_) | ty::Uint(_) => "42".into(), + ty::Float(_) => "3.14159".into(), + ty::Slice(_) => "[]".to_string(), + ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => { + "vec![]".to_string() + } + ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::String) => { + "String::new()".to_string() + } + ty::Adt(def, args) if def.is_box() => { + format!("Box::new({})", self.ty_kind_suggestion(param_env, args[0].expect_ty())?) + } + ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Option) => { + "None".to_string() + } + ty::Adt(def, args) if Some(def.did()) == tcx.get_diagnostic_item(sym::Result) => { + format!("Ok({})", self.ty_kind_suggestion(param_env, args[0].expect_ty())?) + } + ty::Adt(_, _) if implements_default(ty) => "Default::default()".to_string(), + ty::Ref(_, ty, mutability) => { + if let (ty::Str, hir::Mutability::Not) = (ty.kind(), mutability) { + "\"\"".to_string() + } else { + let ty = self.ty_kind_suggestion(param_env, *ty)?; + format!("&{}{ty}", mutability.prefix_str()) + } + } + ty::Array(ty, len) if let Some(len) = len.try_eval_target_usize(tcx, param_env) => { + format!("[{}; {}]", self.ty_kind_suggestion(param_env, *ty)?, len) + } + ty::Tuple(tys) => format!( + "({}{})", + tys.iter() + .map(|ty| self.ty_kind_suggestion(param_env, ty)) + .collect::>>()? + .join(", "), + if tys.len() == 1 { "," } else { "" } + ), + _ => "value".to_string(), + }) + } } /// Add a hint to add a missing borrow or remove an unnecessary one. diff --git a/tests/ui/consts/value-suggestion-ice-123906.rs b/tests/ui/consts/value-suggestion-ice-123906.rs new file mode 100644 index 0000000000000..531bbc158524a --- /dev/null +++ b/tests/ui/consts/value-suggestion-ice-123906.rs @@ -0,0 +1,8 @@ +fn as_chunks() -> [u8; N] { + loop { + break; + //~^ ERROR mismatched types + } +} + +fn main() {} diff --git a/tests/ui/consts/value-suggestion-ice-123906.stderr b/tests/ui/consts/value-suggestion-ice-123906.stderr new file mode 100644 index 0000000000000..8e2c316400d60 --- /dev/null +++ b/tests/ui/consts/value-suggestion-ice-123906.stderr @@ -0,0 +1,18 @@ +error[E0308]: mismatched types + --> $DIR/value-suggestion-ice-123906.rs:3:9 + | +LL | fn as_chunks() -> [u8; N] { + | ------- expected `[u8; ]` because of this return type +LL | loop { + | ---- this loop is expected to be of type `[u8; N]` +LL | break; + | ^^^^^ expected `[u8; N]`, found `()` + | +help: give the `break` a value of the expected type + | +LL | break value; + | +++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/error-codes/E0069.stderr b/tests/ui/error-codes/E0069.stderr index 20ff8c258a004..ef6c411ae5892 100644 --- a/tests/ui/error-codes/E0069.stderr +++ b/tests/ui/error-codes/E0069.stderr @@ -5,6 +5,11 @@ LL | fn foo() -> u8 { | -- expected `u8` because of this return type LL | return; | ^^^^^^ return type is not `()` + | +help: give the `return` a value of the expected type + | +LL | return 42; + | ++ error: aborting due to 1 previous error diff --git a/tests/ui/ret-non-nil.stderr b/tests/ui/ret-non-nil.stderr index 17567c6016a3a..802900e61a30e 100644 --- a/tests/ui/ret-non-nil.stderr +++ b/tests/ui/ret-non-nil.stderr @@ -5,6 +5,11 @@ LL | fn g() -> isize { return; } | ----- ^^^^^^ return type is not `()` | | | expected `isize` because of this return type + | +help: give the `return` a value of the expected type + | +LL | fn g() -> isize { return 42; } + | ++ error: aborting due to 1 previous error diff --git a/tests/ui/return/suggest-a-value.rs b/tests/ui/return/suggest-a-value.rs new file mode 100644 index 0000000000000..7d23c0c44b781 --- /dev/null +++ b/tests/ui/return/suggest-a-value.rs @@ -0,0 +1,6 @@ +fn test() -> (i32,) { + return; + //~^ ERROR `return;` in a function whose return type is not `()` +} + +fn main() {} diff --git a/tests/ui/return/suggest-a-value.stderr b/tests/ui/return/suggest-a-value.stderr new file mode 100644 index 0000000000000..573ade7b712d8 --- /dev/null +++ b/tests/ui/return/suggest-a-value.stderr @@ -0,0 +1,16 @@ +error[E0069]: `return;` in a function whose return type is not `()` + --> $DIR/suggest-a-value.rs:2:5 + | +LL | fn test() -> (i32,) { + | ------ expected `(i32,)` because of this return type +LL | return; + | ^^^^^^ return type is not `()` + | +help: give the `return` a value of the expected type + | +LL | return (42,); + | +++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0069`.