Skip to content

Commit 37d354a

Browse files
Rollup merge of rust-lang#151954 - Muhtasim-Rasheed:issue-109829-help-message, r=eholk
Add help message suggesting explicit reference cast for From/TryFrom Closes rust-lang#109829 Improves E0277 diagnostics when a `From` or `TryFrom` implementation is expected, but the provided type is a reference that can be explicitly cast to a type the trait can convert from.
2 parents f21b4c0 + ac159dd commit 37d354a

3 files changed

Lines changed: 203 additions & 0 deletions

File tree

compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,28 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
277277
};
278278

279279
let mut err = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg);
280+
281+
let trait_def_id = main_trait_predicate.def_id();
282+
if self.tcx.is_diagnostic_item(sym::From, trait_def_id)
283+
|| self.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id)
284+
{
285+
let found_ty = leaf_trait_predicate.skip_binder().trait_ref.args.type_at(1);
286+
let ty = main_trait_predicate.skip_binder().self_ty();
287+
if let Some(cast_ty) = self.find_explicit_cast_type(
288+
obligation.param_env,
289+
found_ty,
290+
ty,
291+
) {
292+
let found_ty_str = self.tcx.short_string(found_ty, &mut long_ty_file);
293+
let cast_ty_str = self.tcx.short_string(cast_ty, &mut long_ty_file);
294+
err.help(
295+
format!(
296+
"consider casting the `{found_ty_str}` value to `{cast_ty_str}`",
297+
),
298+
);
299+
}
300+
}
301+
280302
*err.long_ty_path() = long_ty_file;
281303

282304
let mut suggested = false;
@@ -2932,6 +2954,69 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
29322954
})
29332955
}
29342956

