1
Fork 0

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) {}
```
This commit is contained in:
bors 2022-04-19 01:59:35 +00:00
commit e2661bac6d
6 changed files with 82 additions and 24 deletions

View file

@ -10,7 +10,6 @@ use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, DUMMY_SP};
use std::collections::BTreeSet;
use std::iter;
impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
/// 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 {
let mut suggestions = vec![];
let mut types_count = 0;
let mut where_constraints = vec![];
let mut already_has_generics_args_suggestion = false;
for (span, assoc_items) in &associated_types {
let mut names: FxHashMap<_, usize> = FxHashMap::default();
for item in assoc_items {
@ -343,16 +343,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
}
if potential_assoc_types.len() == assoc_items.len() {
// Only suggest when the amount of missing associated types equals the number of
// extra type arguments present, as that gives us a relatively high confidence
// that the user forgot to give the associated type's name. The canonical
// example would be trying to use `Iterator<isize>` instead of
// `Iterator<Item = isize>`.
for (potential, item) in iter::zip(&potential_assoc_types, assoc_items) {
if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) {
suggestions.push((*potential, format!("{} = {}", item.name, snippet)));
}
}
// When the amount of missing associated types equals the number of
// extra type arguments present. A suggesting to replace the generic args with
// associated types is already emitted.
already_has_generics_args_suggestion = true;
} else if let (Ok(snippet), false) =
(tcx.sess.source_map().span_to_snippet(*span), dupes)
{
@ -382,7 +376,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// the same associated type name.
err.help(where_msg);
}
if suggestions.len() != 1 {
if suggestions.len() != 1 || already_has_generics_args_suggestion {
// We don't need this label if there's an inline suggestion, show otherwise.
for (span, assoc_items) in &associated_types {
let mut names: FxHashMap<_, usize> = FxHashMap::default();

View file

@ -6,9 +6,10 @@ use rustc_errors::{
use rustc_hir as hir;
use rustc_middle::hir::map::fn_sig;
use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath;
use rustc_middle::ty::{self as ty, TyCtxt};
use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
use rustc_session::Session;
use rustc_span::def_id::DefId;
use std::iter;
use GenericArgsInfo::*;
@ -334,6 +335,22 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
.join(", ")
}
fn get_unbound_associated_types(&self) -> Vec<String> {
if self.tcx.is_trait(self.def_id) {
let items: &AssocItems<'_> = self.tcx.associated_items(self.def_id);
items
.in_definition_order()
.filter(|item| item.kind == AssocKind::Type)
.filter(|item| {
!self.gen_args.bindings.iter().any(|binding| binding.ident.name == item.name)
})
.map(|item| item.name.to_ident_string())
.collect()
} else {
Vec::default()
}
}
fn create_error_message(&self) -> String {
let def_path = self.tcx.def_path_str(self.def_id);
let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
@ -618,6 +635,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
fn suggest_removing_args_or_generics(&self, err: &mut Diagnostic) {
let num_provided_lt_args = self.num_provided_lifetime_args();
let num_provided_type_const_args = self.num_provided_type_or_const_args();
let unbound_types = self.get_unbound_associated_types();
let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
assert!(num_provided_args > 0);
@ -629,6 +647,8 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
let redundant_type_or_const_args = num_redundant_type_or_const_args > 0;
let remove_entire_generics = num_redundant_args >= self.gen_args.args.len();
let provided_args_matches_unbound_traits =
unbound_types.len() == num_redundant_type_or_const_args;
let remove_lifetime_args = |err: &mut Diagnostic| {
let mut lt_arg_spans = Vec::new();
@ -713,7 +733,28 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
);
};
if remove_entire_generics {
// If there is a single unbound associated type and a single excess generic param
// suggest replacing the generic param with the associated type bound
if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
let mut suggestions = vec![];
let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
for (potential, name) in iter::zip(unused_generics, &unbound_types) {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(potential.span()) {
suggestions.push((potential.span(), format!("{} = {}", name, snippet)));
}
}
if !suggestions.is_empty() {
err.multipart_suggestion(
&format!(
"replace the generic bound{s} with the associated type{s}",
s = pluralize!(unbound_types.len())
),
suggestions,
Applicability::MaybeIncorrect,
);
}
} else if remove_entire_generics {
let span = self
.path_segment
.args