Skip to content

Commit 3a795fb

Browse files
committed
On type mismatch involving associated type, suggest constraint
When an associated type is found when a specific type was expected, if possible provide a structured suggestion constraining the associated type in a bound. ``` error[E0271]: type mismatch resolving `<T as Foo>::Y == i32` --> $DIR/associated-types-multiple-types-one-trait.rs:13:5 | LL | want_y(t); | ^^^^^^ expected `i32`, found associated type ... LL | fn want_y<T:Foo<Y=i32>>(t: &T) { } | ----- required by this bound in `want_y` | = note: expected type `i32` found associated type `<T as Foo>::Y` help: consider constraining the associated type `<T as Foo>::Y` to `i32` | LL | fn have_x_want_y<T:Foo<X=u32, Y = i32>>(t: &T) | ^^^^^^^^^ ``` ``` error[E0308]: mismatched types --> $DIR/trait-with-missing-associated-type-restriction.rs:12:9 | LL | qux(x.func()) | ^^^^^^^^ expected `usize`, found associated type | = note: expected type `usize` found associated type `<impl Trait as Trait>::A` help: consider constraining the associated type `<impl Trait as Trait>::A` to `usize` | LL | fn foo(x: impl Trait<A = usize>) { | ^^^^^^^^^^ ```
1 parent f05a524 commit 3a795fb

22 files changed

+469
-66
lines changed

src/librustc_middle/ty/error.rs

Lines changed: 134 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
44
use rustc_hir as hir;
55
use rustc_hir::def_id::DefId;
66
use rustc_span::symbol::sym;
7-
use rustc_span::Span;
7+
use rustc_span::{BytePos, Span};
88
use rustc_target::spec::abi;
99

