about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-11-12 06:27:16 +0100
committerGitHub <noreply@github.com>2024-11-12 06:27:16 +0100
commit9098e03fb21ec6591e4c394059e9a02df297d29f (patch)
treee53d5ab06c2b2cbee57bc04bf8a29d2fd5dddd95
parent67f21277cd40cd12e69aa34089f4a20926fd6dc5 (diff)
parent02add7d0fbc65296af285fe5c62b36b42a9f476f (diff)
downloadrust-9098e03fb21ec6591e4c394059e9a02df297d29f.tar.gz
rust-9098e03fb21ec6591e4c394059e9a02df297d29f.zip
Rollup merge of #132487 - dianne:include-trait-args-in-suggestion, r=fmease
Provide placeholder generics for traits in "no method found for type parameter" suggestions

In the diagnostics for the error ``no method named `method` found for type parameter `T` in the current scope [E0599]``, the compiler will suggest adding bounds on `T` for traits that define a method named `method`. However, these suggestions didn't include any generic arguments, so applying them would result in a `missing generics for trait` or `missing lifetime specifier` error. This PR adds placeholder arguments to the suggestion in such cases. Specifically, I tried to base the placeholders off of what's done in suggestions for when generics are missing or too few are provided:
- The placeholder for a parameter without a default is the name of the parameter.
- Placeholders are not provided for parameters with defaults.

Placeholder arguments are enclosed in `/*` and `*/`, and the applicability of the suggestion is downgraded to `Applicability::HasPlaceholders` if any placeholders are provided.

Fixes #132407
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs64
-rw-r--r--tests/ui/suggestions/no-method-found-suggest-trait-args.rs30
-rw-r--r--tests/ui/suggestions/no-method-found-suggest-trait-args.stderr63
4 files changed, 137 insertions, 21 deletions
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index f5987a11fa5..63dccf8b0ce 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -4,6 +4,7 @@
 #![feature(array_windows)]
 #![feature(box_patterns)]
 #![feature(if_let_guard)]
+#![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(try_blocks)]
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index cb20a1d7c7b..8772599e316 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -3874,22 +3874,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 param.name.ident(),
                             ));
                             let bounds_span = hir_generics.bounds_span_for_suggestions(def_id);
