From 3a795fba038bf64452abb8c2240fd1221185e274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Apr 2020 13:36:35 -0700 Subject: [PATCH 01/11] 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 `::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: &T) { } | ----- required by this bound in `want_y` | = note: expected type `i32` found associated type `::Y` help: consider constraining the associated type `::Y` to `i32` | LL | fn have_x_want_y>(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 `::A` help: consider constraining the associated type `::A` to `usize` | LL | fn foo(x: impl Trait) { | ^^^^^^^^^^ ``` --- src/librustc_middle/ty/error.rs | 144 ++++++++++++++++-- .../associated-types-eq-3.stderr | 6 +- .../associated-types-issue-20346.stderr | 2 - ...ated-types-multiple-types-one-trait.stderr | 12 +- .../generic-associated-types/iterable.stderr | 4 +- src/test/ui/hrtb/issue-62203-hrtb-ice.stderr | 2 +- .../bound-normalization-fail.stderr | 12 +- src/test/ui/impl-trait/equality2.stderr | 2 +- .../universal-mismatched-type.stderr | 2 - src/test/ui/issues/issue-13853.stderr | 2 - src/test/ui/issues/issue-20225.stderr | 6 - src/test/ui/issues/issue-69306.stderr | 12 -- .../ui/mismatched_types/issue-35030.stderr | 2 - .../specialization-default-projection.stderr | 2 +- .../specialization-default-types.stderr | 2 +- .../expected-boxed-future-isnt-pinned.stderr | 2 - ...-associated-type-restriction-fixable.fixed | 43 ++++++ ...ing-associated-type-restriction-fixable.rs | 43 ++++++ ...associated-type-restriction-fixable.stderr | 94 ++++++++++++ ...ith-missing-associated-type-restriction.rs | 41 +++++ ...missing-associated-type-restriction.stderr | 88 +++++++++++ .../enum-variant-generic-args.stderr | 12 -- 22 files changed, 469 insertions(+), 66 deletions(-) create mode 100644 src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.fixed create mode 100644 src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.rs create mode 100644 src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.stderr create mode 100644 src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs create mode 100644 src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index 78a94b62d4722..eb5a5702c456e 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -4,7 +4,7 @@ use rustc_errors::{pluralize, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_span::symbol::sym; -use rustc_span::Span; +use rustc_span::{BytePos, Span}; use rustc_target::spec::abi; use std::borrow::Cow; @@ -401,7 +401,10 @@ impl<'tcx> TyCtxt<'tcx> { (ty::Param(_), ty::Projection(_)) | (ty::Projection(_), ty::Param(_)) => { db.note("you might be missing a type parameter or trait bound"); } - (ty::Param(p), _) | (_, ty::Param(p)) => { + (ty::Param(p), ty::Dynamic(..)) + | (ty::Dynamic(..), ty::Param(p)) + | (ty::Param(p), ty::Opaque(..)) + | (ty::Opaque(..), ty::Param(p)) => { let generics = self.generics_of(body_owner_def_id); let p_span = self.def_span(generics.type_param(p, self).def_id); if !sp.contains(p_span) { @@ -441,11 +444,18 @@ impl Trait for X { #traits-as-parameters", ); } + (ty::Param(p), _) | (_, ty::Param(p)) => { + let generics = self.generics_of(body_owner_def_id); + let p_span = self.def_span(generics.type_param(p, self).def_id); + if !sp.contains(p_span) { + db.span_label(p_span, "this type parameter"); + } + } (ty::Projection(_), _) => { db.note(&format!( "consider constraining the associated type `{}` to `{}` or calling a \ - method that returns `{}`", - values.expected, values.found, values.expected, + method that returns `{0}`", + values.expected, values.found, )); if self.sess.teach(&db.get_code().unwrap()) { db.help( @@ -470,15 +480,18 @@ impl Trait for X { https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", ); } - (_, ty::Projection(_)) => { - db.note(&format!( + (_, ty::Projection(proj_ty)) => { + let msg = format!( "consider constraining the associated type `{}` to `{}`", values.found, values.expected, - )); - db.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", ); + if !self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values) { + db.help(&msg); + db.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + ); + } } _ => {} } @@ -513,4 +526,115 @@ impl Trait for X { _ => {} } } + + fn suggest_constraint( + &self, + db: &mut DiagnosticBuilder<'_>, + msg: &str, + body_owner_def_id: DefId, + proj_ty: &ty::ProjectionTy<'tcx>, + values: &ExpectedFound>, + ) -> bool { + let assoc = self.associated_item(proj_ty.item_def_id); + let trait_ref = proj_ty.trait_ref(*self); + if let Some(item) = self.hir().get_if_local(body_owner_def_id) { + if let Some(hir_generics) = item.generics() { + // Get the `DefId` for the type parameter corresponding to `A` in `::Foo`. + // This will also work for `impl Trait`. + let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind { + let generics = self.generics_of(body_owner_def_id); + generics.type_param(¶m_ty, *self).def_id + } else { + return false; + }; + + // First look in the `where` clause, as this might be + // `fn foo(x: T) where T: Trait`. + for predicate in hir_generics.where_clause.predicates { + if let hir::WherePredicate::BoundPredicate(pred) = predicate { + if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = + pred.bounded_ty.kind + { + if path.res.opt_def_id() == Some(def_id) { + // This predicate is binding type param `A` in `::Foo` to + // something, potentially `T`. + } else { + continue; + } + } else { + continue; + } + + if self.constrain_associated_type_structured_suggestion( + db, + &trait_ref, + pred.bounds, + &assoc, + values, + msg, + ) { + return true; + } + } + } + for param in hir_generics.params { + if self.hir().opt_local_def_id(param.hir_id).map(|id| id.to_def_id()) + == Some(def_id) + { + // This is type param `A` in `::Foo`. + return self.constrain_associated_type_structured_suggestion( + db, + &trait_ref, + param.bounds, + &assoc, + values, + msg, + ); + } + } + } + } + false + } + + fn constrain_associated_type_structured_suggestion( + &self, + db: &mut DiagnosticBuilder<'_>, + trait_ref: &ty::TraitRef<'tcx>, + bounds: hir::GenericBounds<'_>, + assoc: &ty::AssocItem, + values: &ExpectedFound>, + msg: &str, + ) -> bool { + for bound in bounds { + match bound { + hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => { + // Relate the type param against `T` in `::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, values.expected)) + } else { + ( + ptr.span.shrink_to_hi(), + format!("<{} = {}>", assoc.ident, values.expected), + ) + }; + db.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect); + return true; + } + } + } + _ => {} + } + } + false + } } diff --git a/src/test/ui/associated-types/associated-types-eq-3.stderr b/src/test/ui/associated-types/associated-types-eq-3.stderr index a8608abb4d992..dffa4780a09ff 100644 --- a/src/test/ui/associated-types/associated-types-eq-3.stderr +++ b/src/test/ui/associated-types/associated-types-eq-3.stderr @@ -8,8 +8,10 @@ LL | let _: Bar = x.boo(); | = note: expected struct `Bar` found associated type `::A` - = note: consider constraining the associated type `::A` to `Bar` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: consider constraining the associated type `::A` to `Bar` + | +LL | fn foo2>(x: I) { + | ^^^^^^^^^ error[E0271]: type mismatch resolving `::A == Bar` --> $DIR/associated-types-eq-3.rs:38:5 diff --git a/src/test/ui/associated-types/associated-types-issue-20346.stderr b/src/test/ui/associated-types/associated-types-issue-20346.stderr index 8f2b760840c08..db35c1af17147 100644 --- a/src/test/ui/associated-types/associated-types-issue-20346.stderr +++ b/src/test/ui/associated-types/associated-types-issue-20346.stderr @@ -12,8 +12,6 @@ LL | is_iterator_of::, _>(&adapter); | = note: expected enum `std::option::Option` found type `T` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error: aborting due to previous error diff --git a/src/test/ui/associated-types/associated-types-multiple-types-one-trait.stderr b/src/test/ui/associated-types/associated-types-multiple-types-one-trait.stderr index 4e481411b4d2e..b8f20d00ff8e4 100644 --- a/src/test/ui/associated-types/associated-types-multiple-types-one-trait.stderr +++ b/src/test/ui/associated-types/associated-types-multiple-types-one-trait.stderr @@ -9,8 +9,10 @@ LL | fn want_y>(t: &T) { } | = note: expected type `i32` found associated type `::Y` - = note: consider constraining the associated type `::Y` to `i32` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: consider constraining the associated type `::Y` to `i32` + | +LL | fn have_x_want_y>(t: &T) + | ^^^^^^^^^ error[E0271]: type mismatch resolving `::X == u32` --> $DIR/associated-types-multiple-types-one-trait.rs:18:5 @@ -23,8 +25,10 @@ LL | fn want_x>(t: &T) { } | = note: expected type `u32` found associated type `::X` - = note: consider constraining the associated type `::X` to `u32` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: consider constraining the associated type `::X` to `u32` + | +LL | fn have_y_want_x>(t: &T) + | ^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/src/test/ui/generic-associated-types/iterable.stderr b/src/test/ui/generic-associated-types/iterable.stderr index b1298163aabf0..4a839ac4bcb6a 100644 --- a/src/test/ui/generic-associated-types/iterable.stderr +++ b/src/test/ui/generic-associated-types/iterable.stderr @@ -6,7 +6,7 @@ LL | type Item<'a> where T: 'a = as Iterator>::Item | = note: expected reference `&T` found associated type ` as Iterable>::Item<'_>` - = note: consider constraining the associated type ` as Iterable>::Item<'_>` to `&_` + = help: consider constraining the associated type ` as Iterable>::Item<'_>` to `&_` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html 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 = as Iterator>::Item | = note: expected reference `&T` found associated type `<[T] as Iterable>::Item<'_>` - = note: consider constraining the associated type `<[T] as Iterable>::Item<'_>` to `&_` + = help: consider constraining the associated type `<[T] as Iterable>::Item<'_>` to `&_` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0271]: type mismatch resolving `for<'a> < as Iterable>::Iter<'a> as std::iter::Iterator>::Item == as Iterable>::Item<'a>` diff --git a/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr b/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr index 759c7302d13c6..1c7bfa65d7cfe 100644 --- a/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr +++ b/src/test/ui/hrtb/issue-62203-hrtb-ice.stderr @@ -6,7 +6,7 @@ LL | let v = Unit2.m( | = note: expected struct `Unit4` found associated type `<_ as Ty<'_>>::V` - = note: consider constraining the associated type `<_ as Ty<'_>>::V` to `Unit4` + = help: consider constraining the associated type `<_ as Ty<'_>>::V` to `Unit4` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0271]: type mismatch resolving `<[closure@$DIR/issue-62203-hrtb-ice.rs:42:17: 42:39] as std::ops::FnOnce<((&u8,),)>>::Output == Unit3` diff --git a/src/test/ui/impl-trait/bound-normalization-fail.stderr b/src/test/ui/impl-trait/bound-normalization-fail.stderr index 314ed96fd5ef0..f5092044627f6 100644 --- a/src/test/ui/impl-trait/bound-normalization-fail.stderr +++ b/src/test/ui/impl-trait/bound-normalization-fail.stderr @@ -14,9 +14,11 @@ LL | fn foo_fail() -> impl FooLike { | = note: expected type `()` found associated type `::Assoc` - = note: consider constraining the associated type `::Assoc` to `()` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html = note: the return type of a function must have a statically known size +help: consider constraining the associated type `::Assoc` to `()` + | +LL | fn foo_fail>() -> impl FooLike { + | ^^^^^^^^^^^^ error: `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope --> $DIR/bound-normalization-fail.rs:43:41 @@ -32,9 +34,11 @@ LL | fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike { | = note: expected type `()` found associated type `>::Assoc` - = note: consider constraining the associated type `>::Assoc` to `()` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html = note: the return type of a function must have a statically known size +help: consider constraining the associated type `>::Assoc` to `()` + | +LL | fn foo2_fail<'a, T: Trait<'a, Assoc = ()>>() -> impl FooLike { + | ^^^^^^^^^^^^ error: aborting due to 3 previous errors; 1 warning emitted diff --git a/src/test/ui/impl-trait/equality2.stderr b/src/test/ui/impl-trait/equality2.stderr index b882514f61609..2454c218ffc8b 100644 --- a/src/test/ui/impl-trait/equality2.stderr +++ b/src/test/ui/impl-trait/equality2.stderr @@ -25,7 +25,7 @@ LL | let _: i32 = Leak::leak(hide(0_i32)); | = note: expected type `i32` found associated type `::T` - = note: consider constraining the associated type `::T` to `i32` + = help: consider constraining the associated type `::T` to `i32` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types diff --git a/src/test/ui/impl-trait/universal-mismatched-type.stderr b/src/test/ui/impl-trait/universal-mismatched-type.stderr index 3ffa2b55712eb..a12b01b4d2b0d 100644 --- a/src/test/ui/impl-trait/universal-mismatched-type.stderr +++ b/src/test/ui/impl-trait/universal-mismatched-type.stderr @@ -10,8 +10,6 @@ LL | x | = note: expected struct `std::string::String` found type parameter `impl Debug` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error: aborting due to previous error diff --git a/src/test/ui/issues/issue-13853.stderr b/src/test/ui/issues/issue-13853.stderr index 2f31636f8adf9..3f1b955dddb2b 100644 --- a/src/test/ui/issues/issue-13853.stderr +++ b/src/test/ui/issues/issue-13853.stderr @@ -9,8 +9,6 @@ LL | self.iter() | = note: expected type parameter `I` found struct `std::slice::Iter<'_, N>` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0599]: no method named `iter` found for reference `&G` in the current scope --> $DIR/issue-13853.rs:27:23 diff --git a/src/test/ui/issues/issue-20225.stderr b/src/test/ui/issues/issue-20225.stderr index 1c5911e05f767..3bcc50ded8425 100644 --- a/src/test/ui/issues/issue-20225.stderr +++ b/src/test/ui/issues/issue-20225.stderr @@ -8,8 +8,6 @@ LL | extern "rust-call" fn call(&self, (_,): (T,)) {} | = note: expected fn pointer `extern "rust-call" fn(&Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(&Foo, (T,))` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0053]: method `call_mut` has an incompatible type for trait --> $DIR/issue-20225.rs:11:3 @@ -21,8 +19,6 @@ LL | extern "rust-call" fn call_mut(&mut self, (_,): (T,)) {} | = note: expected fn pointer `extern "rust-call" fn(&mut Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(&mut Foo, (T,))` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0053]: method `call_once` has an incompatible type for trait --> $DIR/issue-20225.rs:18:3 @@ -35,8 +31,6 @@ LL | extern "rust-call" fn call_once(self, (_,): (T,)) {} | = note: expected fn pointer `extern "rust-call" fn(Foo, (&'a T,))` found fn pointer `extern "rust-call" fn(Foo, (T,))` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error: aborting due to 3 previous errors diff --git a/src/test/ui/issues/issue-69306.stderr b/src/test/ui/issues/issue-69306.stderr index a2a42739ca8be..58e85ec700d2d 100644 --- a/src/test/ui/issues/issue-69306.stderr +++ b/src/test/ui/issues/issue-69306.stderr @@ -8,8 +8,6 @@ LL | const C: S0 = Self(0); | = note: expected type parameter `T` found type `{integer}` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0308]: mismatched types --> $DIR/issue-69306.rs:5:23 @@ -21,8 +19,6 @@ LL | const C: S0 = Self(0); | = note: expected struct `S0` found struct `S0` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0308]: mismatched types --> $DIR/issue-69306.rs:10:14 @@ -35,8 +31,6 @@ LL | Self(0); | = note: expected type parameter `T` found type `{integer}` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0308]: mismatched types --> $DIR/issue-69306.rs:27:14 @@ -49,8 +43,6 @@ LL | Self(0); | = note: expected type parameter `T` found type `{integer}` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0308]: mismatched types --> $DIR/issue-69306.rs:33:32 @@ -62,8 +54,6 @@ LL | const C: S1 = Self(0, 1); | = note: expected type parameter `T` found type `{integer}` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0308]: mismatched types --> $DIR/issue-69306.rs:33:27 @@ -75,8 +65,6 @@ LL | const C: S1 = Self(0, 1); | = note: expected struct `S1` found struct `S1` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0308]: mismatched types --> $DIR/issue-69306.rs:41:14 diff --git a/src/test/ui/mismatched_types/issue-35030.stderr b/src/test/ui/mismatched_types/issue-35030.stderr index 6fb04ef5c998f..9f4e4398984ae 100644 --- a/src/test/ui/mismatched_types/issue-35030.stderr +++ b/src/test/ui/mismatched_types/issue-35030.stderr @@ -9,8 +9,6 @@ LL | Some(true) | = note: expected type parameter `bool` (type parameter `bool`) found type `bool` (`bool`) - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error: aborting due to previous error diff --git a/src/test/ui/specialization/specialization-default-projection.stderr b/src/test/ui/specialization/specialization-default-projection.stderr index d03aec7ab30d4..4344fe23bbcc4 100644 --- a/src/test/ui/specialization/specialization-default-projection.stderr +++ b/src/test/ui/specialization/specialization-default-projection.stderr @@ -25,7 +25,7 @@ LL | generic::<()>() | = note: expected unit type `()` found associated type `<() as Foo>::Assoc` - = note: consider constraining the associated type `<() as Foo>::Assoc` to `()` + = help: consider constraining the associated type `<() as Foo>::Assoc` to `()` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to 2 previous errors diff --git a/src/test/ui/specialization/specialization-default-types.stderr b/src/test/ui/specialization/specialization-default-types.stderr index 257c114252cfc..d66aaa9627489 100644 --- a/src/test/ui/specialization/specialization-default-types.stderr +++ b/src/test/ui/specialization/specialization-default-types.stderr @@ -21,7 +21,7 @@ LL | Example::generate(t) | = note: expected struct `std::boxed::Box` found associated type `::Output` - = note: consider constraining the associated type `::Output` to `std::boxed::Box` + = help: consider constraining the associated type `::Output` to `std::boxed::Box` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr index ff256eb30947a..0e68e81d7ab8e 100644 --- a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -12,8 +12,6 @@ LL | x | = note: expected struct `std::pin::Pin + std::marker::Send + 'static)>>` found type parameter `F` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0308]: mismatched types --> $DIR/expected-boxed-future-isnt-pinned.rs:18:5 diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.fixed b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.fixed new file mode 100644 index 0000000000000..8ef7e34ab3050 --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.fixed @@ -0,0 +1,43 @@ +// run-rustfix +#![allow(unused)] // for the fixed file + +trait Trait { + type A; + + fn func(&self) -> Self::A; +} + +struct S(T); +impl S { + fn foo<'a, T: Trait + 'a>(&self, _: impl Trait, x: impl Trait, _: T) { + qux(x.func()) //~ ERROR mismatched types + } + + fn ban(x: T) where T: Trait { + qux(x.func()) //~ ERROR mismatched types + } +} + +fn foo<'a, T: Trait + 'a>(_: impl Trait, x: impl Trait, _: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar>(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn foo2(x: impl Trait) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar2>(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn ban(x: T) where T: Trait { + qux(x.func()) //~ ERROR mismatched types +} + +fn qux(_: usize) {} + +fn main() {} diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.rs b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.rs new file mode 100644 index 0000000000000..7bd38d0d45d90 --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.rs @@ -0,0 +1,43 @@ +// run-rustfix +#![allow(unused)] // for the fixed file + +trait Trait { + type A; + + fn func(&self) -> Self::A; +} + +struct S(T); +impl S { + fn foo<'a, T: Trait + 'a>(&self, _: impl Trait, x: impl Trait, _: T) { + qux(x.func()) //~ ERROR mismatched types + } + + fn ban(x: T) where T: Trait { + qux(x.func()) //~ ERROR mismatched types + } +} + +fn foo<'a, T: Trait + 'a>(_: impl Trait, x: impl Trait, _: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn foo2(x: impl Trait) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar2>(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn ban(x: T) where T: Trait { + qux(x.func()) //~ ERROR mismatched types +} + +fn qux(_: usize) {} + +fn main() {} diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.stderr b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.stderr new file mode 100644 index 0000000000000..f785f7b84a76f --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction-fixable.stderr @@ -0,0 +1,94 @@ +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:13:13 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn foo<'a, T: Trait + 'a>(&self, _: impl Trait, x: impl Trait, _: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:17:13 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn ban(x: T) where T: Trait { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:22:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn foo<'a, T: Trait + 'a>(_: impl Trait, x: impl Trait, _: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:26:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn bar>(x: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:30:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type ` as Trait>::A` +help: consider constraining the associated type ` as Trait>::A` to `usize` + | +LL | fn foo2(x: impl Trait) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:34:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `>::A` +help: consider constraining the associated type `>::A` to `usize` + | +LL | fn bar2>(x: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction-fixable.rs:38:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn ban(x: T) where T: Trait { + | ^^^^^^^^^^^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs new file mode 100644 index 0000000000000..1166785d73996 --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs @@ -0,0 +1,41 @@ +// These are all the possible variations of this error I could think of for. +// `trait-with-missing-associated-type-restriction-fixable.rs` contains the subset of these that +// can be fixed with `rustfix`. + +trait Trait { + type A; + + fn func(&self) -> Self::A; +} + +fn foo(_: impl Trait, x: impl Trait) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn foo2(x: impl Trait) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bar2>(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn baz>(x: T) { + qux(x.func()) //~ ERROR mismatched types +} + +fn bat(x: &mut dyn Trait<(), A = ()>) { + qux(x) //~ ERROR mismatched types +} + +fn ban(x: T) where T: Trait { + qux(x.func()) //~ ERROR mismatched types +} + +fn qux(_: usize) {} + +fn main() {} diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr new file mode 100644 index 0000000000000..08788db6ec908 --- /dev/null +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr @@ -0,0 +1,88 @@ +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 `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn foo(_: impl Trait, x: impl Trait) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:16:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn bar>(x: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:20:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type ` as Trait>::A` +help: consider constraining the associated type ` as Trait>::A` to `usize` + | +LL | fn foo2(x: impl Trait) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:24:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `>::A` +help: consider constraining the associated type `>::A` to `usize` + | +LL | fn bar2>(x: T) { + | ^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:28:9 + | +LL | fn baz>(x: T) { + | - this type parameter +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found type parameter `D` + | + = note: expected type `usize` + found type parameter `D` + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:32:9 + | +LL | qux(x) + | ^ expected `usize`, found mutable reference + | + = note: expected type `usize` + found mutable reference `&mut dyn Trait<(), A = ()>` + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:36:9 + | +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found associated type + | + = note: expected type `usize` + found associated type `::A` +help: consider constraining the associated type `::A` to `usize` + | +LL | fn ban(x: T) where T: Trait { + | ^^^^^^^^^^^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr b/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr index 412b4dbda4fde..caea791e6536b 100644 --- a/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr +++ b/src/test/ui/type-alias-enum-variants/enum-variant-generic-args.stderr @@ -9,8 +9,6 @@ LL | Self::TSVariant(()); | = note: expected type parameter `T` found unit type `()` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:15:27 @@ -35,8 +33,6 @@ LL | Self::<()>::TSVariant(()); | = note: expected type parameter `T` found unit type `()` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:20:16 @@ -61,8 +57,6 @@ LL | Self::SVariant { v: () }; | = note: expected type parameter `T` found unit type `()` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:28:26 @@ -81,8 +75,6 @@ LL | Self::SVariant::<()> { v: () }; | = note: expected type parameter `T` found unit type `()` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:31:16 @@ -101,8 +93,6 @@ LL | Self::<()>::SVariant { v: () }; | = note: expected type parameter `T` found unit type `()` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:34:16 @@ -127,8 +117,6 @@ LL | Self::<()>::SVariant::<()> { v: () }; | = note: expected type parameter `T` found unit type `()` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error[E0109]: type arguments are not allowed for this type --> $DIR/enum-variant-generic-args.rs:41:26 From 31b356619657928113902b975453a5e19371bb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Apr 2020 19:46:34 -0700 Subject: [PATCH 02/11] When a projection is expected, suggest constraining or calling method --- .../infer/error_reporting/mod.rs | 5 +- src/librustc_middle/ty/error.rs | 198 ++++++++++++++---- ...ssociated-const-generic-obligations.stderr | 2 +- .../defaults-in-other-trait-items.rs | 4 +- .../defaults-in-other-trait-items.stderr | 4 +- .../defaults-specialization.stderr | 38 ++-- .../ui/associated-types/issue-26681.stderr | 2 +- .../generic-associated-types/iterable.stderr | 4 +- src/test/ui/issues/issue-32323.stderr | 6 +- .../specialization-default-projection.stderr | 2 +- .../specialization-default-types.stderr | 2 +- ...ith-missing-associated-type-restriction.rs | 4 +- ...missing-associated-type-restriction.stderr | 41 ++-- 13 files changed, 228 insertions(+), 84 deletions(-) diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index 9de7dcc845f1d..13c7989e5c88e 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -1388,6 +1388,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { terr: &TypeError<'tcx>, ) { let span = cause.span(self.tcx); + debug!("note_type_err cause={:?} values={:?}, terr={:?}", cause, values, terr); // For some types of errors, expected-found does not make // sense, so just ignore the values we were given. @@ -1599,11 +1600,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.tcx.hir().body_owner_def_id(hir::BodyId { hir_id: cause.body_id }) }); self.check_and_note_conflicting_crates(diag, terr); - self.tcx.note_and_explain_type_err(diag, terr, span, body_owner_def_id.to_def_id()); + self.tcx.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id.to_def_id()); // It reads better to have the error origin as the final // thing. - self.note_error_origin(diag, &cause, exp_found); + self.note_error_origin(diag, cause, exp_found); } /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate, diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index eb5a5702c456e..99ea0068fed2a 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -1,10 +1,12 @@ +use crate::traits::{ObligationCause, ObligationCauseCode}; use crate::ty::{self, BoundRegion, Region, Ty, TyCtxt}; use rustc_ast::ast; -use rustc_errors::{pluralize, Applicability, DiagnosticBuilder}; +use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; +use rustc_errors::{pluralize, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_span::symbol::sym; -use rustc_span::{BytePos, Span}; +use rustc_span::{BytePos, MultiSpan, Span}; use rustc_target::spec::abi; use std::borrow::Cow; @@ -332,6 +334,7 @@ impl<'tcx> TyCtxt<'tcx> { self, db: &mut DiagnosticBuilder<'_>, err: &TypeError<'tcx>, + cause: &ObligationCause<'tcx>, sp: Span, body_owner_def_id: DefId, ) { @@ -370,7 +373,7 @@ impl<'tcx> TyCtxt<'tcx> { sp, "use a float literal", format!("{}.0", snippet), - Applicability::MachineApplicable, + MachineApplicable, ); } } @@ -451,33 +454,13 @@ impl Trait for X { db.span_label(p_span, "this type parameter"); } } - (ty::Projection(_), _) => { - db.note(&format!( - "consider constraining the associated type `{}` to `{}` or calling a \ - method that returns `{0}`", - values.expected, values.found, - )); - if self.sess.teach(&db.get_code().unwrap()) { - db.help( - "given an associated type `T` and a method `foo`: -``` -trait Trait { - type T; - fn foo(&self) -> Self::T; -} -``` -the only way of implementing method `foo` is to constrain `T` with an explicit associated type: -``` -impl Trait for X { - type T = String; - fn foo(&self) -> Self::T { String::new() } -} -```", - ); - } - db.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + (ty::Projection(proj_ty), _) => { + self.expected_projection( + db, + proj_ty, + values, + body_owner_def_id, + &cause.code, ); } (_, ty::Projection(proj_ty)) => { @@ -485,7 +468,13 @@ impl Trait for X { "consider constraining the associated type `{}` to `{}`", values.found, values.expected, ); - if !self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values) { + if !self.suggest_constraint( + db, + &msg, + body_owner_def_id, + proj_ty, + values.expected, + ) { db.help(&msg); db.note( "for more information, visit \ @@ -533,7 +522,7 @@ impl Trait for X { msg: &str, body_owner_def_id: DefId, proj_ty: &ty::ProjectionTy<'tcx>, - values: &ExpectedFound>, + ty: Ty<'tcx>, ) -> bool { let assoc = self.associated_item(proj_ty.item_def_id); let trait_ref = proj_ty.trait_ref(*self); @@ -570,7 +559,7 @@ impl Trait for X { &trait_ref, pred.bounds, &assoc, - values, + ty, msg, ) { return true; @@ -587,7 +576,7 @@ impl Trait for X { &trait_ref, param.bounds, &assoc, - values, + ty, msg, ); } @@ -597,15 +586,145 @@ impl Trait for X { false } + fn expected_projection( + &self, + db: &mut DiagnosticBuilder<'_>, + proj_ty: &ty::ProjectionTy<'tcx>, + values: &ExpectedFound>, + body_owner_def_id: DefId, + cause_code: &ObligationCauseCode<'_>, + ) { + let msg = format!( + "consider constraining the associated type `{}` to `{}`", + values.expected, values.found + ); + let mut suggested = false; + let body_owner = self.hir().get_if_local(body_owner_def_id); + let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); + + let callable_scope = match body_owner { + Some( + hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Trait(..) + | hir::ItemKind::Impl { .. } + | hir::ItemKind::Const(..) + | hir::ItemKind::Enum(..) + | hir::ItemKind::Struct(..) + | hir::ItemKind::Union(..), + .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(..), + .. + }), + ) => false, + _ => true, + }; + let impl_comparison = + matches!(cause_code, ObligationCauseCode::CompareImplMethodObligation { .. }); + 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. + let methods: Vec<(Span, String)> = items + .items + .iter() + .filter(|(name, item)| { + ty::AssocKind::Method == item.kind && Some(**name) != current_method_ident + }) + .filter_map(|(_, item)| { + let method = self.fn_sig(item.def_id); + match method.output().skip_binder().kind { + ty::Projection(ty::ProjectionTy { item_def_id, .. }) + if item_def_id == proj_ty.item_def_id => + { + Some(( + self.sess.source_map().guess_head_span(self.def_span(item.def_id)), + format!("consider calling `{}`", self.def_path_str(item.def_id)), + )) + } + _ => None, + } + }) + .collect(); + if !methods.is_empty() { + // Use a single `help:` to show all the methods in the trait that can + // be used to construct the expected associated type. + let mut span: MultiSpan = + methods.iter().map(|(sp, _)| *sp).collect::>().into(); + let msg = format!( + "{some} method{s} {are} available that return{r} `{ty}`", + some = if methods.len() == 1 { "a" } else { "some" }, + s = pluralize!(methods.len()), + are = if methods.len() == 1 { "is" } else { "are" }, + r = if methods.len() == 1 { "s" } else { "" }, + ty = values.expected + ); + for (sp, label) in methods.into_iter() { + span.push_span_label(sp, label); + } + db.span_help(span, &msg); + suggested = true; + } + // Possibly suggest constraining the associated type to conform to the + // found type. + suggested |= + self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found); + } + if !suggested && !impl_comparison { + // Generic suggestion when we can't be more specific. + if callable_scope { + db.help( + &format!("{} or calling a method that returns `{}`", msg, values.expected,), + ); + } else { + db.help(&msg); + } + db.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch19-03-advanced-traits.html", + ); + } + if self.sess.teach(&db.get_code().unwrap()) { + db.help( + "given an associated type `T` and a method `foo`: +``` +trait Trait { +type T; +fn foo(&self) -> Self::T; +} +``` +the only way of implementing method `foo` is to constrain `T` with an explicit associated type: +``` +impl Trait for X { +type T = String; +fn foo(&self) -> Self::T { String::new() } +} +```", + ); + } + } + fn constrain_associated_type_structured_suggestion( &self, db: &mut DiagnosticBuilder<'_>, trait_ref: &ty::TraitRef<'tcx>, bounds: hir::GenericBounds<'_>, assoc: &ty::AssocItem, - values: &ExpectedFound>, + ty: Ty<'tcx>, msg: &str, ) -> bool { + // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. for bound in bounds { match bound { hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => { @@ -620,14 +739,11 @@ impl Trait for X { 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, values.expected)) + (span, format!(", {} = {}", assoc.ident, ty)) } else { - ( - ptr.span.shrink_to_hi(), - format!("<{} = {}>", assoc.ident, values.expected), - ) + (ptr.span.shrink_to_hi(), format!("<{} = {}>", assoc.ident, ty)) }; - db.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect); + db.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect); return true; } } diff --git a/src/test/ui/associated-const/associated-const-generic-obligations.stderr b/src/test/ui/associated-const/associated-const-generic-obligations.stderr index d6cdcd4747ff2..6e3ec4ed155b3 100644 --- a/src/test/ui/associated-const/associated-const-generic-obligations.stderr +++ b/src/test/ui/associated-const/associated-const-generic-obligations.stderr @@ -9,7 +9,7 @@ LL | const FROM: &'static str = "foo"; | = note: expected associated type `::Out` found reference `&'static str` - = note: consider constraining the associated type `::Out` to `&'static str` or calling a method that returns `::Out` + = help: consider constraining the associated type `::Out` to `&'static str` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to previous error diff --git a/src/test/ui/associated-types/defaults-in-other-trait-items.rs b/src/test/ui/associated-types/defaults-in-other-trait-items.rs index 9f2e8aca47712..41dc67d6f4759 100644 --- a/src/test/ui/associated-types/defaults-in-other-trait-items.rs +++ b/src/test/ui/associated-types/defaults-in-other-trait-items.rs @@ -10,7 +10,7 @@ trait Tr { //~^ ERROR mismatched types //~| NOTE expected associated type, found `()` //~| NOTE expected associated type `::A` - //~| NOTE consider constraining the associated type + //~| HELP consider constraining the associated type //~| NOTE for more information, visit } } @@ -38,7 +38,7 @@ trait AssocConst { //~^ ERROR mismatched types //~| NOTE expected associated type, found `u8` //~| NOTE expected associated type `::Ty` - //~| NOTE consider constraining the associated type + //~| HELP consider constraining the associated type //~| NOTE for more information, visit } diff --git a/src/test/ui/associated-types/defaults-in-other-trait-items.stderr b/src/test/ui/associated-types/defaults-in-other-trait-items.stderr index 9ecfe49c2b571..a5b170d05c45e 100644 --- a/src/test/ui/associated-types/defaults-in-other-trait-items.stderr +++ b/src/test/ui/associated-types/defaults-in-other-trait-items.stderr @@ -6,7 +6,7 @@ LL | let () = p; | = note: expected associated type `::A` found unit type `()` - = note: consider constraining the associated type `::A` to `()` or calling a method that returns `::A` + = help: consider constraining the associated type `::A` to `()` or calling a method that returns `::A` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types @@ -17,7 +17,7 @@ LL | const C: Self::Ty = 0u8; | = note: expected associated type `::Ty` found type `u8` - = note: consider constraining the associated type `::Ty` to `u8` or calling a method that returns `::Ty` + = help: consider constraining the associated type `::Ty` to `u8` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-types/defaults-specialization.stderr b/src/test/ui/associated-types/defaults-specialization.stderr index 1dd536ec6360f..2f3ce832725cf 100644 --- a/src/test/ui/associated-types/defaults-specialization.stderr +++ b/src/test/ui/associated-types/defaults-specialization.stderr @@ -9,8 +9,6 @@ LL | fn make() -> u8 { 0 } | = note: expected fn pointer `fn() -> as Tr>::Ty` found fn pointer `fn() -> u8` - = note: consider constraining the associated type ` as Tr>::Ty` to `u8` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0053]: method `make` has an incompatible type for trait --> $DIR/defaults-specialization.rs:34:18 @@ -23,8 +21,6 @@ LL | fn make() -> bool { true } | = note: expected fn pointer `fn() -> as Tr>::Ty` found fn pointer `fn() -> bool` - = note: consider constraining the associated type ` as Tr>::Ty` to `bool` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:9:9 @@ -36,7 +32,7 @@ LL | 0u8 | = note: expected associated type `::Ty` found type `u8` - = note: consider constraining the associated type `::Ty` to `u8` or calling a method that returns `::Ty` + = help: consider constraining the associated type `::Ty` to `u8` or calling a method that returns `::Ty` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types @@ -49,7 +45,7 @@ LL | fn make() -> Self::Ty { 0u8 } | = note: expected associated type ` as Tr>::Ty` found type `u8` - = note: consider constraining the associated type ` as Tr>::Ty` to `u8` or calling a method that returns ` as Tr>::Ty` + = help: consider constraining the associated type ` as Tr>::Ty` to `u8` or calling a method that returns ` as Tr>::Ty` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types @@ -62,7 +58,7 @@ LL | fn make() -> Self::Ty { true } | = note: expected associated type ` as Tr>::Ty` found type `bool` - = note: consider constraining the associated type ` as Tr>::Ty` to `bool` or calling a method that returns ` as Tr>::Ty` + = help: consider constraining the associated type ` as Tr>::Ty` to `bool` or calling a method that returns ` as Tr>::Ty` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types @@ -75,8 +71,11 @@ LL | let _: as Tr>::Ty = 0u8; | = note: expected associated type ` as Tr>::Ty` found type `u8` - = note: consider constraining the associated type ` as Tr>::Ty` to `u8` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: a method is available that returns ` as Tr>::Ty` + --> $DIR/defaults-specialization.rs:8:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:87:32 @@ -88,8 +87,11 @@ LL | let _: as Tr>::Ty = true; | = note: expected associated type ` as Tr>::Ty` found type `bool` - = note: consider constraining the associated type ` as Tr>::Ty` to `bool` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: a method is available that returns ` as Tr>::Ty` + --> $DIR/defaults-specialization.rs:8:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:88:33 @@ -101,8 +103,11 @@ LL | let _: as Tr>::Ty = 0u8; | = note: expected associated type ` as Tr>::Ty` found type `u8` - = note: consider constraining the associated type ` as Tr>::Ty` to `u8` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: a method is available that returns ` as Tr>::Ty` + --> $DIR/defaults-specialization.rs:8:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:89:33 @@ -114,8 +119,11 @@ LL | let _: as Tr>::Ty = true; | = note: expected associated type ` as Tr>::Ty` found type `bool` - = note: consider constraining the associated type ` as Tr>::Ty` to `bool` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: a method is available that returns ` as Tr>::Ty` + --> $DIR/defaults-specialization.rs:8:5 + | +LL | fn make() -> Self::Ty { + | ^^^^^^^^^^^^^^^^^^^^^ consider calling `Tr::make` error: aborting due to 9 previous errors diff --git a/src/test/ui/associated-types/issue-26681.stderr b/src/test/ui/associated-types/issue-26681.stderr index da10933df92b0..74411008c9dda 100644 --- a/src/test/ui/associated-types/issue-26681.stderr +++ b/src/test/ui/associated-types/issue-26681.stderr @@ -6,7 +6,7 @@ LL | const C: ::Bar = 6665; | = note: expected associated type `<::Fv as Foo>::Bar` found type `{integer}` - = note: consider constraining the associated type `<::Fv as Foo>::Bar` to `{integer}` or calling a method that returns `<::Fv as Foo>::Bar` + = help: consider constraining the associated type `<::Fv as Foo>::Bar` to `{integer}` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to previous error diff --git a/src/test/ui/generic-associated-types/iterable.stderr b/src/test/ui/generic-associated-types/iterable.stderr index 4a839ac4bcb6a..6e75462122513 100644 --- a/src/test/ui/generic-associated-types/iterable.stderr +++ b/src/test/ui/generic-associated-types/iterable.stderr @@ -34,7 +34,7 @@ LL | fn iter<'a>(&'a self) -> Self::Iter<'a> { | = note: expected associated type ` as Iterable>::Item<'_>` found reference `&T` - = note: consider constraining the associated type ` as Iterable>::Item<'_>` to `&_` or calling a method that returns ` as Iterable>::Item<'_>` + = help: consider constraining the associated type ` as Iterable>::Item<'_>` to `&_` or calling a method that returns ` as Iterable>::Item<'_>` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0271]: type mismatch resolving `for<'a> <<[T] as Iterable>::Iter<'a> as std::iter::Iterator>::Item == <[T] as Iterable>::Item<'a>` @@ -51,7 +51,7 @@ LL | fn iter<'a>(&'a self) -> Self::Iter<'a> { | = note: expected associated type `<[T] as Iterable>::Item<'_>` found reference `&T` - = note: consider constraining the associated type `<[T] as Iterable>::Item<'_>` to `&_` or calling a method that returns `<[T] as Iterable>::Item<'_>` + = help: consider constraining the associated type `<[T] as Iterable>::Item<'_>` to `&_` or calling a method that returns `<[T] as Iterable>::Item<'_>` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-32323.stderr b/src/test/ui/issues/issue-32323.stderr index 7c0928b192499..369f56b9869a3 100644 --- a/src/test/ui/issues/issue-32323.stderr +++ b/src/test/ui/issues/issue-32323.stderr @@ -8,8 +8,10 @@ LL | pub fn f<'a, T: Tr<'a>>() -> >::Out {} | = note: expected associated type `>::Out` found unit type `()` - = note: consider constraining the associated type `>::Out` to `()` or calling a method that returns `>::Out` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +help: consider constraining the associated type `>::Out` to `()` + | +LL | pub fn f<'a, T: Tr<'a, Out = ()>>() -> >::Out {} + | ^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/specialization/specialization-default-projection.stderr b/src/test/ui/specialization/specialization-default-projection.stderr index 4344fe23bbcc4..ac15ab0681a02 100644 --- a/src/test/ui/specialization/specialization-default-projection.stderr +++ b/src/test/ui/specialization/specialization-default-projection.stderr @@ -9,7 +9,7 @@ LL | () | = note: expected associated type `::Assoc` found unit type `()` - = note: consider constraining the associated type `::Assoc` to `()` or calling a method that returns `::Assoc` + = help: consider constraining the associated type `::Assoc` to `()` or calling a method that returns `::Assoc` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types diff --git a/src/test/ui/specialization/specialization-default-types.stderr b/src/test/ui/specialization/specialization-default-types.stderr index d66aaa9627489..4dccf9ad9ab97 100644 --- a/src/test/ui/specialization/specialization-default-types.stderr +++ b/src/test/ui/specialization/specialization-default-types.stderr @@ -8,7 +8,7 @@ LL | Box::new(self) | = note: expected associated type `::Output` found struct `std::boxed::Box` - = note: consider constraining the associated type `::Output` to `std::boxed::Box` or calling a method that returns `::Output` + = help: consider constraining the associated type `::Output` to `std::boxed::Box` or calling a method that returns `::Output` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs index 1166785d73996..7465049787f59 100644 --- a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.rs @@ -6,6 +6,7 @@ trait Trait { type A; fn func(&self) -> Self::A; + fn funk(&self, _: Self::A); } fn foo(_: impl Trait, x: impl Trait) { @@ -21,6 +22,7 @@ fn foo2(x: impl Trait) { } fn bar2>(x: T) { + x.funk(3); //~ ERROR mismatched types qux(x.func()) //~ ERROR mismatched types } @@ -29,7 +31,7 @@ fn baz>(x: T) { } fn bat(x: &mut dyn Trait<(), A = ()>) { - qux(x) //~ ERROR mismatched types + qux(x.func()) //~ ERROR mismatched types } fn ban(x: T) where T: Trait { diff --git a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr index 08788db6ec908..5ae1d45c6b703 100644 --- a/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr +++ b/src/test/ui/suggestions/trait-with-missing-associated-type-restriction.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/trait-with-missing-associated-type-restriction.rs:12:9 + --> $DIR/trait-with-missing-associated-type-restriction.rs:13:9 | LL | qux(x.func()) | ^^^^^^^^ expected `usize`, found associated type @@ -12,7 +12,7 @@ LL | fn foo(_: impl Trait, x: impl Trait) { | ^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/trait-with-missing-associated-type-restriction.rs:16:9 + --> $DIR/trait-with-missing-associated-type-restriction.rs:17:9 | LL | qux(x.func()) | ^^^^^^^^ expected `usize`, found associated type @@ -25,7 +25,7 @@ LL | fn bar>(x: T) { | ^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/trait-with-missing-associated-type-restriction.rs:20:9 + --> $DIR/trait-with-missing-associated-type-restriction.rs:21:9 | LL | qux(x.func()) | ^^^^^^^^ expected `usize`, found associated type @@ -38,7 +38,25 @@ LL | fn foo2(x: impl Trait) { | ^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/trait-with-missing-associated-type-restriction.rs:24:9 + --> $DIR/trait-with-missing-associated-type-restriction.rs:25:12 + | +LL | x.funk(3); + | ^ expected associated type, found integer + | + = note: expected associated type `>::A` + found type `{integer}` +help: a method is available that returns `>::A` + --> $DIR/trait-with-missing-associated-type-restriction.rs:8:5 + | +LL | fn func(&self) -> Self::A; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ consider calling `Trait::func` +help: consider constraining the associated type `>::A` to `{integer}` + | +LL | fn bar2>(x: T) { + | ^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/trait-with-missing-associated-type-restriction.rs:26:9 | LL | qux(x.func()) | ^^^^^^^^ expected `usize`, found associated type @@ -51,7 +69,7 @@ LL | fn bar2>(x: T) { | ^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/trait-with-missing-associated-type-restriction.rs:28:9 + --> $DIR/trait-with-missing-associated-type-restriction.rs:30:9 | LL | fn baz>(x: T) { | - this type parameter @@ -62,16 +80,13 @@ LL | qux(x.func()) found type parameter `D` error[E0308]: mismatched types - --> $DIR/trait-with-missing-associated-type-restriction.rs:32:9 + --> $DIR/trait-with-missing-associated-type-restriction.rs:34:9 | -LL | qux(x) - | ^ expected `usize`, found mutable reference - | - = note: expected type `usize` - found mutable reference `&mut dyn Trait<(), A = ()>` +LL | qux(x.func()) + | ^^^^^^^^ expected `usize`, found `()` error[E0308]: mismatched types - --> $DIR/trait-with-missing-associated-type-restriction.rs:36:9 + --> $DIR/trait-with-missing-associated-type-restriction.rs:38:9 | LL | qux(x.func()) | ^^^^^^^^ expected `usize`, found associated type @@ -83,6 +98,6 @@ help: consider constraining the associated type `::A` to `usize` LL | fn ban(x: T) where T: Trait { | ^^^^^^^^^^^ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0308`. From ee96b8b11945f8bee1bcfa8f39ed06097c1dc9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Apr 2020 19:48:46 -0700 Subject: [PATCH 03/11] review comment: use or patterns --- src/librustc_middle/ty/error.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index 99ea0068fed2a..5720a2a02cbb9 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -404,10 +404,8 @@ impl<'tcx> TyCtxt<'tcx> { (ty::Param(_), ty::Projection(_)) | (ty::Projection(_), ty::Param(_)) => { db.note("you might be missing a type parameter or trait bound"); } - (ty::Param(p), ty::Dynamic(..)) - | (ty::Dynamic(..), ty::Param(p)) - | (ty::Param(p), ty::Opaque(..)) - | (ty::Opaque(..), ty::Param(p)) => { + (ty::Param(p), ty::Dynamic(..) | ty::Opaque(..)) + | (ty::Dynamic(..) | ty::Opaque(..), ty::Param(p)) => { let generics = self.generics_of(body_owner_def_id); let p_span = self.def_span(generics.type_param(p, self).def_id); if !sp.contains(p_span) { From 299bd12fe5edb4a99f85b1a94b9f9e7ce5365654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 14 Apr 2020 15:12:11 -0700 Subject: [PATCH 04/11] Point at associated types when they have a default type Associated types with a default type in a trait can't be relied upon to remain of that default type when in use, so literals of that type can't be used in the trait's items. Point at the associated type and state that information. Reduce verbosity for associated consts of the wrong type. --- src/librustc_middle/traits/mod.rs | 3 + .../traits/structural_impls.rs | 1 + src/librustc_middle/ty/error.rs | 70 +++++++++++++++++-- .../traits/error_reporting/suggestions.rs | 7 ++ src/librustc_typeck/check/compare_method.rs | 1 + ...ssociated-const-generic-obligations.stderr | 2 - .../defaults-in-other-trait-items.rs | 8 +-- .../defaults-in-other-trait-items.stderr | 12 ++-- .../defaults-specialization.stderr | 13 ++-- .../specialization-default-types.stderr | 4 +- 10 files changed, 96 insertions(+), 25 deletions(-) diff --git a/src/librustc_middle/traits/mod.rs b/src/librustc_middle/traits/mod.rs index d6989fd8e4e57..3a05d577bfa7e 100644 --- a/src/librustc_middle/traits/mod.rs +++ b/src/librustc_middle/traits/mod.rs @@ -193,6 +193,9 @@ pub enum ObligationCauseCode<'tcx> { DerivedObligation(DerivedObligationCause<'tcx>), + /// Error derived when matching traits/impls; see ObligationCause for more details + CompareImplConstObligation, + /// Error derived when matching traits/impls; see ObligationCause for more details CompareImplMethodObligation { item_name: ast::Name, diff --git a/src/librustc_middle/traits/structural_impls.rs b/src/librustc_middle/traits/structural_impls.rs index 69a5213d3e4a5..668c84ad5e6df 100644 --- a/src/librustc_middle/traits/structural_impls.rs +++ b/src/librustc_middle/traits/structural_impls.rs @@ -164,6 +164,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> { tcx.lift(cause).map(super::ImplDerivedObligation) } super::DerivedObligation(ref cause) => tcx.lift(cause).map(super::DerivedObligation), + super::CompareImplConstObligation => Some(super::CompareImplConstObligation), super::CompareImplMethodObligation { item_name, impl_item_def_id, diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index 5720a2a02cbb9..14909fff1cedb 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -339,7 +339,7 @@ impl<'tcx> TyCtxt<'tcx> { body_owner_def_id: DefId, ) { use self::TypeError::*; - + debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause); match err { Sorts(values) => { let expected_str = values.expected.sort_string(self); @@ -623,8 +623,12 @@ impl Trait for X { ) => false, _ => true, }; - let impl_comparison = - matches!(cause_code, ObligationCauseCode::CompareImplMethodObligation { .. }); + let impl_comparison = matches!( + cause_code, + ObligationCauseCode::CompareImplMethodObligation { .. } + | ObligationCauseCode::CompareImplTypeObligation { .. } + | ObligationCauseCode::CompareImplConstObligation + ); 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 @@ -679,12 +683,66 @@ impl Trait for X { suggested |= self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found); } + if let (Some(hir_id), false) = (self.hir().as_local_hir_id(body_owner_def_id), suggested) { + // When `body_owner` is an `impl` or `trait` item, look in its associated types for + // `expected` and point at it. + let parent_id = self.hir().get_parent_item(hir_id); + let item = self.hir().find(parent_id); + debug!("expected_projection parent item {:?}", item); + match item { + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(.., items), .. + })) => { + // FIXME: account for `#![feature(specialization)]` + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type | hir::AssocItemKind::OpaqueTy => { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) + == values.found + { + if let hir::Defaultness::Default { has_value: true } = + item.defaultness + { + db.span_label( + item.span, + "associated type defaults can't be assumed inside the \ + trait defining them", + ); + } else { + db.span_label(item.span, "expected this associated type"); + } + suggested = true; + } + } + _ => {} + } + } + } + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl { items, .. }, + .. + })) => { + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type | hir::AssocItemKind::OpaqueTy => { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) + == values.found + { + db.span_label(item.span, "expected this associated type"); + suggested = true; + } + } + _ => {} + } + } + } + _ => {} + } + } if !suggested && !impl_comparison { // Generic suggestion when we can't be more specific. if callable_scope { - db.help( - &format!("{} or calling a method that returns `{}`", msg, values.expected,), - ); + db.help(&format!("{} or calling a method that returns `{}`", msg, values.expected)); } else { db.help(&msg); } diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs index 5ec2d68ab2a7d..ce7b1390d46b6 100644 --- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs +++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs @@ -1738,6 +1738,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { predicate )); } + ObligationCauseCode::CompareImplConstObligation => { + err.note(&format!( + "the requirement `{}` appears on the associated impl constant \ + but not on the corresponding associated trait constant", + predicate + )); + } ObligationCauseCode::ReturnType | ObligationCauseCode::ReturnValue(_) | ObligationCauseCode::BlockTailExpression(_) => (), diff --git a/src/librustc_typeck/check/compare_method.rs b/src/librustc_typeck/check/compare_method.rs index 6e4af6d769add..29cd9681295be 100644 --- a/src/librustc_typeck/check/compare_method.rs +++ b/src/librustc_typeck/check/compare_method.rs @@ -966,6 +966,7 @@ crate fn compare_const_impl<'tcx>( let impl_ty = tcx.type_of(impl_c.def_id); let trait_ty = tcx.type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs); let mut cause = ObligationCause::misc(impl_c_span, impl_c_hir_id); + cause.code = ObligationCauseCode::CompareImplConstObligation; // There is no "body" here, so just pass dummy id. let impl_ty = diff --git a/src/test/ui/associated-const/associated-const-generic-obligations.stderr b/src/test/ui/associated-const/associated-const-generic-obligations.stderr index 6e3ec4ed155b3..d8bac07e058da 100644 --- a/src/test/ui/associated-const/associated-const-generic-obligations.stderr +++ b/src/test/ui/associated-const/associated-const-generic-obligations.stderr @@ -9,8 +9,6 @@ LL | const FROM: &'static str = "foo"; | = note: expected associated type `::Out` found reference `&'static str` - = help: consider constraining the associated type `::Out` to `&'static str` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to previous error diff --git a/src/test/ui/associated-types/defaults-in-other-trait-items.rs b/src/test/ui/associated-types/defaults-in-other-trait-items.rs index 41dc67d6f4759..4014f46285d70 100644 --- a/src/test/ui/associated-types/defaults-in-other-trait-items.rs +++ b/src/test/ui/associated-types/defaults-in-other-trait-items.rs @@ -3,15 +3,13 @@ // Associated type defaults may not be assumed inside the trait defining them. // ie. they only resolve to `::A`, not the actual type `()` trait Tr { - type A = (); + type A = (); //~ NOTE associated type defaults can't be assumed inside the trait defining them fn f(p: Self::A) { let () = p; //~^ ERROR mismatched types //~| NOTE expected associated type, found `()` //~| NOTE expected associated type `::A` - //~| HELP consider constraining the associated type - //~| NOTE for more information, visit } } @@ -31,15 +29,13 @@ impl Tr for u8 { } trait AssocConst { - type Ty = u8; + type Ty = u8; //~ NOTE associated type defaults can't be assumed inside the trait defining them // Assoc. consts also cannot assume that default types hold const C: Self::Ty = 0u8; //~^ ERROR mismatched types //~| NOTE expected associated type, found `u8` //~| NOTE expected associated type `::Ty` - //~| HELP consider constraining the associated type - //~| NOTE for more information, visit } // An impl can, however diff --git a/src/test/ui/associated-types/defaults-in-other-trait-items.stderr b/src/test/ui/associated-types/defaults-in-other-trait-items.stderr index a5b170d05c45e..493df30a64daf 100644 --- a/src/test/ui/associated-types/defaults-in-other-trait-items.stderr +++ b/src/test/ui/associated-types/defaults-in-other-trait-items.stderr @@ -1,24 +1,26 @@ error[E0308]: mismatched types --> $DIR/defaults-in-other-trait-items.rs:9:13 | +LL | type A = (); + | ------------ associated type defaults can't be assumed inside the trait defining them +... LL | let () = p; | ^^ expected associated type, found `()` | = note: expected associated type `::A` found unit type `()` - = help: consider constraining the associated type `::A` to `()` or calling a method that returns `::A` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types - --> $DIR/defaults-in-other-trait-items.rs:37:25 + --> $DIR/defaults-in-other-trait-items.rs:35:25 | +LL | type Ty = u8; + | ------------- associated type defaults can't be assumed inside the trait defining them +... LL | const C: Self::Ty = 0u8; | ^^^ expected associated type, found `u8` | = note: expected associated type `::Ty` found type `u8` - = help: consider constraining the associated type `::Ty` to `u8` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error: aborting due to 2 previous errors diff --git a/src/test/ui/associated-types/defaults-specialization.stderr b/src/test/ui/associated-types/defaults-specialization.stderr index 2f3ce832725cf..37a4d9b60fdfd 100644 --- a/src/test/ui/associated-types/defaults-specialization.stderr +++ b/src/test/ui/associated-types/defaults-specialization.stderr @@ -16,6 +16,9 @@ error[E0053]: method `make` has an incompatible type for trait LL | fn make() -> Self::Ty { | -------- type in trait ... +LL | default type Ty = bool; + | ----------------------- expected this associated type +LL | LL | fn make() -> bool { true } | ^^^^ expected associated type, found `bool` | @@ -25,6 +28,9 @@ LL | fn make() -> bool { true } error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:9:9 | +LL | type Ty = u8; + | ------------- associated type defaults can't be assumed inside the trait defining them +LL | LL | fn make() -> Self::Ty { | -------- expected `::Ty` because of return type LL | 0u8 @@ -32,8 +38,6 @@ LL | 0u8 | = note: expected associated type `::Ty` found type `u8` - = help: consider constraining the associated type `::Ty` to `u8` or calling a method that returns `::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:25:29 @@ -51,6 +55,9 @@ LL | fn make() -> Self::Ty { 0u8 } error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:43:29 | +LL | default type Ty = bool; + | ----------------------- expected this associated type +LL | LL | fn make() -> Self::Ty { true } | -------- ^^^^ expected associated type, found `bool` | | @@ -58,8 +65,6 @@ LL | fn make() -> Self::Ty { true } | = note: expected associated type ` as Tr>::Ty` found type `bool` - = help: consider constraining the associated type ` as Tr>::Ty` to `bool` or calling a method that returns ` as Tr>::Ty` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types --> $DIR/defaults-specialization.rs:86:32 diff --git a/src/test/ui/specialization/specialization-default-types.stderr b/src/test/ui/specialization/specialization-default-types.stderr index 4dccf9ad9ab97..7233387eba1fa 100644 --- a/src/test/ui/specialization/specialization-default-types.stderr +++ b/src/test/ui/specialization/specialization-default-types.stderr @@ -1,6 +1,8 @@ error[E0308]: mismatched types --> $DIR/specialization-default-types.rs:15:9 | +LL | default type Output = Box; + | ----------------------------- expected this associated type LL | default fn generate(self) -> Self::Output { | ------------ expected `::Output` because of return type LL | Box::new(self) @@ -8,8 +10,6 @@ LL | Box::new(self) | = note: expected associated type `::Output` found struct `std::boxed::Box` - = help: consider constraining the associated type `::Output` to `std::boxed::Box` or calling a method that returns `::Output` - = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types --> $DIR/specialization-default-types.rs:25:5 From 6648a08b30636cffa9dafd258b372a1bc25da549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 14 Apr 2020 15:21:19 -0700 Subject: [PATCH 05/11] fix rebase --- src/librustc_middle/ty/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index 14909fff1cedb..1b6526cd49cfe 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -642,7 +642,7 @@ impl Trait for X { .items .iter() .filter(|(name, item)| { - ty::AssocKind::Method == item.kind && Some(**name) != current_method_ident + ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident }) .filter_map(|(_, item)| { let method = self.fn_sig(item.def_id); From b0085c86fc6c0c0a48237e5966f0fc54b6b45e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 14 Apr 2020 22:53:52 -0700 Subject: [PATCH 06/11] Suggest constraint on `impl Trait` in return type Fix #71035. --- src/librustc_middle/ty/error.rs | 68 +++++++++++++------ .../impl-trait-return-missing-constraint.rs | 33 +++++++++ ...mpl-trait-return-missing-constraint.stderr | 20 ++++++ 3 files changed, 100 insertions(+), 21 deletions(-) create mode 100644 src/test/ui/associated-types/impl-trait-return-missing-constraint.rs create mode 100644 src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index 1b6526cd49cfe..8d57f39c1a2b2 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -552,7 +552,7 @@ impl Trait 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 Trait for X { == Some(def_id) { // This is type param `A` in `::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 Trait 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 Trait 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 `::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 + } } diff --git a/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs b/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs new file mode 100644 index 0000000000000..8036655d27592 --- /dev/null +++ b/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs @@ -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 { +//~^ ERROR type mismatch resolving `::Item == i32` + bar() +} + +fn main() { + let _ = baz(); +} + diff --git a/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr b/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr new file mode 100644 index 0000000000000..566e390a31e48 --- /dev/null +++ b/src/test/ui/associated-types/impl-trait-return-missing-constraint.stderr @@ -0,0 +1,20 @@ +error[E0271]: type mismatch resolving `::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 { + | ^^^^^^^^^^^^^^^^^^^^ expected associated type, found `i32` + | + = note: expected associated type `::Item` + found type `i32` + = note: the return type of a function must have a statically known size +help: consider constraining the associated type `::Item` to `i32` + | +LL | fn bar() -> impl Bar { + | ^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0271`. From 9bb5b549aa152550c9a79007d630792ff5b2a63f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 14 Apr 2020 23:09:30 -0700 Subject: [PATCH 07/11] Add docs --- src/librustc_middle/ty/error.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index 8d57f39c1a2b2..4b0b648b3b483 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -584,6 +584,19 @@ impl Trait for X { false } + /// An associated type was expected and a different type was found. + /// + /// We perform a few different checks to see what we can suggest: + /// + /// - In the current item, look for associated functions that return the expected type and + /// suggest calling them. (Not a structured suggestion.) + /// - If any of the item's generic bounds can be constrained, we suggest constraining the + /// associated type to the found type. + /// - If the associated type has a default type and was expected inside of a `trait`, we + /// mention that this is disallowed. + /// - If all other things fail, and the error is not because of a mismatch between the `trait` + /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc + /// fn that returns the type. fn expected_projection( &self, db: &mut DiagnosticBuilder<'_>, @@ -600,6 +613,7 @@ impl Trait for X { let body_owner = self.hir().get_if_local(body_owner_def_id); let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); + // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. let callable_scope = match body_owner { Some( hir::Node::Item(hir::Item { @@ -784,6 +798,8 @@ fn foo(&self) -> Self::T { String::new() } } } + /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` + /// requirement, provide a strucuted suggestion to constrain it to a given type `ty`. fn constrain_generic_bound_associated_type_structured_suggestion( &self, db: &mut DiagnosticBuilder<'_>, @@ -812,6 +828,8 @@ fn foo(&self) -> Self::T { String::new() } false } + /// Given a span corresponding to a bound, provide a structured suggestion to set an + /// associated type to a given type `ty`. fn constrain_associated_type_structured_suggestion( &self, db: &mut DiagnosticBuilder<'_>, From 74b7ed78b1a70ff61fd518f33858ffb3a27c6f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 15 Apr 2020 08:26:32 -0700 Subject: [PATCH 08/11] trailing newlines --- .../ui/associated-types/impl-trait-return-missing-constraint.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs b/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs index 8036655d27592..5f994f26534bd 100644 --- a/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs +++ b/src/test/ui/associated-types/impl-trait-return-missing-constraint.rs @@ -30,4 +30,3 @@ fn baz() -> impl Bar { fn main() { let _ = baz(); } - From de3b4d4dae5bc9f55a6ed1b56b6caf98711b2a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 3 May 2020 14:09:58 -0700 Subject: [PATCH 09/11] fix rebase --- src/librustc_middle/ty/error.rs | 4 +++- .../ui/suggestions/expected-boxed-future-isnt-pinned.stderr | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index 4b0b648b3b483..16da3eac87557 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -698,7 +698,9 @@ impl Trait for X { suggested |= self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found); } - if let (Some(hir_id), false) = (self.hir().as_local_hir_id(body_owner_def_id), suggested) { + if let (Some(hir_id), false) = + (body_owner_def_id.as_local().map(|id| self.hir().as_local_hir_id(id)), suggested) + { // When `body_owner` is an `impl` or `trait` item, look in its associated types for // `expected` and point at it. let parent_id = self.hir().get_parent_item(hir_id); diff --git a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr index 0e68e81d7ab8e..52e13dbc2dd85 100644 --- a/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr +++ b/src/test/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -38,8 +38,6 @@ LL | Pin::new(x) | = note: expected struct `std::boxed::Box + std::marker::Send>` found type parameter `F` - = help: type parameters must be constrained to match other types - = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html error[E0277]: `dyn std::future::Future + std::marker::Send` cannot be unpinned From 3c872e2dc70cf20b5ac7c5ced4191824cd64bd2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 3 May 2020 16:42:54 -0700 Subject: [PATCH 10/11] review comments: move logic to their own methods --- src/librustc_middle/ty/error.rs | 276 ++++++++++++++++---------------- 1 file changed, 139 insertions(+), 137 deletions(-) diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index 16da3eac87557..22f576db08d13 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -5,7 +5,7 @@ use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; use rustc_errors::{pluralize, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, MultiSpan, Span}; use rustc_target::spec::abi; @@ -616,26 +616,11 @@ impl Trait for X { // We don't want to suggest calling an assoc fn in a scope where that isn't feasible. let callable_scope = match body_owner { Some( - hir::Node::Item(hir::Item { - kind: - hir::ItemKind::Trait(..) - | hir::ItemKind::Impl { .. } - | hir::ItemKind::Const(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::Union(..), - .. - }) - | hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..), - .. - }) - | hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(..), - .. - }), - ) => false, - _ => true, + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) + | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) + | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }), + ) => true, + _ => false, }; let impl_comparison = matches!( cause_code, @@ -649,112 +634,20 @@ impl Trait for X { // type error is a comparison of an `impl` with its `trait` or when the // scope is outside of a `Body`. } else { - 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() - .filter(|(name, item)| { - ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident - }) - .filter_map(|(_, item)| { - let method = self.fn_sig(item.def_id); - match method.output().skip_binder().kind { - ty::Projection(ty::ProjectionTy { item_def_id, .. }) - if item_def_id == proj_ty.item_def_id => - { - Some(( - self.sess.source_map().guess_head_span(self.def_span(item.def_id)), - format!("consider calling `{}`", self.def_path_str(item.def_id)), - )) - } - _ => None, - } - }) - .collect(); - if !methods.is_empty() { - // Use a single `help:` to show all the methods in the trait that can - // be used to construct the expected associated type. - let mut span: MultiSpan = - methods.iter().map(|(sp, _)| *sp).collect::>().into(); - let msg = format!( - "{some} method{s} {are} available that return{r} `{ty}`", - some = if methods.len() == 1 { "a" } else { "some" }, - s = pluralize!(methods.len()), - are = if methods.len() == 1 { "is" } else { "are" }, - r = if methods.len() == 1 { "s" } else { "" }, - ty = values.expected - ); - for (sp, label) in methods.into_iter() { - span.push_span_label(sp, label); - } - db.span_help(span, &msg); - suggested = true; - } + suggested |= self.point_at_methods_that_satisfy_associated_type( + db, + assoc.container.id(), + current_method_ident, + proj_ty.item_def_id, + values.expected, + ); // Possibly suggest constraining the associated type to conform to the // found type. suggested |= self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found); } - if let (Some(hir_id), false) = - (body_owner_def_id.as_local().map(|id| self.hir().as_local_hir_id(id)), suggested) - { - // When `body_owner` is an `impl` or `trait` item, look in its associated types for - // `expected` and point at it. - let parent_id = self.hir().get_parent_item(hir_id); - let item = self.hir().find(parent_id); - debug!("expected_projection parent item {:?}", item); - match item { - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(.., items), .. - })) => { - // FIXME: account for `#![feature(specialization)]` - for item in &items[..] { - match item.kind { - hir::AssocItemKind::Type | hir::AssocItemKind::OpaqueTy => { - if self.type_of(self.hir().local_def_id(item.id.hir_id)) - == values.found - { - if let hir::Defaultness::Default { has_value: true } = - item.defaultness - { - db.span_label( - item.span, - "associated type defaults can't be assumed inside the \ - trait defining them", - ); - } else { - db.span_label(item.span, "expected this associated type"); - } - suggested = true; - } - } - _ => {} - } - } - } - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl { items, .. }, - .. - })) => { - for item in &items[..] { - match item.kind { - hir::AssocItemKind::Type | hir::AssocItemKind::OpaqueTy => { - if self.type_of(self.hir().local_def_id(item.id.hir_id)) - == values.found - { - db.span_label(item.span, "expected this associated type"); - suggested = true; - } - } - _ => {} - } - } - } - _ => {} - } + if !suggested { + suggested = self.point_at_associated_type(db, body_owner_def_id, values.found); } 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 @@ -800,6 +693,121 @@ fn foo(&self) -> Self::T { String::new() } } } + fn point_at_methods_that_satisfy_associated_type( + &self, + db: &mut DiagnosticBuilder<'_>, + assoc_container_id: DefId, + current_method_ident: Option, + proj_ty_item_def_id: DefId, + expected: Ty<'tcx>, + ) -> bool { + 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() + .filter(|(name, item)| { + ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident + }) + .filter_map(|(_, item)| { + let method = self.fn_sig(item.def_id); + match method.output().skip_binder().kind { + ty::Projection(ty::ProjectionTy { item_def_id, .. }) + if item_def_id == proj_ty_item_def_id => + { + Some(( + self.sess.source_map().guess_head_span(self.def_span(item.def_id)), + format!("consider calling `{}`", self.def_path_str(item.def_id)), + )) + } + _ => None, + } + }) + .collect(); + if !methods.is_empty() { + // Use a single `help:` to show all the methods in the trait that can + // be used to construct the expected associated type. + let mut span: MultiSpan = + methods.iter().map(|(sp, _)| *sp).collect::>().into(); + let msg = format!( + "{some} method{s} {are} available that return{r} `{ty}`", + some = if methods.len() == 1 { "a" } else { "some" }, + s = pluralize!(methods.len()), + are = if methods.len() == 1 { "is" } else { "are" }, + r = if methods.len() == 1 { "s" } else { "" }, + ty = expected + ); + for (sp, label) in methods.into_iter() { + span.push_span_label(sp, label); + } + db.span_help(span, &msg); + return true; + } + false + } + + fn point_at_associated_type( + &self, + db: &mut DiagnosticBuilder<'_>, + body_owner_def_id: DefId, + found: Ty<'tcx>, + ) -> bool { + let hir_id = match body_owner_def_id.as_local().map(|id| self.hir().as_local_hir_id(id)) { + Some(hir_id) => hir_id, + None => return false, + }; + // When `body_owner` is an `impl` or `trait` item, look in its associated types for + // `expected` and point at it. + let parent_id = self.hir().get_parent_item(hir_id); + let item = self.hir().find(parent_id); + debug!("expected_projection parent item {:?}", item); + match item { + Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => { + // FIXME: account for `#![feature(specialization)]` + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type | hir::AssocItemKind::OpaqueTy => { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { + if let hir::Defaultness::Default { has_value: true } = + item.defaultness + { + db.span_label( + item.span, + "associated type defaults can't be assumed inside the \ + trait defining them", + ); + } else { + db.span_label(item.span, "expected this associated type"); + } + return true; + } + } + _ => {} + } + } + } + Some(hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl { items, .. }, .. + })) => { + for item in &items[..] { + match item.kind { + hir::AssocItemKind::Type | hir::AssocItemKind::OpaqueTy => { + if self.type_of(self.hir().local_def_id(item.id.hir_id)) == found { + db.span_label(item.span, "expected this associated type"); + return true; + } + } + _ => {} + } + } + } + _ => {} + } + false + } + /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref` /// requirement, provide a strucuted suggestion to constrain it to a given type `ty`. fn constrain_generic_bound_associated_type_structured_suggestion( @@ -812,22 +820,16 @@ fn foo(&self) -> Self::T { String::new() } msg: &str, ) -> bool { // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting. - for bound in bounds { - match bound { - hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => { - // Relate the type param against `T` in `::Foo`. - 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; - } - } - _ => {} + bounds.iter().any(|bound| match bound { + hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => { + // Relate the type param against `T` in `::Foo`. + ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id) + && self.constrain_associated_type_structured_suggestion( + db, ptr.span, assoc, ty, msg, + ) } - } - false + _ => false, + }) } /// Given a span corresponding to a bound, provide a structured suggestion to set an From b368229d9bbaa840e777d33e36e649967e7ecb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 3 May 2020 18:20:53 -0700 Subject: [PATCH 11/11] review comment: use early return --- src/librustc_middle/ty/error.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/librustc_middle/ty/error.rs b/src/librustc_middle/ty/error.rs index 22f576db08d13..4e1a8b0e92f13 100644 --- a/src/librustc_middle/ty/error.rs +++ b/src/librustc_middle/ty/error.rs @@ -609,7 +609,6 @@ impl Trait for X { "consider constraining the associated type `{}` to `{}`", values.expected, values.found ); - let mut suggested = false; let body_owner = self.hir().get_if_local(body_owner_def_id); let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name); @@ -634,7 +633,10 @@ impl Trait for X { // type error is a comparison of an `impl` with its `trait` or when the // scope is outside of a `Body`. } else { - suggested |= self.point_at_methods_that_satisfy_associated_type( + // If we find a suitable associated function that returns the expected type, we don't + // want the more general suggestion later in this method about "consider constraining + // the associated type or calling a method that returns the associated type". + let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type( db, assoc.container.id(), current_method_ident, @@ -643,25 +645,32 @@ impl Trait for X { ); // Possibly suggest constraining the associated type to conform to the // found type. - suggested |= - self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found); - } - if !suggested { - suggested = self.point_at_associated_type(db, body_owner_def_id, values.found); + if self.suggest_constraint(db, &msg, body_owner_def_id, proj_ty, values.found) + || point_at_assoc_fn + { + return; + } } + 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( + if self.constrain_associated_type_structured_suggestion( db, self.def_span(def_id), &assoc, values.found, &msg, - ); + ) { + return; + } + } + + if self.point_at_associated_type(db, body_owner_def_id, values.found) { + return; } - if !suggested && !impl_comparison { + if !impl_comparison { // Generic suggestion when we can't be more specific. if callable_scope { db.help(&format!("{} or calling a method that returns `{}`", msg, values.expected));