Skip to content

Commit

Permalink
Unrolled build for rust-lang#122651
Browse files Browse the repository at this point in the history
Rollup merge of rust-lang#122651 - kornelski:flat-turbofish, r=spastorino,compiler-errors

Suggest `_` for missing generic arguments in turbofish

The compiler may suggest unusable generic type names for missing generic arguments in an expression context:

```rust
fn main() {
    (0..1).collect::<Vec>()
}
```

> help: add missing generic argument
>
>      (0..1).collect::<Vec<T>>()

but `T` is not a valid name in this context, and this suggestion won't compile.

I've changed it to use `_` inside method calls (turbofish), so it will suggest `(0..1).collect::<Vec<_>>()` which _may_ compile.

It's possible that the suggested `_` will be ambiguous, but there is very extensive E0283 that will help resolve that, which is more helpful than a basic "cannot find type `T` in this scope" users would get otherwise.

Out of caution to limit scope of the change I've limited it to just turbofish, but I suspect `_` could be the better choice in more cases. Perhaps in all expressions?
  • Loading branch information
rust-timer authored Mar 23, 2024
2 parents 85e449a + 3bbbe3c commit 109e6a1
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,22 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
&self,
num_params_to_take: usize,
) -> String {
let is_in_a_method_call = self
.tcx
.hir()
.parent_iter(self.path_segment.hir_id)
.skip(1)
.find_map(|(_, node)| match node {
hir::Node::Expr(expr) => Some(expr),
_ => None,
})
.is_some_and(|expr| {
matches!(
expr.kind,
hir::ExprKind::MethodCall(hir::PathSegment { args: Some(_), .. }, ..)
)
});

let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig);
let is_used_in_input = |def_id| {
fn_sig.is_some_and(|fn_sig| {
Expand All @@ -453,14 +469,17 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
.skip(self.params_offset + self.num_provided_type_or_const_args())
.take(num_params_to_take)
.map(|param| match param.kind {
// This is being inferred from the item's inputs, no need to set it.
ty::GenericParamDefKind::Type { .. } if is_used_in_input(param.def_id) => {
"_".to_string()
// If it's in method call (turbofish), it might be inferred from the expression (e.g. `.collect::<Vec<_>>()`)
// If it is being inferred from the item's inputs, no need to set it.
ty::GenericParamDefKind::Type { .. }
if is_in_a_method_call || is_used_in_input(param.def_id) =>
{
"_"
}
_ => param.name.to_string(),
_ => param.name.as_str(),
})
.collect::<Vec<_>>()
.join(", ")
.intersperse(", ")
.collect()
}

fn get_unbound_associated_types(&self) -> Vec<String> {
Expand Down
16 changes: 16 additions & 0 deletions tests/ui/generics/generic-type-less-params-with-defaults.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,23 @@ struct Heap;
struct Vec<T, A = Heap>(
marker::PhantomData<(T,A)>);

struct HashMap<K, V, S = ()>(marker::PhantomData<(K,V,S)>);

fn main() {
let _: Vec;
//~^ ERROR missing generics for struct `Vec`
//~| SUGGESTION <T>

let _x = (1..10).collect::<HashMap>();
//~^ ERROR missing generics for struct `HashMap`
//~| SUGGESTION <_, _>

().extend::<[(); 0]>({
fn not_the_extend() {
let _: Vec;
//~^ ERROR missing generics for struct `Vec`
//~| SUGGESTION <T>
}
[]
});
}
36 changes: 34 additions & 2 deletions tests/ui/generics/generic-type-less-params-with-defaults.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0107]: missing generics for struct `Vec`
--> $DIR/generic-type-less-params-with-defaults.rs:9:12
--> $DIR/generic-type-less-params-with-defaults.rs:11:12
|
LL | let _: Vec;
| ^^^ expected at least 1 generic argument
Expand All @@ -14,6 +14,38 @@ help: add missing generic argument
LL | let _: Vec<T>;
| +++

error: aborting due to 1 previous error
error[E0107]: missing generics for struct `HashMap`
--> $DIR/generic-type-less-params-with-defaults.rs:15:32
|
LL | let _x = (1..10).collect::<HashMap>();
| ^^^^^^^ expected at least 2 generic arguments
|
note: struct defined here, with at least 2 generic parameters: `K`, `V`
--> $DIR/generic-type-less-params-with-defaults.rs:8:8
|
LL | struct HashMap<K, V, S = ()>(marker::PhantomData<(K,V,S)>);
| ^^^^^^^ - -
help: add missing generic arguments
|
LL | let _x = (1..10).collect::<HashMap<_, _>>();
| ++++++

error[E0107]: missing generics for struct `Vec`
--> $DIR/generic-type-less-params-with-defaults.rs:21:20
|
LL | let _: Vec;
| ^^^ expected at least 1 generic argument
|
note: struct defined here, with at least 1 generic parameter: `T`
--> $DIR/generic-type-less-params-with-defaults.rs:5:8
|
LL | struct Vec<T, A = Heap>(
| ^^^ -
help: add missing generic argument
|
LL | let _: Vec<T>;
| +++

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0107`.

0 comments on commit 109e6a1

Please sign in to comment.