Skip to content

Commit e2661ba

Browse files
committed
Auto merge of #95379 - icewind1991:suggest-associated-type-more, r=jackh726
show suggestion to replace generic bounds with associated types in more cases Moves the hint to replace generic parameters with associated type bounds from the "not all associated type bounds are specified"(`E0191`) to "to many generic type parameters provided"(`E0107`). Since `E0191` is only emitted in places where all associated types must be specified (when creating `dyn` types), the suggesting is currently not shown for other generic type uses (such as in generic type bounds). With this change the suggesting is always emitted when the number of excess generic parameters matches the number of unbound associated types. Main motivation for the change was a lack of useful suggesting when doing ```rust fn foo<I: Iterator<usize>>(i: I) {} ```
2 parents d5ae66c + decc04d commit e2661ba

File tree

6 files changed

+82
-24
lines changed

6 files changed

+82
-24
lines changed

compiler/rustc_typeck/src/astconv/errors.rs

+6-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ use rustc_span::symbol::{sym, Ident};
1010
use rustc_span::{Span, DUMMY_SP};
1111

1212
use std::collections::BTreeSet;
13-
use std::iter;
1413

1514
impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
1615
/// On missing type parameters, emit an E0393 error and provide a structured suggestion using
@@ -323,6 +322,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
323322
let mut suggestions = vec![];
324323
let mut types_count = 0;
325324
let mut where_constraints = vec![];
325+
let mut already_has_generics_args_suggestion = false;
326326
for (span, assoc_items) in &associated_types {
327327
let mut names: FxHashMap<_, usize> = FxHashMap::default();
328328
for item in assoc_items {
@@ -343,16 +343,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
343343
}
344344
}
345345
if potential_assoc_types.len() == assoc_items.len() {
346-
// Only suggest when the amount of missing associated types equals the number of
347-
// extra type arguments present, as that gives us a relatively high confidence
348-
// that the user forgot to give the associated type's name. The canonical
349-
// example would be trying to use `Iterator<isize>` instead of
350-
// `Iterator<Item = isize>`.
351-
for (potential, item) in iter::zip(&potential_assoc_types, assoc_items) {
352-
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) {
353-
suggestions.push((*potential, format!("{} = {}", item.name, snippet)));
354-
}
355-
}
346+
// When the amount of missing associated types equals the number of
347+
// extra type arguments present. A suggesting to replace the generic args with
348+
// associated types is already emitted.
349+
already_has_generics_args_suggestion = true;
356350
} else if let (Ok(snippet), false) =
357351
(tcx.sess.source_map().span_to_snippet(*span), dupes)
358352
{
@@ -382,7 +376,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
382376
// the same associated type name.
383377
err.help(where_msg);
384378
}
385-
if suggestions.len() != 1 {
379+
if suggestions.len() != 1 || already_has_generics_args_suggestion {
386380
// We don't need this label if there's an inline suggestion, show otherwise.
387381
for (span, assoc_items) in &associated_types {
388382
let mut names: FxHashMap<_, usize> = FxHashMap::default();

compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs

+43-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ use rustc_errors::{
66
use rustc_hir as hir;
77
use rustc_middle::hir::map::fn_sig;
88
use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath;
9-
use rustc_middle::ty::{self as ty, TyCtxt};
9+
use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
1010
use rustc_session::Session;
1111
use rustc_span::def_id::DefId;
12+
use std::iter;
1213

1314
use GenericArgsInfo::*;
1415

@@ -334,6 +335,22 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
334335
.join(", ")
335336
}
336337

338+
fn get_unbound_associated_types(&self) -> Vec<String> {
339+
if self.tcx.is_trait(self.def_id) {
340+
let items: &AssocItems<'_> = self.tcx.associated_items(self.def_id);
341+
items
342+
.in_definition_order()
343+
.filter(|item| item.kind == AssocKind::Type)
344+
.filter(|item| {
345+
!self.gen_args.bindings.iter().any(|binding| binding.ident.name == item.name)
346+
})
347+
.map(|item| item.name.to_ident_string())
348+
.collect()
349+
} else {
350+
Vec::default()
351+
}
352+
}
353+
337354
fn create_error_message(&self) -> String {
338355
let def_path = self.tcx.def_path_str(self.def_id);
339356
let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
@@ -618,6 +635,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
618635
fn suggest_removing_args_or_generics(&self, err: &mut Diagnostic) {
619636
let num_provided_lt_args = self.num_provided_lifetime_args();
620637
let num_provided_type_const_args = self.num_provided_type_or_const_args();
638+
let unbound_types = self.get_unbound_associated_types();
621639
let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
622640
assert!(num_provided_args > 0);
623641

@@ -629,6 +647,8 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
629647
let redundant_type_or_const_args = num_redundant_type_or_const_args > 0;
630648

631649
let remove_entire_generics = num_redundant_args >= self.gen_args.args.len();
650+
let provided_args_matches_unbound_traits =
651+
unbound_types.len() == num_redundant_type_or_const_args;
632652

633653
let remove_lifetime_args = |err: &mut Diagnostic| {
634654
let mut lt_arg_spans = Vec::new();
@@ -713,7 +733,28 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
713733
);
714734
};
715735

716-
if remove_entire_generics {
736+
// If there is a single unbound associated type and a single excess generic param
737+
// suggest replacing the generic param with the associated type bound
738+
if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
739+
let mut suggestions = vec![];
740+
let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
741+
for (potential, name) in iter::zip(unused_generics, &unbound_types) {
742+
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(potential.span()) {
743+
suggestions.push((potential.span(), format!("{} = {}", name, snippet)));
744+
}
745+
}
746+
747+
if !suggestions.is_empty() {
748+
err.multipart_suggestion(
749+
&format!(
750+
"replace the generic bound{s} with the associated type{s}",
751+
s = pluralize!(unbound_types.len())
752+
),
753+
suggestions,
754+
Applicability::MaybeIncorrect,
755+
);
756+
}
757+
} else if remove_entire_generics {
717758
let span = self
718759
.path_segment
719760
.args

src/test/ui/const-generics/issues/issue-87493.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ error[E0107]: this trait takes 0 generic arguments but 1 generic argument was su
1313
--> $DIR/issue-87493.rs:8:8
1414
|
1515
LL | T: MyTrait<Assoc == S::Assoc>,
16-
| ^^^^^^^------------------- help: remove these generics
16+
| ^^^^^^^ ----------------- help: replace the generic bound with the associated type: `Assoc = Assoc == S::Assoc`
1717
| |
1818
| expected 0 generic arguments
1919
|

src/test/ui/error-codes/E0107.rs

+10
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,14 @@ struct Baz<'a, 'b, 'c> {
4747
//~| HELP remove this lifetime argument
4848
}
4949

50+
pub trait T {
51+
type A;
52+
type B;
53+
}
54+
55+
fn trait_bound_generic<I: T<u8, u16>>(_i: I) {
56+
//~^ ERROR this trait takes 0 generic arguments
57+
//~| HELP replace the generic bounds with the associated types
58+
}
59+
5060
fn main() {}

src/test/ui/error-codes/E0107.stderr

+17-1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,22 @@ note: struct defined here, with 0 lifetime parameters
128128
LL | struct Quux<T>(T);
129129
| ^^^^
130130

131-
error: aborting due to 9 previous errors
131+
error[E0107]: this trait takes 0 generic arguments but 2 generic arguments were supplied
132+
--> $DIR/E0107.rs:55:27
133+
|
134+
LL | fn trait_bound_generic<I: T<u8, u16>>(_i: I) {
135+
| ^ expected 0 generic arguments
136+
|
137+
note: trait defined here, with 0 generic parameters
138+
--> $DIR/E0107.rs:50:11
139+
|
140+
LL | pub trait T {
141+
| ^
142+
help: replace the generic bounds with the associated types
143+
|
144+
LL | fn trait_bound_generic<I: T<A = u8, B = u16>>(_i: I) {
145+
| ~~~~~~ ~~~~~~~
146+
147+
error: aborting due to 10 previous errors
132148

133149
For more information about this error, try `rustc --explain E0107`.

src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.stderr

+5-8
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@ error[E0107]: this trait takes 2 generic arguments but 4 generic arguments were
22
--> $DIR/use-type-argument-instead-of-assoc-type.rs:7:16
33
|
44
LL | i: Box<dyn T<usize, usize, usize, usize, B=usize>>,
5-
| ^ ------------ help: remove these generic arguments
6-
| |
7-
| expected 2 generic arguments
5+
| ^ expected 2 generic arguments
86
|
97
note: trait defined here, with 2 generic parameters: `X`, `Y`
108
--> $DIR/use-type-argument-instead-of-assoc-type.rs:1:11
119
|
1210
LL | pub trait T<X, Y> {
1311
| ^ - -
12+
help: replace the generic bounds with the associated types
13+
|
14+
LL | i: Box<dyn T<usize, usize, A = usize, C = usize, B=usize>>,
15+
| ~~~~~~~~~ ~~~~~~~~~
1416

1517
error[E0191]: the value of the associated types `A` (from trait `T`), `C` (from trait `T`) must be specified
1618
--> $DIR/use-type-argument-instead-of-assoc-type.rs:7:16
@@ -23,11 +25,6 @@ LL | type C;
2325
...
2426
LL | i: Box<dyn T<usize, usize, usize, usize, B=usize>>,
2527
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated types `A`, `C` must be specified
26-
|
27-
help: specify the associated types
28-
|
29-
LL | i: Box<dyn T<usize, usize, A = usize, C = usize, B=usize>>,
30-
| ~~~~~~~~~ ~~~~~~~~~
3128

3229
error: aborting due to 2 previous errors
3330

0 commit comments

Comments
 (0)