Skip to content

Commit 36a17e4

Browse files
committed
parser_fn_front_matter: allow const .. extern
1 parent c30f068 commit 36a17e4

File tree

7 files changed

+104
-107
lines changed

7 files changed

+104
-107
lines changed

src/librustc_parse/parser/item.rs

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -798,12 +798,12 @@ impl<'a> Parser<'a> {
798798
let defaultness = self.parse_defaultness();
799799
let (name, kind, generics) = if self.eat_keyword(kw::Type) {
800800
self.parse_assoc_ty()?
801-
} else if self.is_const_item() {
802-
self.parse_assoc_const()?
801+
} else if self.is_fn_front_matter() {
802+
self.parse_assoc_fn(at_end, &mut attrs, is_name_required)?
803803
} else if let Some(mac) = self.parse_assoc_macro_invoc("associated", Some(&vis), at_end)? {
804804
(Ident::invalid(), AssocItemKind::Macro(mac), Generics::default())
805805
} else {
806-
self.parse_assoc_fn(at_end, &mut attrs, is_name_required)?
806+
self.parse_assoc_const()?
807807
};
808808

809809
Ok(AssocItem {
@@ -819,12 +819,6 @@ impl<'a> Parser<'a> {
819819
})
820820
}
821821

822-
/// Returns `true` if we are looking at `const ID`
823-
/// (returns `false` for things like `const fn`, etc.).
824-
fn is_const_item(&self) -> bool {
825-
self.token.is_keyword(kw::Const) && !self.is_keyword_ahead(1, &[kw::Fn, kw::Unsafe])
826-
}
827-
828822
/// This parses the grammar:
829823
///
830824
/// AssocConst = "const" Ident ":" Ty "=" Expr ";"
@@ -1034,21 +1028,20 @@ impl<'a> Parser<'a> {
10341028

10351029
let attrs = self.parse_outer_attributes()?;
10361030
let lo = self.token.span;
1037-
let visibility = self.parse_visibility(FollowedByType::No)?;
1031+
let vis = self.parse_visibility(FollowedByType::No)?;
10381032

1039-
// FOREIGN TYPE ITEM
10401033
if self.check_keyword(kw::Type) {
1041-
return self.parse_item_foreign_type(visibility, lo, attrs);
1042-
}
1043-
1044-
// FOREIGN STATIC ITEM
1045-
if self.is_static_global() {
1034+
// FOREIGN TYPE ITEM
1035+
self.parse_item_foreign_type(vis, lo, attrs)
1036+
} else if self.is_fn_front_matter() {
1037+
// FOREIGN FUNCTION ITEM
1038+
self.parse_item_foreign_fn(vis, lo, attrs)
1039+
} else if self.is_static_global() {
1040+
// FOREIGN STATIC ITEM
10461041
self.bump(); // `static`
1047-
return self.parse_item_foreign_static(visibility, lo, attrs);
1048-
}
1049-
1050-
// Treat `const` as `static` for error recovery, but don't add it to expected tokens.
1051-
if self.is_kw_followed_by_ident(kw::Const) {
1042+
self.parse_item_foreign_static(vis, lo, attrs)
1043+
} else if self.token.is_keyword(kw::Const) {
1044+
// Treat `const` as `static` for error recovery, but don't add it to expected tokens.
10521045
self.bump(); // `const`
10531046
self.struct_span_err(self.prev_span, "extern items cannot be `const`")
10541047
.span_suggestion(
@@ -1058,32 +1051,17 @@ impl<'a> Parser<'a> {
10581051
Applicability::MachineApplicable,
10591052
)
10601053
.emit();
1061-
return self.parse_item_foreign_static(visibility, lo, attrs);
1062-
}
1063-
1064-
// FOREIGN FUNCTION ITEM
1065-
const MAY_INTRODUCE_FN: &[Symbol] = &[kw::Const, kw::Async, kw::Unsafe, kw::Extern, kw::Fn];
1066-
if MAY_INTRODUCE_FN.iter().any(|&kw| self.check_keyword(kw)) {
1067-
return self.parse_item_foreign_fn(visibility, lo, attrs);
1068-
}
1069-
1070-
match self.parse_assoc_macro_invoc("extern", Some(&visibility), &mut false)? {
1071-
Some(mac) => Ok(P(ForeignItem {
1072-
ident: Ident::invalid(),
1073-
span: lo.to(self.prev_span),
1074-
id: DUMMY_NODE_ID,
1075-
attrs,
1076-
vis: visibility,
1077-
kind: ForeignItemKind::Macro(mac),
1078-
tokens: None,
1079-
})),
1080-
None => {
1081-
if !attrs.is_empty() {
1082-
self.expected_item_err(&attrs)?;
1083-
}
1084-
1085-
self.unexpected()
1054+
self.parse_item_foreign_static(vis, lo, attrs)
1055+
} else if let Some(mac) = self.parse_assoc_macro_invoc("extern", Some(&vis), &mut false)? {
1056+
let kind = ForeignItemKind::Macro(mac);
1057+
let span = lo.to(self.prev_span);
1058+
let ident = Ident::invalid();
1059+
Ok(P(ForeignItem { ident, span, id: DUMMY_NODE_ID, attrs, vis, kind, tokens: None }))
1060+
} else {
1061+
if !attrs.is_empty() {
1062+
self.expected_item_err(&attrs)?;
10861063
}
1064+
self.unexpected()
10871065
}
10881066
}
10891067

@@ -1752,6 +1730,29 @@ impl<'a> Parser<'a> {
17521730
Ok(body)
17531731
}
17541732

1733+
/// Is the current token unambiguously the start of an `FnHeader`?
1734+
fn is_fn_front_matter(&mut self) -> bool {
1735+
// We use an over-approximation here.
1736+
// `const const`, `fn const` won't parse, but we're not stepping over other syntax either.
1737+
// This works for `async fn` and similar as `async async` is an invalid
1738+
// parse and `async fn` is never a valid parse on previous editions.
1739+
const QUALIFIER: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern];
1740+
1741+
let check_qual_follow = |this: &mut Self, dist| {
1742+
this.look_ahead(dist, |t| {
1743+
// ...qualified and then `fn`, e.g. `const fn`.
1744+
t.is_keyword(kw::Fn)
1745+
// Two qualifiers. This is enough.
1746+
|| QUALIFIER.iter().any(|&kw| t.is_keyword(kw))
1747+
})
1748+
};
1749+
self.check_keyword(kw::Fn) // Definitely an `fn`.
1750+
// `$qual fn` or `$qual $qual`:
1751+
|| QUALIFIER.iter().any(|&kw| self.check_keyword(kw)) && check_qual_follow(self, 1)
1752+
// `extern ABI fn` or `extern ABI $qual`; skip 1 for the ABI.
1753+
|| self.check_keyword(kw::Extern) && check_qual_follow(self, 2)
1754+
}
1755+
17551756
/// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
17561757
/// up to and including the `fn` keyword. The formal grammar is:
17571758
///
@@ -1763,16 +1764,13 @@ impl<'a> Parser<'a> {
17631764
fn parse_fn_front_matter(&mut self) -> PResult<'a, FnHeader> {
17641765
let constness = self.parse_constness();
17651766
let asyncness = self.parse_asyncness();
1767+
let unsafety = self.parse_unsafety();
1768+
let ext = self.parse_extern()?;
1769+
17661770
if let Async::Yes { span, .. } = asyncness {
17671771
self.ban_async_in_2015(span);
17681772
}
1769-
let unsafety = self.parse_unsafety();
1770-
let (constness, unsafety, ext) = if let Const::Yes(_) = constness {
1771-
(constness, unsafety, Extern::None)
1772-
} else {
1773-
let ext = self.parse_extern()?;
1774-
(Const::No, unsafety, ext)
1775-
};
1773+
17761774
if !self.eat_keyword(kw::Fn) {
17771775
// It is possible for `expect_one_of` to recover given the contents of
17781776
// `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't
@@ -1781,6 +1779,7 @@ impl<'a> Parser<'a> {
17811779
unreachable!()
17821780
}
17831781
}
1782+
17841783
Ok(FnHeader { constness, unsafety, asyncness, ext })
17851784
}
17861785

src/librustc_parse/parser/stmt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ impl<'a> Parser<'a> {
199199
}
200200
}
201201

202-
pub(super) fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool {
202+
fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool {
203203
self.token.is_keyword(kw) && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident())
204204
}
205205

src/test/ui/issues/issue-60075.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@ trait T {
66
});
77
//~^ ERROR expected one of `async`
88
//~| ERROR expected one of `.`, `;`, `?`, `else`, or an operator, found `}`
9-
//~| ERROR expected identifier, found `;`
109
Some(4)
1110
}

