Add help message suggesting explicit reference cast for From/TryFrom
Make fmt check happy
This commit is contained in:
parent
c4dc07385e
commit
ac159dd9bd
3 changed files with 203 additions and 0 deletions
|
|
@ -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>,
|
||||
|
|
|
|||
50
tests/ui/traits/explicit-reference-cast.rs
Normal file
50
tests/ui/traits/explicit-reference-cast.rs
Normal 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`
|
||||
}
|
||||
68
tests/ui/traits/explicit-reference-cast.stderr
Normal file
68
tests/ui/traits/explicit-reference-cast.stderr
Normal 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`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue