diff --git a/src/librustc_parse/parser/generics.rs b/src/librustc_parse/parser/generics.rs index 0984263bb283e..4d9a3266567f3 100644 --- a/src/librustc_parse/parser/generics.rs +++ b/src/librustc_parse/parser/generics.rs @@ -1,7 +1,6 @@ use super::Parser; use rustc_errors::PResult; -use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::{kw, sym}; use syntax::ast::{self, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause}; use syntax::token; @@ -160,7 +159,10 @@ impl<'a> Parser<'a> { }; Ok(ast::Generics { params, - where_clause: WhereClause { predicates: Vec::new(), span: DUMMY_SP }, + where_clause: WhereClause { + predicates: Vec::new(), + span: self.prev_span.shrink_to_hi(), + }, span, }) } diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 7e98797d0c2c9..410b8883a29d9 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -7,7 +7,7 @@ use rustc::hir::map as hir_map; use rustc::hir::map::Map; use rustc::ty::print::with_crate_prefix; use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, Res}; @@ -537,10 +537,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !unsatisfied_predicates.is_empty() { let def_span = |def_id| self.tcx.sess.source_map().def_span(self.tcx.def_span(def_id)); + let mut type_params = FxHashMap::default(); let mut bound_spans = vec![]; + let mut collect_type_param_suggestions = + |self_ty: Ty<'_>, parent_pred: &ty::Predicate<'_>, obligation: &str| { + if let (ty::Param(_), ty::Predicate::Trait(p, _)) = + (&self_ty.kind, parent_pred) + { + if let ty::Adt(def, _) = p.skip_binder().trait_ref.self_ty().kind { + let id = self.tcx.hir().as_local_hir_id(def.did).unwrap(); + let node = self.tcx.hir().get(id); + match node { + hir::Node::Item(hir::Item { kind, .. }) => { + if let Some(g) = kind.generics() { + let key = match &g.where_clause.predicates[..] { + [.., pred] => { + (pred.span().shrink_to_hi(), false) + } + [] => ( + g.where_clause + .span_for_predicates_or_empty_place(), + true, + ), + }; + type_params + .entry(key) + .or_insert_with(FxHashSet::default) + .insert(obligation.to_owned()); + } + } + _ => {} + } + } + } + }; let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { let msg = format!( - "doesn't satisfy {}", + "doesn't satisfy `{}`", if obligation.len() > 50 { quiet } else { obligation } ); match &self_ty.kind { @@ -560,7 +593,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Point at the closure that couldn't satisfy the bound. ty::Closure(def_id, _) => bound_spans - .push((def_span(*def_id), format!("doesn't satisfy {}", quiet))), + .push((def_span(*def_id), format!("doesn't satisfy `{}`", quiet))), _ => {} } }; @@ -574,25 +607,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .tcx .associated_item(pred.skip_binder().projection_ty.item_def_id); let ty = pred.skip_binder().ty; - let obligation = - format!("`{}::{} = {}`", trait_ref, assoc.ident, ty); + let obligation = format!("{}::{} = {}", trait_ref, assoc.ident, ty); let quiet = format!( - "`<_ as {}>::{} = {}`", + "<_ as {}>::{} = {}", trait_ref.print_only_trait_path(), assoc.ident, ty ); bound_span_label(trait_ref.self_ty(), &obligation, &quiet); - Some(obligation) + Some((obligation, trait_ref.self_ty())) } ty::Predicate::Trait(poly_trait_ref, _) => { let p = poly_trait_ref.skip_binder().trait_ref; let self_ty = p.self_ty(); let path = p.print_only_trait_path(); - let obligation = format!("`{}: {}`", self_ty, path); - let quiet = format!("`_: {}`", path); + let obligation = format!("{}: {}", self_ty, path); + let quiet = format!("_: {}", path); bound_span_label(self_ty, &obligation, &quiet); - Some(obligation) + Some((obligation, self_ty)) } _ => None, } @@ -600,17 +632,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut bound_list = unsatisfied_predicates .iter() .filter_map(|(pred, parent_pred)| { - format_pred(*pred).map(|pred| match parent_pred { - None => pred, + format_pred(*pred).map(|(p, self_ty)| match parent_pred { + None => format!("`{}`", p), Some(parent_pred) => match format_pred(*parent_pred) { - None => pred, - Some(parent_pred) => { - format!("{} which is required by {}", pred, parent_pred) + None => format!("`{}`", p), + Some((parent_p, _)) => { + collect_type_param_suggestions(self_ty, parent_pred, &p); + format!("`{}` which is required by `{}`", p, parent_p) } }, }) }) .collect::>(); + for ((span, empty_where), obligations) in type_params.into_iter() { + err.span_suggestion_verbose( + span, + &format!( + "consider restricting the type parameter{s} to satisfy the \ + obligation{s}", + s = pluralize!(obligations.len()) + ), + format!( + "{} {}", + if empty_where { " where" } else { "," }, + obligations.into_iter().collect::>().join(", ") + ), + Applicability::MaybeIncorrect, + ); + } + bound_list.sort(); bound_list.dedup(); // #35677 bound_spans.sort(); diff --git a/src/test/ui/suggestions/missing-trait-bounds-for-method-call.rs b/src/test/ui/suggestions/missing-trait-bounds-for-method-call.rs new file mode 100644 index 0000000000000..fefdf149f556c --- /dev/null +++ b/src/test/ui/suggestions/missing-trait-bounds-for-method-call.rs @@ -0,0 +1,31 @@ +#[derive(Default, PartialEq)] +struct Foo { + bar: Box<[T]>, +} + +trait Bar { + fn foo(&self) {} +} + +impl Bar for Foo {} + +impl Foo { + fn bar(&self) { + self.foo(); + //~^ ERROR no method named `foo` found for reference `&Foo` in the current scope + } +} + +struct Fin where T: Bar { + bar: Box<[T]>, +} + +impl Bar for Fin {} + +impl Fin { + fn bar(&self) { + self.foo(); + //~^ ERROR no method named `foo` found for reference `&Fin` in the current scope + } +} +fn main() {} diff --git a/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr b/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr new file mode 100644 index 0000000000000..b1b31a67650b6 --- /dev/null +++ b/src/test/ui/suggestions/missing-trait-bounds-for-method-call.stderr @@ -0,0 +1,44 @@ +error[E0599]: no method named `foo` found for reference `&Foo` in the current scope + --> $DIR/missing-trait-bounds-for-method-call.rs:14:14 + | +LL | struct Foo { + | ------------- doesn't satisfy `Foo: Bar` +... +LL | trait Bar { + | --------- `Bar` defines an item `foo`, perhaps you need to implement it +... +LL | self.foo(); + | ^^^ method not found in `&Foo` + | + = note: the method `foo` exists but the following trait bounds were not satisfied: + `T: Bar` which is required by `Foo: Bar` + `T: std::default::Default` which is required by `Foo: Bar` + = help: items from traits can only be used if the trait is implemented and in scope +help: consider restricting the type parameters to satisfy the obligations + | +LL | struct Foo where T: Bar, T: std::default::Default { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0599]: no method named `foo` found for reference `&Fin` in the current scope + --> $DIR/missing-trait-bounds-for-method-call.rs:27:14 + | +LL | trait Bar { + | --------- `Bar` defines an item `foo`, perhaps you need to implement it +... +LL | struct Fin where T: Bar { + | -------------------------- doesn't satisfy `Fin: Bar` +... +LL | self.foo(); + | ^^^ method not found in `&Fin` + | + = note: the method `foo` exists but the following trait bounds were not satisfied: + `T: std::default::Default` which is required by `Fin: Bar` + = help: items from traits can only be used if the trait is implemented and in scope +help: consider restricting the type parameter to satisfy the obligation + | +LL | struct Fin where T: Bar, T: std::default::Default { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`.