Recover fn keyword as Fn trait in bounds

This commit is contained in:
Michael Goulet 2022-12-27 06:14:40 +00:00
parent caa64e5b5e
commit aff403cf68
9 changed files with 148 additions and 79 deletions

View File

@ -365,3 +365,6 @@ parse_invalid_identifier_with_leading_number = expected identifier, found number
parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn`
.suggestion = replace `fn` with `impl` here
parse_expected_fn_path_found_fn_keyword = expected identifier, found keyword `fn`
.suggestion = use `Fn` to refer to the trait

View File

@ -626,7 +626,7 @@ impl<'a> ExtCtxt<'a> {
// Builds `#[name = val]`.
//
// Note: `span` is used for both the identifer and the value.
// Note: `span` is used for both the identifier and the value.
pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute {
let g = &self.sess.parse_sess.attr_id_generator;
attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, name, val, span)

View File

@ -851,7 +851,7 @@ impl Cursor<'_> {
}
// Eats the identifier. Note: succeeds on `_`, which isn't a valid
// identifer.
// identifier.
fn eat_identifier(&mut self) {
if !is_id_start(self.first()) {
return;

View File

@ -1229,3 +1229,11 @@ pub(crate) struct FnTypoWithImpl {
#[suggestion(applicability = "maybe-incorrect", code = "impl", style = "verbose")]
pub fn_span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_expected_fn_path_found_fn_keyword)]
pub(crate) struct ExpectedFnPathFoundFnKeyword {
#[primary_span]
#[suggestion(applicability = "machine-applicable", code = "Fn", style = "verbose")]
pub fn_token_span: Span,
}

View File

@ -1,8 +1,9 @@
use super::{Parser, PathStyle, TokenType};
use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg};
use crate::errors::{ExpectedFnPathFoundFnKeyword, FnPtrWithGenerics, FnPtrWithGenericsSugg};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use ast::DUMMY_NODE_ID;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::util::case::Case;
@ -12,7 +13,9 @@ use rustc_ast::{
};
use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym};
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Symbol;
use thin_vec::thin_vec;
/// Any `?` or `~const` modifiers that appear at the start of a bound.
struct BoundModifiers {
@ -912,7 +915,14 @@ impl<'a> Parser<'a> {
modifiers: BoundModifiers,
) -> PResult<'a, GenericBound> {
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
let path = self.parse_path(PathStyle::Type)?;
let path = if self.token.is_keyword(kw::Fn)
&& self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
&& let Some(path) = self.recover_path_from_fn()
{
path
} else {
self.parse_path(PathStyle::Type)?
};
if has_parens {
if self.token.is_like_plus() {
// Someone has written something like `&dyn (Trait + Other)`. The correct code
@ -941,6 +951,38 @@ impl<'a> Parser<'a> {
Ok(GenericBound::Trait(poly_trait, modifier))
}
// recovers a `Fn(..)` parenthesized-style path from `fn(..)`
fn recover_path_from_fn(&mut self) -> Option<ast::Path> {
let fn_token_span = self.token.span;
self.bump();
let args_lo = self.token.span;
let snapshot = self.create_snapshot_for_diagnostic();
match self.parse_fn_decl(|_| false, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) {
Ok(decl) => {
self.sess.emit_err(ExpectedFnPathFoundFnKeyword { fn_token_span });
Some(ast::Path {
span: fn_token_span.to(self.prev_token.span),
segments: thin_vec![ast::PathSegment {
ident: Ident::new(Symbol::intern("Fn"), fn_token_span),
id: DUMMY_NODE_ID,
args: Some(P(ast::GenericArgs::Parenthesized(ast::ParenthesizedArgs {
span: args_lo.to(self.prev_token.span),
inputs: decl.inputs.iter().map(|a| a.ty.clone()).collect(),
inputs_span: args_lo.until(decl.output.span()),
output: decl.output.clone(),
}))),
}],
tokens: None,
})
}
Err(diag) => {
diag.cancel();
self.restore_snapshot(snapshot);
None
}
}
}
/// Optionally parses `for<$generic_params>`.
pub(super) fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<GenericParam>> {
if self.eat_keyword(kw::For) {

View File

@ -4,21 +4,13 @@ fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
//~^ ERROR expected identifier, found keyword `fn`
//~| ERROR expected identifier, found keyword `fn`
//~| ERROR expected identifier, found keyword `fn`
//~| ERROR cannot find trait `r#fn` in this scope
//~| ERROR cannot find trait `r#fn` in this scope
//~| ERROR cannot find trait `r#fn` in this scope
//~| HELP a trait with a similar name exists
//~| HELP a trait with a similar name exists
//~| HELP a trait with a similar name exists
//~| HELP escape `fn` to use it as an identifier
//~| HELP escape `fn` to use it as an identifier
//~| HELP escape `fn` to use it as an identifier
//~| HELP use `Fn` to refer to the trait
//~| HELP use `Fn` to refer to the trait
//~| HELP use `Fn` to refer to the trait
where
G: fn(),
//~^ ERROR expected identifier, found keyword `fn`
//~| ERROR cannot find trait `r#fn` in this scope
//~| HELP a trait with a similar name exists
//~| HELP escape `fn` to use it as an identifier
//~| HELP use `Fn` to refer to the trait
{}
fn _g<A: struct, B>(_: impl struct, _: &dyn struct)

View File

@ -2,48 +2,48 @@ error: expected identifier, found keyword `fn`
--> $DIR/kw-in-trait-bounds.rs:3:10
|
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
| ^^ expected identifier, found keyword
| ^^
|
help: escape `fn` to use it as an identifier
help: use `Fn` to refer to the trait
|
LL | fn _f<F: r#fn(), G>(_: impl fn(), _: &dyn fn())
| ++
LL | fn _f<F: Fn(), G>(_: impl fn(), _: &dyn fn())
| ~~
error: expected identifier, found keyword `fn`
--> $DIR/kw-in-trait-bounds.rs:3:27
|
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
| ^^ expected identifier, found keyword
| ^^
|
help: escape `fn` to use it as an identifier
help: use `Fn` to refer to the trait
|
LL | fn _f<F: fn(), G>(_: impl r#fn(), _: &dyn fn())
| ++
LL | fn _f<F: fn(), G>(_: impl Fn(), _: &dyn fn())
| ~~
error: expected identifier, found keyword `fn`
--> $DIR/kw-in-trait-bounds.rs:3:41
|
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
| ^^ expected identifier, found keyword
| ^^
|
help: escape `fn` to use it as an identifier
help: use `Fn` to refer to the trait
|
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn r#fn())
| ++
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn Fn())
| ~~
error: expected identifier, found keyword `fn`
--> $DIR/kw-in-trait-bounds.rs:17:4
--> $DIR/kw-in-trait-bounds.rs:11:4
|
LL | G: fn(),
| ^^ expected identifier, found keyword
| ^^
|
help: escape `fn` to use it as an identifier
help: use `Fn` to refer to the trait
|
LL | G: r#fn(),
| ++
LL | G: Fn(),
| ~~
error: expected identifier, found keyword `struct`
--> $DIR/kw-in-trait-bounds.rs:24:10
--> $DIR/kw-in-trait-bounds.rs:16:10
|
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
| ^^^^^^ expected identifier, found keyword
@ -54,7 +54,7 @@ LL | fn _g<A: r#struct, B>(_: impl struct, _: &dyn struct)
| ++
error: expected identifier, found keyword `struct`
--> $DIR/kw-in-trait-bounds.rs:24:29
--> $DIR/kw-in-trait-bounds.rs:16:29
|
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
| ^^^^^^ expected identifier, found keyword
@ -65,7 +65,7 @@ LL | fn _g<A: struct, B>(_: impl r#struct, _: &dyn struct)
| ++
error: expected identifier, found keyword `struct`
--> $DIR/kw-in-trait-bounds.rs:24:45
--> $DIR/kw-in-trait-bounds.rs:16:45
|
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
| ^^^^^^ expected identifier, found keyword
@ -76,7 +76,7 @@ LL | fn _g<A: struct, B>(_: impl struct, _: &dyn r#struct)
| ++
error: expected identifier, found keyword `struct`
--> $DIR/kw-in-trait-bounds.rs:38:8
--> $DIR/kw-in-trait-bounds.rs:30:8
|
LL | B: struct,
| ^^^^^^ expected identifier, found keyword
@ -86,44 +86,8 @@ help: escape `struct` to use it as an identifier
LL | B: r#struct,
| ++
error[E0405]: cannot find trait `r#fn` in this scope
--> $DIR/kw-in-trait-bounds.rs:3:10
|
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
= note: similarly named trait `Fn` defined here
error[E0405]: cannot find trait `r#fn` in this scope
--> $DIR/kw-in-trait-bounds.rs:17:4
|
LL | G: fn(),
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
= note: similarly named trait `Fn` defined here
error[E0405]: cannot find trait `r#fn` in this scope
--> $DIR/kw-in-trait-bounds.rs:3:27
|
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
= note: similarly named trait `Fn` defined here
error[E0405]: cannot find trait `r#fn` in this scope
--> $DIR/kw-in-trait-bounds.rs:3:41
|
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
|
= note: similarly named trait `Fn` defined here
error[E0405]: cannot find trait `r#struct` in this scope
--> $DIR/kw-in-trait-bounds.rs:24:10
--> $DIR/kw-in-trait-bounds.rs:16:10
|
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
@ -132,7 +96,7 @@ LL | trait Struct {}
| ------------ similarly named trait `Struct` defined here
error[E0405]: cannot find trait `r#struct` in this scope
--> $DIR/kw-in-trait-bounds.rs:38:8
--> $DIR/kw-in-trait-bounds.rs:30:8
|
LL | B: struct,
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
@ -141,7 +105,7 @@ LL | trait Struct {}
| ------------ similarly named trait `Struct` defined here
error[E0405]: cannot find trait `r#struct` in this scope
--> $DIR/kw-in-trait-bounds.rs:24:29
--> $DIR/kw-in-trait-bounds.rs:16:29
|
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
@ -150,7 +114,7 @@ LL | trait Struct {}
| ------------ similarly named trait `Struct` defined here
error[E0405]: cannot find trait `r#struct` in this scope
--> $DIR/kw-in-trait-bounds.rs:24:45
--> $DIR/kw-in-trait-bounds.rs:16:45
|
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
@ -158,6 +122,6 @@ LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
LL | trait Struct {}
| ------------ similarly named trait `Struct` defined here
error: aborting due to 16 previous errors
error: aborting due to 12 previous errors
For more information about this error, try `rustc --explain E0405`.

View File

@ -0,0 +1,12 @@
fn foo(_: impl fn() -> i32) {}
//~^ ERROR expected identifier, found keyword `fn`
fn foo2<T: fn(i32)>(_: T) {}
//~^ ERROR expected identifier, found keyword `fn`
fn main() {
foo(|| ());
//~^ mismatched types
foo2(|_: ()| {});
//~^ type mismatch in closure arguments
}

View File

@ -0,0 +1,48 @@
error: expected identifier, found keyword `fn`
--> $DIR/recover-fn-trait-from-fn-kw.rs:1:16
|
LL | fn foo(_: impl fn() -> i32) {}
| ^^
|
help: use `Fn` to refer to the trait
|
LL | fn foo(_: impl Fn() -> i32) {}
| ~~
error: expected identifier, found keyword `fn`
--> $DIR/recover-fn-trait-from-fn-kw.rs:4:12
|
LL | fn foo2<T: fn(i32)>(_: T) {}
| ^^
|
help: use `Fn` to refer to the trait
|
LL | fn foo2<T: Fn(i32)>(_: T) {}
| ~~
error[E0308]: mismatched types
--> $DIR/recover-fn-trait-from-fn-kw.rs:8:12
|
LL | foo(|| ());
| ^^ expected `i32`, found `()`
error[E0631]: type mismatch in closure arguments
--> $DIR/recover-fn-trait-from-fn-kw.rs:10:5
|
LL | foo2(|_: ()| {});
| ^^^^ ------- found signature defined here
| |
| expected due to this
|
= note: expected closure signature `fn(i32) -> _`
found closure signature `fn(()) -> _`
note: required by a bound in `foo2`
--> $DIR/recover-fn-trait-from-fn-kw.rs:4:12
|
LL | fn foo2<T: fn(i32)>(_: T) {}
| ^^^^^^^ required by this bound in `foo2`
error: aborting due to 4 previous errors
Some errors have detailed explanations: E0308, E0631.
For more information about an error, try `rustc --explain E0308`.