Skip to content

Commit 7818a1d

Browse files
committed
Suggest constraining type parameters
1 parent cfbb746 commit 7818a1d

File tree

4 files changed

+144
-17
lines changed

4 files changed

+144
-17
lines changed

src/librustc_parse/parser/generics.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use super::Parser;
22

33
use rustc_errors::PResult;
4-
use rustc_span::source_map::DUMMY_SP;
54
use rustc_span::symbol::{kw, sym};
65
use syntax::ast::{self, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause};
76
use syntax::token;
@@ -160,7 +159,10 @@ impl<'a> Parser<'a> {
160159
};
161160
Ok(ast::Generics {
162161
params,
163-
where_clause: WhereClause { predicates: Vec::new(), span: DUMMY_SP },
162+
where_clause: WhereClause {
163+
predicates: Vec::new(),
164+
span: self.prev_span.shrink_to_hi(),
165+
},
164166
span,
165167
})
166168
}

src/librustc_typeck/check/method/suggest.rs

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc::hir::map as hir_map;
77
use rustc::hir::map::Map;
88
use rustc::ty::print::with_crate_prefix;
99
use rustc::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness};
10-
use rustc_data_structures::fx::FxHashSet;
10+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1111
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
1212
use rustc_hir as hir;
1313
use rustc_hir::def::{DefKind, Namespace, Res};
@@ -537,10 +537,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
537537
if !unsatisfied_predicates.is_empty() {
538538
let def_span =
539539
|def_id| self.tcx.sess.source_map().def_span(self.tcx.def_span(def_id));
540+
let mut type_params = FxHashMap::default();
540541
let mut bound_spans = vec![];
542+
let mut collect_type_param_suggestions =
543+
|self_ty: Ty<'_>, parent_pred: &ty::Predicate<'_>, obligation: &str| {
544+
if let (ty::Param(_), ty::Predicate::Trait(p, _)) =
545+
(&self_ty.kind, parent_pred)
546+
{
547+
if let ty::Adt(def, _) = p.skip_binder().trait_ref.self_ty().kind {
548+
let id = self.tcx.hir().as_local_hir_id(def.did).unwrap();
549+
let node = self.tcx.hir().get(id);
550+
match node {
551+
hir::Node::Item(hir::Item { kind, .. }) => {
552+
if let Some(g) = kind.generics() {
553+
let key = match &g.where_clause.predicates[..] {
554+
[.., pred] => {
555+
(pred.span().shrink_to_hi(), false)
556+
}
557+
[] => (
558+
g.where_clause
559+
.span_for_predicates_or_empty_place(),
560+
true,
561+
),
562+
};
563+
type_params
564+
.entry(key)
565+
.or_insert_with(FxHashSet::default)
566+
.insert(obligation.to_owned());
567+
}
568+
}
569+
_ => {}
570+
}
571+
}
572+
}
573+
};
541574
let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| {
542575
let msg = format!(
543-
"doesn't satisfy {}",
576+
"doesn't satisfy `{}`",
544577
if obligation.len() > 50 { quiet } else { obligation }
545578
);
546579
match &self_ty.kind {
@@ -560,7 +593,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
560593
}
561594
// Point at the closure that couldn't satisfy the bound.
562595
ty::Closure(def_id, _) => bound_spans
563-
.push((def_span(*def_id), format!("doesn't satisfy {}", quiet))),
596+
.push((def_span(*def_id), format!("doesn't satisfy `{}`", quiet))),
564597
_ => {}
565598
}
566599
};
@@ -574,43 +607,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
574607
.tcx
575608
.associated_item(pred.skip_binder().projection_ty.item_def_id);
576609
let ty = pred.skip_binder().ty;
577-
let obligation =
578-
format!("`{}::{} = {}`", trait_ref, assoc.ident, ty);
610+
let obligation = format!("{}::{} = {}", trait_ref, assoc.ident, ty);
579611
let quiet = format!(
580-
"`<_ as {}>::{} = {}`",
612+
"<_ as {}>::{} = {}",
581613
trait_ref.print_only_trait_path(),
582614
assoc.ident,
583615
ty
584616
);
585617
bound_span_label(trait_ref.self_ty(), &obligation, &quiet);
586-
Some(obligation)
618+
Some((obligation, trait_ref.self_ty()))
587619
}
588620
ty::Predicate::Trait(poly_trait_ref, _) => {
589621
let p = poly_trait_ref.skip_binder().trait_ref;
590622
let self_ty = p.self_ty();
591623
let path = p.print_only_trait_path();
592-
let obligation = format!("`{}: {}`", self_ty, path);
593-
let quiet = format!("`_: {}`", path);
624+
let obligation = format!("{}: {}", self_ty, path);
625+
let quiet = format!("_: {}", path);
594626
bound_span_label(self_ty, &obligation, &quiet);
595-
Some(obligation)
627+
Some((obligation, self_ty))
596628
}
597629
_ => None,
598630
}
599631
};
600632
let mut bound_list = unsatisfied_predicates
601633
.iter()
602634
.filter_map(|(pred, parent_pred)| {
603-
format_pred(*pred).map(|pred| match parent_pred {
604-
None => pred,
635+
format_pred(*pred).map(|(p, self_ty)| match parent_pred {
636+
None => format!("`{}`", p),
605637
Some(parent_pred) => match format_pred(*parent_pred) {
606-
None => pred,
607-
Some(parent_pred) => {
608-
format!("{} which is required by {}", pred, parent_pred)
638+
None => format!("`{}`", p),
639+
Some((parent_p, _)) => {
640+
collect_type_param_suggestions(self_ty, parent_pred, &p);
641+
format!("`{}` which is required by `{}`", p, parent_p)
609642
}
610643
},
611644
})
612645
})
613646
.collect::<Vec<String>>();
647+
for ((span, empty_where), obligations) in type_params.into_iter() {
648+
err.span_suggestion_verbose(
649+
span,
650+
&format!(
651+
"consider restricting the type parameter{s} to satisfy the \
652+
obligation{s}",
653+
s = pluralize!(obligations.len())
654+
),
655+
format!(
656+
"{} {}",
657+
if empty_where { " where" } else { "," },
658+
obligations.into_iter().collect::<Vec<_>>().join(", ")
659+
),
660+
Applicability::MaybeIncorrect,
661+
);
662+
}
663+
614664
bound_list.sort();
615665
bound_list.dedup(); // #35677
616666
bound_spans.sort();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#[derive(Default, PartialEq)]
2+
struct Foo<T> {
3+
bar: Box<[T]>,
4+
}
5+
6+
trait Bar {
7+
fn foo(&self) {}
8+
}
9+
10+
impl<T: Default + Bar> Bar for Foo<T> {}
11+
12+
impl<T> Foo<T> {
13+
fn bar(&self) {
14+
self.foo();
15+
//~^ ERROR no method named `foo` found for reference `&Foo<T>` in the current scope
16+
}
17+
}
18+
19+
struct Fin<T> where T: Bar {
20+
bar: Box<[T]>,
21+
}
22+
23+
impl<T: Default + Bar> Bar for Fin<T> {}
24+
25+
impl<T: Bar> Fin<T> {
26+
fn bar(&self) {
27+
self.foo();
28+
//~^ ERROR no method named `foo` found for reference `&Fin<T>` in the current scope
29+
}
30+
}
31+
fn main() {}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error[E0599]: no method named `foo` found for reference `&Foo<T>` in the current scope
2+
--> $DIR/missing-trait-bounds-for-method-call.rs:14:14
3+
|
4+
LL | struct Foo<T> {
5+
| ------------- doesn't satisfy `Foo<T>: Bar`
6+
...
7+
LL | trait Bar {
8+
| --------- `Bar` defines an item `foo`, perhaps you need to implement it
9+
...
10+
LL | self.foo();
11+
| ^^^ method not found in `&Foo<T>`
12+
|
13+
= note: the method `foo` exists but the following trait bounds were not satisfied:
14+
`T: Bar` which is required by `Foo<T>: Bar`
15+
`T: std::default::Default` which is required by `Foo<T>: Bar`
16+
= help: items from traits can only be used if the trait is implemented and in scope
17+
help: consider restricting the type parameters to satisfy the obligations
18+
|
19+
LL | struct Foo<T> where T: Bar, T: std::default::Default {
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21+
22+
error[E0599]: no method named `foo` found for reference `&Fin<T>` in the current scope
23+
--> $DIR/missing-trait-bounds-for-method-call.rs:27:14
24+
|
25+
LL | trait Bar {
26+
| --------- `Bar` defines an item `foo`, perhaps you need to implement it
27+
...
28+
LL | struct Fin<T> where T: Bar {
29+
| -------------------------- doesn't satisfy `Fin<T>: Bar`
30+
...
31+
LL | self.foo();
32+
| ^^^ method not found in `&Fin<T>`
33+
|
34+
= note: the method `foo` exists but the following trait bounds were not satisfied:
35+
`T: std::default::Default` which is required by `Fin<T>: Bar`
36+
= help: items from traits can only be used if the trait is implemented and in scope
37+
help: consider restricting the type parameter to satisfy the obligation
38+
|
39+
LL | struct Fin<T> where T: Bar, T: std::default::Default {
40+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
41+
42+
error: aborting due to 2 previous errors
43+
44+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)