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}") + } + }) + .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 { + fn method(&self) {} +} + +trait Trait2<'a, A, const B: u8, C = (), const D: u8 = 0> { + fn method2(&self) {} +} + +fn foo(value: T) { + //~^ SUGGESTION : Trait + //~| SUGGESTION : Trait2 + 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 + //~| SUGGESTION + Trait2 + 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(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>(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(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>(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) { + | ++++++++++++++++ + +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) { + | ++++++++++++++++++++++++ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0599`.