1010
use std::borrow::Cow;
@@ -401,7 +401,10 @@ impl<'tcx> TyCtxt<'tcx> {
401401
(ty::Param(_), ty::Projection(_)) | (ty::Projection(_), ty::Param(_)) => {
402402
db.note("you might be missing a type parameter or trait bound");
403403
}
404-
(ty::Param(p), _) | (_, ty::Param(p)) => {
404+
(ty::Param(p), ty::Dynamic(..))
405+
| (ty::Dynamic(..), ty::Param(p))
406+
| (ty::Param(p), ty::Opaque(..))
407+
| (ty::Opaque(..), ty::Param(p)) => {
405408
let generics = self.generics_of(body_owner_def_id);
406409
let p_span = self.def_span(generics.type_param(p, self).def_id);
407410
if !sp.contains(p_span) {
@@ -441,11 +444,18 @@ impl<T> Trait<T> for X {
441444
#traits-as-parameters",
442445
);
443446
}
447+
(ty::Param(p), _) | (_, ty::Param(p)) => {
448+
let generics = self.generics_of(body_owner_def_id);
449+
let p_span = self.def_span(generics.type_param(p, self).def_id);
450+
if !sp.contains(p_span) {
451+
db.span_label(p_span, "this type parameter");
452+
}
453+
}
444454
(ty::Projection(_), _) => {
445455
db.note(&format!(
446456
"consider constraining the associated type `{}` to `{}` or calling a \
447-
method that returns `{}`",
448-
values.expected, values.found, values.expected,
457+
method that returns `{0}`",
458+
values.expected, values.found,
449459
));
450460
if self.sess.teach(&db.get_code().unwrap()) {
451461
db.help(
@@ -470,15 +480,18 @@ impl Trait for X {
470480
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
471481
);
472482
}
473-
(_, ty::Projection(_)) => {
474-
db.note(&format!(
483+
(_, ty::Projection(proj_ty)) => {
484+
let msg = format!(
475485
"consider constraining the associated type `{}` to `{}`",
476486
values.found, values.expected,
477-
));
478-
db.note(
479-
"for more information, visit \
480-
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
481487
);
488+
if !self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values) {
489+
db.help(&msg);
490+
db.note(
491+
"for more information, visit \
492+
https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
493+
);
494+
}
482495
}
483496
_ => {}
484497
}
@@ -513,4 +526,115 @@ impl Trait for X {
513526
_ => {}
514527
}
515528
}
529+
530+
fn suggest_constraint(
531+
&self,
532+
db: &mut DiagnosticBuilder<'_>,
533+
msg: &str,
534+
body_owner_def_id: DefId,
535+
proj_ty: &ty::ProjectionTy<'tcx>,
536+
values: &ExpectedFound<Ty<'tcx>>,
537+
) -> bool {
538+
let assoc = self.associated_item(proj_ty.item_def_id);
539+
let trait_ref = proj_ty.trait_ref(*self);
540+
if let Some(item) = self.hir().get_if_local(body_owner_def_id) {
541+
if let Some(hir_generics) = item.generics() {
542+
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
543+
// This will also work for `impl Trait`.
544+
let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind {
545+
let generics = self.generics_of(body_owner_def_id);
546+
generics.type_param(&param_ty, *self).def_id
547+
} else {
548+
return false;
549+
};
550+
551+
// First look in the `where` clause, as this might be
552+
// `fn foo<T>(x: T) where T: Trait`.
553+
for predicate in hir_generics.where_clause.predicates {
554+
if let hir::WherePredicate::BoundPredicate(pred) = predicate {
555+
if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) =
556+
pred.bounded_ty.kind
557+
{
558+
if path.res.opt_def_id() == Some(def_id) {
559+
// This predicate is binding type param `A` in `<A as T>::Foo` to
560+
// something, potentially `T`.
561+
} else {
562+
continue;
563+
}
564+
} else {
565+
continue;
566+
}
567+
568+
if self.constrain_associated_type_structured_suggestion(
569+
db,
570+
&trait_ref,
571+
pred.bounds,
572+
&assoc,
573+
values,
574+
msg,
575+
) {
576+
return true;
577+
}
578+
}
579+
}
580+
for param in hir_generics.params {
581+
if self.hir().opt_local_def_id(param.hir_id).map(|id| id.to_def_id())
582+
== Some(def_id)
583+
{
584+
// This is type param `A` in `<A as T>::Foo`.
585+
return self.constrain_associated_type_structured_suggestion(
586+
db,
587+
&trait_ref,
588+
param.bounds,
589+
&assoc,
590+
values,
591+
msg,
592+
);
593+
}
594+
}
595+
}
596+
}
597+
false
598+
}
599+
600+
fn constrain_associated_type_structured_suggestion(
601+
&self,
602+
db: &mut DiagnosticBuilder<'_>,
603+
trait_ref: &ty::TraitRef<'tcx>,
604+
bounds: hir::GenericBounds<'_>,
605+
assoc: &ty::AssocItem,
606+
values: &ExpectedFound<Ty<'tcx>>,
607+
msg: &str,
608+
) -> bool {
609+
for bound in bounds {
610+
match bound {
611+
hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => {
612+
// Relate the type param against `T` in `<A as T>::Foo`.
613+
if ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id) {
614+
if let Ok(has_params) = self
615+
.sess
616+
.source_map()
617+
.span_to_snippet(ptr.span)
618+
.map(|snippet| snippet.ends_with('>'))
619+
{
620+
let (span, sugg) = if has_params {
621+
let pos = ptr.span.hi() - BytePos(1);
622+
let span = Span::new(pos, pos, ptr.span.ctxt());
623+
(span, format!(", {} = {}", assoc.ident, values.expected))
624+
} else {
625+
(
626+
ptr.span.shrink_to_hi(),
627+
format!("<{} = {}>", assoc.ident, values.expected),
628+
)
629+
};
630+
db.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect);
631+
return true;
632+
}
633+
}
634+
}
635+
_ => {}
636+
}
637+
}
638+
false
639+
}
516640
}

