Skip to content

Commit c2b906b

Browse files
committed
Recover from fn ptr tys with generic param list
1 parent b7b7f27 commit c2b906b

File tree

6 files changed

+228
-3
lines changed

6 files changed

+228
-3
lines changed

compiler/rustc_error_messages/locales/en-US/parser.ftl

+9
Original file line numberDiff line numberDiff line change
@@ -375,3 +375,12 @@ parser_async_move_order_incorrect = the order of `move` and `async` is incorrect
375375
376376
parser_double_colon_in_bound = expected `:` followed by trait or lifetime
377377
.suggestion = use single colon
378+
379+
parser_fn_ptr_with_generics = function pointer types may not have generic parameters
380+
.suggestion = consider moving the lifetime {$arity ->
381+
[one] parameter
382+
*[other] parameters
383+
} to {$for_param_list_exists ->
384+
[true] the
385+
*[false] a
386+
} `for` parameter list

compiler/rustc_parse/src/errors.rs

+21
Original file line numberDiff line numberDiff line change
@@ -1278,3 +1278,24 @@ pub(crate) struct DoubleColonInBound {
12781278
#[suggestion(code = ": ", applicability = "machine-applicable")]
12791279
pub between: Span,
12801280
}
1281+
1282+
#[derive(Diagnostic)]
1283+
#[diag(parser_fn_ptr_with_generics)]
1284+
pub(crate) struct FnPtrWithGenerics {
1285+
#[primary_span]
1286+
pub span: Span,
1287+
#[subdiagnostic]
1288+
pub sugg: Option<FnPtrWithGenericsSugg>,
1289+
}
1290+
1291+
#[derive(Subdiagnostic)]
1292+
#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
1293+
pub(crate) struct FnPtrWithGenericsSugg {
1294+
#[suggestion_part(code = "{snippet}")]
1295+
pub left: Span,
1296+
pub snippet: String,
1297+
#[suggestion_part(code = "")]
1298+
pub right: Span,
1299+
pub arity: usize,
1300+
pub for_param_list_exists: bool,
1301+
}

compiler/rustc_parse/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![feature(array_windows)]
44
#![feature(box_patterns)]
55
#![feature(if_let_guard)]
6+
#![feature(iter_intersperse)]
67
#![feature(let_chains)]
78
#![feature(never_type)]
89
#![feature(rustc_attrs)]

compiler/rustc_parse/src/parser/ty.rs

+55-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::{Parser, PathStyle, TokenType};
22

3+
use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg};
34
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
45