src/test/ui/issues/issue-60075.stderr

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,5 @@ LL | let _ = if true {
1313
LL | });
1414
| ^ help: `}` may belong here
1515

16-
error: expected identifier, found `;`
17-
--> $DIR/issue-60075.rs:6:11
18-
|
19-
LL | });
20-
| ^ expected identifier
21-
22-
error: aborting due to 3 previous errors
16+
error: aborting due to 2 previous errors
2317

src/test/ui/parser/fn-header-semantic-fail.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ fn main() {
1818
unsafe fn ft2(); // OK.
1919
const fn ft3(); //~ ERROR trait fns cannot be declared const
2020
extern "C" fn ft4(); // OK.
21-
/* const */ async unsafe extern "C" fn ft5();
21+
const async unsafe extern "C" fn ft5();
2222
//~^ ERROR trait fns cannot be declared `async`
23-
//^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically.
23+
//~| ERROR trait fns cannot be declared const
2424
}
2525

2626
struct Y;
@@ -30,28 +30,25 @@ fn main() {
3030
unsafe fn ft2() {} // OK.
3131
const fn ft3() {} //~ ERROR trait fns cannot be declared const
3232
extern "C" fn ft4() {}
33-
/* const */ async unsafe extern "C" fn ft5() {}
33+
const async unsafe extern "C" fn ft5() {}
3434
//~^ ERROR trait fns cannot be declared `async`
35+
//~| ERROR trait fns cannot be declared const
3536
//~| ERROR method `ft5` has an incompatible type for trait
36-
//^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically.
3737
}
3838

