Skip to content

Commit 2af2cda

Browse files
authored
Rollup merge of #100838 - hkmatsumoto:move-gen-args-to-trait-when-appropriate, r=davidtwco
Suggest moving redundant generic args of an assoc fn to its trait Closes #89064
2 parents 6438f4a + 1529137 commit 2af2cda

File tree

4 files changed

+245
-3
lines changed

4 files changed

+245
-3
lines changed

compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs

+118
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
523523
if self.not_enough_args_provided() {
524524
self.suggest_adding_args(err);
525525
} else if self.too_many_args_provided() {
526+
self.suggest_moving_args_from_assoc_fn_to_trait(err);
526527
self.suggest_removing_args_or_generics(err);
527528
} else {
528529
unreachable!();
@@ -653,6 +654,123 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
653654
}
654655
}
655656

657+
/// Suggests moving redundant argument(s) of an associate function to the
658+
/// trait it belongs to.
659+
///
660+
/// ```compile_fail
661+
/// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
662+
/// ```
663+
fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) {
664+
let trait_ = match self.tcx.trait_of_item(self.def_id) {
665+
Some(def_id) => def_id,
666+
None => return,
667+
};
668+
669+
// Skip suggestion when the associated function is itself generic, it is unclear
670+
// how to split the provided parameters between those to suggest to the trait and
671+
// those to remain on the associated type.
672+
let num_assoc_fn_expected_args =
673+
self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
674+
if num_assoc_fn_expected_args > 0 {
675+
return;
676+
}
677+
678+
let num_assoc_fn_excess_args =
679+
self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
680+
681+
let trait_generics = self.tcx.generics_of(trait_);
682+
let num_trait_generics_except_self =
683+
trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
684+
685+
let msg = format!(
686+
"consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
687+
these = pluralize!("this", num_assoc_fn_excess_args),
688+
s = pluralize!(num_assoc_fn_excess_args),
689+
name = self.tcx.item_name(trait_),
690+
num = num_trait_generics_except_self,
691+
);
692+
693+
if let Some(hir_id) = self.path_segment.hir_id
694+
&& let Some(parent_node) = self.tcx.hir().find_parent_node(hir_id)
695+
&& let Some(parent_node) = self.tcx.hir().find(parent_node)
696+
&& let hir::Node::Expr(expr) = parent_node {
697+
match expr.kind {
698+
hir::ExprKind::Path(ref qpath) => {
699+
self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
700+
err,
701+
qpath,
702+
msg,
703+
num_assoc_fn_excess_args,
704+
num_trait_generics_except_self
705+
)
706+
},
707+
hir::ExprKind::MethodCall(..) => {
708+
self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
709+
err,
710+
trait_,
711+
expr,
712+
msg,
713+
num_assoc_fn_excess_args,
714+
num_trait_generics_except_self
715+
)
716+
},
717+
_ => return,
718+
}
719+
}
720+
}
721+
722+
fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
723+
&self,
724+
err: &mut Diagnostic,
725+
qpath: &'tcx hir::QPath<'tcx>,
726+
msg: String,
727+
num_assoc_fn_excess_args: usize,
728+
num_trait_generics_except_self: usize,
729+
) {
730+
if let hir::QPath::Resolved(_, path) = qpath
731+
&& let Some(trait_path_segment) = path.segments.get(0) {
732+
let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params();
733+
734+
if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait {
735+
if let Some(span) = self.gen_args.span_ext()
736+
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
737+
let sugg = vec![
738+
(self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)),
739+
(span.with_lo(self.path_segment.ident.span.hi()), "".to_owned())
740+
];
741+
742+
err.multipart_suggestion(
743+
msg,
744+
sugg,
745+
Applicability::MaybeIncorrect
746+
);
747+
}
748+
}
749+
}
750+
}
751+
752+
fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
753+
&self,
754+
err: &mut Diagnostic,
755+
trait_: DefId,
756+
expr: &'tcx hir::Expr<'tcx>,
757+
msg: String,
758+
num_assoc_fn_excess_args: usize,
759+
num_trait_generics_except_self: usize,
760+
) {
761+
if let hir::ExprKind::MethodCall(_, args, _) = expr.kind {
762+
assert_eq!(args.len(), 1);
763+
if num_assoc_fn_excess_args == num_trait_generics_except_self {
764+
if let Some(gen_args) = self.gen_args.span_ext()
765+
&& let Ok(gen_args) = self.tcx.sess.source_map().span_to_snippet(gen_args)
766+
&& let Ok(args) = self.tcx.sess.source_map().span_to_snippet(args[0].span) {
767+
let sugg = format!("{}::{}::{}({})", self.tcx.item_name(trait_), gen_args, self.tcx.item_name(self.def_id), args);
768+
err.span_suggestion(expr.span, msg, sugg, Applicability::MaybeIncorrect);
769+
}
770+
}
771+
}
772+
}
773+
656774
/// Suggests to remove redundant argument(s):
657775
///
658776
/// ```text