56
use rustc_ast::ptr::P;
@@ -270,14 +271,19 @@ impl<'a> Parser<'a> {
270271
TyKind::Infer
271272
} else if self.check_fn_front_matter(false, Case::Sensitive) {
272273
// Function pointer type
273-
self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)?
274+
self.parse_ty_bare_fn(lo, Vec::new(), None, recover_return_sign)?
274275
} else if self.check_keyword(kw::For) {
275276
// Function pointer type or bound list (trait object type) starting with a poly-trait.
276277
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
277278
// `for<'lt> Trait1<'lt> + Trait2 + 'a`
278279
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
279280
if self.check_fn_front_matter(false, Case::Sensitive) {
280-
self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)?
281+
self.parse_ty_bare_fn(
282+
lo,
283+
lifetime_defs,
284+
Some(self.prev_token.span.shrink_to_lo()),
285+
recover_return_sign,
286+
)?
281287
} else {
282288
let path = self.parse_path(PathStyle::Type)?;
283289
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
@@ -519,7 +525,8 @@ impl<'a> Parser<'a> {
519525
fn parse_ty_bare_fn(
520526
&mut self,
521527
lo: Span,
522-
params: Vec<GenericParam>,
528+
mut params: Vec<GenericParam>,
529+
param_insertion_point: Option<Span>,
523530
recover_return_sign: RecoverReturnSign,
524531
) -> PResult<'a, TyKind> {
525532
let inherited_vis = rustc_ast::Visibility {
@@ -530,6 +537,9 @@ impl<'a> Parser<'a> {
530537
let span_start = self.token.span;
531538
let ast::FnHeader { ext, unsafety, constness, asyncness } =
532539
self.parse_fn_front_matter(&inherited_vis)?;
540+
if self.may_recover() && self.token.kind == TokenKind::Lt {
541+
self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?;
542+
}
533543
let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?;
534544
let whole_span = lo.to(self.prev_token.span);
535545
if let ast::Const::Yes(span) = constness {
@@ -545,6 +555,48 @@ impl<'a> Parser<'a> {
545555
Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span })))
546556
}
547557

558+
/// Recover from function pointer types with a generic parameter list (e.g. `fn<'a>(&'a str)`).
559+
fn recover_fn_ptr_with_generics(
560+
&mut self,
561+
lo: Span,
562+
params: &mut Vec<GenericParam>,
563+
param_insertion_point: Option<Span>,
564+
) -> PResult<'a, ()> {
565+
let generics = self.parse_generics()?;
566+
let arity = generics.params.len();
567+
568+
let mut lifetimes: Vec<_> = generics
569+
.params
570+
.into_iter()
571+
.filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime))
572+
.collect();
573+
574+
let sugg = if !lifetimes.is_empty() {
575+
let snippet =
576+
lifetimes.iter().map(|param| param.ident.as_str()).intersperse(", ").collect();
577+
578+
let (left, snippet) = if let Some(span) = param_insertion_point {
579+
(span, if params.is_empty() { snippet } else { format!(", {snippet}") })
580+
} else {
581+
(lo.shrink_to_lo(), format!("for<{snippet}> "))
582+
};
583+
584+
Some(FnPtrWithGenericsSugg {
585+
left,
586+
snippet,
587+
right: generics.span,
588+
arity,
589+
for_param_list_exists: param_insertion_point.is_some(),
590+
})
591+
} else {
592+
None
593+
};
594+
595+
self.sess.emit_err(FnPtrWithGenerics { span: generics.span, sugg });
596+
params.append(&mut lifetimes);
597+
Ok(())
598+
}
599+
548600
/// Emit an error for the given bad function pointer qualifier.
549601
fn error_fn_ptr_bad_qualifier(&self, span: Span, qual_span: Span, qual: &str) {
550602
self.struct_span_err(span, &format!("an `fn` pointer type cannot be `{}`", qual))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
fn main() {
2+
type Predicate = fn<'a>(&'a str) -> bool;
3+
//~^ ERROR function pointer types may not have generic parameters
4+
5+
type Identity = fn<T>(T) -> T;
6+
//~^ ERROR function pointer types may not have generic parameters
7+
//~| ERROR cannot find type `T` in this scope
8+
//~| ERROR cannot find type `T` in this scope
9+
10+
let _: fn<const N: usize, 'e, Q, 'f>();
11+
//~^ ERROR function pointer types may not have generic parameters
12+
13+
let _: for<'outer> fn<'inner>();
14+
//~^ ERROR function pointer types may not have generic parameters
15+
16+
let _: for<> fn<'r>();
17+
//~^ ERROR function pointer types may not have generic parameters
18+
19+
type Hmm = fn<>();
20+
//~^ ERROR function pointer types may not have generic parameters
21+
22+
let _: extern fn<'a: 'static>();
23+
//~^ ERROR function pointer types may not have generic parameters
24+
//~| ERROR lifetime bounds cannot be used in this context
25+
26+
let _: for<'any> extern "C" fn<'u>();
27+
//~^ ERROR function pointer types may not have generic parameters
28+
29+
type QuiteBroken = fn<const>();
30+
//~^ ERROR expected identifier, found `>`
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
error: function pointer types may not have generic parameters
2+
--> $DIR/recover-fn-ptr-with-generics.rs:2:24
3+
|
4+
LL | type Predicate = fn<'a>(&'a str) -> bool;
5+
| ^^^^
6+
|
7+
help: consider moving the lifetime parameter to a `for` parameter list
8+
|
9+
LL - type Predicate = fn<'a>(&'a str) -> bool;
10+
LL + type Predicate = for<'a> fn(&'a str) -> bool;
11+
|
12+
13+
error: function pointer types may not have generic parameters
14+
--> $DIR/recover-fn-ptr-with-generics.rs:5:23
15+
|
16+
LL | type Identity = fn<T>(T) -> T;
17+
| ^^^
18+
19+
error: function pointer types may not have generic parameters
20+
--> $DIR/recover-fn-ptr-with-generics.rs:10:14
21+
|
22+
LL | let _: fn<const N: usize, 'e, Q, 'f>();
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
24+
|
25+
help: consider moving the lifetime parameters to a `for` parameter list
26+
|
27+
LL - let _: fn<const N: usize, 'e, Q, 'f>();
28+
LL + let _: for<'e, 'f> fn();
29+
|
30+
31+
error: function pointer types may not have generic parameters
32+
--> $DIR/recover-fn-ptr-with-generics.rs:13:26
33+
|
34+
LL | let _: for<'outer> fn<'inner>();
35+
| ^^^^^^^^
36+
|
37+
help: consider moving the lifetime parameter to the `for` parameter list
38+
|
39+
LL - let _: for<'outer> fn<'inner>();
40+
LL + let _: for<'outer, 'inner> fn();
41+
|
42+
43+
error: function pointer types may not have generic parameters
44+
--> $DIR/recover-fn-ptr-with-generics.rs:16:20
45+
|
46+
LL | let _: for<> fn<'r>();
47+
| ^^^^
48+
|
49+
help: consider moving the lifetime parameter to the `for` parameter list
50+
|
51+
LL - let _: for<> fn<'r>();
52+
LL + let _: for<'r> fn();
53+
|
54+
55+
error: function pointer types may not have generic parameters
56+
--> $DIR/recover-fn-ptr-with-generics.rs:19:18
57+
|
58+
LL | type Hmm = fn<>();
59+
| ^^
60+
61+
error: function pointer types may not have generic parameters
62+
--> $DIR/recover-fn-ptr-with-generics.rs:22:21
63+
|
64+
LL | let _: extern fn<'a: 'static>();
65+
| ^^^^^^^^^^^^^
66+
|
67+
help: consider moving the lifetime parameter to a `for` parameter list
68+
|
69+
LL - let _: extern fn<'a: 'static>();
70+
LL + let _: for<'a> extern fn();
71+
|
72+
73+
error: function pointer types may not have generic parameters
74+
--> $DIR/recover-fn-ptr-with-generics.rs:26:35
75+
|
76+
LL | let _: for<'any> extern "C" fn<'u>();
77+
| ^^^^
78+
|
79+
help: consider moving the lifetime parameter to the `for` parameter list
80+
|
81+
LL - let _: for<'any> extern "C" fn<'u>();
82+
LL + let _: for<'any, 'u> extern "C" fn();
83+
|
84+
85+
error: expected identifier, found `>`
86+
--> $DIR/recover-fn-ptr-with-generics.rs:29:32
87+
|
88+
LL | type QuiteBroken = fn<const>();
89+
| ^ expected identifier
90+
91+
error: lifetime bounds cannot be used in this context
92+
--> $DIR/recover-fn-ptr-with-generics.rs:22:26
93+
|
94+
LL | let _: extern fn<'a: 'static>();
95+
| ^^^^^^^
96+
97+
error[E0412]: cannot find type `T` in this scope
98+
--> $DIR/recover-fn-ptr-with-generics.rs:5:27
99+
|
100+
LL | type Identity = fn<T>(T) -> T;
101+
| ^ not found in this scope
102+
103+
error[E0412]: cannot find type `T` in this scope
104+
--> $DIR/recover-fn-ptr-with-generics.rs:5:33
105+
|
106+
LL | type Identity = fn<T>(T) -> T;
107+
| ^ not found in this scope
108+
109+
error: aborting due to 12 previous errors
110+
111+
For more information about this error, try `rustc --explain E0412`.

0 commit comments

Comments
 (0)