about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-08-31 21:30:08 +0200
committerGitHub <noreply@github.com>2022-08-31 21:30:08 +0200
commit2af2cda32a251d792bfef58e49a25a866f622766 (patch)
treec4310e0aa37b90450ddf3427e4119c868b44b719
parent6438f4addcf0aa012048448a7b208eaee0851d06 (diff)
parent152913767a365d963f2486fb88eff976501d7f72 (diff)
downloadrust-2af2cda32a251d792bfef58e49a25a866f622766.tar.gz
rust-2af2cda32a251d792bfef58e49a25a866f622766.zip
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
-rw-r--r--compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs118
-rw-r--r--src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr13
-rw-r--r--src/test/ui/suggestions/issue-89064.rs35
-rw-r--r--src/test/ui/suggestions/issue-89064.stderr82
4 files changed, 245 insertions, 3 deletions
diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
index 6aa1c915542..d4b5e5e2fe4 100644
--- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
@@ -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
diff --git a/src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr b/src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr
index b1b359619dc..d955b4f9651 100644
--- a/src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr
+++ b/src/test/ui/const-generics/invalid-const-arg-for-type-param.stderr
@@ -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
diff --git a/src/test/ui/suggestions/issue-89064.rs b/src/test/ui/suggestions/issue-89064.rs
new file mode 100644
index 00000000000..fa5fc899dc0
--- /dev/null
+++ b/src/test/ui/suggestions/issue-89064.rs
@@ -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
+}
diff --git a/src/test/ui/suggestions/issue-89064.stderr b/src/test/ui/suggestions/issue-89064.stderr
new file mode 100644
index 00000000000..8b2a3881628
--- /dev/null
+++ b/src/test/ui/suggestions/issue-89064.stderr
@@ -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`.