Skip to content

Commit a31c5bf

Browse files
committed
Account for type parameters in bound suggestion
When encountering a missing bound that involves a type parameter, on associated functions we look at the generics to see if the type parameter is present. If so, we suggest the bound on the associated function instead of on the impl/trait. At the impl/trait doesn't have another type parameter of that name, then we don't suggest the bound at that item level. ``` error[E0277]: the trait bound `B: From<T>` is not satisfied --> $DIR/suggest-restriction-involving-type-param.rs:20:33 | LL | B { x: v.iter().map(|e| B::from(e.clone()).x).collect::<Vec<String>>().join(" ") } | ^ the trait `From<T>` is not implemented for `B` | help: consider further restricting the type | LL | pub fn from_many<T: Into<B> + Clone>(v: Vec<T>) -> Self where B: From<T> { | ++++++++++++++++ ``` Fix rust-lang#104089.
1 parent 3c1e750 commit a31c5bf

File tree

4 files changed

+140
-3
lines changed

4 files changed

+140
-3
lines changed

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

+64-3
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ use rustc_middle::ty::print::{
3333
};
3434
use rustc_middle::ty::{
3535
self, AdtKind, GenericArgs, InferTy, IsSuggestable, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable,
36-
TypeFolder, TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast,
37-
suggest_arbitrary_trait_bound, suggest_constraining_type_param,
36+
TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitableExt, TypeVisitor,
37+
TypeckResults, Upcast, UpcastFrom, suggest_arbitrary_trait_bound,
38+
suggest_constraining_type_param,
3839
};
3940
use rustc_middle::{bug, span_bug};
4041
use rustc_span::def_id::LocalDefId;
@@ -260,6 +261,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
260261
_ => (false, None),
261262
};
262263

264+
let mut v = ParamFinder { params: vec![] };
265+
// Get all type parameters from the predicate. If the predicate references a type parameter
266+
// at all, then we can only suggestion a bound on an item that has access to that parameter.
267+
v.visit_predicate(UpcastFrom::upcast_from(trait_pred, self.tcx));
268+
263269
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
264270
// don't suggest `T: Sized + ?Sized`.
265271
loop {
@@ -327,6 +333,40 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
327333
);
328334
return;
329335
}
336+
337+
hir::Node::TraitItem(hir::TraitItem {
338+
generics,
339+
kind: hir::TraitItemKind::Fn(fn_sig, ..),
340+
..
341+
})
342+
| hir::Node::ImplItem(hir::ImplItem {
343+
generics,
344+
kind: hir::ImplItemKind::Fn(fn_sig, ..),
345+
..
346+
}) if projection.is_none()
347+
&& !param_ty
348+
&& generics.params.iter().any(|param| {
349+
v.params.iter().any(|p| p.name == param.name.ident().name)
350+
}) =>
351+
{
352+
// This associated function has a generic type parameter that matches a
353+
// parameter from the trait predicate, which means that we should suggest
354+
// constraining the complex type here, and not at the trait/impl level (if
355+
// it doesn't have that type parameter). This can be something like
356+
// `Type: From<Param>`.
357+
suggest_restriction(
358+
self.tcx,
359+
body_id,
360+
generics,
361+
"the type",
362+
err,
363+
Some(fn_sig),
364+
projection,
365+
trait_pred,
366+
None,
367+
);
368+
}
369+
330370
hir::Node::Item(hir::Item {
331371
kind:
332372
hir::ItemKind::Trait(_, _, generics, ..)
@@ -425,7 +465,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
425465
| hir::ItemKind::Const(_, generics, _)
426466
| hir::ItemKind::TraitAlias(generics, _),
427467
..
428-
}) if !param_ty => {
468+
}) if !param_ty
469+
&& (generics.params.iter().any(|param| {
470+
v.params.iter().any(|p| p.name == param.name.ident().name)
471+
}) || v.params.is_empty()) =>
472+
{
429473
// Missing generic type parameter bound.
430474
if suggest_arbitrary_trait_bound(
431475
self.tcx,
@@ -5439,6 +5483,23 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) {
54395483
(ty, refs)
54405484
}
54415485

5486+
/// Look for type parameters.
5487+
struct ParamFinder {
5488+
params: Vec<ty::ParamTy>,
5489+
}
5490+
5491+
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParamFinder {
5492+
fn visit_ty(&mut self, t: Ty<'tcx>) {
5493+
match t.kind() {
5494+
ty::Param(param) if param.name != kw::SelfUpper => {
5495+
self.params.push(*param);
5496+
}
5497+
_ => {}
5498+
}
5499+
t.super_visit_with(self)
5500+
}
5501+
}
5502+
54425503
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
54435504
/// `param: ?Sized` would be a valid constraint.
54445505
struct FindTypeParam {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ run-rustfix
2+
3+
#[derive(Clone)]
4+
struct A {
5+
x: String
6+
}
7+
8+
struct B {
9+
x: String
10+
}
11+
12+
impl From<A> for B {
13+
fn from(a: A) -> Self {
14+
B { x: a.x }
15+
}
16+
}
17+
18+
impl B {
19+
pub fn from_many<T: Into<B> + Clone>(v: Vec<T>) -> Self where B: From<T> {
20+
B { x: v.iter().map(|e| B::from(e.clone()).x).collect::<Vec<String>>().join(" ") }
21+
//~^ ERROR the trait bound `B: From<T>` is not satisfied
22+
}
23+
}
24+
25+
fn main() {
26+
let _b: B = B { x: "foobar".to_string() };
27+
let a: A = A { x: "frob".to_string() };
28+
let ab: B = a.into();
29+
println!("Hello, {}!", ab.x);
30+
let _c: B = B::from_many(vec![A { x: "x".to_string() }]);
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//@ run-rustfix
2+
3+
#[derive(Clone)]
4+
struct A {
5+
x: String
6+
}
7+
8+
struct B {
9+
x: String
10+
}
11+
12+
impl From<A> for B {
13+
fn from(a: A) -> Self {
14+
B { x: a.x }
15+
}
16+
}
17+
18+
impl B {
19+
pub fn from_many<T: Into<B> + Clone>(v: Vec<T>) -> Self {
20+
B { x: v.iter().map(|e| B::from(e.clone()).x).collect::<Vec<String>>().join(" ") }
21+
//~^ ERROR the trait bound `B: From<T>` is not satisfied
22+
}
23+
}
24+
25+
fn main() {
26+
let _b: B = B { x: "foobar".to_string() };
27+
let a: A = A { x: "frob".to_string() };
28+
let ab: B = a.into();
29+
println!("Hello, {}!", ab.x);
30+
let _c: B = B::from_many(vec![A { x: "x".to_string() }]);
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0277]: the trait bound `B: From<T>` is not satisfied
2+
--> $DIR/suggest-restriction-involving-type-param.rs:20:33
3+
|
4+
LL | B { x: v.iter().map(|e| B::from(e.clone()).x).collect::<Vec<String>>().join(" ") }
5+
| ^ the trait `From<T>` is not implemented for `B`
6+
|
7+
help: consider further restricting the type
8+
|
9+
LL | pub fn from_many<T: Into<B> + Clone>(v: Vec<T>) -> Self where B: From<T> {
10+
| ++++++++++++++++
11+
12+
error: aborting due to 1 previous error
13+
14+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)