Skip to content

Commit 202c706

Browse files
authored
Rollup merge of #103478 - SpanishPear:spanishpear/issue_103366_fix, r=TaKO8Ki
Suggest fix for misplaced generic params on fn item #103366 fixes #103366 This still has some work to go, but works for 2/3 of the initial base cases described in #1033366 simple fn: ``` error: expected identifier, found `<` --> shreys/test_1.rs:1:3 | 1 | fn<T> id(x: T) -> T { x } | ^ expected identifier | help: help: place the generic parameter list after the function name: | 1 | fn id<T>(x: T) -> T { x } | ~~~~ ``` Complicated bounds ``` error: expected identifier, found `<` --> spanishpear/test_2.rs:1:3 | 1 | fn<'a, B: 'a + std::ops::Add<Output = u32>> f(_x: B) { } | ^ expected identifier | help: help: place the generic parameter list after the function name: | 1 | fn f<'a, B: 'a + std::ops::Add<Output = u32>>(_x: B) { } | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``` Opening a draft PR for comments on approach, particularly I have the following questions: - [x] Is it okay to be using `err.span_suggestion` over struct derives? I struggled to get the initial implementation (particularly the correct suggestion message) on struct derives, although I think given what I've learned since starting, I could attempt re-doing it with that approach. - [x] in the case where the snippet cannot be obtained from a span, is the `help` but no suggestion okay? I think yes (also, when does this case occur?) - [x] are there any red flags for the generalisation of this work for relevant item kinds (i.e. `struct`, `enum`, `trait`, and `union`). My basic testing indicates it does work for those types except the help tip is currently hardcoded to `after the function name` - which should change dependent on the item. - [x] I am planning to not show the suggestion if there is already a `<` after the item identifier, (i.e. if there are already generics, as after a function name per the original issue). Any major objections? - [x] Is the style of error okay? I wasn't sure if there was a way to make it display nicer, or if thats handled by span_suggestion These aren't blocking questions, and I will keep working on: - check if there is a `<` after the ident (and if so, not showing the suggestion) - generalize the help message - figuring out how to write/run/etc ui tests (including reading the docs for them) - logic cleanups
2 parents 9bb6e60 + a3d32bb commit 202c706

23 files changed

+287
-2
lines changed

compiler/rustc_parse/src/parser/diagnostics.rs

+56-2
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ impl<'a> Parser<'a> {
284284
self.sess.source_map().span_to_snippet(span)
285285
}
286286