3939
impl Y {
4040
async fn fi1() {} // OK.
4141
unsafe fn fi2() {} // OK.
4242
const fn fi3() {} // OK.
4343
extern "C" fn fi4() {} // OK.
44-
/* const */ async unsafe extern "C" fn fi5() {} // OK.
45-
//^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically.
44+
const async unsafe extern "C" fn fi5() {} // OK.
4645
}
4746

4847
extern {
4948
async fn fe1(); //~ ERROR functions in `extern` blocks cannot have qualifiers
5049
unsafe fn fe2(); //~ ERROR functions in `extern` blocks cannot have qualifiers
5150
const fn fe3(); //~ ERROR functions in `extern` blocks cannot have qualifiers
5251
extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot have qualifiers
53-
/* const */ async unsafe extern "C" fn fe5();
54-
//~^ ERROR functions in `extern` blocks cannot have qualifiers
55-
//^ FIXME(Centril): `const` should be legal syntactically, ensure it's illegal semantically.
52+
const async unsafe extern "C" fn fe5(); //~ ERROR functions in `extern` blocks
5653
}
5754
}

src/test/ui/parser/fn-header-semantic-fail.stderr

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,19 @@ error[E0379]: trait fns cannot be declared const
1515
LL | const fn ft3();
1616
| ^^^^^ trait fns cannot be const
1717

18+
error[E0379]: trait fns cannot be declared const
19+
--> $DIR/fn-header-semantic-fail.rs:21:9
20+
|
21+
LL | const async unsafe extern "C" fn ft5();
22+
| ^^^^^ trait fns cannot be const
23+
1824
error[E0706]: trait fns cannot be declared `async`
19-
--> $DIR/fn-header-semantic-fail.rs:21:21
25+
--> $DIR/fn-header-semantic-fail.rs:21:9
2026
|
21-
LL | /* const */ async unsafe extern "C" fn ft5();
22-
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23-
| |
24-
| `async` because of this
27+
LL | const async unsafe extern "C" fn ft5();
28+
| ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29+
| |
30+
| `async` because of this
2531
|
2632
= note: `async` trait functions are not currently supported
2733
= note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
@@ -43,19 +49,25 @@ error[E0379]: trait fns cannot be declared const
4349
LL | const fn ft3() {}
4450
| ^^^^^ trait fns cannot be const
4551

