Skip to content

Commit aecbcd1

Browse files
committed
Auto merge of #55808 - estebank:type-arguments, r=petrochenkov
Suggest correct syntax when writing type arg instead of assoc type - When confusing an associated type with a type argument, suggest the appropriate syntax. Given `Iterator<isize>`, suggest `Iterator<Item = isize>`. - When encountering multiple missing associated types, emit only one diagnostic. - Point at associated type def span for context. - Point at each extra type argument. Follow up to #48288, fix #20977.
2 parents 6a2d1b4 + 510f836 commit aecbcd1

20 files changed

+222
-94
lines changed

src/librustc_typeck/astconv.rs

+121-60
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
182182
item_segment: &hir::PathSegment)
183183
-> &'tcx Substs<'tcx>
184184
{
185-
let (substs, assoc_bindings) = item_segment.with_generic_args(|generic_args| {
185+
let (substs, assoc_bindings, _) = item_segment.with_generic_args(|generic_args| {
186186
self.create_substs_for_ast_path(
187187
span,
188188
def_id,
@@ -256,7 +256,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
256256
},
257257
def.parent.is_none() && def.has_self, // `has_self`
258258
seg.infer_types || suppress_mismatch, // `infer_types`
259-
)
259+
).0
260260
}
261261

262262
/// Check that the correct number of generic arguments have been provided.
@@ -269,7 +269,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
269269
position: GenericArgPosition,
270270
has_self: bool,
271271
infer_types: bool,
272-
) -> bool {
272+
) -> (bool, Option<Vec<Span>>) {
273273
// At this stage we are guaranteed that the generic arguments are in the correct order, e.g.
274274
// that lifetimes will proceed types. So it suffices to check the number of each generic
275275
// arguments in order to validate them with respect to the generic parameters.
@@ -303,13 +303,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
303303
let mut err = tcx.sess.struct_span_err(span, msg);
304304
err.span_note(span_late, note);
305305
err.emit();
306-
return true;
306+
return (true, None);
307307
} else {
308308
let mut multispan = MultiSpan::from_span(span);
309309
multispan.push_span_label(span_late, note.to_string());
310310
tcx.lint_node(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
311311
args.args[0].id(), multispan, msg);
312-
return false;
312+
return (false, None);
313313
}
314314
}
315315
}
@@ -323,7 +323,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
323323
// For kinds without defaults (i.e. lifetimes), `required == permitted`.
324324
// For other kinds (i.e. types), `permitted` may be greater than `required`.
325325
if required <= provided && provided <= permitted {
326-
return false;
326+
return (false, None);
327327
}
328328

329329
// Unfortunately lifetime and type parameter mismatches are typically styled
@@ -338,33 +338,28 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
338338
(required, "")
339339
};
340340

341-
let mut span = span;
342-
let label = if required == permitted && provided > permitted {
343-
let diff = provided - permitted;
344-
if diff == 1 {
345-
// In the case when the user has provided too many arguments,
346-
// we want to point to the first unexpected argument.
347-
let first_superfluous_arg: &GenericArg = &args.args[offset + permitted];
348-
span = first_superfluous_arg.span();
349-
}
350-
format!(
351-
"{}unexpected {} argument{}",
352-
if diff != 1 { format!("{} ", diff) } else { String::new() },
353-
kind,
354-
if diff != 1 { "s" } else { "" },
355-
)
341+
let mut potential_assoc_types: Option<Vec<Span>> = None;
342+
let (spans, label) = if required == permitted && provided > permitted {
343+
// In the case when the user has provided too many arguments,
344+
// we want to point to the unexpected arguments.
345+
let spans: Vec<Span> = args.args[offset+permitted .. offset+provided]
346+
.iter()
347+
.map(|arg| arg.span())
348+
.collect();
349+
potential_assoc_types = Some(spans.clone());
350+
(spans, format!( "unexpected {} argument", kind))
356351
} else {
357-
format!(
352+
(vec![span], format!(
358353
"expected {}{} {} argument{}",
359354
quantifier,
360355
bound,
361356
kind,
362357
if bound != 1 { "s" } else { "" },
363-
)
358+
))
364359
};
365360

366-
tcx.sess.struct_span_err_with_code(
367-
span,
361+
let mut err = tcx.sess.struct_span_err_with_code(
362+
spans.clone(),
368363
&format!(
369364
"wrong number of {} arguments: expected {}{}, found {}",
370365
kind,
@@ -373,9 +368,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
373368
provided,
374369
),
375370
DiagnosticId::Error("E0107".into())
376-
).span_label(span, label).emit();
371+
);
372+
for span in spans {
373+
err.span_label(span, label.as_str());
374+
}
375+
err.emit();
377376

378-
provided > required // `suppress_error`
377+
(provided > required, // `suppress_error`
378+
potential_assoc_types)
379379
};
380380

