Skip to content

Recover fn keyword as Fn trait in bounds #106176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_error_messages/locales/en-US/parse.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lexer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
48 changes: 45 additions & 3 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
16 changes: 4 additions & 12 deletions src/test/ui/parser/kw-in-trait-bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
88 changes: 26 additions & 62 deletions src/test/ui/parser/kw-in-trait-bounds.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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`
Expand All @@ -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`
Expand All @@ -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`
Expand All @@ -150,14 +114,14 @@ 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`
...
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`.
12 changes: 12 additions & 0 deletions src/test/ui/parser/recover-fn-trait-from-fn-kw.rs
Original file line number Diff line number Diff line change
@@ -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
}
48 changes: 48 additions & 0 deletions src/test/ui/parser/recover-fn-trait-from-fn-kw.stderr
Original file line number Diff line number Diff line change
@@ -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`.