Auto merge of #99918 - WaffleLapkin:fnFnfun, r=estebank

Recover wrong-cased keywords that start items

(_this pr was inspired by [this tweet](https://twitter.com/Azumanga/status/1552982326409367561)_)

r? `@estebank`

We've talked a bit about this recovery, but I just wanted to make sure that this is the right approach :)

For now I've only added the case insensitive recovery to `use`s, since most other items like `impl` blocks, modules, functions can start with multiple keywords which complicates the matter.
This commit is contained in:
bors 2022-11-11 02:07:52 +00:00
commit 5b82ea74b7
10 changed files with 286 additions and 42 deletions

View file

@ -29,6 +29,7 @@ extern crate rustc_macros;
extern crate tracing; extern crate tracing;
pub mod util { pub mod util {
pub mod case;
pub mod classify; pub mod classify;
pub mod comments; pub mod comments;
pub mod literal; pub mod literal;

View file

@ -5,6 +5,7 @@ pub use TokenKind::*;
use crate::ast; use crate::ast;
use crate::ptr::P; use crate::ptr::P;
use crate::util::case::Case;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
@ -615,6 +616,15 @@ impl Token {
self.is_non_raw_ident_where(|id| id.name == kw) self.is_non_raw_ident_where(|id| id.name == kw)
} }
/// Returns `true` if the token is a given keyword, `kw` or if `case` is `Insensitive` and this token is an identifier equal to `kw` ignoring the case.
pub fn is_keyword_case(&self, kw: Symbol, case: Case) -> bool {
self.is_keyword(kw)
|| (case == Case::Insensitive
&& self.is_non_raw_ident_where(|id| {
id.name.as_str().to_lowercase() == kw.as_str().to_lowercase()
}))
}
pub fn is_path_segment_keyword(&self) -> bool { pub fn is_path_segment_keyword(&self) -> bool {
self.is_non_raw_ident_where(Ident::is_path_segment_keyword) self.is_non_raw_ident_where(Ident::is_path_segment_keyword)
} }

View file

@ -0,0 +1,6 @@
/// Whatever to ignore case (`fn` vs `Fn` vs `FN`) or not. Used for recovering.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Case {
Sensitive,
Insensitive,
}

View file

@ -33,6 +33,7 @@ use core::mem;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::Spacing; use rustc_ast::tokenstream::Spacing;
use rustc_ast::util::case::Case;
use rustc_ast::util::classify; use rustc_ast::util::classify;
use rustc_ast::util::literal::LitError; use rustc_ast::util::literal::LitError;
use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
@ -2090,7 +2091,7 @@ impl<'a> Parser<'a> {
if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable }; if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
let asyncness = if self.token.uninterpolated_span().rust_2018() { let asyncness = if self.token.uninterpolated_span().rust_2018() {
self.parse_asyncness() self.parse_asyncness(Case::Sensitive)
} else { } else {
Async::No Async::No
}; };

View file

@ -8,6 +8,7 @@ use rustc_ast::ast::*;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, TokenKind}; use rustc_ast::token::{self, Delimiter, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree}; use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast::util::case::Case;
use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID}; use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID};
use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind}; use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind}; use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind};
@ -34,7 +35,7 @@ impl<'a> Parser<'a> {
/// Parses a `mod <foo> { ... }` or `mod <foo>;` item. /// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> { fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemInfo> {
let unsafety = self.parse_unsafety(); let unsafety = self.parse_unsafety(Case::Sensitive);
self.expect_keyword(kw::Mod)?; self.expect_keyword(kw::Mod)?;
let id = self.parse_ident()?; let id = self.parse_ident()?;
let mod_kind = if self.eat(&token::Semi) { let mod_kind = if self.eat(&token::Semi) {
@ -143,8 +144,15 @@ impl<'a> Parser<'a> {
let lo = self.token.span; let lo = self.token.span;
let vis = self.parse_visibility(FollowedByType::No)?; let vis = self.parse_visibility(FollowedByType::No)?;
let mut def = self.parse_defaultness(); let mut def = self.parse_defaultness();
let kind = let kind = self.parse_item_kind(
self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, fn_parse_mode)?; &mut attrs,
mac_allowed,
lo,
&vis,
&mut def,
fn_parse_mode,
Case::Sensitive,
)?;
if let Some((ident, kind)) = kind { if let Some((ident, kind)) = kind {
self.error_on_unconsumed_default(def, &kind); self.error_on_unconsumed_default(def, &kind);
let span = lo.to(self.prev_token.span); let span = lo.to(self.prev_token.span);
@ -205,16 +213,17 @@ impl<'a> Parser<'a> {
vis: &Visibility, vis: &Visibility,
def: &mut Defaultness, def: &mut Defaultness,
fn_parse_mode: FnParseMode, fn_parse_mode: FnParseMode,
case: Case,
) -> PResult<'a, Option<ItemInfo>> { ) -> PResult<'a, Option<ItemInfo>> {
let def_final = def == &Defaultness::Final; let def_final = def == &Defaultness::Final;
let mut def = || mem::replace(def, Defaultness::Final); let mut def_ = || mem::replace(def, Defaultness::Final);
let info = if self.eat_keyword(kw::Use) { let info = if self.eat_keyword_case(kw::Use, case) {
self.parse_use_item()? self.parse_use_item()?
} else if self.check_fn_front_matter(def_final) { } else if self.check_fn_front_matter(def_final, case) {
// FUNCTION ITEM // FUNCTION ITEM
let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?; let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo, vis)?;
(ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body }))) (ident, ItemKind::Fn(Box::new(Fn { defaultness: def_(), sig, generics, body })))
} else if self.eat_keyword(kw::Extern) { } else if self.eat_keyword(kw::Extern) {
if self.eat_keyword(kw::Crate) { if self.eat_keyword(kw::Crate) {
// EXTERN CRATE // EXTERN CRATE
@ -225,7 +234,7 @@ impl<'a> Parser<'a> {
} }
} else if self.is_unsafe_foreign_mod() { } else if self.is_unsafe_foreign_mod() {
// EXTERN BLOCK // EXTERN BLOCK
let unsafety = self.parse_unsafety(); let unsafety = self.parse_unsafety(Case::Sensitive);
self.expect_keyword(kw::Extern)?; self.expect_keyword(kw::Extern)?;
self.parse_item_foreign_mod(attrs, unsafety)? self.parse_item_foreign_mod(attrs, unsafety)?
} else if self.is_static_global() { } else if self.is_static_global() {
@ -234,15 +243,15 @@ impl<'a> Parser<'a> {
let m = self.parse_mutability(); let m = self.parse_mutability();
let (ident, ty, expr) = self.parse_item_global(Some(m))?; let (ident, ty, expr) = self.parse_item_global(Some(m))?;
(ident, ItemKind::Static(ty, m, expr)) (ident, ItemKind::Static(ty, m, expr))
} else if let Const::Yes(const_span) = self.parse_constness() { } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) {
// CONST ITEM // CONST ITEM
if self.token.is_keyword(kw::Impl) { if self.token.is_keyword(kw::Impl) {
// recover from `const impl`, suggest `impl const` // recover from `const impl`, suggest `impl const`
self.recover_const_impl(const_span, attrs, def())? self.recover_const_impl(const_span, attrs, def_())?
} else { } else {
self.recover_const_mut(const_span); self.recover_const_mut(const_span);
let (ident, ty, expr) = self.parse_item_global(None)?; let (ident, ty, expr) = self.parse_item_global(None)?;
(ident, ItemKind::Const(def(), ty, expr)) (ident, ItemKind::Const(def_(), ty, expr))
} }
} else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() {
// TRAIT ITEM // TRAIT ITEM
@ -251,7 +260,7 @@ impl<'a> Parser<'a> {
|| self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Impl]) || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Impl])
{ {
// IMPL ITEM // IMPL ITEM
self.parse_item_impl(attrs, def())? self.parse_item_impl(attrs, def_())?
} else if self.check_keyword(kw::Mod) } else if self.check_keyword(kw::Mod)
|| self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod]) || self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Mod])
{ {
@ -259,7 +268,7 @@ impl<'a> Parser<'a> {
self.parse_item_mod(attrs)? self.parse_item_mod(attrs)?
} else if self.eat_keyword(kw::Type) { } else if self.eat_keyword(kw::Type) {
// TYPE ITEM // TYPE ITEM
self.parse_type_alias(def())? self.parse_type_alias(def_())?
} else if self.eat_keyword(kw::Enum) { } else if self.eat_keyword(kw::Enum) {
// ENUM ITEM // ENUM ITEM
self.parse_item_enum()? self.parse_item_enum()?
@ -286,6 +295,19 @@ impl<'a> Parser<'a> {
} else if self.isnt_macro_invocation() && vis.kind.is_pub() { } else if self.isnt_macro_invocation() && vis.kind.is_pub() {
self.recover_missing_kw_before_item()?; self.recover_missing_kw_before_item()?;
return Ok(None); return Ok(None);
} else if self.isnt_macro_invocation() && case == Case::Sensitive {
_ = def_;
// Recover wrong cased keywords
return self.parse_item_kind(
attrs,
macros_allowed,
lo,
vis,
def,
fn_parse_mode,
Case::Insensitive,
);
} else if macros_allowed && self.check_path() { } else if macros_allowed && self.check_path() {
// MACRO INVOCATION ITEM // MACRO INVOCATION ITEM
(Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?))) (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?)))
@ -538,7 +560,7 @@ impl<'a> Parser<'a> {
attrs: &mut AttrVec, attrs: &mut AttrVec,
defaultness: Defaultness, defaultness: Defaultness,
) -> PResult<'a, ItemInfo> { ) -> PResult<'a, ItemInfo> {
let unsafety = self.parse_unsafety(); let unsafety = self.parse_unsafety(Case::Sensitive);
self.expect_keyword(kw::Impl)?; self.expect_keyword(kw::Impl)?;
// First, parse generic parameters if necessary. // First, parse generic parameters if necessary.
@ -552,7 +574,7 @@ impl<'a> Parser<'a> {
generics generics
}; };
let constness = self.parse_constness(); let constness = self.parse_constness(Case::Sensitive);
if let Const::Yes(span) = constness { if let Const::Yes(span) = constness {
self.sess.gated_spans.gate(sym::const_trait_impl, span); self.sess.gated_spans.gate(sym::const_trait_impl, span);
} }
@ -796,7 +818,7 @@ impl<'a> Parser<'a> {
/// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`. /// Parses `unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> { fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemInfo> {
let unsafety = self.parse_unsafety(); let unsafety = self.parse_unsafety(Case::Sensitive);
// Parse optional `auto` prefix. // Parse optional `auto` prefix.
let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No }; let is_auto = if self.eat_keyword(kw::Auto) { IsAuto::Yes } else { IsAuto::No };
@ -1762,7 +1784,7 @@ impl<'a> Parser<'a> {
let (ident, is_raw) = self.ident_or_err()?; let (ident, is_raw) = self.ident_or_err()?;
if !is_raw && ident.is_reserved() { if !is_raw && ident.is_reserved() {
let snapshot = self.create_snapshot_for_diagnostic(); let snapshot = self.create_snapshot_for_diagnostic();
let err = if self.check_fn_front_matter(false) { let err = if self.check_fn_front_matter(false, Case::Sensitive) {
let inherited_vis = Visibility { let inherited_vis = Visibility {
span: rustc_span::DUMMY_SP, span: rustc_span::DUMMY_SP,
kind: VisibilityKind::Inherited, kind: VisibilityKind::Inherited,
@ -2172,7 +2194,7 @@ impl<'a> Parser<'a> {
/// ///
/// `check_pub` adds additional `pub` to the checks in case users place it /// `check_pub` adds additional `pub` to the checks in case users place it
/// wrongly, can be used to ensure `pub` never comes after `default`. /// wrongly, can be used to ensure `pub` never comes after `default`.
pub(super) fn check_fn_front_matter(&mut self, check_pub: bool) -> bool { pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool {
// We use an over-approximation here. // We use an over-approximation here.
// `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either.
// `pub` is added in case users got confused with the ordering like `async pub fn`, // `pub` is added in case users got confused with the ordering like `async pub fn`,
@ -2182,23 +2204,30 @@ impl<'a> Parser<'a> {
} else { } else {
&[kw::Const, kw::Async, kw::Unsafe, kw::Extern] &[kw::Const, kw::Async, kw::Unsafe, kw::Extern]
}; };
self.check_keyword(kw::Fn) // Definitely an `fn`. self.check_keyword_case(kw::Fn, case) // Definitely an `fn`.
// `$qual fn` or `$qual $qual`: // `$qual fn` or `$qual $qual`:
|| quals.iter().any(|&kw| self.check_keyword(kw)) || quals.iter().any(|&kw| self.check_keyword_case(kw, case))
&& self.look_ahead(1, |t| { && self.look_ahead(1, |t| {
// `$qual fn`, e.g. `const fn` or `async fn`. // `$qual fn`, e.g. `const fn` or `async fn`.
t.is_keyword(kw::Fn) t.is_keyword_case(kw::Fn, case)
// Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`. // Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`.
|| t.is_non_raw_ident_where(|i| quals.contains(&i.name) || (
// Rule out 2015 `const async: T = val`. (
&& i.is_reserved() t.is_non_raw_ident_where(|i|
quals.contains(&i.name)
// Rule out 2015 `const async: T = val`.
&& i.is_reserved()
)
|| case == Case::Insensitive
&& t.is_non_raw_ident_where(|i| quals.iter().any(|qual| qual.as_str() == i.name.as_str().to_lowercase()))
)
// Rule out unsafe extern block. // Rule out unsafe extern block.
&& !self.is_unsafe_foreign_mod()) && !self.is_unsafe_foreign_mod())
}) })
// `extern ABI fn` // `extern ABI fn`
|| self.check_keyword(kw::Extern) || self.check_keyword_case(kw::Extern, case)
&& self.look_ahead(1, |t| t.can_begin_literal_maybe_minus()) && self.look_ahead(1, |t| t.can_begin_literal_maybe_minus())
&& self.look_ahead(2, |t| t.is_keyword(kw::Fn)) && self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case))
} }
/// Parses all the "front matter" (or "qualifiers") for a `fn` declaration, /// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
@ -2214,22 +2243,22 @@ impl<'a> Parser<'a> {
/// `Visibility::Inherited` when no visibility is known. /// `Visibility::Inherited` when no visibility is known.
pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> { pub(super) fn parse_fn_front_matter(&mut self, orig_vis: &Visibility) -> PResult<'a, FnHeader> {
let sp_start = self.token.span; let sp_start = self.token.span;
let constness = self.parse_constness(); let constness = self.parse_constness(Case::Insensitive);
let async_start_sp = self.token.span; let async_start_sp = self.token.span;
let asyncness = self.parse_asyncness(); let asyncness = self.parse_asyncness(Case::Insensitive);
let unsafe_start_sp = self.token.span; let unsafe_start_sp = self.token.span;
let unsafety = self.parse_unsafety(); let unsafety = self.parse_unsafety(Case::Insensitive);
let ext_start_sp = self.token.span; let ext_start_sp = self.token.span;
let ext = self.parse_extern(); let ext = self.parse_extern(Case::Insensitive);
if let Async::Yes { span, .. } = asyncness { if let Async::Yes { span, .. } = asyncness {
self.ban_async_in_2015(span); self.ban_async_in_2015(span);
} }
if !self.eat_keyword(kw::Fn) { if !self.eat_keyword_case(kw::Fn, Case::Insensitive) {
// It is possible for `expect_one_of` to recover given the contents of // It is possible for `expect_one_of` to recover given the contents of
// `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't // `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't
// account for this. // account for this.

View file

@ -22,6 +22,7 @@ use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind};
use rustc_ast::tokenstream::AttributesData; use rustc_ast::tokenstream::AttributesData;
use rustc_ast::tokenstream::{self, DelimSpan, Spacing}; use rustc_ast::tokenstream::{self, DelimSpan, Spacing};
use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::util::case::Case;
use rustc_ast::AttrId; use rustc_ast::AttrId;
use rustc_ast::DUMMY_NODE_ID; use rustc_ast::DUMMY_NODE_ID;
use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, Extern}; use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, Extern};
@ -636,6 +637,20 @@ impl<'a> Parser<'a> {
self.token.is_keyword(kw) self.token.is_keyword(kw)
} }
fn check_keyword_case(&mut self, kw: Symbol, case: Case) -> bool {
if self.check_keyword(kw) {
return true;
}
if case == Case::Insensitive
&& let Some((ident, /* is_raw */ false)) = self.token.ident()
&& ident.as_str().to_lowercase() == kw.as_str().to_lowercase() {
true
} else {
false
}
}
/// If the next token is the given keyword, eats it and returns `true`. /// If the next token is the given keyword, eats it and returns `true`.
/// Otherwise, returns `false`. An expectation is also added for diagnostics purposes. /// Otherwise, returns `false`. An expectation is also added for diagnostics purposes.
// Public for rustfmt usage. // Public for rustfmt usage.
@ -648,6 +663,33 @@ impl<'a> Parser<'a> {
} }
} }
/// Eats a keyword, optionally ignoring the case.
/// If the case differs (and is ignored) an error is issued.
/// This is useful for recovery.
fn eat_keyword_case(&mut self, kw: Symbol, case: Case) -> bool {
if self.eat_keyword(kw) {
return true;
}
if case == Case::Insensitive
&& let Some((ident, /* is_raw */ false)) = self.token.ident()
&& ident.as_str().to_lowercase() == kw.as_str().to_lowercase() {
self
.struct_span_err(ident.span, format!("keyword `{kw}` is written in a wrong case"))
.span_suggestion(
ident.span,
"write it in the correct case",
kw,
Applicability::MachineApplicable
).emit();
self.bump();
return true;
}
false
}
fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool { fn eat_keyword_noexpect(&mut self, kw: Symbol) -> bool {
if self.token.is_keyword(kw) { if self.token.is_keyword(kw) {
self.bump(); self.bump();
@ -1127,8 +1169,8 @@ impl<'a> Parser<'a> {
} }
/// Parses asyncness: `async` or nothing. /// Parses asyncness: `async` or nothing.
fn parse_asyncness(&mut self) -> Async { fn parse_asyncness(&mut self, case: Case) -> Async {
if self.eat_keyword(kw::Async) { if self.eat_keyword_case(kw::Async, case) {
let span = self.prev_token.uninterpolated_span(); let span = self.prev_token.uninterpolated_span();
Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID } Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID }
} else { } else {
@ -1137,8 +1179,8 @@ impl<'a> Parser<'a> {
} }
/// Parses unsafety: `unsafe` or nothing. /// Parses unsafety: `unsafe` or nothing.
fn parse_unsafety(&mut self) -> Unsafe { fn parse_unsafety(&mut self, case: Case) -> Unsafe {
if self.eat_keyword(kw::Unsafe) { if self.eat_keyword_case(kw::Unsafe, case) {
Unsafe::Yes(self.prev_token.uninterpolated_span()) Unsafe::Yes(self.prev_token.uninterpolated_span())
} else { } else {
Unsafe::No Unsafe::No
@ -1146,10 +1188,10 @@ impl<'a> Parser<'a> {
} }
/// Parses constness: `const` or nothing. /// Parses constness: `const` or nothing.
fn parse_constness(&mut self) -> Const { fn parse_constness(&mut self, case: Case) -> Const {
// Avoid const blocks to be parsed as const items // Avoid const blocks to be parsed as const items
if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace)) if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace))
&& self.eat_keyword(kw::Const) && self.eat_keyword_case(kw::Const, case)
{ {
Const::Yes(self.prev_token.uninterpolated_span()) Const::Yes(self.prev_token.uninterpolated_span())
} else { } else {
@ -1404,8 +1446,8 @@ impl<'a> Parser<'a> {
} }
/// Parses `extern string_literal?`. /// Parses `extern string_literal?`.
fn parse_extern(&mut self) -> Extern { fn parse_extern(&mut self, case: Case) -> Extern {
if self.eat_keyword(kw::Extern) { if self.eat_keyword_case(kw::Extern, case) {
let mut extern_span = self.prev_token.span; let mut extern_span = self.prev_token.span;
let abi = self.parse_abi(); let abi = self.parse_abi();
if let Some(abi) = abi { if let Some(abi) = abi {

View file

@ -4,6 +4,7 @@ use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::util::case::Case;
use rustc_ast::{ use rustc_ast::{
self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime, self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind, MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
@ -267,7 +268,7 @@ impl<'a> Parser<'a> {
} else if self.eat_keyword(kw::Underscore) { } else if self.eat_keyword(kw::Underscore) {
// A type to be inferred `_` // A type to be inferred `_`
TyKind::Infer TyKind::Infer
} else if self.check_fn_front_matter(false) { } else if self.check_fn_front_matter(false, Case::Sensitive) {
// Function pointer type // Function pointer type
self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)? self.parse_ty_bare_fn(lo, Vec::new(), recover_return_sign)?
} else if self.check_keyword(kw::For) { } else if self.check_keyword(kw::For) {
@ -275,7 +276,7 @@ impl<'a> Parser<'a> {
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T` // `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
// `for<'lt> Trait1<'lt> + Trait2 + 'a` // `for<'lt> Trait1<'lt> + Trait2 + 'a`
let lifetime_defs = self.parse_late_bound_lifetime_defs()?; let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
if self.check_fn_front_matter(false) { if self.check_fn_front_matter(false, Case::Sensitive) {
self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)? self.parse_ty_bare_fn(lo, lifetime_defs, recover_return_sign)?
} else { } else {
let path = self.parse_path(PathStyle::Type)?; let path = self.parse_path(PathStyle::Type)?;

View file

@ -0,0 +1,34 @@
// run-rustfix
// edition:2018
#![allow(unused_imports)]
fn main() {}
use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case
use std::ptr::write; //~ ERROR keyword `use` is written in a wrong case
async fn _a() {}
//~^ ERROR keyword `fn` is written in a wrong case
fn _b() {}
//~^ ERROR keyword `fn` is written in a wrong case
async fn _c() {}
//~^ ERROR keyword `async` is written in a wrong case
//~| ERROR keyword `fn` is written in a wrong case
async fn _d() {}
//~^ ERROR keyword `async` is written in a wrong case
const unsafe fn _e() {}
//~^ ERROR keyword `const` is written in a wrong case
//~| ERROR keyword `unsafe` is written in a wrong case
//~| ERROR keyword `fn` is written in a wrong case
unsafe extern fn _f() {}
//~^ ERROR keyword `unsafe` is written in a wrong case
//~| ERROR keyword `extern` is written in a wrong case
extern "C" fn _g() {}
//~^ ERROR keyword `extern` is written in a wrong case
//~| ERROR keyword `fn` is written in a wrong case

View file

@ -0,0 +1,34 @@
// run-rustfix
// edition:2018
#![allow(unused_imports)]
fn main() {}
Use std::ptr::read; //~ ERROR keyword `use` is written in a wrong case
USE std::ptr::write; //~ ERROR keyword `use` is written in a wrong case
async Fn _a() {}
//~^ ERROR keyword `fn` is written in a wrong case
Fn _b() {}
//~^ ERROR keyword `fn` is written in a wrong case
aSYNC fN _c() {}
//~^ ERROR keyword `async` is written in a wrong case
//~| ERROR keyword `fn` is written in a wrong case
Async fn _d() {}
//~^ ERROR keyword `async` is written in a wrong case
CONST UNSAFE FN _e() {}
//~^ ERROR keyword `const` is written in a wrong case
//~| ERROR keyword `unsafe` is written in a wrong case
//~| ERROR keyword `fn` is written in a wrong case
unSAFE EXTern fn _f() {}
//~^ ERROR keyword `unsafe` is written in a wrong case
//~| ERROR keyword `extern` is written in a wrong case
EXTERN "C" FN _g() {}
//~^ ERROR keyword `extern` is written in a wrong case
//~| ERROR keyword `fn` is written in a wrong case

View file

@ -0,0 +1,86 @@
error: keyword `use` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:7:1
|
LL | Use std::ptr::read;
| ^^^ help: write it in the correct case (notice the capitalization): `use`
error: keyword `use` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:8:1
|
LL | USE std::ptr::write;
| ^^^ help: write it in the correct case: `use`
error: keyword `fn` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:10:7
|
LL | async Fn _a() {}
| ^^ help: write it in the correct case (notice the capitalization): `fn`
error: keyword `fn` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:13:1
|
LL | Fn _b() {}
| ^^ help: write it in the correct case (notice the capitalization): `fn`
error: keyword `async` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:16:1
|
LL | aSYNC fN _c() {}
| ^^^^^ help: write it in the correct case: `async`
error: keyword `fn` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:16:7
|
LL | aSYNC fN _c() {}
| ^^ help: write it in the correct case: `fn`
error: keyword `async` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:20:1
|
LL | Async fn _d() {}
| ^^^^^ help: write it in the correct case: `async`
error: keyword `const` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:23:1
|
LL | CONST UNSAFE FN _e() {}
| ^^^^^ help: write it in the correct case: `const`
error: keyword `unsafe` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:23:7
|
LL | CONST UNSAFE FN _e() {}
| ^^^^^^ help: write it in the correct case: `unsafe`
error: keyword `fn` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:23:14
|
LL | CONST UNSAFE FN _e() {}
| ^^ help: write it in the correct case: `fn`
error: keyword `unsafe` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:28:1
|
LL | unSAFE EXTern fn _f() {}
| ^^^^^^ help: write it in the correct case: `unsafe`
error: keyword `extern` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:28:8
|
LL | unSAFE EXTern fn _f() {}
| ^^^^^^ help: write it in the correct case: `extern`
error: keyword `extern` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:32:1
|
LL | EXTERN "C" FN _g() {}
| ^^^^^^ help: write it in the correct case: `extern`
error: keyword `fn` is written in a wrong case
--> $DIR/item-kw-case-mismatch.rs:32:12
|
LL | EXTERN "C" FN _g() {}
| ^^ help: write it in the correct case: `fn`
error: aborting due to 14 previous errors