381381
if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
@@ -397,7 +397,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
397397
arg_counts.lifetimes,
398398
)
399399
} else {
400-
false
400+
(false, None)
401401
}
402402
}
403403

@@ -555,7 +555,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
555555
generic_args: &hir::GenericArgs,
556556
infer_types: bool,
557557
self_ty: Option<Ty<'tcx>>)
558-
-> (&'tcx Substs<'tcx>, Vec<ConvertedBinding<'tcx>>)
558+
-> (&'tcx Substs<'tcx>, Vec<ConvertedBinding<'tcx>>, Option<Vec<Span>>)
559559
{
560560
// If the type is parameterized by this region, then replace this
561561
// region with the current anon region binding (in other words,
@@ -571,7 +571,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
571571
assert_eq!(generic_params.has_self, self_ty.is_some());
572572

573573
let has_self = generic_params.has_self;
574-
Self::check_generic_arg_count(
574+
let (_, potential_assoc_types) = Self::check_generic_arg_count(
575575
self.tcx(),
576576
span,
577577
&generic_params,
@@ -676,7 +676,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
676676
debug!("create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}",
677677
generic_params, self_ty, substs);
678678

679-
(substs, assoc_bindings)
679+
(substs, assoc_bindings, potential_assoc_types)
680680
}
681681

682682
/// Instantiates the path for the given trait reference, assuming that it's
@@ -718,19 +718,20 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
718718
self_ty: Ty<'tcx>,
719719
poly_projections: &mut Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>,
720720
speculative: bool)
721-
-> ty::PolyTraitRef<'tcx>
721+
-> (ty::PolyTraitRef<'tcx>, Option<Vec<Span>>)
722722
{
723723
let trait_def_id = self.trait_def_id(trait_ref);
724724

725725
debug!("instantiate_poly_trait_ref({:?}, def_id={:?})", trait_ref, trait_def_id);
726726

727727
self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1);
728728

729-
let (substs, assoc_bindings) =
730-
self.create_substs_for_ast_trait_ref(trait_ref.path.span,
731-
trait_def_id,
732-
self_ty,
733-
trait_ref.path.segments.last().unwrap());
729+
let (substs, assoc_bindings, potential_assoc_types) = self.create_substs_for_ast_trait_ref(
730+
trait_ref.path.span,
731+
trait_def_id,
732+
self_ty,
733+
trait_ref.path.segments.last().unwrap(),
734+
);
734735
let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs));
735736

736737
let mut dup_bindings = FxHashMap::default();
@@ -745,14 +746,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
745746

746747
debug!("instantiate_poly_trait_ref({:?}, projections={:?}) -> {:?}",
747748
trait_ref, poly_projections, poly_trait_ref);
748-
poly_trait_ref
749+
(poly_trait_ref, potential_assoc_types)
749750
}
750751

751752
pub fn instantiate_poly_trait_ref(&self,
752753
poly_trait_ref: &hir::PolyTraitRef,
753754
self_ty: Ty<'tcx>,
754755
poly_projections: &mut Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>)
755-
-> ty::PolyTraitRef<'tcx>
756+
-> (ty::PolyTraitRef<'tcx>, Option<Vec<Span>>)
756757
{
757758
self.instantiate_poly_trait_ref_inner(&poly_trait_ref.trait_ref, self_ty,
758759
poly_projections, false)
@@ -765,7 +766,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
765766
trait_segment: &hir::PathSegment)
766767
-> ty::TraitRef<'tcx>
767768
{
768-
let (substs, assoc_bindings) =
769+
let (substs, assoc_bindings, _) =
769770
self.create_substs_for_ast_trait_ref(span,
770771
trait_def_id,
771772
self_ty,
@@ -774,13 +775,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
774775
ty::TraitRef::new(trait_def_id, substs)
775776
}
776777

777-
fn create_substs_for_ast_trait_ref(&self,
778-
span: Span,
779-
trait_def_id: DefId,
780-
self_ty: Ty<'tcx>,
781-
trait_segment: &hir::PathSegment)
782-
-> (&'tcx Substs<'tcx>, Vec<ConvertedBinding<'tcx>>)
783-
{
778+
fn create_substs_for_ast_trait_ref(
779+
&self,
780+
span: Span,
781+
trait_def_id: DefId,
782+
self_ty: Ty<'tcx>,
783+
trait_segment: &hir::PathSegment,
784+
) -> (&'tcx Substs<'tcx>, Vec<ConvertedBinding<'tcx>>, Option<Vec<Span>>) {
784785
debug!("create_substs_for_ast_trait_ref(trait_segment={:?})",
785786
trait_segment);
786787

@@ -970,9 +971,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
970971

971972
let mut projection_bounds = Vec::new();
972973
let dummy_self = tcx.mk_ty(TRAIT_OBJECT_DUMMY_SELF);
973-
let principal = self.instantiate_poly_trait_ref(&trait_bounds[0],
974-
dummy_self,
975-
&mut projection_bounds);
974+
let (principal, potential_assoc_types) = self.instantiate_poly_trait_ref(
975+
&trait_bounds[0],
976+
dummy_self,
977+
&mut projection_bounds,
978+
);
976979
debug!("principal: {:?}", principal);
977980

978981
for trait_bound in trait_bounds[1..].iter() {
@@ -1027,16 +1030,74 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
10271030
associated_types.remove(&projection_bound.projection_def_id());
10281031
}
10291032

1030-
for item_def_id in associated_types {
1031-
let assoc_item = tcx.associated_item(item_def_id);
1032-
let trait_def_id = assoc_item.container.id();
1033-
struct_span_err!(tcx.sess, span, E0191, "the value of the associated type `{}` \
1034-
(from the trait `{}`) must be specified",
1035-
assoc_item.ident,
1036-
tcx.item_path_str(trait_def_id))
1037-
.span_label(span, format!("missing associated type `{}` value",
1038-
assoc_item.ident))
1039-
.emit();
1033+
if !associated_types.is_empty() {
1034+
let names = associated_types.iter().map(|item_def_id| {
1035+
let assoc_item = tcx.associated_item(*item_def_id);
1036+
let trait_def_id = assoc_item.container.id();
1037+
format!(
1038+
"`{}` (from the trait `{}`)",
1039+
assoc_item.ident,
1040+
tcx.item_path_str(trait_def_id),
1041+
)
1042+
}).collect::<Vec<_>>().join(", ");
1043+
let mut err = struct_span_err!(
1044+
tcx.sess,
1045+
span,
1046+
E0191,
1047+
"the value of the associated type{} {} must be specified",
1048+
if associated_types.len() == 1 { "" } else { "s" },
1049+
names,
1050+
);
1051+
let mut suggest = false;
1052+
let mut potential_assoc_types_spans = vec![];
1053+
if let Some(potential_assoc_types) = potential_assoc_types {
1054+
if potential_assoc_types.len() == associated_types.len() {
1055+
// Only suggest when the amount of missing associated types is equals to the
1056+
// extra type arguments present, as that gives us a relatively high confidence
1057+
// that the user forgot to give the associtated type's name. The canonical
1058+
// example would be trying to use `Iterator<isize>` instead of
1059+
// `Iterator<Item=isize>`.
1060+
suggest = true;
1061+
potential_assoc_types_spans = potential_assoc_types;
1062+
}
1063+
}
1064+
let mut suggestions = vec![];
1065+
for (i, item_def_id) in associated_types.iter().enumerate() {
1066+
let assoc_item = tcx.associated_item(*item_def_id);
1067+
err.span_label(
1068+
span,
1069+
format!("associated type `{}` must be specified", assoc_item.ident),
1070+
);
1071+
if item_def_id.is_local() {
1072+
err.span_label(
1073+
tcx.def_span(*item_def_id),
1074+
format!("`{}` defined here", assoc_item.ident),
1075+
);
1076+
}
1077+
if suggest {
1078+
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(
1079+
potential_assoc_types_spans[i],
1080+
) {
1081+
suggestions.push((
1082+
potential_assoc_types_spans[i],
1083+
format!("{} = {}", assoc_item.ident, snippet),
1084+
));
1085+
}
1086+
}
1087+
}
1088+
if !suggestions.is_empty() {
1089+
let msg = if suggestions.len() == 1 {
1090+
"if you meant to specify the associated type, write"
1091+
} else {
1092+
"if you meant to specify the associated types, write"
1093+
};
1094+
err.multipart_suggestion_with_applicability(
1095+
msg,
1096+
suggestions,
1097+
Applicability::MaybeIncorrect,
1098+
);
1099+
}
1100+
err.emit();
10401101
}
10411102

10421103
// Erase the `dummy_self` (`TRAIT_OBJECT_DUMMY_SELF`) used above.

src/librustc_typeck/collect.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -1892,7 +1892,7 @@ fn explicit_predicates_of<'a, 'tcx>(
18921892
&hir::GenericBound::Trait(ref poly_trait_ref, _) => {
18931893
let mut projections = Vec::new();
18941894

1895-
let trait_ref = AstConv::instantiate_poly_trait_ref(
1895+
let (trait_ref, _) = AstConv::instantiate_poly_trait_ref(
18961896
&icx,
18971897
poly_trait_ref,
18981898
ty,
@@ -2016,7 +2016,12 @@ pub fn compute_bounds<'gcx: 'tcx, 'tcx>(
20162016
let mut projection_bounds = Vec::new();
20172017

20182018
let mut trait_bounds: Vec<_> = trait_bounds.iter().map(|&bound| {
2019-
(astconv.instantiate_poly_trait_ref(bound, param_ty, &mut projection_bounds), bound.span)
2019+
let (poly_trait_ref, _) = astconv.instantiate_poly_trait_ref(
2020+
bound,
2021+
param_ty,
2022+
&mut projection_bounds,
2023+
);
2024+
(poly_trait_ref, bound.span)
20202025
}).collect();
20212026

20222027
let region_bounds = region_bounds
@@ -2057,7 +2062,7 @@ fn predicates_from_bound<'tcx>(
20572062
match *bound {
20582063
hir::GenericBound::Trait(ref tr, hir::TraitBoundModifier::None) => {
20592064
let mut projections = Vec::new();
2060-
let pred = astconv.instantiate_poly_trait_ref(tr, param_ty, &mut projections);
2065+
let (pred, _) = astconv.instantiate_poly_trait_ref(tr, param_ty, &mut projections);
20612066
iter::once((pred.to_predicate(), tr.span)).chain(
20622067
projections
20632068
.into_iter()

src/librustc_typeck/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ pub fn hir_trait_to_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, hir_trait:
389389
let env_def_id = tcx.hir.local_def_id(env_node_id);
390390
let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id);
391391
let mut projections = Vec::new();
392-
let principal = astconv::AstConv::instantiate_poly_trait_ref_inner(
392+
let (principal, _) = astconv::AstConv::instantiate_poly_trait_ref_inner(
393393
&item_cx, hir_trait, tcx.types.err, &mut projections, true
394394
);
395395

src/test/compile-fail/issue-23595-1.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ trait Hierarchy {
1616
type Value;
1717
type ChildKey;
1818
type Children = Index<Self::ChildKey, Output=Hierarchy>;
19-
//~^ ERROR: the value of the associated type `ChildKey`
20-
//~^^ ERROR: the value of the associated type `Children`
21-
//~^^^ ERROR: the value of the associated type `Value`
19+
//~^ ERROR: the value of the associated types `Value` (from the trait `Hierarchy`), `ChildKey`
2220

2321
fn data(&self) -> Option<(Self::Value, Self::Children)>;
2422
}

src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ LL | fn dent_object<COLOR>(c: BoxCar<Color=COLOR>) {
2525
error[E0191]: the value of the associated type `Color` (from the trait `Vehicle`) must be specified
2626
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:33:26
2727
|
28+
LL | type Color;
29+
| ----------- `Color` defined here
30+
...
2831
LL | fn dent_object<COLOR>(c: BoxCar<Color=COLOR>) {
29-
| ^^^^^^^^^^^^^^^^^^^ missing associated type `Color` value
32+
| ^^^^^^^^^^^^^^^^^^^ associated type `Color` must be specified
3033

3134
error[E0221]: ambiguous associated type `Color` in bounds of `C`
3235
--> $DIR/associated-type-projection-from-multiple-supertraits.rs:38:29

src/test/ui/associated-types/associated-types-incomplete-object.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,5 @@ pub fn main() {
3737
//~^ ERROR the value of the associated type `A` (from the trait `Foo`) must be specified
3838

3939
let d = &42isize as &Foo;
40-
//~^ ERROR the value of the associated type `A` (from the trait `Foo`) must be specified
41-
//~| ERROR the value of the associated type `B` (from the trait `Foo`) must be specified
40+
//~^ ERROR the value of the associated types `A` (from the trait `Foo`), `B` (from the trait
4241
}

0 commit comments

Comments
 (0)