Suggest constraint on impl Trait in return type

Fix #71035.
This commit is contained in:
Esteban Küber 2020-04-14 22:53:52 -07:00
parent 6648a08b30
commit b0085c86fc
3 changed files with 100 additions and 21 deletions

View File

@ -552,7 +552,7 @@ impl<T> Trait<T> for X {
continue;
}
if self.constrain_associated_type_structured_suggestion(
if self.constrain_generic_bound_associated_type_structured_suggestion(
db,
&trait_ref,
pred.bounds,
@ -569,7 +569,7 @@ impl<T> Trait<T> for X {
== Some(def_id)
{
// This is type param `A` in `<A as T>::Foo`.
return self.constrain_associated_type_structured_suggestion(
return self.constrain_generic_bound_associated_type_structured_suggestion(
db,
&trait_ref,
param.bounds,
@ -629,15 +629,16 @@ impl<T> Trait<T> for X {
| ObligationCauseCode::CompareImplTypeObligation { .. }
| ObligationCauseCode::CompareImplConstObligation
);
let assoc = self.associated_item(proj_ty.item_def_id);
if !callable_scope || impl_comparison {
// We do not want to suggest calling functions when the reason of the
// type error is a comparison of an `impl` with its `trait` or when the
// scope is outside of a `Body`.
} else {
let assoc = self.associated_item(proj_ty.item_def_id);
let items = self.associated_items(assoc.container.id());
// Find all the methods in the trait that could be called to construct the
// expected associated type.
// FIXME: consider suggesting the use of associated `const`s.
let methods: Vec<(Span, String)> = items
.items
.iter()
@ -739,6 +740,18 @@ impl<T> Trait<T> for X {
_ => {}
}
}
if let ty::Opaque(def_id, _) = proj_ty.self_ty().kind {
// When the expected `impl Trait` is not defined in the current item, it will come from
// a return type. This can occur when dealing with `TryStream` (#71035).
suggested |= self.constrain_associated_type_structured_suggestion(
db,
self.def_span(def_id),
&assoc,
values.found,
&msg,
);
}
if !suggested && !impl_comparison {
// Generic suggestion when we can't be more specific.
if callable_scope {
@ -771,7 +784,7 @@ fn foo(&self) -> Self::T { String::new() }
}
}
fn constrain_associated_type_structured_suggestion(
fn constrain_generic_bound_associated_type_structured_suggestion(
&self,
db: &mut DiagnosticBuilder<'_>,
trait_ref: &ty::TraitRef<'tcx>,
@ -785,23 +798,12 @@ fn foo(&self) -> Self::T { String::new() }
match bound {
hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => {
// Relate the type param against `T` in `<A as T>::Foo`.
if ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id) {
if let Ok(has_params) = self
.sess
.source_map()
.span_to_snippet(ptr.span)
.map(|snippet| snippet.ends_with('>'))
{
let (span, sugg) = if has_params {
let pos = ptr.span.hi() - BytePos(1);
let span = Span::new(pos, pos, ptr.span.ctxt());
(span, format!(", {} = {}", assoc.ident, ty))
} else {
(ptr.span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty))
};
db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
return true;
}
if ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id)
&& self.constrain_associated_type_structured_suggestion(
db, ptr.span, assoc, ty, msg,
)
{
return true;
}
}
_ => {}
@ -809,4 +811,28 @@ fn foo(&self) -> Self::T { String::new() }
}
false
}
fn constrain_associated_type_structured_suggestion(
&self,
db: &mut DiagnosticBuilder<'_>,
span: Span,
assoc: &ty::AssocItem,
ty: Ty<'tcx>,
msg: &str,
) -> bool {
if let Ok(has_params) =
self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
{
let (span, sugg) = if has_params {
let pos = span.hi() - BytePos(1);
let span = Span::new(pos, pos, span.ctxt());
(span, format!(", {} = {}", assoc.ident, ty))
} else {
(span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty))
};
db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
return true;
}
false
}
}

View File

@ -0,0 +1,33 @@
trait Foo {
type Item;
}
trait Bar: Foo {}
struct S;
impl Foo for S {
type Item = i32;
}
impl Bar for S {}
struct T;
impl Foo for T {
type Item = u32;
}
impl Bar for T {}
fn bar() -> impl Bar {
T
}
fn baz() -> impl Bar<Item = i32> {
//~^ ERROR type mismatch resolving `<impl Bar as Foo>::Item == i32`
bar()
}
fn main() {
let _ = baz();
}

View File

@ -0,0 +1,20 @@
error[E0271]: type mismatch resolving `<impl Bar as Foo>::Item == i32`
--> $DIR/impl-trait-return-missing-constraint.rs:25:13
|
LL | fn bar() -> impl Bar {
| -------- the expected opaque type
...
LL | fn baz() -> impl Bar<Item = i32> {
| ^^^^^^^^^^^^^^^^^^^^ expected associated type, found `i32`
|
= note: expected associated type `<impl Bar as Foo>::Item`
found type `i32`
= note: the return type of a function must have a statically known size
help: consider constraining the associated type `<impl Bar as Foo>::Item` to `i32`
|
LL | fn bar() -> impl Bar<Item = i32> {
| ^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0271`.