+                            let mut applicability = Applicability::MaybeIncorrect;
+                            // Format the path of each suggested candidate, providing placeholders
+                            // for any generic arguments without defaults.
+                            let candidate_strs: Vec<_> = candidates
+                                .iter()
+                                .map(|cand| {
+                                    let cand_path = self.tcx.def_path_str(cand.def_id);
+                                    let cand_params = &self.tcx.generics_of(cand.def_id).own_params;
+                                    let cand_args: String = cand_params
+                                        .iter()
+                                        .skip(1)
+                                        .filter_map(|param| match param.kind {
+                                            ty::GenericParamDefKind::Type {
+                                                has_default: true,
+                                                ..
+                                            }
+                                            | ty::GenericParamDefKind::Const {
+                                                has_default: true,
+                                                ..
+                                            } => None,
+                                            _ => Some(param.name.as_str()),
+                                        })
+                                        .intersperse(", ")
+                                        .collect();
+                                    if cand_args.is_empty() {
+                                        cand_path
+                                    } else {
+                                        applicability = Applicability::HasPlaceholders;
+                                        format!("{cand_path}</* {cand_args} */>")
+                                    }
+                                })
+                                .collect();
+
                             if rcvr_ty.is_ref()
                                 && param.is_impl_trait()
                                 && let Some((bounds_span, _)) = bounds_span
                             {
                                 err.multipart_suggestions(
                                     msg,
-                                    candidates.iter().map(|t| {
+                                    candidate_strs.iter().map(|cand| {
                                         vec![
                                             (param.span.shrink_to_lo(), "(".to_string()),
-                                            (
-                                                bounds_span,
-                                                format!(" + {})", self.tcx.def_path_str(t.def_id)),
-                                            ),
+                                            (bounds_span, format!(" + {cand})")),
                                         ]
                                     }),
-                                    Applicability::MaybeIncorrect,
+                                    applicability,
                                 );
                                 return;
                             }
@@ -3905,16 +3935,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     (param.span.shrink_to_hi(), Introducer::Colon, None)
                                 };
 
-                            let all_suggs = candidates.iter().map(|cand| {
-                                let suggestion = format!(
-                                    "{} {}",
-                                    match introducer {
-                                        Introducer::Plus => " +",
-                                        Introducer::Colon => ":",
-                                        Introducer::Nothing => "",
-                                    },
-                                    self.tcx.def_path_str(cand.def_id)
-                                );
+                            let all_suggs = candidate_strs.iter().map(|cand| {
+                                let suggestion = format!("{} {cand}", match introducer {
+                                    Introducer::Plus => " +",
+                                    Introducer::Colon => ":",
+                                    Introducer::Nothing => "",
+                                },);
 
                                 let mut suggs = vec![];
 
@@ -3928,11 +3954,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 suggs
                             });
 
-                            err.multipart_suggestions(
-                                msg,
-                                all_suggs,
-                                Applicability::MaybeIncorrect,
-                            );
+                            err.multipart_suggestions(msg, all_suggs, applicability);
 
                             return;
                         }
diff --git a/tests/ui/suggestions/no-method-found-suggest-trait-args.rs b/tests/ui/suggestions/no-method-found-suggest-trait-args.rs
new file mode 100644
index 00000000000..d51f86b29e8
--- /dev/null
+++ b/tests/ui/suggestions/no-method-found-suggest-trait-args.rs
@@ -0,0 +1,30 @@
+/// Tests that suggestions to add trait bounds that would enable using a method include appropriate
+/// placeholder arguments for that trait.
+
+trait Trait<I> {
+    fn method(&self) {}
+}
+
+trait Trait2<'a, A, const B: u8, C = (), const D: u8 = 0> {
+    fn method2(&self) {}
+}
+
+fn foo<T>(value: T) {
+    //~^ SUGGESTION : Trait</* I */>
+    //~| SUGGESTION : Trait2</* 'a, A, B */>
+    value.method();
+    //~^ ERROR no method named `method` found for type parameter `T` in the current scope [E0599]
+    value.method2();
+    //~^ ERROR no method named `method2` found for type parameter `T` in the current scope [E0599]
+}
+
+fn bar(value: impl Copy) {
+    //~^ SUGGESTION + Trait</* I */>
+    //~| SUGGESTION + Trait2</* 'a, A, B */>
+    value.method();
+    //~^ ERROR no method named `method` found for type parameter `impl Copy` in the current scope [E0599]
+    value.method2();
+    //~^ ERROR no method named `method2` found for type parameter `impl Copy` in the current scope [E0599]
+}
+
+fn main() {}
diff --git a/tests/ui/suggestions/no-method-found-suggest-trait-args.stderr b/tests/ui/suggestions/no-method-found-suggest-trait-args.stderr
new file mode 100644
index 00000000000..3dcd4667fa0
--- /dev/null
+++ b/tests/ui/suggestions/no-method-found-suggest-trait-args.stderr
@@ -0,0 +1,63 @@
+error[E0599]: no method named `method` found for type parameter `T` in the current scope
+  --> $DIR/no-method-found-suggest-trait-args.rs:15:11
+   |
+LL | fn foo<T>(value: T) {
+   |        - method `method` not found for this type parameter
+...
+LL |     value.method();
+   |           ^^^^^^ method not found in `T`
+   |
+   = help: items from traits can only be used if the type parameter is bounded by the trait
+help: the following trait defines an item `method`, perhaps you need to restrict type parameter `T` with it:
+   |
+LL | fn foo<T: Trait</* I */>>(value: T) {
+   |         ++++++++++++++++
+
+error[E0599]: no method named `method2` found for type parameter `T` in the current scope
+  --> $DIR/no-method-found-suggest-trait-args.rs:17:11
+   |
+LL | fn foo<T>(value: T) {
+   |        - method `method2` not found for this type parameter
+...
+LL |     value.method2();
+   |           ^^^^^^^ method not found in `T`
+   |
+   = help: items from traits can only be used if the type parameter is bounded by the trait
+help: the following trait defines an item `method2`, perhaps you need to restrict type parameter `T` with it:
+   |
+LL | fn foo<T: Trait2</* 'a, A, B */>>(value: T) {
+   |         ++++++++++++++++++++++++
+
+error[E0599]: no method named `method` found for type parameter `impl Copy` in the current scope
+  --> $DIR/no-method-found-suggest-trait-args.rs:24:11
+   |
+LL | fn bar(value: impl Copy) {
+   |               --------- method `method` not found for this type parameter
+...
+LL |     value.method();
+   |           ^^^^^^ method not found in `impl Copy`
+   |
+   = help: items from traits can only be used if the type parameter is bounded by the trait
+help: the following trait defines an item `method`, perhaps you need to restrict type parameter `impl Copy` with it:
+   |
+LL | fn bar(value: impl Copy + Trait</* I */>) {
+   |                         ++++++++++++++++
+
+error[E0599]: no method named `method2` found for type parameter `impl Copy` in the current scope
+  --> $DIR/no-method-found-suggest-trait-args.rs:26:11
+   |
+LL | fn bar(value: impl Copy) {
+   |               --------- method `method2` not found for this type parameter
+...
+LL |     value.method2();
+   |           ^^^^^^^ method not found in `impl Copy`
+   |
+   = help: items from traits can only be used if the type parameter is bounded by the trait
+help: the following trait defines an item `method2`, perhaps you need to restrict type parameter `impl Copy` with it:
+   |
+LL | fn bar(value: impl Copy + Trait2</* 'a, A, B */>) {
+   |                         ++++++++++++++++++++++++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0599`.