Skip to content

Commit decc04d

Browse files
committed
show suggestion to replace generic bounds with associated types in more cases
1 parent 878c783 commit decc04d

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)