src/test/ui/associated-types/associated-types-eq-3.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ LL | let _: Bar = x.boo();
88
|
99
= note: expected struct `Bar`
1010
found associated type `<I as Foo>::A`
11-
= note: consider constraining the associated type `<I as Foo>::A` to `Bar`
12-
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
11+
help: consider constraining the associated type `<I as Foo>::A` to `Bar`
12+
|
13+
LL | fn foo2<I: Foo<A = Bar>>(x: I) {
14+
| ^^^^^^^^^
1315

1416
error[E0271]: type mismatch resolving `<isize as Foo>::A == Bar`
1517
--> $DIR/associated-types-eq-3.rs:38:5

src/test/ui/associated-types/associated-types-issue-20346.stderr

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ LL | is_iterator_of::<Option<T>, _>(&adapter);
1212
|
1313
= note: expected enum `std::option::Option<T>`
1414
found type `T`
15-
= help: type parameters must be constrained to match other types
16-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
1715

1816
error: aborting due to previous error
1917

src/test/ui/associated-types/associated-types-multiple-types-one-trait.stderr

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ LL | fn want_y<T:Foo<Y=i32>>(t: &T) { }
99
|
1010
= note: expected type `i32`
1111
found associated type `<T as Foo>::Y`
12-
= note: consider constraining the associated type `<T as Foo>::Y` to `i32`
13-
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
12+
help: consider constraining the associated type `<T as Foo>::Y` to `i32`
13+
|
14+
LL | fn have_x_want_y<T:Foo<X=u32, Y = i32>>(t: &T)
15+
| ^^^^^^^^^
1416

1517
error[E0271]: type mismatch resolving `<T as Foo>::X == u32`
1618
--> $DIR/associated-types-multiple-types-one-trait.rs:18:5
@@ -23,8 +25,10 @@ LL | fn want_x<T:Foo<X=u32>>(t: &T) { }
2325
|
2426
= note: expected type `u32`
2527
found associated type `<T as Foo>::X`
26-
= note: consider constraining the associated type `<T as Foo>::X` to `u32`
27-
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
28+
help: consider constraining the associated type `<T as Foo>::X` to `u32`
29+
|
30+
LL | fn have_y_want_x<T:Foo<Y=i32, X = u32>>(t: &T)
31+
| ^^^^^^^^^
2832

2933
error: aborting due to 2 previous errors
3034

src/test/ui/generic-associated-types/iterable.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | type Item<'a> where T: 'a = <std::slice::Iter<'a, T> as Iterator>::Item
66
|
77
= note: expected reference `&T`
88
found associated type `<std::vec::Vec<T> as Iterable>::Item<'_>`
9-
= note: consider constraining the associated type `<std::vec::Vec<T> as Iterable>::Item<'_>` to `&_`
9+
= help: consider constraining the associated type `<std::vec::Vec<T> as Iterable>::Item<'_>` to `&_`
1010
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
1111

1212
error[E0271]: type mismatch resolving `for<'a> <<[T] as Iterable>::Iter<'a> as std::iter::Iterator>::Item == <[T] as Iterable>::Item<'a>`
@@ -17,7 +17,7 @@ LL | type Item<'a> where T: 'a = <std::slice::Iter<'a, T> as Iterator>::Item
1717
|
1818
= note: expected reference `&T`
1919
found associated type `<[T] as Iterable>::Item<'_>`
20-
= note: consider constraining the associated type `<[T] as Iterable>::Item<'_>` to `&_`
20+
= help: consider constraining the associated type `<[T] as Iterable>::Item<'_>` to `&_`
2121
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
2222

2323
error[E0271]: type mismatch resolving `for<'a> <<std::vec::Vec<T> as Iterable>::Iter<'a> as std::iter::Iterator>::Item == <std::vec::Vec<T> as Iterable>::Item<'a>`

src/test/ui/hrtb/issue-62203-hrtb-ice.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | let v = Unit2.m(
66
|
77
= note: expected struct `Unit4`
88
found associated type `<_ as Ty<'_>>::V`
9-
= note: consider constraining the associated type `<_ as Ty<'_>>::V` to `Unit4`
9+
= help: consider constraining the associated type `<_ as Ty<'_>>::V` to `Unit4`
1010
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
1111

1212
error[E0271]: type mismatch resolving `<[closure@$DIR/issue-62203-hrtb-ice.rs:42:17: 42:39] as std::ops::FnOnce<((&u8,),)>>::Output == Unit3`

src/test/ui/impl-trait/bound-normalization-fail.stderr

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ LL | fn foo_fail<T: Trait>() -> impl FooLike<Output=T::Assoc> {
1414
|
1515
= note: expected type `()`
1616
found associated type `<T as impl_trait::Trait>::Assoc`
17-
= note: consider constraining the associated type `<T as impl_trait::Trait>::Assoc` to `()`
18-
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
1917
= note: the return type of a function must have a statically known size
18+
help: consider constraining the associated type `<T as impl_trait::Trait>::Assoc` to `()`
19+
|
20+
LL | fn foo_fail<T: Trait<Assoc = ()>>() -> impl FooLike<Output=T::Assoc> {
21+
| ^^^^^^^^^^^^
2022

2123
error: `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
2224
--> $DIR/bound-normalization-fail.rs:43:41
@@ -32,9 +34,11 @@ LL | fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output=T::Assoc> {
3234
|
3335
= note: expected type `()`
3436
found associated type `<T as lifetimes::Trait<'static>>::Assoc`
35-
= note: consider constraining the associated type `<T as lifetimes::Trait<'static>>::Assoc` to `()`
36-
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
3737
= note: the return type of a function must have a statically known size
38+
help: consider constraining the associated type `<T as lifetimes::Trait<'static>>::Assoc` to `()`
39+
|
40+
LL | fn foo2_fail<'a, T: Trait<'a, Assoc = ()>>() -> impl FooLike<Output=T::Assoc> {
41+
| ^^^^^^^^^^^^
3842

3943
error: aborting due to 3 previous errors; 1 warning emitted
4044

src/test/ui/impl-trait/equality2.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ LL | let _: i32 = Leak::leak(hide(0_i32));
2525
|
2626
= note: expected type `i32`
2727
found associated type `<impl Foo as Leak>::T`
28-
= note: consider constraining the associated type `<impl Foo as Leak>::T` to `i32`
28+
= help: consider constraining the associated type `<impl Foo as Leak>::T` to `i32`
2929
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
3030

3131
error[E0308]: mismatched types

src/test/ui/impl-trait/universal-mismatched-type.stderr

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ LL | x
1010
|
1111
= note: expected struct `std::string::String`
1212
found type parameter `impl Debug`
13-
= help: type parameters must be constrained to match other types
14-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
1513

1614
error: aborting due to previous error
1715

src/test/ui/issues/issue-13853.stderr

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ LL | self.iter()
99
|
1010
= note: expected type parameter `I`
1111
found struct `std::slice::Iter<'_, N>`
12-
= help: type parameters must be constrained to match other types
13-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
1412

1513
error[E0599]: no method named `iter` found for reference `&G` in the current scope
1614
--> $DIR/issue-13853.rs:27:23

src/test/ui/issues/issue-20225.stderr

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ LL | extern "rust-call" fn call(&self, (_,): (T,)) {}
88
|
99
= note: expected fn pointer `extern "rust-call" fn(&Foo, (&'a T,))`
1010
found fn pointer `extern "rust-call" fn(&Foo, (T,))`
11-
= help: type parameters must be constrained to match other types
12-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
1311

1412
error[E0053]: method `call_mut` has an incompatible type for trait
1513
--> $DIR/issue-20225.rs:11:3
@@ -21,8 +19,6 @@ LL | extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {}
2119
|
2220
= note: expected fn pointer `extern "rust-call" fn(&mut Foo, (&'a T,))`
2321
found fn pointer `extern "rust-call" fn(&mut Foo, (T,))`
24-
= help: type parameters must be constrained to match other types
25-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
2622

2723
error[E0053]: method `call_once` has an incompatible type for trait
2824
--> $DIR/issue-20225.rs:18:3
@@ -35,8 +31,6 @@ LL | extern "rust-call" fn call_once(self, (_,): (T,)) {}
3531
|
3632
= note: expected fn pointer `extern "rust-call" fn(Foo, (&'a T,))`
3733
found fn pointer `extern "rust-call" fn(Foo, (T,))`
38-
= help: type parameters must be constrained to match other types
39-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
4034

4135
error: aborting due to 3 previous errors
4236

src/test/ui/issues/issue-69306.stderr

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ LL | const C: S0<u8> = Self(0);
88
|
99
= note: expected type parameter `T`
1010
found type `{integer}`
11-
= help: type parameters must be constrained to match other types
12-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
1311

1412
error[E0308]: mismatched types
1513
--> $DIR/issue-69306.rs:5:23
@@ -21,8 +19,6 @@ LL | const C: S0<u8> = Self(0);
2119
|
2220
= note: expected struct `S0<u8>`
2321
found struct `S0<T>`
24-
= help: type parameters must be constrained to match other types
25-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
2622

2723
error[E0308]: mismatched types
2824
--> $DIR/issue-69306.rs:10:14
@@ -35,8 +31,6 @@ LL | Self(0);
3531
|
3632
= note: expected type parameter `T`
3733
found type `{integer}`
38-
= help: type parameters must be constrained to match other types
39-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
4034

4135
error[E0308]: mismatched types
4236
--> $DIR/issue-69306.rs:27:14
@@ -49,8 +43,6 @@ LL | Self(0);
4943
|
5044
= note: expected type parameter `T`
5145
found type `{integer}`
52-
= help: type parameters must be constrained to match other types
53-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
5446

5547
error[E0308]: mismatched types
5648
--> $DIR/issue-69306.rs:33:32
@@ -62,8 +54,6 @@ LL | const C: S1<u8, u8> = Self(0, 1);
6254
|
6355
= note: expected type parameter `T`
6456
found type `{integer}`
65-
= help: type parameters must be constrained to match other types
66-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
6757

6858
error[E0308]: mismatched types
6959
--> $DIR/issue-69306.rs:33:27
@@ -75,8 +65,6 @@ LL | const C: S1<u8, u8> = Self(0, 1);
7565
|
7666
= note: expected struct `S1<u8, _>`
7767
found struct `S1<T, _>`
78-
= help: type parameters must be constrained to match other types
79-
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
8068

8169
error[E0308]: mismatched types
8270
--> $DIR/issue-69306.rs:41:14

0 commit comments

Comments
 (0)