Skip to content

Commit 38b0865

Browse files
committed
Recover wrong cased keywords starting functions
1 parent 3694429 commit 38b0865

File tree

8 files changed

+205
-51
lines changed

8 files changed

+205
-51
lines changed

compiler/rustc_ast/src/token.rs

+9
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,15 @@ impl Token {
618618
self.is_non_raw_ident_where(|id| id.name == kw)
619619
}
620620

621+
/// Returns `true` if the token is a given keyword, `kw` or if `case_insensitive` is true and this token is an identifier equal to `kw` ignoring the case.
622+
pub fn is_keyword_case(&self, kw: Symbol, case_insensitive: bool) -> bool {
623+
self.is_keyword(kw)
624+
|| (case_insensitive
625+
&& self.is_non_raw_ident_where(|id| {
626+
id.name.as_str().to_lowercase() == kw.as_str().to_lowercase()
627+
}))
628+
}
629+
621630
pub fn is_path_segment_keyword(&self) -> bool {
622631
self.is_non_raw_ident_where(Ident::is_path_segment_keyword)
623632
}

compiler/rustc_parse/src/parser/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2024,7 +2024,7 @@ impl<'a> Parser<'a> {
20242024
if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
20252025

20262026
let asyncness = if self.token.uninterpolated_span().rust_2018() {
2027-
self.parse_asyncness()
2027+
self.parse_asyncness(false)
20282028
} else {
20292029
Async::No
20302030
};

compiler/rustc_parse/src/parser/item.rs

+42-37
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl<'a> Parser<'a> {
3434

3535
/// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
3636
fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> {
37-
let unsafety = self.parse_unsafety();
37+
let unsafety = self.parse_unsafety(false);
3838
self.expect_keyword(kw::Mod)?;
3939
let id = self.parse_ident()?;
4040
let mod_kind = if self.eat(&token::Semi) {
@@ -215,14 +215,14 @@ impl<'a> Parser<'a> {
215215
kw_case_insensitive: bool,
216216
) -> PResult<'a, Option<ItemInfo>> {
217217
let def_final = def == &Defaultness::Final;
218-
let mut def = || mem::replace(def, Defaultness::Final);
218+
let mut def_ = || mem::replace(def, Defaultness::Final);
219219

220220
let info = if self.eat_keyword_case(kw::Use, kw_case_insensitive) {
221221
self.parse_use_item()?
222-
} else if self.check_fn_front_matter(def_final) {
222+
} else if self.check_fn_front_matter(def_final, kw_case_insensitive) {
223223
// FUNCTION ITEM
224224
let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?;
225-
(ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body })))
225+
(ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body })))
226226
} else if self.eat_keyword(kw::Extern) {
227227
if self.eat_keyword(kw::Crate) {
228228
// EXTERN CRATE
@@ -233,7 +233,7 @@ impl<'a> Parser<'a> {
233233
}
234234
} else if self.is_unsafe_foreign_mod() {
235235
// EXTERN BLOCK
236-
let unsafety = self.parse_unsafety();
236+
let unsafety = self.parse_unsafety(false);
237237
self.expect_keyword(kw::Extern)?;
238238
self.parse_item_foreign_mod(attrs, unsafety)?
239239
} else if self.is_static_global() {
@@ -242,15 +242,15 @@ impl<'a> Parser<'a> {
242242
let m = self.parse_mutability();
243243
let (ident, ty, expr) = self.parse_item_global(Some(m))?;
244244
(ident, ItemKind::Static(ty, m, expr))
245-
} else if let Const::Yes(const_span) = self.parse_constness() {
245+
} else if let Const::Yes(const_span) = self.parse_constness(false) {
246246
// CONST ITEM
247247
if self.token.is_keyword(kw::Impl) {
248248
// recover from `const impl`, suggest `impl const`
249-
self.recover_const_impl(const_span, attrs, def())?
249+
self.recover_const_impl(const_span, attrs, def_())?
250250
} else {
251251
self.recover_const_mut(const_span);
252252
let (ident, ty, expr) = self.parse_item_global(None)?;
253-
(ident, ItemKind::Const(def(), ty, expr))
253+
(ident, ItemKind::Const(def_(), ty, expr))
254254
}
255255
} else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() {
256256
// TRAIT ITEM
@@ -259,15 +259,15 @@ impl<'a> Parser<'a> {
259259
|| self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Impl])
260260
{
261261
// IMPL ITEM
262-
self.parse_item_impl(attrs, def())?
262+
self.parse_item_impl(attrs, def_())?
263263
} else if self.check_keyword(kw::Mod)
264264
|| self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod])
265265
{
266266
// MODULE ITEM
267267
self.parse_item_mod(attrs)?
268268
} else if self.eat_keyword(kw::Type) {
269269
// TYPE ITEM
270-
self.parse_type_alias(def())?
270+
self.parse_type_alias(def_())?
271271
} else if self.eat_keyword(kw::Enum) {
272272
// ENUM ITEM
273273
self.parse_item_enum()?
@@ -295,16 +295,10 @@ impl<'a> Parser<'a> {
295295
self.recover_missing_kw_before_item()?;
296296
return Ok(None);
297297
} else if self.isnt_macro_invocation() && !kw_case_insensitive {
298+
_ = def_;
299+
298300
// Recover wrong cased keywords
299-
return self.parse_item_kind(
300-
attrs,
301-
macros_allowed,
302-
lo,
303-
vis,
304-
&mut def(),
305-
fn_parse_mode,
306-
true,
307-
);
301+
return self.parse_item_kind(attrs, macros_allowed, lo, vis, def, fn_parse_mode, true);
308302
} else if macros_allowed && self.check_path() {
309303
// MACRO INVOCATION ITEM
310304
(Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?)))
@@ -557,7 +551,7 @@ impl<'a> Parser<'a> {
557551
attrs: &mut AttrVec,
558552
defaultness: Defaultness,
559553
) -> PResult<'a, ItemInfo> {
560-
let unsafety = self.parse_unsafety();
554+
let unsafety = self.parse_unsafety(false);
561555
self.expect_keyword(kw::Impl)?;
562556