2957+
/// If `found_ty` is a reference that can be explicitly cast to another reference type for which
2958+
/// a `From` / `TryFrom` impl exists for `self_ty`, return that type.
2959+
fn find_explicit_cast_type(
2960+
&self,
2961+
param_env: ty::ParamEnv<'tcx>,
2962+
found_ty: Ty<'tcx>,
2963+
self_ty: Ty<'tcx>,
2964+
) -> Option<Ty<'tcx>> {
2965+
let ty::Ref(region, inner_ty, mutbl) = *found_ty.kind() else {
2966+
return None;
2967+
};
2968+
2969+
let mut derefs = (self.autoderef_steps)(inner_ty).into_iter();
2970+
derefs.next(); // skip the first one, which is inner_ty itself
2971+
let deref_target = derefs.into_iter().next()?.0;
2972+
2973+
let cast_ty = Ty::new_ref(self.tcx, region, deref_target, mutbl);
2974+
2975+
let Some(from_def_id) = self.tcx.get_diagnostic_item(sym::From) else {
2976+
return None;
2977+
};
2978+
let Some(try_from_def_id) = self.tcx.get_diagnostic_item(sym::TryFrom) else {
2979+
return None;
2980+
};
2981+
2982+
if self.has_impl_for_type(
2983+
param_env,
2984+
ty::TraitRef::new(
2985+
self.tcx,
2986+
from_def_id,
2987+
self.tcx.mk_args(&[self_ty.into(), cast_ty.into()]),
2988+
),
2989+
) {
2990+
Some(cast_ty)
2991+
} else if self.has_impl_for_type(
2992+
param_env,
2993+
ty::TraitRef::new(
2994+
self.tcx,
2995+
try_from_def_id,
2996+
self.tcx.mk_args(&[self_ty.into(), cast_ty.into()]),
2997+
),
2998+
) {
2999+
Some(cast_ty)
3000+
} else {
3001+
None
3002+
}
3003+
}
3004+
3005+
fn has_impl_for_type(
3006+
&self,
3007+
param_env: ty::ParamEnv<'tcx>,
3008+
trait_ref: ty::TraitRef<'tcx>,
3009+
) -> bool {
3010+
let obligation = Obligation::new(
3011+
self.tcx,
3012+
ObligationCause::dummy(),
3013+
param_env,
3014+
ty::TraitPredicate { trait_ref, polarity: ty::PredicatePolarity::Positive },
3015+
);
3016+
3017+
self.predicate_must_hold_modulo_regions(&obligation)
3018+
}
3019+
29353020
fn add_tuple_trait_message(
29363021
&self,
29373022
obligation_cause_code: &ObligationCauseCode<'tcx>,
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// compile-fail
2+
3+
use std::convert::TryFrom;
4+
use std::path::{Path, PathBuf};
5+
6+
pub struct ToolA(PathBuf);
7+
//~^ HELP the trait `From<&PathBuf>` is not implemented for `ToolA`
8+
9+
impl From<&Path> for ToolA {
10+
//~^ HELP the following other types implement trait `From<T>`
11+
fn from(p: &Path) -> ToolA {
12+
ToolA(p.to_path_buf())
13+
}
14+
}
15+
16+
// Add a different From<T> impl to ensure we suggest the correct cast
17+
impl From<&str> for ToolA {
18+
fn from(s: &str) -> ToolA {
19+
ToolA(PathBuf::from(s))
20+
}
21+
}
22+
23+
pub struct ToolB(PathBuf);
24+
//~^ HELP the trait `From<&PathBuf>` is not implemented for `ToolB`
25+
//~| HELP the trait `From<&PathBuf>` is not implemented for `ToolB`
26+
27+
impl TryFrom<&Path> for ToolB {
28+
//~^ HELP the trait `TryFrom<&PathBuf>` is not implemented for `ToolB`
29+
//~| HELP the trait `TryFrom<&PathBuf>` is not implemented for `ToolB`
30+
type Error = ();
31+
32+
fn try_from(p: &Path) -> Result<ToolB, ()> {
33+
Ok(ToolB(p.to_path_buf()))
34+
}
35+
}
36+
37+
fn main() {
38+
let path = PathBuf::new();
39+
40+
let _ = ToolA::from(&path);
41+
//~^ ERROR the trait bound `ToolA: From<&PathBuf>` is not satisfied
42+
//~| HELP consider casting the `&PathBuf` value to `&Path`
43+
let _ = ToolB::try_from(&path);
44+
//~^ ERROR the trait bound `ToolB: TryFrom<&PathBuf>` is not satisfied
45+
//~| ERROR the trait bound `ToolB: From<&PathBuf>` is not satisfied
46+
//~| HELP consider casting the `&PathBuf` value to `&Path`
47+
//~| HELP consider casting the `&PathBuf` value to `&Path`
48+
//~| HELP for that trait implementation, expected `Path`, found `PathBuf`
49+
//~| HELP for that trait implementation, expected `Path`, found `PathBuf`
50+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
error[E0277]: the trait bound `ToolA: From<&PathBuf>` is not satisfied
2+
--> $DIR/explicit-reference-cast.rs:40:13
3+
|
4+
LL | let _ = ToolA::from(&path);
5+
| ^^^^^ unsatisfied trait bound
6+
|
7+
= help: consider casting the `&PathBuf` value to `&Path`
8+
help: the trait `From<&PathBuf>` is not implemented for `ToolA`
9+
--> $DIR/explicit-reference-cast.rs:6:1
10+
|
11+
LL | pub struct ToolA(PathBuf);
12+
| ^^^^^^^^^^^^^^^^
13+
help: the following other types implement trait `From<T>`
14+
--> $DIR/explicit-reference-cast.rs:9:1
15+
|
16+
LL | impl From<&Path> for ToolA {
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `ToolA` implements `From<&Path>`
18+
...
19+
LL | impl From<&str> for ToolA {
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ `ToolA` implements `From<&str>`
21+
22+
error[E0277]: the trait bound `ToolB: TryFrom<&PathBuf>` is not satisfied
23+
--> $DIR/explicit-reference-cast.rs:43:13
24+
|
25+
LL | let _ = ToolB::try_from(&path);
26+
| ^^^^^ unsatisfied trait bound
27+
|
28+
= help: consider casting the `&PathBuf` value to `&Path`
29+
help: the trait `From<&PathBuf>` is not implemented for `ToolB`
30+
--> $DIR/explicit-reference-cast.rs:23:1
31+
|
32+
LL | pub struct ToolB(PathBuf);
33+
| ^^^^^^^^^^^^^^^^
34+
help: the trait `TryFrom<&PathBuf>` is not implemented for `ToolB`
35+
but trait `TryFrom<&Path>` is implemented for it
36+
--> $DIR/explicit-reference-cast.rs:27:1
37+
|
38+
LL | impl TryFrom<&Path> for ToolB {
39+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40+
= help: for that trait implementation, expected `Path`, found `PathBuf`
41+
= note: required for `&PathBuf` to implement `Into<ToolB>`
42+
= note: required for `ToolB` to implement `TryFrom<&PathBuf>`
43+
44+
error[E0277]: the trait bound `ToolB: From<&PathBuf>` is not satisfied
45+
--> $DIR/explicit-reference-cast.rs:43:13
46+
|
47+
LL | let _ = ToolB::try_from(&path);
48+
| ^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
49+
|
50+
= help: consider casting the `&PathBuf` value to `&Path`
51+
help: the trait `From<&PathBuf>` is not implemented for `ToolB`
52+
--> $DIR/explicit-reference-cast.rs:23:1
53+
|
54+
LL | pub struct ToolB(PathBuf);
55+
| ^^^^^^^^^^^^^^^^
56+
help: the trait `TryFrom<&PathBuf>` is not implemented for `ToolB`
57+
but trait `TryFrom<&Path>` is implemented for it
58+
--> $DIR/explicit-reference-cast.rs:27:1
59+
|
60+
LL | impl TryFrom<&Path> for ToolB {
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
62+
= help: for that trait implementation, expected `Path`, found `PathBuf`
63+
= note: required for `&PathBuf` to implement `Into<ToolB>`
64+
= note: required for `ToolB` to implement `TryFrom<&PathBuf>`
65+
66+
error: aborting due to 3 previous errors
67+
68+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)