From decc04dbfbfe240e649cd4f891ca14d2f501c0ff Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 27 Mar 2022 20:45:08 +0200 Subject: [PATCH] show suggestion to replace generic bounds with associated types in more cases --- compiler/rustc_typeck/src/astconv/errors.rs | 18 +++----- .../wrong_number_of_generic_args.rs | 45 ++++++++++++++++++- .../const-generics/issues/issue-87493.stderr | 2 +- src/test/ui/error-codes/E0107.rs | 10 +++++ src/test/ui/error-codes/E0107.stderr | 18 +++++++- ...type-argument-instead-of-assoc-type.stderr | 13 +++--- 6 files changed, 82 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs index 1a10b07cc2706..38cc74a5e3734 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_typeck/src/astconv/errors.rs @@ -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` instead of - // `Iterator`. - 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(); diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs index 1b1a2037d9eaa..5cd7a7d578e4b 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs @@ -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 { + 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 diff --git a/src/test/ui/const-generics/issues/issue-87493.stderr b/src/test/ui/const-generics/issues/issue-87493.stderr index 8f92eeaffd19d..f998c1187d810 100644 --- a/src/test/ui/const-generics/issues/issue-87493.stderr +++ b/src/test/ui/const-generics/issues/issue-87493.stderr @@ -13,7 +13,7 @@ error[E0107]: this trait takes 0 generic arguments but 1 generic argument was su --> $DIR/issue-87493.rs:8:8 | LL | T: MyTrait, - | ^^^^^^^------------------- help: remove these generics + | ^^^^^^^ ----------------- help: replace the generic bound with the associated type: `Assoc = Assoc == S::Assoc` | | | expected 0 generic arguments | diff --git a/src/test/ui/error-codes/E0107.rs b/src/test/ui/error-codes/E0107.rs index 840700c9cc6d6..d369fc2a5658b 100644 --- a/src/test/ui/error-codes/E0107.rs +++ b/src/test/ui/error-codes/E0107.rs @@ -47,4 +47,14 @@ struct Baz<'a, 'b, 'c> { //~| HELP remove this lifetime argument } +pub trait T { + type A; + type B; +} + +fn trait_bound_generic>(_i: I) { + //~^ ERROR this trait takes 0 generic arguments + //~| HELP replace the generic bounds with the associated types +} + fn main() {} diff --git a/src/test/ui/error-codes/E0107.stderr b/src/test/ui/error-codes/E0107.stderr index c90f85df96741..5ca03b45d82b3 100644 --- a/src/test/ui/error-codes/E0107.stderr +++ b/src/test/ui/error-codes/E0107.stderr @@ -128,6 +128,22 @@ note: struct defined here, with 0 lifetime parameters LL | struct Quux(T); | ^^^^ -error: aborting due to 9 previous errors +error[E0107]: this trait takes 0 generic arguments but 2 generic arguments were supplied + --> $DIR/E0107.rs:55:27 + | +LL | fn trait_bound_generic>(_i: I) { + | ^ expected 0 generic arguments + | +note: trait defined here, with 0 generic parameters + --> $DIR/E0107.rs:50:11 + | +LL | pub trait T { + | ^ +help: replace the generic bounds with the associated types + | +LL | fn trait_bound_generic>(_i: I) { + | ~~~~~~ ~~~~~~~ + +error: aborting due to 10 previous errors For more information about this error, try `rustc --explain E0107`. diff --git a/src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.stderr b/src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.stderr index 5a158e5876a06..5409e32c436fb 100644 --- a/src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.stderr +++ b/src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.stderr @@ -2,15 +2,17 @@ error[E0107]: this trait takes 2 generic arguments but 4 generic arguments were --> $DIR/use-type-argument-instead-of-assoc-type.rs:7:16 | LL | i: Box>, - | ^ ------------ help: remove these generic arguments - | | - | expected 2 generic arguments + | ^ expected 2 generic arguments | note: trait defined here, with 2 generic parameters: `X`, `Y` --> $DIR/use-type-argument-instead-of-assoc-type.rs:1:11 | LL | pub trait T { | ^ - - +help: replace the generic bounds with the associated types + | +LL | i: Box>, + | ~~~~~~~~~ ~~~~~~~~~ error[E0191]: the value of the associated types `A` (from trait `T`), `C` (from trait `T`) must be specified --> $DIR/use-type-argument-instead-of-assoc-type.rs:7:16 @@ -23,11 +25,6 @@ LL | type C; ... LL | i: Box>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated types `A`, `C` must be specified - | -help: specify the associated types - | -LL | i: Box>, - | ~~~~~~~~~ ~~~~~~~~~ error: aborting due to 2 previous errors