563557
// First, parse generic parameters if necessary.
@@ -571,7 +565,7 @@ impl<'a> Parser<'a> {
571565
generics
572566
};
573567

574-
let constness = self.parse_constness();
568+
let constness = self.parse_constness(false);
575569
if let Const::Yes(span) = constness {
576570
self.sess.gated_spans.gate(sym::const_trait_impl, span);
577571
}
@@ -815,7 +809,7 @@ impl<'a> Parser<'a> {
815809

816810
/// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
817811
fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> {
818-
let unsafety = self.parse_unsafety();
812+
let unsafety = self.parse_unsafety(false);
819813
// Parse optional `auto` prefix.
820814
let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No };
821815

@@ -1764,7 +1758,7 @@ impl<'a> Parser<'a> {
17641758
let (ident, is_raw) = self.ident_or_err()?;
17651759
if !is_raw && ident.is_reserved() {
17661760
let snapshot = self.create_snapshot_for_diagnostic();
1767-
let err = if self.check_fn_front_matter(false) {
1761+
let err = if self.check_fn_front_matter(false, false) {
17681762
let inherited_vis = Visibility {
17691763
span: rustc_span::DUMMY_SP,
17701764
kind: VisibilityKind::Inherited,
@@ -2153,7 +2147,11 @@ impl<'a> Parser<'a> {
21532147
///
21542148
/// `check_pub` adds additional `pub` to the checks in case users place it
21552149
/// wrongly, can be used to ensure `pub` never comes after `default`.
2156-
pub(super) fn check_fn_front_matter(&mut self, check_pub: bool) -> bool {
2150+
pub(super) fn check_fn_front_matter(
2151+
&mut self,
2152+
check_pub: bool,
2153+
kw_case_insensitive: bool,
2154+
) -> bool {
21572155
// We use an over-approximation here.
21582156
// `const const`, `fn const` won't parse, but we're not stepping over other syntax either.
21592157
// `pub` is added in case users got confused with the ordering like `async pub fn`,
@@ -2163,23 +2161,30 @@ impl<'a> Parser<'a> {
21632161
} else {
21642162
&[kw::Const, kw::Async, kw::Unsafe, kw::Extern]
21652163
};
2166-
self.check_keyword(kw::Fn) // Definitely an `fn`.
2164+
self.check_keyword_case(kw::Fn, kw_case_insensitive) // Definitely an `fn`.
21672165
// `$qual fn` or `$qual $qual`:
2168-
|| quals.iter().any(|&kw| self.check_keyword(kw))
2166+
|| quals.iter().any(|&kw| self.check_keyword_case(kw, kw_case_insensitive))
21692167
&& self.look_ahead(1, |t| {
21702168
// `$qual fn`, e.g. `const fn` or `async fn`.
2171-
t.is_keyword(kw::Fn)
2169+
t.is_keyword_case(kw::Fn, kw_case_insensitive)
21722170
// Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`.
2173-
|| t.is_non_raw_ident_where(|i| quals.contains(&i.name)
2174-
// Rule out 2015 `const async: T = val`.
2175-
&& i.is_reserved()
2171+
|| (
2172+
(
2173+
t.is_non_raw_ident_where(|i|
2174+
quals.contains(&i.name)
2175+
// Rule out 2015 `const async: T = val`.
2176+
&& i.is_reserved()
2177+
)
2178+
|| kw_case_insensitive
2179+
&& t.is_non_raw_ident_where(|i| quals.iter().any(|qual| qual.as_str() == i.name.as_str().to_lowercase()))
2180+
)
21762181
// Rule out unsafe extern block.
21772182
&& !self.is_unsafe_foreign_mod())
21782183
})
21792184
// `extern ABI fn`
2180-
|| self.check_keyword(kw::Extern)
2185+
|| self.check_keyword_case(kw::Extern, kw_case_insensitive)
21812186
&& self.look_ahead(1, |t| t.can_begin_literal_maybe_minus())
2182-
&& self.look_ahead(2, |t| t.is_keyword(kw::Fn))
2187+
&& self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, kw_case_insensitive))
21832188
}
21842189

21852190
/// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
@@ -2195,22 +2200,22 @@ impl<'a> Parser<'a> {
21952200
/// `Visibility::Inherited` when no visibility is known.
21962201
pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> {
21972202
let sp_start = self.token.span;
2198-
let constness = self.parse_constness();
2203+
let constness = self.parse_constness(true);
21992204

22002205
let async_start_sp = self.token.span;
2201-
let asyncness = self.parse_asyncness();
2206+
let asyncness = self.parse_asyncness(true);
22022207

22032208
let unsafe_start_sp = self.token.span;
2204-
let unsafety = self.parse_unsafety();
2209+
let unsafety = self.parse_unsafety(true);
22052210

22062211
let ext_start_sp = self.token.span;
2207-
let ext = self.parse_extern();
2212+
let ext = self.parse_extern(true);
22082213

22092214
if let Async::Yes { span, .. } = asyncness {
22102215
self.ban_async_in_2015(span);
22112216
}
22122217

2213-
if !self.eat_keyword(kw::Fn) {
2218+
if !self.eat_keyword_case(kw::Fn, true) {
22142219
// It is possible for `expect_one_of` to recover given the contents of
22152220
// `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't
22162221
// account for this.

compiler/rustc_parse/src/parser/mod.rs

+22-8
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,20 @@ impl<'a> Parser<'a> {
604604
self.token.is_keyword(kw)
605605
}
606606

607+
fn check_keyword_case(&mut self, kw: Symbol, case_insensitive: bool) -> bool {
608+
if self.check_keyword(kw) {
609+
return true;
610+
}
611+
612+
if case_insensitive
613+
&& let Some((ident, /* is_raw */ false)) = self.token.ident()
614+
&& ident.as_str().to_lowercase() == kw.as_str().to_lowercase() {
615+
true
616+
} else {
617+
false
618+
}
619+
}
620+
607621
/// If the next token is the given keyword, eats it and returns `true`.
608622
/// Otherwise, returns `false`. An expectation is also added for diagnostics purposes.
609623
// Public for rustfmt usage.
@@ -1122,8 +1136,8 @@ impl<'a> Parser<'a> {
11221136
}
11231137

11241138
/// Parses asyncness: `async` or nothing.
1125-
fn parse_asyncness(&mut self) -> Async {
1126-
if self.eat_keyword(kw::Async) {
1139+
fn parse_asyncness(&mut self, case_insensitive: bool) -> Async {
1140+
if self.eat_keyword_case(kw::Async, case_insensitive) {
11271141
let span = self.prev_token.uninterpolated_span();
11281142
Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID }
11291143
} else {
@@ -1132,19 +1146,19 @@ impl<'a> Parser<'a> {
11321146
}
11331147

11341148
/// Parses unsafety: `unsafe` or nothing.
1135-
fn parse_unsafety(&mut self) -> Unsafe {
1136-
if self.eat_keyword(kw::Unsafe) {
1149+
fn parse_unsafety(&mut self, case_insensitive: bool) -> Unsafe {
1150+
if self.eat_keyword_case(kw::Unsafe, case_insensitive) {
11371151
Unsafe::Yes(self.prev_token.uninterpolated_span())
11381152
} else {
11391153
Unsafe::No
11401154
}
11411155
}
11421156

11431157
/// Parses constness: `const` or nothing.
1144-
fn parse_constness(&mut self) -> Const {
1158+
fn parse_constness(&mut self, case_insensitive: bool) -> Const {
11451159
// Avoid const blocks to be parsed as const items
11461160
if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace))
1147-
&& self.eat_keyword(kw::Const)
1161+
&& self.eat_keyword_case(kw::Const, case_insensitive)
11481162
{
11491163
Const::Yes(self.prev_token.uninterpolated_span())
11501164
} else {
@@ -1399,8 +1413,8 @@ impl<'a> Parser<'a> {
13991413
}
14001414

14011415
/// Parses `extern string_literal?`.
1402-
fn parse_extern(&mut self) -> Extern {
1403-
if self.eat_keyword(kw::Extern) {
1416+
fn parse_extern(&mut self, case_insensitive: bool) -> Extern {
1417+
if self.eat_keyword_case(kw::Extern, case_insensitive) {
14041418
let mut extern_span = self.prev_token.span;
14051419
let abi = self.parse_abi();
14061420
if let Some(abi) = abi {

compiler/rustc_parse/src/parser/ty.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -267,15 +267,15 @@ impl<'a> Parser<'a> {
267267
} else if self.eat_keyword(kw::Underscore) {
268268
// A type to be inferred `_`
269269
TyKind::Infer
270-
} else if self.check_fn_front_matter(false) {
270+
} else if self.check_fn_front_matter(false, false) {
271271
// Function pointer type
272272
self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)?
273273
} else if self.check_keyword(kw::For) {
274274
// Function pointer type or bound list (trait object type) starting with a poly-trait.
275275
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
276276
// `for<'lt> Trait1<'lt> + Trait2 + 'a`
277277
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
278-
if self.check_fn_front_matter(false) {
278+
if self.check_fn_front_matter(false, false) {
279279
self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)?
280280
} else {
281281
let path = self.parse_path(PathStyle::Type)?;
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
11
// run-rustfix
2+
// edition:2018
23
#![allow(unused_imports)]
34

45
fn main() {}
56

67
use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case
78
use std::ptr::write; //~ ERROR keyword `use` is written in a wrong case
9+
10+
async fn _a() {}
11+
//~^ ERROR keyword `fn` is written in a wrong case
12+
13+
fn _b() {}
14+
//~^ ERROR keyword `fn` is written in a wrong case
15+
16+
async fn _c() {}
17+
//~^ ERROR keyword `async` is written in a wrong case
18+
//~| ERROR keyword `fn` is written in a wrong case
19+
20+
async fn _d() {}
21+
//~^ ERROR keyword `async` is written in a wrong case
22+
23+
const unsafe fn _e() {}
24+
//~^ ERROR keyword `const` is written in a wrong case
25+
//~| ERROR keyword `unsafe` is written in a wrong case
26+
//~| ERROR keyword `fn` is written in a wrong case
27+
28+
unsafe extern fn _f() {}
29+
//~^ ERROR keyword `unsafe` is written in a wrong case
30+
//~| ERROR keyword `extern` is written in a wrong case
31+
32+
extern "C" fn _g() {}
33+
//~^ ERROR keyword `extern` is written in a wrong case
34+
//~| ERROR keyword `fn` is written in a wrong case
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
11
// run-rustfix
2+
// edition:2018
23
#![allow(unused_imports)]
34

45
fn main() {}
56

67
Use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case
78
USE std::ptr::write; //~ ERROR keyword `use` is written in a wrong case
9+
10+
async Fn _a() {}
11+
//~^ ERROR keyword `fn` is written in a wrong case
12+
13+
Fn _b() {}
14+
//~^ ERROR keyword `fn` is written in a wrong case
15+
16+
aSYNC fN _c() {}
17+
//~^ ERROR keyword `async` is written in a wrong case
18+
//~| ERROR keyword `fn` is written in a wrong case
19+
20+
Async fn _d() {}
21+
//~^ ERROR keyword `async` is written in a wrong case
22+
23+
CONST UNSAFE FN _e() {}
24+
//~^ ERROR keyword `const` is written in a wrong case
25+
//~| ERROR keyword `unsafe` is written in a wrong case
26+
//~| ERROR keyword `fn` is written in a wrong case
27+
28+
unSAFE EXTern fn _f() {}
29+
//~^ ERROR keyword `unsafe` is written in a wrong case
30+
//~| ERROR keyword `extern` is written in a wrong case
31+
32+
EXTERN "C" FN _g() {}
33+
//~^ ERROR keyword `extern` is written in a wrong case
34+
//~| ERROR keyword `fn` is written in a wrong case

0 commit comments

Comments
 (0)