mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Rollup merge of #123924 - compiler-errors:tuple-sugg, r=estebank
Fix various bugs in `ty_kind_suggestion` Consolidates two implementations of `ty_kind_suggestion` Fixes some misuse of the empty param-env Fixes a problem where we suggested `(42)` instead of `(42,)` for tuple suggestions Suggest a value when `return;`, making it consistent with `break;` Fixes #123906
This commit is contained in:
commit
20a5fb3b4a
@ -671,68 +671,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
err
|
||||
}
|
||||
|
||||
fn ty_kind_suggestion(&self, ty: Ty<'tcx>) -> Option<String> {
|
||||
// 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::<Option<Vec<String>>>()?
|
||||
.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;
|
||||
};
|
||||
|
||||
|
@ -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<String> {
|
||||
// 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::<Option<Vec<String>>>()?
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
@ -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, ..) => {
|
||||
|
@ -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",
|
||||
|
@ -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)]
|
||||
|
@ -4540,6 +4540,61 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
fn ty_kind_suggestion(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option<String> {
|
||||
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::<Option<Vec<String>>>()?
|
||||
.join(", "),
|
||||
if tys.len() == 1 { "," } else { "" }
|
||||
),
|
||||
_ => "value".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a hint to add a missing borrow or remove an unnecessary one.
|
||||
|
8
tests/ui/consts/value-suggestion-ice-123906.rs
Normal file
8
tests/ui/consts/value-suggestion-ice-123906.rs
Normal file
@ -0,0 +1,8 @@
|
||||
fn as_chunks<const N: usize>() -> [u8; N] {
|
||||
loop {
|
||||
break;
|
||||
//~^ ERROR mismatched types
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
18
tests/ui/consts/value-suggestion-ice-123906.stderr
Normal file
18
tests/ui/consts/value-suggestion-ice-123906.stderr
Normal file
@ -0,0 +1,18 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/value-suggestion-ice-123906.rs:3:9
|
||||
|
|
||||
LL | fn as_chunks<const N: usize>() -> [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`.
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
6
tests/ui/return/suggest-a-value.rs
Normal file
6
tests/ui/return/suggest-a-value.rs
Normal file
@ -0,0 +1,6 @@
|
||||
fn test() -> (i32,) {
|
||||
return;
|
||||
//~^ ERROR `return;` in a function whose return type is not `()`
|
||||
}
|
||||
|
||||
fn main() {}
|
16
tests/ui/return/suggest-a-value.stderr
Normal file
16
tests/ui/return/suggest-a-value.stderr
Normal file
@ -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`.
|
Loading…
Reference in New Issue
Block a user