Suggest constraining type parameters
This commit is contained in:
parent
1e7bcc733a
commit
392d853589
4 changed files with 144 additions and 17 deletions
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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::<Vec<String>>();
|
||||
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::<Vec<_>>().join(", ")
|
||||
),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
||||
bound_list.sort();
|
||||
bound_list.dedup(); // #35677
|
||||
bound_spans.sort();
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
#[derive(Default, PartialEq)]
|
||||
struct Foo<T> {
|
||||
bar: Box<[T]>,
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
fn foo(&self) {}
|
||||
}
|
||||
|
||||
impl<T: Default + Bar> Bar for Foo<T> {}
|
||||
|
||||
impl<T> Foo<T> {
|
||||
fn bar(&self) {
|
||||
self.foo();
|
||||
//~^ ERROR no method named `foo` found for reference `&Foo<T>` in the current scope
|
||||
}
|
||||
}
|
||||
|
||||
struct Fin<T> where T: Bar {
|
||||
bar: Box<[T]>,
|
||||
}
|
||||
|
||||
impl<T: Default + Bar> Bar for Fin<T> {}
|
||||
|
||||
impl<T: Bar> Fin<T> {
|
||||
fn bar(&self) {
|
||||
self.foo();
|
||||
//~^ ERROR no method named `foo` found for reference `&Fin<T>` in the current scope
|
||||
}
|
||||
}
|
||||
fn main() {}
|
|
@ -0,0 +1,44 @@
|
|||
error[E0599]: no method named `foo` found for reference `&Foo<T>` in the current scope
|
||||
--> $DIR/missing-trait-bounds-for-method-call.rs:14:14
|
||||
|
|
||||
LL | struct Foo<T> {
|
||||
| ------------- doesn't satisfy `Foo<T>: Bar`
|
||||
...
|
||||
LL | trait Bar {
|
||||
| --------- `Bar` defines an item `foo`, perhaps you need to implement it
|
||||
...
|
||||
LL | self.foo();
|
||||
| ^^^ method not found in `&Foo<T>`
|
||||
|
|
||||
= note: the method `foo` exists but the following trait bounds were not satisfied:
|
||||
`T: Bar` which is required by `Foo<T>: Bar`
|
||||
`T: std::default::Default` which is required by `Foo<T>: 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<T> where T: Bar, T: std::default::Default {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0599]: no method named `foo` found for reference `&Fin<T>` 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<T> where T: Bar {
|
||||
| -------------------------- doesn't satisfy `Fin<T>: Bar`
|
||||
...
|
||||
LL | self.foo();
|
||||
| ^^^ method not found in `&Fin<T>`
|
||||
|
|
||||
= note: the method `foo` exists but the following trait bounds were not satisfied:
|
||||
`T: std::default::Default` which is required by `Fin<T>: 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<T> where T: Bar, T: std::default::Default {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
Loading…
Add table
Reference in a new issue