Skip to content

Commit bafa89b

Browse files
committed
Suggest moving redundant generic args of an assoc fn to its trait
1 parent 908fc5b commit bafa89b

File tree

3 files changed

+160
-0
lines changed

3 files changed

+160
-0
lines changed

compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
525525
self.suggest_adding_args(err);
526526
} else if self.too_many_args_provided() {
527527
self.suggest_removing_args_or_generics(err);
528+
self.suggest_moving_args(err);
528529
} else {
529530
unreachable!();
530531
}
@@ -654,6 +655,64 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
654655
}
655656
}
656657

658+
/// Suggests moving redundant argument(s) of an associate function to the
659+
/// trait it belongs to.
660+
///
661+
/// ```compile_fail
662+
/// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
663+
/// ```
664+
fn suggest_moving_args(&self, err: &mut Diagnostic) {
665+
if let Some(trait_) = self.tcx.trait_of_item(self.def_id) {
666+
// HACK(hkmatsumoto): Ugly way to tell "<trait>::<assoc fn>()" from "x.<assoc fn>()";
667+
// we don't care the latter (for now).
668+
if self.path_segment.res == Some(hir::def::Res::Err) {
669+
return;
670+
}
671+
672+
// Say, if the assoc fn takes `A`, `B` and `C` as generic arguments while expecting 1
673+
// argument, and its trait expects 2 arguments. It is hard to "split" them right as
674+
// there are too many cases to handle: `A` `B` | `C`, `A` `B` | `C`, `A` `C` | `B`, ...
675+
let num_assoc_fn_expected_args =
676+
self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
677+
if num_assoc_fn_expected_args > 0 {
678+
return;
679+
}
680+
681+
let num_assoc_fn_excess_args =
682+
self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
683+
684+
let trait_generics = self.tcx.generics_of(trait_);
685+
let num_trait_generics_except_self =
686+
trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
687+
688+
// FIXME(hkmatsumoto): RHS of this condition ideally should be
689+
// `num_trait_generics_except_self` - "# of generic args already provided to trait"
690+
// but unable to get that information with `self.def_id`.
691+
if num_assoc_fn_excess_args == num_trait_generics_except_self {
692+
if let Some(span) = self.gen_args.span_ext()
693+
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
694+
let msg = format!(
695+
"consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
696+
these = pluralize!("this", num_assoc_fn_excess_args),
697+
s = pluralize!(num_assoc_fn_excess_args),
698+
name = self.tcx.item_name(trait_),
699+
num = num_trait_generics_except_self,
700+
);
701+
let sugg = vec![
702+
(self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)),
703+
(span.with_lo(self.path_segment.ident.span.hi()), "".to_owned())
704+
];
705+
706+
err.multipart_suggestion(
707+
msg,
708+
sugg,
709+
Applicability::MaybeIncorrect
710+
);
711+
}
712+
}
713+
}
714+
}
715+
657716
/// Suggests to remove redundant argument(s):
658717
///
659718
/// ```text
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use std::convert::TryInto;
2+
3+
trait A<T> {
4+
fn foo() {}
5+
}
6+
7+
trait B<T, U> {
8+
fn bar() {}
9+
}
10+
11+
struct S;
12+
13+
impl<T> A<T> for S {}
14+
impl<T, U> B<T, U> for S {}
15+
16+
fn main() {
17+
let _ = A::foo::<S>();
18+
//~^ ERROR
19+
//~| HELP remove these generics
20+
//~| HELP consider moving this generic argument
21+
22+
let _ = B::bar::<S, S>();
23+
//~^ ERROR
24+
//~| HELP remove these generics
25+
//~| HELP consider moving these generic arguments
26+
27+
// bad suggestion
28+
let _ = A::<S>::foo::<S>();
29+
//~^ ERROR
30+
//~| HELP remove these generics
31+
//~| HELP consider moving this generic argument
32+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
2+
--> $DIR/issue-89064.rs:17:16
3+
|
4+
LL | let _ = A::foo::<S>();
5+
| ^^^ expected 0 generic arguments
6+
|
7+
note: associated function defined here, with 0 generic parameters
8+
--> $DIR/issue-89064.rs:4:8
9+
|
10+
LL | fn foo() {}
11+
| ^^^
12+
help: remove these generics
13+
|
14+
LL - let _ = A::foo::<S>();
15+
LL + let _ = A::foo();
16+
|
17+
help: consider moving this generic argument to the `A` trait, which takes up to 1 argument
18+
|
19+
LL - let _ = A::foo::<S>();
20+
LL + let _ = A::<S>::foo();
21+
|
22+
23+
error[E0107]: this associated function takes 0 generic arguments but 2 generic arguments were supplied
24+
--> $DIR/issue-89064.rs:22:16
25+
|
26+
LL | let _ = B::bar::<S, S>();
27+
| ^^^ expected 0 generic arguments
28+
|
29+
note: associated function defined here, with 0 generic parameters
30+
--> $DIR/issue-89064.rs:8:8
31+
|
32+
LL | fn bar() {}
33+
| ^^^
34+
help: remove these generics
35+
|
36+
LL - let _ = B::bar::<S, S>();
37+
LL + let _ = B::bar();
38+
|
39+
help: consider moving these generic arguments to the `B` trait, which takes up to 2 arguments
40+
|
41+
LL - let _ = B::bar::<S, S>();
42+
LL + let _ = B::<S, S>::bar();
43+
|
44+
45+
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
46+
--> $DIR/issue-89064.rs:28:21
47+
|
48+
LL | let _ = A::<S>::foo::<S>();
49+
| ^^^ expected 0 generic arguments
50+
|
51+
note: associated function defined here, with 0 generic parameters
52+
--> $DIR/issue-89064.rs:4:8
53+
|
54+
LL | fn foo() {}
55+
| ^^^
56+
help: remove these generics
57+
|
58+
LL - let _ = A::<S>::foo::<S>();
59+
LL + let _ = A::<S>::foo();
60+
|
61+
help: consider moving this generic argument to the `A` trait, which takes up to 1 argument
62+
|
63+
LL - let _ = A::<S>::foo::<S>();
64+
LL + let _ = A::<S>::<S>::foo();
65+
|
66+
67+
error: aborting due to 3 previous errors
68+
69+
For more information about this error, try `rustc --explain E0107`.

0 commit comments

Comments
 (0)