Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

show suggestion to replace generic bounds with associated types in more cases #95379

Merged
merged 1 commit into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions compiler/rustc_typeck/src/astconv/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
{
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand All @@ -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();
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/const-generics/issues/issue-87493.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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<Assoc == S::Assoc>,
| ^^^^^^^------------------- help: remove these generics
| ^^^^^^^ ----------------- help: replace the generic bound with the associated type: `Assoc = Assoc == S::Assoc`
| |
| expected 0 generic arguments
|
Expand Down
10 changes: 10 additions & 0 deletions src/test/ui/error-codes/E0107.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: T<u8, u16>>(_i: I) {
//~^ ERROR this trait takes 0 generic arguments
//~| HELP replace the generic bounds with the associated types
}

fn main() {}
18 changes: 17 additions & 1 deletion src/test/ui/error-codes/E0107.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,22 @@ note: struct defined here, with 0 lifetime parameters
LL | struct Quux<T>(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: T<u8, u16>>(_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: T<A = u8, B = u16>>(_i: I) {
| ~~~~~~ ~~~~~~~

error: aborting due to 10 previous errors

For more information about this error, try `rustc --explain E0107`.
Original file line number Diff line number Diff line change
Expand Up @@ -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<dyn T<usize, usize, usize, usize, B=usize>>,
| ^ ------------ 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<X, Y> {
| ^ - -
help: replace the generic bounds with the associated types
|
LL | i: Box<dyn T<usize, usize, A = usize, C = usize, B=usize>>,
| ~~~~~~~~~ ~~~~~~~~~

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
Expand All @@ -23,11 +25,6 @@ LL | type C;
...
LL | i: Box<dyn T<usize, usize, usize, usize, B=usize>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated types `A`, `C` must be specified
|
help: specify the associated types
|
LL | i: Box<dyn T<usize, usize, A = usize, C = usize, B=usize>>,
| ~~~~~~~~~ ~~~~~~~~~

error: aborting due to 2 previous errors

Expand Down