Skip to content

Commit

Permalink
Provide placeholder generic arguments for traits in "no method found …
Browse files Browse the repository at this point in the history
…for type parameter" suggestions
  • Loading branch information
dianne committed Nov 11, 2024
1 parent de27914 commit 02add7d
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 21 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
64 changes: 43 additions & 21 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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![];

Expand All @@ -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;
}
Expand Down
30 changes: 30 additions & 0 deletions tests/ui/suggestions/no-method-found-suggest-trait-args.rs
Original file line number Diff line number Diff line change
@@ -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() {}
63 changes: 63 additions & 0 deletions tests/ui/suggestions/no-method-found-suggest-trait-args.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit 02add7d

Please sign in to comment.