Add help message suggesting explicit reference cast for From/TryFrom

Make fmt check happy
This commit is contained in:
Muhtasim-Rasheed 2026-02-01 19:01:19 +06:00
parent c4dc07385e
commit ac159dd9bd
3 changed files with 203 additions and 0 deletions

View file

@ -277,6 +277,28 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
};
let mut err = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg);
let trait_def_id = main_trait_predicate.def_id();
if self.tcx.is_diagnostic_item(sym::From, trait_def_id)
|| self.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id)
{
let found_ty = leaf_trait_predicate.skip_binder().trait_ref.args.type_at(1);
let ty = main_trait_predicate.skip_binder().self_ty();
if let Some(cast_ty) = self.find_explicit_cast_type(
obligation.param_env,
found_ty,
ty,
) {
let found_ty_str = self.tcx.short_string(found_ty, &mut long_ty_file);
let cast_ty_str = self.tcx.short_string(cast_ty, &mut long_ty_file);
err.help(
format!(
"consider casting the `{found_ty_str}` value to `{cast_ty_str}`",
),
);
}
}
*err.long_ty_path() = long_ty_file;
let mut suggested = false;
@ -2930,6 +2952,69 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
})
}
/// If `found_ty` is a reference that can be explicitly cast to another reference type for which
/// a `From` / `TryFrom` impl exists for `self_ty`, return that type.
fn find_explicit_cast_type(
&self,
param_env: ty::ParamEnv<'tcx>,
found_ty: Ty<'tcx>,
self_ty: Ty<'tcx>,
) -> Option<Ty<'tcx>> {
let ty::Ref(region, inner_ty, mutbl) = *found_ty.kind() else {
return None;
};
let mut derefs = (self.autoderef_steps)(inner_ty).into_iter();
derefs.next(); // skip the first one, which is inner_ty itself
let deref_target = derefs.into_iter().next()?.0;
let cast_ty = Ty::new_ref(self.tcx, region, deref_target, mutbl);
let Some(from_def_id) = self.tcx.get_diagnostic_item(sym::From) else {
return None;
};
let Some(try_from_def_id) = self.tcx.get_diagnostic_item(sym::TryFrom) else {
return None;
};
if self.has_impl_for_type(
param_env,
ty::TraitRef::new(
self.tcx,
from_def_id,
self.tcx.mk_args(&[self_ty.into(), cast_ty.into()]),
),
) {
Some(cast_ty)
} else if self.has_impl_for_type(
param_env,
ty::TraitRef::new(
self.tcx,
try_from_def_id,
self.tcx.mk_args(&[self_ty.into(), cast_ty.into()]),
),
) {
Some(cast_ty)
} else {
None
}
}
fn has_impl_for_type(
&self,
param_env: ty::ParamEnv<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
) -> bool {
let obligation = Obligation::new(
self.tcx,
ObligationCause::dummy(),
param_env,
ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive },
);
self.predicate_must_hold_modulo_regions(&obligation)
}
fn add_tuple_trait_message(
&self,
obligation_cause_code: &ObligationCauseCode<'tcx>,

View file

@ -0,0 +1,50 @@
// compile-fail
use std::convert::TryFrom;
use std::path::{Path, PathBuf};
pub struct ToolA(PathBuf);
//~^ HELP the trait `From<&PathBuf>` is not implemented for `ToolA`
impl From<&Path> for ToolA {
//~^ HELP the following other types implement trait `From<T>`
fn from(p: &Path) -> ToolA {
ToolA(p.to_path_buf())
}
}
// Add a different From<T> impl to ensure we suggest the correct cast
impl From<&str> for ToolA {
fn from(s: &str) -> ToolA {
ToolA(PathBuf::from(s))
}
}
pub struct ToolB(PathBuf);
//~^ HELP the trait `From<&PathBuf>` is not implemented for `ToolB`
//~| HELP the trait `From<&PathBuf>` is not implemented for `ToolB`
impl TryFrom<&Path> for ToolB {
//~^ HELP the trait `TryFrom<&PathBuf>` is not implemented for `ToolB`
//~| HELP the trait `TryFrom<&PathBuf>` is not implemented for `ToolB`
type Error = ();
fn try_from(p: &Path) -> Result<ToolB, ()> {
Ok(ToolB(p.to_path_buf()))
}
}
fn main() {
let path = PathBuf::new();
let _ = ToolA::from(&path);
//~^ ERROR the trait bound `ToolA: From<&PathBuf>` is not satisfied
//~| HELP consider casting the `&PathBuf` value to `&Path`
let _ = ToolB::try_from(&path);
//~^ ERROR the trait bound `ToolB: TryFrom<&PathBuf>` is not satisfied
//~| ERROR the trait bound `ToolB: From<&PathBuf>` is not satisfied
//~| HELP consider casting the `&PathBuf` value to `&Path`
//~| HELP consider casting the `&PathBuf` value to `&Path`
//~| HELP for that trait implementation, expected `Path`, found `PathBuf`
//~| HELP for that trait implementation, expected `Path`, found `PathBuf`
}

View file

@ -0,0 +1,68 @@
error[E0277]: the trait bound `ToolA: From<&PathBuf>` is not satisfied
--> $DIR/explicit-reference-cast.rs:40:13
|
LL | let _ = ToolA::from(&path);
| ^^^^^ unsatisfied trait bound
|
= help: consider casting the `&PathBuf` value to `&Path`
help: the trait `From<&PathBuf>` is not implemented for `ToolA`
--> $DIR/explicit-reference-cast.rs:6:1
|
LL | pub struct ToolA(PathBuf);
| ^^^^^^^^^^^^^^^^
help: the following other types implement trait `From<T>`
--> $DIR/explicit-reference-cast.rs:9:1
|
LL | impl From<&Path> for ToolA {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `ToolA` implements `From<&Path>`
...
LL | impl From<&str> for ToolA {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ `ToolA` implements `From<&str>`
error[E0277]: the trait bound `ToolB: TryFrom<&PathBuf>` is not satisfied
--> $DIR/explicit-reference-cast.rs:43:13
|
LL | let _ = ToolB::try_from(&path);
| ^^^^^ unsatisfied trait bound
|
= help: consider casting the `&PathBuf` value to `&Path`
help: the trait `From<&PathBuf>` is not implemented for `ToolB`
--> $DIR/explicit-reference-cast.rs:23:1
|
LL | pub struct ToolB(PathBuf);
| ^^^^^^^^^^^^^^^^
help: the trait `TryFrom<&PathBuf>` is not implemented for `ToolB`
but trait `TryFrom<&Path>` is implemented for it
--> $DIR/explicit-reference-cast.rs:27:1
|
LL | impl TryFrom<&Path> for ToolB {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: for that trait implementation, expected `Path`, found `PathBuf`
= note: required for `&PathBuf` to implement `Into<ToolB>`
= note: required for `ToolB` to implement `TryFrom<&PathBuf>`
error[E0277]: the trait bound `ToolB: From<&PathBuf>` is not satisfied
--> $DIR/explicit-reference-cast.rs:43:13
|
LL | let _ = ToolB::try_from(&path);
| ^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
= help: consider casting the `&PathBuf` value to `&Path`
help: the trait `From<&PathBuf>` is not implemented for `ToolB`
--> $DIR/explicit-reference-cast.rs:23:1
|
LL | pub struct ToolB(PathBuf);
| ^^^^^^^^^^^^^^^^
help: the trait `TryFrom<&PathBuf>` is not implemented for `ToolB`
but trait `TryFrom<&Path>` is implemented for it
--> $DIR/explicit-reference-cast.rs:27:1
|
LL | impl TryFrom<&Path> for ToolB {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: for that trait implementation, expected `Path`, found `PathBuf`
= note: required for `&PathBuf` to implement `Into<ToolB>`
= note: required for `ToolB` to implement `TryFrom<&PathBuf>`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`.