52+
error[E0379]: trait fns cannot be declared const
53+
--> $DIR/fn-header-semantic-fail.rs:33:9
54+
|
55+
LL | const async unsafe extern "C" fn ft5() {}
56+
| ^^^^^ trait fns cannot be const
57+
4658
error[E0706]: trait fns cannot be declared `async`
47-
--> $DIR/fn-header-semantic-fail.rs:33:21
59+
--> $DIR/fn-header-semantic-fail.rs:33:9
4860
|
49-
LL | /* const */ async unsafe extern "C" fn ft5() {}
50-
| -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
51-
| |
52-
| `async` because of this
61+
LL | const async unsafe extern "C" fn ft5() {}
62+
| ^^^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
63+
| |
64+
| `async` because of this
5365
|
5466
= note: `async` trait functions are not currently supported
5567
= note: consider using the `async-trait` crate: https://crates.io/crates/async-trait
5668

5769
error: functions in `extern` blocks cannot have qualifiers
58-
--> $DIR/fn-header-semantic-fail.rs:49:18
70+
--> $DIR/fn-header-semantic-fail.rs:48:18
5971
|
6072
LL | extern {
6173
| ------ in this `extern` block
@@ -65,7 +77,7 @@ LL | async fn fe1();
6577
| help: remove the qualifiers: `fn`
6678

6779
error: functions in `extern` blocks cannot have qualifiers
68-
--> $DIR/fn-header-semantic-fail.rs:50:19
80+
--> $DIR/fn-header-semantic-fail.rs:49:19
6981
|
7082
LL | extern {
7183
| ------ in this `extern` block
@@ -76,7 +88,7 @@ LL | unsafe fn fe2();
7688
| help: remove the qualifiers: `fn`
7789

7890
error: functions in `extern` blocks cannot have qualifiers
79-
--> $DIR/fn-header-semantic-fail.rs:51:18
91+
--> $DIR/fn-header-semantic-fail.rs:50:18
8092
|
8193
LL | extern {
8294
| ------ in this `extern` block
@@ -87,7 +99,7 @@ LL | const fn fe3();
8799
| help: remove the qualifiers: `fn`
88100

89101
error: functions in `extern` blocks cannot have qualifiers
90-
--> $DIR/fn-header-semantic-fail.rs:52:23
102+
--> $DIR/fn-header-semantic-fail.rs:51:23
91103
|
92104
LL | extern {
93105
| ------ in this `extern` block
@@ -98,15 +110,15 @@ LL | extern "C" fn fe4();
98110
| help: remove the qualifiers: `fn`
99111

100112
error: functions in `extern` blocks cannot have qualifiers
101-
--> $DIR/fn-header-semantic-fail.rs:53:48
113+
--> $DIR/fn-header-semantic-fail.rs:52:42
102114
|
103115
LL | extern {
104116
| ------ in this `extern` block
105117
...
106-
LL | /* const */ async unsafe extern "C" fn fe5();
107-
| ---------------------------^^^
108-
| |
109-
| help: remove the qualifiers: `fn`
118+
LL | const async unsafe extern "C" fn fe5();
119+
| ---------------------------------^^^
120+
| |
121+
| help: remove the qualifiers: `fn`
110122

111123
error[E0053]: method `ft1` has an incompatible type for trait
112124
--> $DIR/fn-header-semantic-fail.rs:28:24
@@ -124,21 +136,21 @@ LL | async fn ft1() {}
124136
found fn pointer `fn() -> impl std::future::Future`
125137

126138
error[E0053]: method `ft5` has an incompatible type for trait
127-
--> $DIR/fn-header-semantic-fail.rs:33:54
139+
--> $DIR/fn-header-semantic-fail.rs:33:48
128140
|
129-
LL | /* const */ async unsafe extern "C" fn ft5();
130-
| - type in trait
141+
LL | const async unsafe extern "C" fn ft5();
142+
| - type in trait
131143
...
132-
LL | /* const */ async unsafe extern "C" fn ft5() {}
133-
| ^
134-
| |
135-
| the `Output` of this `async fn`'s found opaque type
136-
| expected `()`, found opaque type
144+
LL | const async unsafe extern "C" fn ft5() {}
145+
| ^
146+
| |
147+
| the `Output` of this `async fn`'s found opaque type
148+
| expected `()`, found opaque type
137149
|
138150
= note: expected fn pointer `unsafe extern "C" fn()`
139151
found fn pointer `unsafe extern "C" fn() -> impl std::future::Future`
140152

141-
error: aborting due to 13 previous errors
153+
error: aborting due to 15 previous errors
142154

143155
Some errors have detailed explanations: E0053, E0379, E0706.
144156
For more information about an error, try `rustc --explain E0053`.

0 commit comments

Comments
 (0)