src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr

+10-3
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,22 @@ error[E0107]: this associated function takes 0 generic arguments but 1 generic a
22
--> $DIR/invalid-const-arg-for-type-param.rs:6:23
33
|
44
LL | let _: u32 = 5i32.try_into::<32>().unwrap();
5-
| ^^^^^^^^------ help: remove these generics
6-
| |
7-
| expected 0 generic arguments
5+
| ^^^^^^^^ expected 0 generic arguments
86
|
97
note: associated function defined here, with 0 generic parameters
108
--> $SRC_DIR/core/src/convert/mod.rs:LL:COL
119
|
1210
LL | fn try_into(self) -> Result<T, Self::Error>;
1311
| ^^^^^^^^
12+
help: consider moving this generic argument to the `TryInto` trait, which takes up to 1 argument
13+
|
14+
LL | let _: u32 = TryInto::<32>::try_into(5i32).unwrap();
15+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
16+
help: remove these generics
17+
|
18+
LL - let _: u32 = 5i32.try_into::<32>().unwrap();
19+
LL + let _: u32 = 5i32.try_into().unwrap();
20+
|
1421

1522
error[E0599]: no method named `f` found for struct `S` in the current scope
1623
--> $DIR/invalid-const-arg-for-type-param.rs:9:7
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
let _ = A::<S>::foo::<S>();
28+
//~^ ERROR
29+
//~| HELP remove these generics
30+
31+
let _ = 42.into::<Option<_>>();
32+
//~^ ERROR
33+
//~| HELP remove these generics
34+
//~| HELP consider moving this generic argument
35+
}
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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: consider moving this generic argument to the `A` trait, which takes up to 1 argument
13+
|
14+
LL - let _ = A::foo::<S>();
15+
LL + let _ = A::<S>::foo();
16+
|
17+
help: remove these generics
18+
|
19+
LL - let _ = A::foo::<S>();
20+
LL + let _ = A::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: consider moving these generic arguments to the `B` trait, which takes up to 2 arguments
35+
|
36+
LL - let _ = B::bar::<S, S>();
37+
LL + let _ = B::<S, S>::bar();
38+
|
39+
help: remove these generics
40+
|
41+
LL - let _ = B::bar::<S, S>();
42+
LL + let _ = B::bar();
43+
|
44+
45+
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
46+
--> $DIR/issue-89064.rs:27:21
47+
|
48+
LL | let _ = A::<S>::foo::<S>();
49+
| ^^^----- help: remove these generics
50+
| |
51+
| expected 0 generic arguments
52+
|
53+
note: associated function defined here, with 0 generic parameters
54+
--> $DIR/issue-89064.rs:4:8
55+
|
56+
LL | fn foo() {}
57+
| ^^^
58+
59+
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
60+
--> $DIR/issue-89064.rs:31:16
61+
|
62+
LL | let _ = 42.into::<Option<_>>();
63+
| ^^^^ expected 0 generic arguments
64+
|
65+
note: associated function defined here, with 0 generic parameters
66+
--> $SRC_DIR/core/src/convert/mod.rs:LL:COL
67+
|
68+
LL | fn into(self) -> T;
69+
| ^^^^
70+
help: consider moving this generic argument to the `Into` trait, which takes up to 1 argument
71+
|
72+
LL | let _ = Into::<Option<_>>::into(42);
73+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
74+
help: remove these generics
75+
|
76+
LL - let _ = 42.into::<Option<_>>();
77+
LL + let _ = 42.into();
78+
|
79+
80+
error: aborting due to 4 previous errors
81+
82+
For more information about this error, try `rustc --explain E0107`.

0 commit comments

Comments
 (0)