287-
pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
287+
pub(super) fn expected_ident_found(&mut self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
288288
let valid_follow = &[
289289
TokenKind::Eq,
290290
TokenKind::Colon,
@@ -324,7 +324,61 @@ impl<'a> Parser<'a> {
324324
suggest_raw,
325325
suggest_remove_comma,
326326
};
327-
err.into_diagnostic(&self.sess.span_diagnostic)
327+
let mut err = err.into_diagnostic(&self.sess.span_diagnostic);
328+
329+
// if the token we have is a `<`
330+
// it *might* be a misplaced generic
331+
if self.token == token::Lt {
332+
// all keywords that could have generic applied
333+
let valid_prev_keywords =
334+
[kw::Fn, kw::Type, kw::Struct, kw::Enum, kw::Union, kw::Trait];
335+
336+
// If we've expected an identifier,
337+
// and the current token is a '<'
338+
// if the previous token is a valid keyword
339+
// that might use a generic, then suggest a correct
340+
// generic placement (later on)
341+
let maybe_keyword = self.prev_token.clone();
342+
if valid_prev_keywords.into_iter().any(|x| maybe_keyword.is_keyword(x)) {
343+
// if we have a valid keyword, attempt to parse generics
344+
// also obtain the keywords symbol
345+
match self.parse_generics() {
346+
Ok(generic) => {
347+
if let TokenKind::Ident(symbol, _) = maybe_keyword.kind {
348+
let ident_name = symbol;
349+
// at this point, we've found something like
350+
// `fn <T>id`
351+
// and current token should be Ident with the item name (i.e. the function name)
352+
// if there is a `<` after the fn name, then don't show a suggestion, show help
353+
354+
if !self.look_ahead(1, |t| *t == token::Lt) &&
355+
let Ok(snippet) = self.sess.source_map().span_to_snippet(generic.span) {
356+
err.multipart_suggestion_verbose(
357+
format!("place the generic parameter name after the {ident_name} name"),
358+
vec![
359+
(self.token.span.shrink_to_hi(), snippet),
360+
(generic.span, String::new())
361+
],
362+
Applicability::MaybeIncorrect,
363+
);
364+
} else {
365+
err.help(format!(
366+
"place the generic parameter name after the {ident_name} name"
367+
));
368+
}
369+
}
370+
}
371+
Err(err) => {
372+
// if there's an error parsing the generics,
373+
// then don't do a misplaced generics suggestion
374+
// and emit the expected ident error instead;
375+
err.cancel();
376+
}
377+
}
378+
}
379+
}
380+
381+
err
328382
}
329383

330384
pub(super) fn expected_one_of_not_found(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
enum Foo<T> { Variant(T) }
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the enum name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
enum<T> Foo { Variant(T) }
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the enum name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: expected identifier, found `<`
2+
--> $DIR/enum.rs:5:5
3+
|
4+
LL | enum<T> Foo { Variant(T) }
5+
| ^ expected identifier
6+
|
7+
help: place the generic parameter name after the enum name
8+
|
9+
LL - enum<T> Foo { Variant(T) }
10+
LL + enum Foo<T> { Variant(T) }
11+
|
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366
2+
// there is already an existing generic on f, so don't show a suggestion
3+
4+
#[allow(unused)]
5+
fn<'a, B: 'a + std::ops::Add<Output = u32>> f<T>(_x: B) { }
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the fn name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: expected identifier, found `<`
2+
--> $DIR/existing_generics.rs:5:3
3+
|
4+
LL | fn<'a, B: 'a + std::ops::Add<Output = u32>> f<T>(_x: B) { }
5+
| ^ expected identifier
6+
|
7+
= help: place the generic parameter name after the fn name
8+
9+
error: aborting due to previous error
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
fn f<'a, B: 'a + std::ops::Add<Output = u32>>(_x: B) { }
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the fn name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
fn<'a, B: 'a + std::ops::Add<Output = u32>> f(_x: B) { }
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the fn name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: expected identifier, found `<`
2+
--> $DIR/fn-complex-generics.rs:5:3
3+
|
4+
LL | fn<'a, B: 'a + std::ops::Add<Output = u32>> f(_x: B) { }
5+
| ^ expected identifier
6+
|
7+
help: place the generic parameter name after the fn name
8+
|
9+
LL - fn<'a, B: 'a + std::ops::Add<Output = u32>> f(_x: B) { }
10+
LL + fn f<'a, B: 'a + std::ops::Add<Output = u32>>(_x: B) { }
11+
|
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// The generics fail to parse here, so don't make any suggestions/help
3+
4+
#[allow(unused)]
5+
fn<~>()> id(x: T) -> T { x }
6+
//~^ ERROR expected identifier, found `<`
7+
8+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: expected identifier, found `<`
2+
--> $DIR/fn-invalid-generics.rs:5:3
3+
|
4+
LL | fn<~>()> id(x: T) -> T { x }
5+
| ^ expected identifier
6+
7+
error: aborting due to previous error
8+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
fn id<T>(x: T) -> T { x }
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the fn name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
fn<T> id(x: T) -> T { x }
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the fn name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: expected identifier, found `<`
2+
--> $DIR/fn-simple.rs:5:3
3+
|
4+
LL | fn<T> id(x: T) -> T { x }
5+
| ^ expected identifier
6+
|
7+
help: place the generic parameter name after the fn name
8+
|
9+
LL - fn<T> id(x: T) -> T { x }
10+
LL + fn id<T>(x: T) -> T { x }
11+
|
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
struct Foo<T> { x: T }
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the struct name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
struct<T> Foo { x: T }
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the struct name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: expected identifier, found `<`
2+
--> $DIR/struct.rs:5:7
3+
|
4+
LL | struct<T> Foo { x: T }
5+
| ^ expected identifier
6+
|
7+
help: place the generic parameter name after the struct name
8+
|
9+
LL - struct<T> Foo { x: T }
10+
LL + struct Foo<T> { x: T }
11+
|
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
trait Foo<T> {
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the trait name
8+
}
9+
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
trait<T> Foo {
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the trait name
8+
}
9+
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: expected identifier, found `<`
2+
--> $DIR/trait.rs:5:6
3+
|
4+
LL | trait<T> Foo {
5+
| ^ expected identifier
6+
|
7+
help: place the generic parameter name after the trait name
8+
|
9+
LL - trait<T> Foo {
10+
LL + trait Foo<T> {
11+
|
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
type Foo<T> = T;
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the type name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Issue: 103366 , Suggest fix for misplaced generic params
2+
// run-rustfix
3+
4+
#[allow(unused)]
5+
type<T> Foo = T;
6+
//~^ ERROR expected identifier, found `<`
7+
//~| HELP place the generic parameter name after the type name
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: expected identifier, found `<`
2+
--> $DIR/type.rs:5:5
3+
|
4+
LL | type<T> Foo = T;
5+
| ^ expected identifier
6+
|
7+
help: place the generic parameter name after the type name
8+
|
9+
LL - type<T> Foo = T;
10+
LL + type Foo<T> = T;
11+
|
12+
13+
error: aborting due to previous error
14+

0 commit comments

Comments
 (0)