Tweak E0277 when predicate comes indirectly from ?
When a `?` operation requires an `Into` conversion with additional bounds (like having a concrete error but wanting to convert to a trait object), we handle it speficically and provide the same kind of information we give other `?` related errors. ``` error[E0277]: `?` couldn't convert the error: `E: std::error::Error` is not satisfied --> $DIR/bad-question-mark-on-trait-object.rs:5:13 | LL | fn foo() -> Result<(), Box<dyn std::error::Error>> { | -------------------------------------- required `E: std::error::Error` because of this LL | Ok(bar()?) | ^ the trait `std::error::Error` is not implemented for `E` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = note: required for `Box<dyn std::error::Error>` to implement `From<E>` ``` Avoid talking about `FromResidual` when other more relevant information is being given, particularly from `rust_on_unimplemented`.
This commit is contained in:
parent
28b83ee596
commit
e565eeed78
11 changed files with 103 additions and 65 deletions
|
@ -192,19 +192,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
|
||||
let have_alt_message = message.is_some() || label.is_some();
|
||||
let is_try_conversion = self.is_try_conversion(span, main_trait_predicate.def_id());
|
||||
let is_question_mark = matches!(
|
||||
root_obligation.cause.code().peel_derives(),
|
||||
ObligationCauseCode::QuestionMark,
|
||||
) && !(
|
||||
self.tcx.is_diagnostic_item(sym::FromResidual, main_trait_predicate.def_id())
|
||||
|| self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::Try)
|
||||
);
|
||||
let is_unsize =
|
||||
self.tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Unsize);
|
||||
let question_mark_message = "the question mark operation (`?`) implicitly \
|
||||
performs a conversion on the error value \
|
||||
using the `From` trait";
|
||||
let (message, notes, append_const_msg) = if is_try_conversion {
|
||||
// We have a `-> Result<_, E1>` and `gives_E2()?`.
|
||||
(
|
||||
Some(format!(
|
||||
"`?` couldn't convert the error to `{}`",
|
||||
main_trait_predicate.skip_binder().self_ty(),
|
||||
)),
|
||||
vec![
|
||||
"the question mark operation (`?`) implicitly performs a \
|
||||
conversion on the error value using the `From` trait"
|
||||
.to_owned(),
|
||||
],
|
||||
vec![question_mark_message.to_owned()],
|
||||
Some(AppendConstMessage::Default),
|
||||
)
|
||||
} else if is_question_mark {
|
||||
// Similar to the case above, but in this case the conversion is for a
|
||||
// trait object: `-> Result<_, Box<dyn Error>` and `gives_E()?` when
|
||||
// `E: Error` isn't met.
|
||||
(
|
||||
Some(format!(
|
||||
"`?` couldn't convert the error: `{main_trait_predicate}` is \
|
||||
not satisfied",
|
||||
)),
|
||||
vec![question_mark_message.to_owned()],
|
||||
Some(AppendConstMessage::Default),
|
||||
)
|
||||
} else {
|
||||
|
@ -220,8 +239,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
&mut long_ty_file,
|
||||
);
|
||||
|
||||
let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::TransmuteTrait)
|
||||
{
|
||||
let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(
|
||||
main_trait_predicate.def_id(),
|
||||
LangItem::TransmuteTrait,
|
||||
) {
|
||||
// Recompute the safe transmute reason and use that for the error reporting
|
||||
match self.get_safe_transmute_error_and_reason(
|
||||
obligation.clone(),
|
||||
|
@ -249,18 +270,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
*err.long_ty_path() = long_ty_file;
|
||||
|
||||
let mut suggested = false;
|
||||
if is_try_conversion {
|
||||
if is_try_conversion || is_question_mark {
|
||||
suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
|
||||
}
|
||||
|
||||
if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) {
|
||||
err.span_label(
|
||||
ret_span,
|
||||
format!(
|
||||
"expected `{}` because of this",
|
||||
main_trait_predicate.skip_binder().self_ty()
|
||||
),
|
||||
);
|
||||
if let Some(ret_span) = self.return_type_span(&obligation) {
|
||||
if is_try_conversion {
|
||||
err.span_label(
|
||||
ret_span,
|
||||
format!(
|
||||
"expected `{}` because of this",
|
||||
main_trait_predicate.skip_binder().self_ty()
|
||||
),
|
||||
);
|
||||
} else if is_question_mark {
|
||||
err.span_label(ret_span, format!("required `{main_trait_predicate}` because of this"));
|
||||
}
|
||||
}
|
||||
|
||||
if tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Tuple) {
|
||||
|
@ -302,10 +327,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
// If it has a custom `#[rustc_on_unimplemented]`
|
||||
// error message, let's display it as the label!
|
||||
err.span_label(span, s);
|
||||
if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) {
|
||||
if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_))
|
||||
// When the self type is a type param We don't need to "the trait
|
||||
// `std::marker::Sized` is not implemented for `T`" as we will point
|
||||
// at the type param with a label to suggest constraining it.
|
||||
&& !self.tcx.is_diagnostic_item(sym::FromResidual, leaf_trait_predicate.def_id())
|
||||
// Don't say "the trait `FromResidual<Option<Infallible>>` is
|
||||
// not implemented for `Result<T, E>`".
|
||||
{
|
||||
err.help(explanation);
|
||||
}
|
||||
} else if let Some(custom_explanation) = safe_transmute_explanation {
|
||||
|
@ -2035,6 +2064,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
return false;
|
||||
}
|
||||
if let &[cand] = &candidates[..] {
|
||||
if self.tcx.is_diagnostic_item(sym::FromResidual, cand.def_id)
|
||||
&& !self.tcx.features().enabled(sym::try_trait_v2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let (desc, mention_castable) =
|
||||
match (cand.self_ty().kind(), trait_pred.self_ty().skip_binder().kind()) {
|
||||
(ty::FnPtr(..), ty::FnDef(..)) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue