Rollup merge of #62772 - estebank:trait-bound, r=matthewjasper
Suggest trait bound on type parameter when it is unconstrained Given ``` trait Foo { fn method(&self) {} } fn call_method<T>(x: &T) { x.method() } ``` suggest constraining `T` with `Foo`. Fix #21673, fix #41030.
This commit is contained in:
commit
e933f54793
@ -643,13 +643,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_traits_to_import<'b>(&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: ast::Ident,
|
||||
source: SelfSource<'b>,
|
||||
valid_out_of_scope_traits: Vec<DefId>) {
|
||||
fn suggest_traits_to_import<'b>(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
span: Span,
|
||||
rcvr_ty: Ty<'tcx>,
|
||||
item_name: ast::Ident,
|
||||
source: SelfSource<'b>,
|
||||
valid_out_of_scope_traits: Vec<DefId>,
|
||||
) {
|
||||
if self.suggest_valid_traits(err, valid_out_of_scope_traits) {
|
||||
return;
|
||||
}
|
||||
@ -683,30 +685,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
candidates.sort_by(|a, b| a.cmp(b).reverse());
|
||||
candidates.dedup();
|
||||
|
||||
// FIXME #21673: this help message could be tuned to the case
|
||||
// of a type parameter: suggest adding a trait bound rather
|
||||
// than implementing.
|
||||
err.help("items from traits can only be used if the trait is implemented and in scope");
|
||||
let mut msg = format!("the following {traits_define} an item `{name}`, \
|
||||
perhaps you need to implement {one_of_them}:",
|
||||
traits_define = if candidates.len() == 1 {
|
||||
"trait defines"
|
||||
} else {
|
||||
"traits define"
|
||||
},
|
||||
one_of_them = if candidates.len() == 1 {
|
||||
"it"
|
||||
} else {
|
||||
"one of them"
|
||||
},
|
||||
name = item_name);
|
||||
let param_type = match rcvr_ty.sty {
|
||||
ty::Param(param) => Some(param),
|
||||
ty::Ref(_, ty, _) => match ty.sty {
|
||||
ty::Param(param) => Some(param),
|
||||
_ => None,
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
err.help(if param_type.is_some() {
|
||||
"items from traits can only be used if the type parameter is bounded by the trait"
|
||||
} else {
|
||||
"items from traits can only be used if the trait is implemented and in scope"
|
||||
});
|
||||
let mut msg = format!(
|
||||
"the following {traits_define} an item `{name}`, perhaps you need to {action} \
|
||||
{one_of_them}:",
|
||||
traits_define = if candidates.len() == 1 {
|
||||
"trait defines"
|
||||
} else {
|
||||
"traits define"
|
||||
},
|
||||
action = if let Some(param) = param_type {
|
||||
format!("restrict type parameter `{}` with", param)
|
||||
} else {
|
||||
"implement".to_string()
|
||||
},
|
||||
one_of_them = if candidates.len() == 1 {
|
||||
"it"
|
||||
} else {
|
||||
"one of them"
|
||||
},
|
||||
name = item_name,
|
||||
);
|
||||
// Obtain the span for `param` and use it for a structured suggestion.
|
||||
let mut suggested = false;
|
||||
if let (Some(ref param), Some(ref table)) = (param_type, self.in_progress_tables) {
|
||||
let table = table.borrow();
|
||||
if let Some(did) = table.local_id_root {
|
||||
let generics = self.tcx.generics_of(did);
|
||||
let type_param = generics.type_param(param, self.tcx);
|
||||
let hir = &self.tcx.hir();
|
||||
if let Some(id) = hir.as_local_hir_id(type_param.def_id) {
|
||||
// Get the `hir::Param` to verify whether it already has any bounds.
|
||||
// We do this to avoid suggesting code that ends up as `T: FooBar`,
|
||||
// instead we suggest `T: Foo + Bar` in that case.
|
||||
let mut has_bounds = false;
|
||||
if let Node::GenericParam(ref param) = hir.get(id) {
|
||||
has_bounds = !param.bounds.is_empty();
|
||||
}
|
||||
let sp = hir.span(id);
|
||||
// `sp` only covers `T`, change it so that it covers
|
||||
// `T:` when appropriate
|
||||
let sp = if has_bounds {
|
||||
sp.to(self.tcx
|
||||
.sess
|
||||
.source_map()
|
||||
.next_point(self.tcx.sess.source_map().next_point(sp)))
|
||||
} else {
|
||||
sp
|
||||
};
|
||||
|
||||
for (i, trait_info) in candidates.iter().enumerate() {
|
||||
msg.push_str(&format!("\ncandidate #{}: `{}`",
|
||||
i + 1,
|
||||
self.tcx.def_path_str(trait_info.def_id)));
|
||||
// FIXME: contrast `t.def_id` against `param.bounds` to not suggest traits
|
||||
// already there. That can happen when the cause is that we're in a const
|
||||
// scope or associated function used as a method.
|
||||
err.span_suggestions(
|
||||
sp,
|
||||
&msg[..],
|
||||
candidates.iter().map(|t| format!(
|
||||
"{}: {}{}",
|
||||
param,
|
||||
self.tcx.def_path_str(t.def_id),
|
||||
if has_bounds { " +"} else { "" },
|
||||
)),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
suggested = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if !suggested {
|
||||
for (i, trait_info) in candidates.iter().enumerate() {
|
||||
msg.push_str(&format!(
|
||||
"\ncandidate #{}: `{}`",
|
||||
i + 1,
|
||||
self.tcx.def_path_str(trait_info.def_id),
|
||||
));
|
||||
}
|
||||
err.note(&msg[..]);
|
||||
}
|
||||
err.note(&msg[..]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,11 @@ error[E0599]: no function or associated item named `dim` found for type `D` in t
|
||||
LL | entries: [T; D::dim()],
|
||||
| ^^^ function or associated item not found in `D`
|
||||
|
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `dim`, perhaps you need to implement it:
|
||||
candidate #1: `Dim`
|
||||
= help: items from traits can only be used if the type parameter is bounded by the trait
|
||||
help: the following trait defines an item `dim`, perhaps you need to restrict type parameter `D` with it:
|
||||
|
|
||||
LL | pub struct Vector<T, D: Dim + Dim> {
|
||||
| ^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -61,9 +61,11 @@ note: the candidate is defined in the trait `ManyImplTrait`
|
||||
LL | fn is_str() -> bool {
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
= help: to disambiguate the method call, write `ManyImplTrait::is_str(t)` instead
|
||||
= help: items from traits can only be used if the trait is implemented and in scope
|
||||
= note: the following trait defines an item `is_str`, perhaps you need to implement it:
|
||||
candidate #1: `ManyImplTrait`
|
||||
= help: items from traits can only be used if the type parameter is bounded by the trait
|
||||
help: the following trait defines an item `is_str`, perhaps you need to restrict type parameter `T` with it:
|
||||
|
|
||||
LL | fn param_bound<T: ManyImplTrait + ManyImplTrait>(t: T) -> bool {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
13
src/test/ui/suggestions/issue-21673.rs
Normal file
13
src/test/ui/suggestions/issue-21673.rs
Normal file
@ -0,0 +1,13 @@
|
||||
trait Foo {
|
||||
fn method(&self) {}
|
||||
}
|
||||
|
||||
fn call_method<T: std::fmt::Debug>(x: &T) {
|
||||
x.method() //~ ERROR E0599
|
||||
}
|
||||
|
||||
fn call_method_2<T>(x: T) {
|
||||
x.method() //~ ERROR E0599
|
||||
}
|
||||
|
||||
fn main() {}
|
27
src/test/ui/suggestions/issue-21673.stderr
Normal file
27
src/test/ui/suggestions/issue-21673.stderr
Normal file
@ -0,0 +1,27 @@
|
||||
error[E0599]: no method named `method` found for type `&T` in the current scope
|
||||
--> $DIR/issue-21673.rs:6:7
|
||||
|
|
||||
LL | x.method()
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: items from traits can only be used if the type parameter is bounded by the trait
|
||||
help: the following trait defines an item `method`, perhaps you need to restrict type parameter `T` with it:
|
||||
|
|
||||
LL | fn call_method<T: Foo + std::fmt::Debug>(x: &T) {
|
||||
| ^^^^^^^^
|
||||
|
||||
error[E0599]: no method named `method` found for type `T` in the current scope
|
||||
--> $DIR/issue-21673.rs:10:7
|
||||
|
|
||||
LL | x.method()
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: items from traits can only be used if the type parameter is bounded by the trait
|
||||
help: the following trait defines an item `method`, perhaps you need to restrict type parameter `T` with it:
|
||||
|
|
||||
LL | fn call_method_2<T: Foo>(x: T) {
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
Loading…
Reference in New Issue
Block a user