mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
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
This commit is contained in:
commit
2af2cda32a
@ -523,6 +523,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
|
||||
if self.not_enough_args_provided() {
|
||||
self.suggest_adding_args(err);
|
||||
} else if self.too_many_args_provided() {
|
||||
self.suggest_moving_args_from_assoc_fn_to_trait(err);
|
||||
self.suggest_removing_args_or_generics(err);
|
||||
} else {
|
||||
unreachable!();
|
||||
@ -653,6 +654,123 @@ 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_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) {
|
||||
let trait_ = match self.tcx.trait_of_item(self.def_id) {
|
||||
Some(def_id) => def_id,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Skip suggestion when the associated function is itself generic, it is unclear
|
||||
// how to split the provided parameters between those to suggest to the trait and
|
||||
// those to remain on the associated type.
|
||||
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 };
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
if let Some(hir_id) = self.path_segment.hir_id
|
||||
&& let Some(parent_node) = self.tcx.hir().find_parent_node(hir_id)
|
||||
&& let Some(parent_node) = self.tcx.hir().find(parent_node)
|
||||
&& let hir::Node::Expr(expr) = parent_node {
|
||||
match expr.kind {
|
||||
hir::ExprKind::Path(ref qpath) => {
|
||||
self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
|
||||
err,
|
||||
qpath,
|
||||
msg,
|
||||
num_assoc_fn_excess_args,
|
||||
num_trait_generics_except_self
|
||||
)
|
||||
},
|
||||
hir::ExprKind::MethodCall(..) => {
|
||||
self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
|
||||
err,
|
||||
trait_,
|
||||
expr,
|
||||
msg,
|
||||
num_assoc_fn_excess_args,
|
||||
num_trait_generics_except_self
|
||||
)
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
qpath: &'tcx hir::QPath<'tcx>,
|
||||
msg: String,
|
||||
num_assoc_fn_excess_args: usize,
|
||||
num_trait_generics_except_self: usize,
|
||||
) {
|
||||
if let hir::QPath::Resolved(_, path) = qpath
|
||||
&& let Some(trait_path_segment) = path.segments.get(0) {
|
||||
let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params();
|
||||
|
||||
if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait {
|
||||
if let Some(span) = self.gen_args.span_ext()
|
||||
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
trait_: DefId,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
msg: String,
|
||||
num_assoc_fn_excess_args: usize,
|
||||
num_trait_generics_except_self: usize,
|
||||
) {
|
||||
if let hir::ExprKind::MethodCall(_, args, _) = expr.kind {
|
||||
assert_eq!(args.len(), 1);
|
||||
if num_assoc_fn_excess_args == num_trait_generics_except_self {
|
||||
if let Some(gen_args) = self.gen_args.span_ext()
|
||||
&& let Ok(gen_args) = self.tcx.sess.source_map().span_to_snippet(gen_args)
|
||||
&& let Ok(args) = self.tcx.sess.source_map().span_to_snippet(args[0].span) {
|
||||
let sugg = format!("{}::{}::{}({})", self.tcx.item_name(trait_), gen_args, self.tcx.item_name(self.def_id), args);
|
||||
err.span_suggestion(expr.span, msg, sugg, Applicability::MaybeIncorrect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Suggests to remove redundant argument(s):
|
||||
///
|
||||
/// ```text
|
||||
|
@ -2,15 +2,22 @@ error[E0107]: this associated function takes 0 generic arguments but 1 generic a
|
||||
--> $DIR/invalid-const-arg-for-type-param.rs:6:23
|
||||
|
|
||||
LL | let _: u32 = 5i32.try_into::<32>().unwrap();
|
||||
| ^^^^^^^^------ help: remove these generics
|
||||
| |
|
||||
| expected 0 generic arguments
|
||||
| ^^^^^^^^ expected 0 generic arguments
|
||||
|
|
||||
note: associated function defined here, with 0 generic parameters
|
||||
--> $SRC_DIR/core/src/convert/mod.rs:LL:COL
|
||||
|
|
||||
LL | fn try_into(self) -> Result<T, Self::Error>;
|
||||
| ^^^^^^^^
|
||||
help: consider moving this generic argument to the `TryInto` trait, which takes up to 1 argument
|
||||
|
|
||||
LL | let _: u32 = TryInto::<32>::try_into(5i32).unwrap();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
help: remove these generics
|
||||
|
|
||||
LL - let _: u32 = 5i32.try_into::<32>().unwrap();
|
||||
LL + let _: u32 = 5i32.try_into().unwrap();
|
||||
|
|
||||
|
||||
error[E0599]: no method named `f` found for struct `S` in the current scope
|
||||
--> $DIR/invalid-const-arg-for-type-param.rs:9:7
|
||||
|
35
src/test/ui/suggestions/issue-89064.rs
Normal file
35
src/test/ui/suggestions/issue-89064.rs
Normal file
@ -0,0 +1,35 @@
|
||||
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
|
||||
|
||||
let _ = A::<S>::foo::<S>();
|
||||
//~^ ERROR
|
||||
//~| HELP remove these generics
|
||||
|
||||
let _ = 42.into::<Option<_>>();
|
||||
//~^ ERROR
|
||||
//~| HELP remove these generics
|
||||
//~| HELP consider moving this generic argument
|
||||
}
|
82
src/test/ui/suggestions/issue-89064.stderr
Normal file
82
src/test/ui/suggestions/issue-89064.stderr
Normal file
@ -0,0 +1,82 @@
|
||||
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: 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();
|
||||
|
|
||||
help: remove these generics
|
||||
|
|
||||
LL - let _ = A::foo::<S>();
|
||||
LL + let _ = A::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: 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();
|
||||
|
|
||||
help: remove these generics
|
||||
|
|
||||
LL - let _ = B::bar::<S, S>();
|
||||
LL + let _ = B::bar();
|
||||
|
|
||||
|
||||
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
|
||||
--> $DIR/issue-89064.rs:27:21
|
||||
|
|
||||
LL | let _ = A::<S>::foo::<S>();
|
||||
| ^^^----- help: remove these generics
|
||||
| |
|
||||
| expected 0 generic arguments
|
||||
|
|
||||
note: associated function defined here, with 0 generic parameters
|
||||
--> $DIR/issue-89064.rs:4:8
|
||||
|
|
||||
LL | fn foo() {}
|
||||
| ^^^
|
||||
|
||||
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
|
||||
--> $DIR/issue-89064.rs:31:16
|
||||
|
|
||||
LL | let _ = 42.into::<Option<_>>();
|
||||
| ^^^^ expected 0 generic arguments
|
||||
|
|
||||
note: associated function defined here, with 0 generic parameters
|
||||
--> $SRC_DIR/core/src/convert/mod.rs:LL:COL
|
||||
|
|
||||
LL | fn into(self) -> T;
|
||||
| ^^^^
|
||||
help: consider moving this generic argument to the `Into` trait, which takes up to 1 argument
|
||||
|
|
||||
LL | let _ = Into::<Option<_>>::into(42);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
help: remove these generics
|
||||
|
|
||||
LL - let _ = 42.into::<Option<_>>();
|
||||
LL + let _ = 42.into();
|
||||
|
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0107`.
|
Loading…
Reference in New Issue
Block a user