Suggest moving redundant generic args of an assoc fn to its trait

This commit is contained in:
Hirochika Matsumoto 2022-08-21 16:59:09 +09:00
parent 908fc5b26d
commit bafa89bc46
3 changed files with 160 additions and 0 deletions

View File

@ -525,6 +525,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
self.suggest_adding_args(err);
} else if self.too_many_args_provided() {
self.suggest_removing_args_or_generics(err);
self.suggest_moving_args(err);
} else {
unreachable!();
}
@ -654,6 +655,64 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
}
/// Suggests moving redundant argument(s) of an associate function to the
/// trait it belongs to.
///
/// ```compile_fail
/// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
/// ```
fn suggest_moving_args(&self, err: &mut Diagnostic) {
if let Some(trait_) = self.tcx.trait_of_item(self.def_id) {
// HACK(hkmatsumoto): Ugly way to tell "<trait>::<assoc fn>()" from "x.<assoc fn>()";
// we don't care the latter (for now).
if self.path_segment.res == Some(hir::def::Res::Err) {
return;
}
// Say, if the assoc fn takes `A`, `B` and `C` as generic arguments while expecting 1
// argument, and its trait expects 2 arguments. It is hard to "split" them right as
// there are too many cases to handle: `A` `B` | `C`, `A` `B` | `C`, `A` `C` | `B`, ...
let num_assoc_fn_expected_args =
self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
if num_assoc_fn_expected_args > 0 {
return;
}
let num_assoc_fn_excess_args =
self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
let trait_generics = self.tcx.generics_of(trait_);
let num_trait_generics_except_self =
trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
// FIXME(hkmatsumoto): RHS of this condition ideally should be
// `num_trait_generics_except_self` - "# of generic args already provided to trait"
// but unable to get that information with `self.def_id`.
if num_assoc_fn_excess_args == num_trait_generics_except_self {
if let Some(span) = self.gen_args.span_ext()
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let msg = format!(
"consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
these = pluralize!("this", num_assoc_fn_excess_args),
s = pluralize!(num_assoc_fn_excess_args),
name = self.tcx.item_name(trait_),
num = num_trait_generics_except_self,
);
let sugg = vec![
(self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)),
(span.with_lo(self.path_segment.ident.span.hi()), "".to_owned())
];
err.multipart_suggestion(
msg,
sugg,
Applicability::MaybeIncorrect
);
}
}
}
}
/// Suggests to remove redundant argument(s):
///
/// ```text

View File

@ -0,0 +1,32 @@
use std::convert::TryInto;
trait A<T> {
fn foo() {}
}
trait B<T, U> {
fn bar() {}
}
struct S;
impl<T> A<T> for S {}
impl<T, U> B<T, U> for S {}
fn main() {
let _ = A::foo::<S>();
//~^ ERROR
//~| HELP remove these generics
//~| HELP consider moving this generic argument
let _ = B::bar::<S, S>();
//~^ ERROR
//~| HELP remove these generics
//~| HELP consider moving these generic arguments
// bad suggestion
let _ = A::<S>::foo::<S>();
//~^ ERROR
//~| HELP remove these generics
//~| HELP consider moving this generic argument
}

View File

@ -0,0 +1,69 @@
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
--> $DIR/issue-89064.rs:17:16
|
LL | let _ = A::foo::<S>();
| ^^^ expected 0 generic arguments
|
note: associated function defined here, with 0 generic parameters
--> $DIR/issue-89064.rs:4:8
|
LL | fn foo() {}
| ^^^
help: remove these generics
|
LL - let _ = A::foo::<S>();
LL + let _ = A::foo();
|
help: consider moving this generic argument to the `A` trait, which takes up to 1 argument
|
LL - let _ = A::foo::<S>();
LL + let _ = A::<S>::foo();
|
error[E0107]: this associated function takes 0 generic arguments but 2 generic arguments were supplied
--> $DIR/issue-89064.rs:22:16
|
LL | let _ = B::bar::<S, S>();
| ^^^ expected 0 generic arguments
|
note: associated function defined here, with 0 generic parameters
--> $DIR/issue-89064.rs:8:8
|
LL | fn bar() {}
| ^^^
help: remove these generics
|
LL - let _ = B::bar::<S, S>();
LL + let _ = B::bar();
|
help: consider moving these generic arguments to the `B` trait, which takes up to 2 arguments
|
LL - let _ = B::bar::<S, S>();
LL + let _ = B::<S, S>::bar();
|
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
--> $DIR/issue-89064.rs:28:21
|
LL | let _ = A::<S>::foo::<S>();
| ^^^ expected 0 generic arguments
|
note: associated function defined here, with 0 generic parameters
--> $DIR/issue-89064.rs:4:8
|
LL | fn foo() {}
| ^^^
help: remove these generics
|
LL - let _ = A::<S>::foo::<S>();
LL + let _ = A::<S>::foo();
|
help: consider moving this generic argument to the `A` trait, which takes up to 1 argument
|
LL - let _ = A::<S>::foo::<S>();
LL + let _ = A::<S>::<S>::foo();
|
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0107`.