blob: 1306f1fcfb1ceeb52b88979d5f200cb654f27d65 [file]
use std::fmt::Write;
use std::mem;
use ast::token::IdentIsRaw;
use rustc_ast as ast;
use rustc_ast::ast::*;
use rustc_ast::token::{self, Delimiter, MetaVarKind, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast::util::case::Case;
use rustc_ast_pretty::pprust;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, PResult, StashKey, msg, struct_span_code_err};
use rustc_span::edit_distance::edit_distance;
use rustc_span::edition::Edition;
use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
use thin_vec::{ThinVec, thin_vec};
use tracing::debug;
use super::diagnostics::ConsumeClosingDelim;
use super::{
AllowConstBlockItems, AttrWrapper, ExpTokenPair, FnContext, FnParseMode, FollowedByType,
ForceCollect, IsDotDotDot, Parser, PathStyle, Recovered, Trailing, UsePreAttrPos,
};
use crate::diagnostics::{
self, MacroExpandsToAdtField, UseDoubleColonSuggestion, UseRegularStructSuggestion,
};
use crate::exp;
impl<'a> Parser<'a> {
/// Parses a source module as a crate. This is the main entry point for the parser.
pub fn parse_crate_mod(&mut self) -> PResult<'a, ast::Crate> {
let (attrs, items, spans) = self.parse_mod(exp!(Eof))?;
Ok(ast::Crate { attrs, items, spans, id: DUMMY_NODE_ID, is_placeholder: false })
}
/// Parses a `mod <foo> { ... }` or `mod <foo>;` item.
fn parse_item_mod(&mut self, attrs: &mut AttrVec) -> PResult<'a, ItemKind> {
let safety = self.parse_safety(Case::Sensitive);
self.expect_keyword(exp!(Mod))?;
let ident = self.parse_ident()?;
let mod_kind = if self.eat(exp!(Semi)) {
ModKind::Unloaded
} else {
self.expect(exp!(OpenBrace))?;
let (inner_attrs, items, inner_span) = self.parse_mod(exp!(CloseBrace))?;
attrs.extend(inner_attrs);
ModKind::Loaded(items, Inline::Yes, inner_span)
};
Ok(ItemKind::Mod(safety, ident, mod_kind))
}
/// Parses the contents of a module (inner attributes followed by module items).
/// We exit once we hit `term` which can be either
/// - EOF (for files)
/// - `}` for mod items
pub fn parse_mod(
&mut self,
term: ExpTokenPair,
) -> PResult<'a, (AttrVec, ThinVec<Box<Item>>, ModSpans)> {
let lo = self.token.span;
let attrs = self.parse_inner_attributes()?;
let post_attr_lo = self.token.span;
let mut items: ThinVec<Box<_>> = ThinVec::new();
// There shouldn't be any stray semicolons before or after items.
// `parse_item` consumes the appropriate semicolons so any leftover is an error.
loop {
while self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) {} // Eat all bad semicolons
let Some(item) = self.parse_item(ForceCollect::No, AllowConstBlockItems::Yes)? else {
break;
};
items.push(item);
}
if !self.eat(term) {
let token_str = super::token_descr(&self.token);
if !self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) {
let is_let = self.token.is_keyword(kw::Let);
let is_let_mut = is_let && self.look_ahead(1, |t| t.is_keyword(kw::Mut));
let let_has_ident = is_let && !is_let_mut && self.is_kw_followed_by_ident(kw::Let);
let msg = format!("expected item, found {token_str}");
let mut err = self.dcx().struct_span_err(self.token.span, msg);
let label = if is_let {
"`let` cannot be used for global variables"
} else {
"expected item"
};
err.span_label(self.token.span, label);
if is_let {
if is_let_mut {
err.help("consider using `static` and a `Mutex` instead of `let mut`");
} else if let_has_ident {
err.span_suggestion_short(
self.token.span,
"consider using `static` or `const` instead of `let`",
"static",
Applicability::MaybeIncorrect,
);
} else {
err.help("consider using `static` or `const` instead of `let`");
}
}
err.note("for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>");
return Err(err);
}
}
let inject_use_span = post_attr_lo.data().with_hi(post_attr_lo.lo());
let mod_spans = ModSpans { inner_span: lo.to(self.prev_token.span), inject_use_span };
Ok((attrs, items, mod_spans))
}
}
enum ReuseKind {
Path,
Impl,
}
impl<'a> Parser<'a> {
pub fn parse_item(
&mut self,
force_collect: ForceCollect,
allow_const_block_items: AllowConstBlockItems,
) -> PResult<'a, Option<Box<Item>>> {
let fn_parse_mode =
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true };
self.parse_item_(fn_parse_mode, force_collect, allow_const_block_items)
.map(|i| i.map(Box::new))
}
fn parse_item_(
&mut self,
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
const_block_items_allowed: AllowConstBlockItems,
) -> PResult<'a, Option<Item>> {
self.recover_vcs_conflict_marker();
let attrs = self.parse_outer_attributes()?;
self.recover_vcs_conflict_marker();
self.parse_item_common(
attrs,
true,
false,
fn_parse_mode,
force_collect,
const_block_items_allowed,
)
}
pub(super) fn parse_item_common(
&mut self,
attrs: AttrWrapper,
mac_allowed: bool,
attrs_allowed: bool,
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
allow_const_block_items: AllowConstBlockItems,
) -> PResult<'a, Option<Item>> {
if let Some(item) = self.eat_metavar_seq(MetaVarKind::Item, |this| {
this.parse_item(ForceCollect::Yes, allow_const_block_items)
}) {
let mut item = item.expect("an actual item");
attrs.prepend_to_nt_inner(&mut item.attrs);
return Ok(Some(*item));
}
self.collect_tokens(None, attrs, force_collect, |this, mut attrs| {
let lo = this.token.span;
let vis = this.parse_visibility(FollowedByType::No)?;
let mut def = this.parse_defaultness();
let kind = this.parse_item_kind(
&mut attrs,
mac_allowed,
allow_const_block_items,
lo,
&vis,
&mut def,
fn_parse_mode,
Case::Sensitive,
)?;
if let Some(kind) = kind {
this.error_on_unconsumed_default(def, &kind);
let span = lo.to(this.prev_token.span);
let id = DUMMY_NODE_ID;
let item = Item { attrs, id, kind, vis, span, tokens: None };
return Ok((Some(item), Trailing::No, UsePreAttrPos::No));
}
// At this point, we have failed to parse an item.
if !matches!(vis.kind, VisibilityKind::Inherited) {
let vis_str = pprust::vis_to_string(&vis).trim_end().to_string();
let mut err = this.dcx().create_err(diagnostics::VisibilityNotFollowedByItem {
span: vis.span,
vis: vis_str,
});
if let Some((ident, _)) = this.token.ident()
&& !ident.is_used_keyword()
&& let Some((similar_kw, is_incorrect_case)) = ident
.name
.find_similar(&rustc_span::symbol::used_keywords(|| ident.span.edition()))
{
err.subdiagnostic(diagnostics::MisspelledKw {
similar_kw: similar_kw.to_string(),
span: ident.span,
is_incorrect_case,
});
}
err.emit();
}
if let Defaultness::Default(span) = def {
this.dcx().emit_err(diagnostics::DefaultNotFollowedByItem { span });
} else if let Defaultness::Final(span) = def {
this.dcx().emit_err(diagnostics::FinalNotFollowedByItem { span });
}
if !attrs_allowed {
this.recover_attrs_no_item(&attrs)?;
}
Ok((None, Trailing::No, UsePreAttrPos::No))
})
}
/// Error in-case `default`/`final` was parsed in an in-appropriate context.
fn error_on_unconsumed_default(&self, def: Defaultness, kind: &ItemKind) {
match def {
Defaultness::Default(span) => {
self.dcx().emit_err(diagnostics::InappropriateDefault {
span,
article: kind.article(),
descr: kind.descr(),
});
}
Defaultness::Final(span) => {
self.dcx().emit_err(diagnostics::InappropriateFinal {
span,
article: kind.article(),
descr: kind.descr(),
});
}
Defaultness::Implicit => (),
}
}
/// Parses one of the items allowed by the flags.
fn parse_item_kind(
&mut self,
attrs: &mut AttrVec,
macros_allowed: bool,
allow_const_block_items: AllowConstBlockItems,
lo: Span,
vis: &Visibility,
def: &mut Defaultness,
fn_parse_mode: FnParseMode,
case: Case,
) -> PResult<'a, Option<ItemKind>> {
let check_pub = def == &Defaultness::Implicit;
let mut def_ = || mem::replace(def, Defaultness::Implicit);
let info = if !self.is_use_closure() && self.eat_keyword_case(exp!(Use), case) {
self.parse_use_item()?
} else if self.check_fn_front_matter(check_pub, case) {
// FUNCTION ITEM
let defaultness = def_();
if let Defaultness::Default(span) = defaultness {
// Default functions should only require feature `min_specialization`. We remove the
// `specialization` tag again as such spans *require* feature `specialization` to be
// enabled. In a later stage, we make `specialization` imply `min_specialization`.
self.psess.gated_spans.gate(sym::min_specialization, span);
self.psess.gated_spans.ungate_last(sym::specialization, span);
}
let (ident, sig, generics, contract, body) =
self.parse_fn(attrs, fn_parse_mode, lo, vis, case)?;
ItemKind::Fn(Box::new(Fn {
defaultness,
ident,
sig,
generics,
contract,
body,
define_opaque: None,
eii_impl: None,
}))
} else if self.eat_keyword_case(exp!(Extern), case) {
if self.eat_keyword_case(exp!(Crate), case) {
// EXTERN CRATE
self.parse_item_extern_crate()?
} else {
// EXTERN BLOCK
self.parse_item_foreign_mod(attrs, Safety::Default)?
}
} else if self.is_unsafe_foreign_mod() {
// EXTERN BLOCK
let safety = self.parse_safety(Case::Sensitive);
self.expect_keyword(exp!(Extern))?;
self.parse_item_foreign_mod(attrs, safety)?
} else if let Some(safety) = self.parse_global_static_front_matter(case) {
// STATIC ITEM
let mutability = self.parse_mutability();
self.parse_static_item(safety, mutability)?
} else if self.check_keyword_case(exp!(Trait), case) || self.check_trait_front_matter() {
// TRAIT ITEM
self.parse_item_trait(attrs, lo)?
} else if self.check_impl_frontmatter(0) {
// IMPL ITEM
self.parse_item_impl(attrs, def_(), false)?
} else if let AllowConstBlockItems::Yes | AllowConstBlockItems::DoesNotMatter =
allow_const_block_items
&& self.check_inline_const(0)
{
// CONST BLOCK ITEM
if let AllowConstBlockItems::DoesNotMatter = allow_const_block_items {
debug!("Parsing a const block item that does not matter: {:?}", self.token.span);
};
ItemKind::ConstBlock(self.parse_const_block_item()?)
} else if let Const::Yes(const_span) = self.parse_constness(case) {
// CONST ITEM
self.recover_const_mut(const_span);
self.recover_missing_kw_before_item()?;
let (ident, generics, ty, body) = self.parse_const_item(const_span)?;
ItemKind::Const(Box::new(ConstItem {
defaultness: def_(),
ident,
generics,
ty,
body,
kind: ConstItemKind::Body,
define_opaque: None,
}))
} else if let Some(kind) = self.is_reuse_item() {
self.parse_item_delegation(attrs, def_(), kind)?
} else if self.check_keyword_case(exp!(Mod), case)
|| self.check_keyword_case(exp!(Unsafe), case) && self.is_keyword_ahead(1, &[kw::Mod])
{
// MODULE ITEM
self.parse_item_mod(attrs)?
} else if self.eat_keyword_case(exp!(Type), case) {
if let Const::Yes(const_span) = self.parse_constness(case) {
// TYPE CONST (mgca)
self.recover_const_mut(const_span);
self.recover_missing_kw_before_item()?;
let (ident, generics, ty, body) = self.parse_const_item(const_span)?;
// Make sure this is only allowed if the feature gate is enabled.
// #![feature(mgca_type_const_syntax)]
self.psess.gated_spans.gate(sym::mgca_type_const_syntax, lo.to(const_span));
ItemKind::Const(Box::new(ConstItem {
defaultness: def_(),
ident,
generics,
ty,
body,
kind: ConstItemKind::TypeConst,
define_opaque: None,
}))
} else {
// TYPE ITEM
self.parse_type_alias(def_())?
}
} else if self.eat_keyword_case(exp!(Enum), case) {
// ENUM ITEM
self.parse_item_enum()?
} else if self.eat_keyword_case(exp!(Struct), case) {
// STRUCT ITEM
self.parse_item_struct()?
} else if self.is_kw_followed_by_ident(kw::Union) {
// UNION ITEM
self.bump(); // `union`
self.parse_item_union()?
} else if self.is_builtin() {
// BUILTIN# ITEM
return self.parse_item_builtin();
} else if self.eat_keyword_case(exp!(Macro), case) {
// MACROS 2.0 ITEM
self.parse_item_decl_macro(lo)?
} else if let IsMacroRulesItem::Yes { has_bang } = self.is_macro_rules_item() {
// MACRO_RULES ITEM
self.parse_item_macro_rules(vis, has_bang)?
} else if self.isnt_macro_invocation()
&& (self.token.is_ident_named(sym::import)
|| self.token.is_ident_named(sym::using)
|| self.token.is_ident_named(sym::include)
|| self.token.is_ident_named(sym::require))
{
return self.recover_import_as_use();
} else if self.isnt_macro_invocation() && vis.kind.is_pub() {
self.recover_missing_kw_before_item()?;
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,
allow_const_block_items,
lo,
vis,
def,
fn_parse_mode,
Case::Insensitive,
);
} else if macros_allowed && self.check_path() {
if self.isnt_macro_invocation() {
self.recover_missing_kw_before_item()?;
}
// MACRO INVOCATION ITEM
ItemKind::MacCall(Box::new(self.parse_item_macro(vis)?))
} else {
return Ok(None);
};
Ok(Some(info))
}
fn recover_import_as_use(&mut self) -> PResult<'a, Option<ItemKind>> {
let span = self.token.span;
let token_name = super::token_descr(&self.token);
let snapshot = self.create_snapshot_for_diagnostic();
self.bump();
match self.parse_use_item() {
Ok(u) => {
self.dcx().emit_err(diagnostics::RecoverImportAsUse { span, token_name });
Ok(Some(u))
}
Err(e) => {
e.cancel();
self.restore_snapshot(snapshot);
Ok(None)
}
}
}
fn parse_use_item(&mut self) -> PResult<'a, ItemKind> {
let tree = self.parse_use_tree()?;
if let Err(mut e) = self.expect_semi() {
match tree.kind {
UseTreeKind::Glob(_) => {
e.note("the wildcard token must be last on the path");
}
UseTreeKind::Nested { .. } => {
e.note("glob-like brace syntax must be last on the path");
}
_ => (),
}
return Err(e);
}
Ok(ItemKind::Use(tree))
}
/// When parsing a statement, would the start of a path be an item?
pub(super) fn is_path_start_item(&mut self) -> bool {
self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }`
|| self.is_reuse_item().is_some() // yes: `reuse impl Trait for Struct { self.0 }`, yes: `reuse some_path::foo;`
|| self.check_trait_front_matter() // no: `auto::b`, yes: `auto trait X { .. }`
|| self.is_async_fn() // no(2015): `async::b`, yes: `async fn`
|| matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac`
}
fn is_reuse_item(&mut self) -> Option<ReuseKind> {
if !self.token.is_keyword(kw::Reuse) {
return None;
}
// no: `reuse ::path` for compatibility reasons with macro invocations
if self.look_ahead(1, |t| t.is_path_start() && *t != token::PathSep) {
Some(ReuseKind::Path)
} else if self.check_impl_frontmatter(1) {
Some(ReuseKind::Impl)
} else {
None
}
}
/// Are we sure this could not possibly be a macro invocation?
fn isnt_macro_invocation(&mut self) -> bool {
self.check_ident() && self.look_ahead(1, |t| *t != token::Bang && *t != token::PathSep)
}
/// Recover on encountering a struct, enum, or method definition where the user
/// forgot to add the `struct`, `enum`, or `fn` keyword
fn recover_missing_kw_before_item(&mut self) -> PResult<'a, ()> {
let is_pub = self.prev_token.is_keyword(kw::Pub);
let is_const = self.prev_token.is_keyword(kw::Const);
let ident_span = self.token.span;
let span = if is_pub { self.prev_token.span.to(ident_span) } else { ident_span };
let insert_span = ident_span.shrink_to_lo();
let ident = if self.token.is_ident()
&& (!is_const || self.look_ahead(1, |t| *t == token::OpenParen))
&& self.look_ahead(1, |t| {
matches!(t.kind, token::Lt | token::OpenBrace | token::OpenParen)
}) {
self.parse_ident_common(true).unwrap()
} else {
return Ok(());
};
let mut found_generics = false;
if self.check(exp!(Lt)) {
found_generics = true;
self.eat_to_tokens(&[exp!(Gt)]);
self.bump(); // `>`
}
let err = if self.check(exp!(OpenBrace)) {
// possible struct or enum definition where `struct` or `enum` was forgotten
if self.look_ahead(1, |t| *t == token::CloseBrace) {
// `S {}` could be unit enum or struct
Some(diagnostics::MissingKeywordForItemDefinition::EnumOrStruct { span })
} else if self.look_ahead(2, |t| *t == token::Colon)
|| self.look_ahead(3, |t| *t == token::Colon)
{
// `S { f:` or `S { pub f:`
Some(diagnostics::MissingKeywordForItemDefinition::Struct {
span,
insert_span,
ident,
})
} else {
Some(diagnostics::MissingKeywordForItemDefinition::Enum {
span,
insert_span,
ident,
})
}
} else if self.check(exp!(OpenParen)) {
// possible function or tuple struct definition where `fn` or `struct` was forgotten
self.bump(); // `(`
let is_method = self.recover_self_param();
self.consume_block(exp!(OpenParen), exp!(CloseParen), ConsumeClosingDelim::Yes);
let err = if self.check(exp!(RArrow)) || self.check(exp!(OpenBrace)) {
self.eat_to_tokens(&[exp!(OpenBrace)]);
self.bump(); // `{`
self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes);
if is_method {
diagnostics::MissingKeywordForItemDefinition::Method {
span,
insert_span,
ident,
}
} else {
diagnostics::MissingKeywordForItemDefinition::Function {
span,
insert_span,
ident,
}
}
} else if is_pub && self.check(exp!(Semi)) {
diagnostics::MissingKeywordForItemDefinition::Struct { span, insert_span, ident }
} else {
diagnostics::MissingKeywordForItemDefinition::Ambiguous {
span,
subdiag: if found_generics {
None
} else if let Ok(snippet) = self.span_to_snippet(ident_span) {
Some(diagnostics::AmbiguousMissingKwForItemSub::SuggestMacro {
span: ident_span,
snippet,
})
} else {
Some(diagnostics::AmbiguousMissingKwForItemSub::HelpMacro)
},
}
};
Some(err)
} else if found_generics {
Some(diagnostics::MissingKeywordForItemDefinition::Ambiguous { span, subdiag: None })
} else {
None
};
if let Some(err) = err { Err(self.dcx().create_err(err)) } else { Ok(()) }
}
fn parse_item_builtin(&mut self) -> PResult<'a, Option<ItemKind>> {
// To be expanded
Ok(None)
}
/// Parses an item macro, e.g., `item!();`.
fn parse_item_macro(&mut self, vis: &Visibility) -> PResult<'a, MacCall> {
let path = self.parse_path(PathStyle::Mod)?; // `foo::bar`
self.expect(exp!(Bang))?; // `!`
match self.parse_delim_args() {
// `( .. )` or `[ .. ]` (followed by `;`), or `{ .. }`.
Ok(args) => {
self.eat_semi_for_macro_if_needed(&args, Some(&path));
self.complain_if_pub_macro(vis, false);
Ok(MacCall { path, args })
}
Err(mut err) => {
// Maybe the user misspelled `macro_rules` (issue #91227)
if self.token.is_ident()
&& let [segment] = path.segments.as_slice()
&& edit_distance("macro_rules", &segment.ident.to_string(), 2).is_some()
{
err.span_suggestion_verbose(
path.span,
"perhaps you meant to define a macro",
"macro_rules",
Applicability::MachineApplicable,
);
}
Err(err)
}
}
}
/// Recover if we parsed attributes and expected an item but there was none.
fn recover_attrs_no_item(&mut self, attrs: &[Attribute]) -> PResult<'a, ()> {
let ([start @ end] | [start, .., end]) = attrs else {
return Ok(());
};
let msg = if end.is_doc_comment() {
"expected item after doc comment"
} else {
"expected item after attributes"
};
let mut err = self.dcx().struct_span_err(end.span, msg);
if end.is_doc_comment() {
err.span_label(end.span, "this doc comment doesn't document anything");
} else {
err.span_label(end.span, "expected an item after this");
if self.token == TokenKind::Semi {
err.span_suggestion_verbose(
self.token.span,
"remove the semicolon after the attribute",
"",
Applicability::MaybeIncorrect,
);
}
}
if let [.., penultimate, _] = attrs {
err.span_label(start.span.to(penultimate.span), "other attributes here");
}
Err(err)
}
fn is_async_fn(&self) -> bool {
self.token.is_keyword(kw::Async) && self.is_keyword_ahead(1, &[kw::Fn])
}
fn parse_polarity(&mut self) -> ast::ImplPolarity {
// Disambiguate `impl !Trait for Type { ... }` and `impl ! { ... }` for the never type.
if self.check(exp!(Bang)) && self.look_ahead(1, |t| t.can_begin_type()) {
self.psess.gated_spans.gate(sym::negative_impls, self.token.span);
self.bump(); // `!`
ast::ImplPolarity::Negative(self.prev_token.span)
} else {
ast::ImplPolarity::Positive
}
}
/// Parses an implementation item.
///
/// ```ignore (illustrative)
/// impl<'a, T> TYPE { /* impl items */ }
/// impl<'a, T> TRAIT for TYPE { /* impl items */ }
/// impl<'a, T> !TRAIT for TYPE { /* impl items */ }
/// impl<'a, T> const TRAIT for TYPE { /* impl items */ }
/// ```
///
/// We actually parse slightly more relaxed grammar for better error reporting and recovery.
/// ```ebnf
/// "impl" GENERICS "const"? "!"? TYPE "for"? (TYPE | "..") ("where" PREDICATES)? "{" BODY "}"
/// "impl" GENERICS "const"? "!"? TYPE ("where" PREDICATES)? "{" BODY "}"
/// ```
fn parse_item_impl(
&mut self,
attrs: &mut AttrVec,
defaultness: Defaultness,
is_reuse: bool,
) -> PResult<'a, ItemKind> {
let constness = self.parse_constness(Case::Sensitive);
let safety = self.parse_safety(Case::Sensitive);
self.expect_keyword(exp!(Impl))?;
let mut generics_snapshot = None;
// First, parse generic parameters if necessary.
let mut generics = if self.choose_generics_over_qpath(0) {
self.parse_generics()?
} else {
// We might be mistakenly trying to use a generic type as a generic parameter.
// impl<X<T>> Trait for Y<T> { ... }
if self.look_ahead(0, |t| t == &token::Lt)
&& self.look_ahead(1, |t| t.is_ident())
&& self.look_ahead(2, |t| t == &token::Lt)
{
generics_snapshot = Some(self.create_snapshot_for_diagnostic());
}
let mut generics = Generics::default();
// impl A for B {}
// /\ this is where `generics.span` should point when there are no type params.
generics.span = self.prev_token.span.shrink_to_hi();
generics
};
if let Const::Yes(span) = constness {
self.psess.gated_spans.gate(sym::const_trait_impl, span);
}
// Parse stray `impl async Trait`
if (self.token_uninterpolated_span().at_least_rust_2018()
&& self.token.is_keyword(kw::Async))
|| self.is_kw_followed_by_ident(kw::Async)
{
self.bump();
self.dcx().emit_err(diagnostics::AsyncImpl { span: self.prev_token.span });
}
let polarity = self.parse_polarity();
// Parse both types and traits as a type, then reinterpret if necessary.
let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt)
{
let span = self.prev_token.span.between(self.token.span);
return Err(self.dcx().create_err(diagnostics::MissingTraitInTraitImpl {
span,
for_span: span.to(self.token.span),
}));
} else {
self.parse_ty_with_generics_recovery(&generics).map_err(|e| {
let Some(mut snapshot) = generics_snapshot else {
return e;
};
snapshot.maybe_type_in_generic_parameter(e)
})?
};
// If `for` is missing we try to recover.
let has_for = self.eat_keyword(exp!(For));
let missing_for_span = self.prev_token.span.between(self.token.span);
let ty_second = if self.token == token::DotDot {
// We need to report this error after `cfg` expansion for compatibility reasons
self.bump(); // `..`, do not add it to expected tokens
// AST validation later detects this `TyKind::Dummy` and emits an
// error. (#121072 will hopefully remove all this special handling
// of the obsolete `impl Trait for ..` and then this can go away.)
Some(self.mk_ty(self.prev_token.span, TyKind::Dummy))
} else if has_for || self.token.can_begin_type() {
Some(self.parse_ty()?)
} else {
None
};
generics.where_clause = self.parse_where_clause()?;
let impl_items = if is_reuse {
Default::default()
} else {
self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?
};
let (of_trait, self_ty) = match ty_second {
Some(ty_second) => {
// impl Trait for Type
if !has_for {
self.dcx()
.emit_err(diagnostics::MissingForInTraitImpl { span: missing_for_span });
}
let ty_first = *ty_first;
let path = match ty_first.kind {
// This notably includes paths passed through `ty` macro fragments (#46438).
TyKind::Path(None, path) => path,
other => {
if let TyKind::ImplTrait(_, bounds) = other
&& let [bound] = bounds.as_slice()
&& let GenericBound::Trait(poly_trait_ref) = bound
{
// Suggest removing extra `impl` keyword:
// `impl<T: Default> impl Default for Wrapper<T>`
// ^^^^^
let extra_impl_kw = ty_first.span.until(bound.span());
self.dcx().emit_err(diagnostics::ExtraImplKeywordInTraitImpl {
extra_impl_kw,
impl_trait_span: ty_first.span,
});
poly_trait_ref.trait_ref.path.clone()
} else {
return Err(self.dcx().create_err(
diagnostics::ExpectedTraitInTraitImplFoundType {
span: ty_first.span,
},
));
}
}
};
let trait_ref = TraitRef { path, ref_id: ty_first.id };
let of_trait =
Some(Box::new(TraitImplHeader { defaultness, safety, polarity, trait_ref }));
(of_trait, ty_second)
}
None => {
let self_ty = ty_first;
let error = |modifier, modifier_name, modifier_span| {
self.dcx().create_err(diagnostics::TraitImplModifierInInherentImpl {
span: self_ty.span,
modifier,
modifier_name,
modifier_span,
self_ty: self_ty.span,
})
};
if let Safety::Unsafe(span) = safety {
error("unsafe", "unsafe", span).with_code(E0197).emit();
}
if let ImplPolarity::Negative(span) = polarity {
error("!", "negative", span).emit();
}
if let Defaultness::Default(def_span) = defaultness {
error("default", "default", def_span).emit();
}
if let Const::Yes(span) = constness {
self.psess.gated_spans.gate(sym::const_trait_impl, span);
}
(None, self_ty)
}
};
Ok(ItemKind::Impl(Impl { generics, of_trait, self_ty, items: impl_items, constness }))
}
fn parse_item_delegation(
&mut self,
attrs: &mut AttrVec,
defaultness: Defaultness,
kind: ReuseKind,
) -> PResult<'a, ItemKind> {
let span = self.token.span;
self.expect_keyword(exp!(Reuse))?;
let item_kind = match kind {
ReuseKind::Path => self.parse_path_like_delegation(),
ReuseKind::Impl => self.parse_impl_delegation(span, attrs, defaultness),
}?;
self.psess.gated_spans.gate(sym::fn_delegation, span.to(self.prev_token.span));
Ok(item_kind)
}
fn parse_delegation_body(&mut self) -> PResult<'a, Option<Box<Block>>> {
Ok(if self.check(exp!(OpenBrace)) {
Some(self.parse_block()?)
} else {
self.expect(exp!(Semi))?;
None
})
}
fn parse_impl_delegation(
&mut self,
span: Span,
attrs: &mut AttrVec,
defaultness: Defaultness,
) -> PResult<'a, ItemKind> {
let mut impl_item = self.parse_item_impl(attrs, defaultness, true)?;
let ItemKind::Impl(Impl { items, of_trait, .. }) = &mut impl_item else { unreachable!() };
let until_expr_span = span.to(self.prev_token.span);
let Some(of_trait) = of_trait else {
return Err(self
.dcx()
.create_err(diagnostics::ImplReuseInherentImpl { span: until_expr_span }));
};
let body = self.parse_delegation_body()?;
let whole_reuse_span = span.to(self.prev_token.span);
items.push(Box::new(AssocItem {
id: DUMMY_NODE_ID,
attrs: Default::default(),
span: whole_reuse_span,
tokens: None,
vis: Visibility { kind: VisibilityKind::Inherited, span: whole_reuse_span },
kind: AssocItemKind::DelegationMac(Box::new(DelegationMac {
qself: None,
prefix: of_trait.trait_ref.path.clone(),
suffixes: DelegationSuffixes::Glob(whole_reuse_span),
body,
})),
}));
Ok(impl_item)
}
fn parse_path_like_delegation(&mut self) -> PResult<'a, ItemKind> {
let (qself, path) = if self.eat_lt() {
let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
(Some(qself), path)
} else {
(None, self.parse_path(PathStyle::Expr)?)
};
let rename = |this: &mut Self| {
Ok(if this.eat_keyword(exp!(As)) { Some(this.parse_ident()?) } else { None })
};
Ok(if self.eat_path_sep() {
let suffixes = if self.eat(exp!(Star)) {
DelegationSuffixes::Glob(self.prev_token.span)
} else {
let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?));
DelegationSuffixes::List(
self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), parse_suffix)?.0,
)
};
ItemKind::DelegationMac(Box::new(DelegationMac {
qself,
prefix: path,
suffixes,
body: self.parse_delegation_body()?,
}))
} else {
let rename = rename(self)?;
let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident);
ItemKind::Delegation(Box::new(Delegation {
id: DUMMY_NODE_ID,
qself,
path,
ident,
rename,
body: self.parse_delegation_body()?,
source: DelegationSource::Single,
}))
})
}
fn parse_item_list<T>(
&mut self,
attrs: &mut AttrVec,
mut parse_item: impl FnMut(&mut Parser<'a>) -> PResult<'a, Option<Option<T>>>,
) -> PResult<'a, ThinVec<T>> {
let open_brace_span = self.token.span;
// Recover `impl Ty;` instead of `impl Ty {}`
if self.token == TokenKind::Semi {
self.dcx().emit_err(diagnostics::UseEmptyBlockNotSemi { span: self.token.span });
self.bump();
return Ok(ThinVec::new());
}
self.expect(exp!(OpenBrace))?;
attrs.extend(self.parse_inner_attributes()?);
let mut items = ThinVec::new();
while !self.eat(exp!(CloseBrace)) {
if self.recover_doc_comment_before_brace() {
continue;
}
self.recover_vcs_conflict_marker();
match parse_item(self) {
Ok(None) => {
let mut is_unnecessary_semicolon = (self.token == token::Semi
&& self.prev_token == token::Semi)
|| !items.is_empty()
// When the close delim is `)` in a case like the following, `token.kind`
// is expected to be `token::CloseParen`, but the actual `token.kind` is
// `token::CloseBrace`. This is because the `token.kind` of the close delim
// is treated as the same as that of the open delim in
// `TokenTreesReader::parse_token_tree`, even if the delimiters of them are
// different. Therefore, `token.kind` should not be compared here.
//
// issue-60075.rs
// ```
// trait T {
// fn qux() -> Option<usize> {
// let _ = if true {
// });
// ^ this close delim
// Some(4)
// }
// ```
&& self
.span_to_snippet(self.prev_token.span)
.is_ok_and(|snippet| snippet == "}")
&& self.token == token::Semi;
let mut semicolon_span = self.token.span;
if !is_unnecessary_semicolon {
// #105369, Detect spurious `;` before assoc fn body
is_unnecessary_semicolon =
self.token == token::OpenBrace && self.prev_token == token::Semi;
semicolon_span = self.prev_token.span;
}
// We have to bail or we'll potentially never make progress.
let non_item_span = self.token.span;
let is_let = self.token.is_keyword(kw::Let);
let mut err =
self.dcx().struct_span_err(non_item_span, "non-item in item list");
self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes);
if is_let {
err.span_suggestion_verbose(
non_item_span,
"consider using `const` instead of `let` for associated const",
"const",
Applicability::MachineApplicable,
);
} else {
err.span_label(open_brace_span, "item list starts here")
.span_label(non_item_span, "non-item starts here")
.span_label(self.prev_token.span, "item list ends here");
}
if is_unnecessary_semicolon {
err.span_suggestion_verbose(
semicolon_span,
"consider removing this semicolon",
"",
Applicability::MaybeIncorrect,
);
}
err.emit();
break;
}
Ok(Some(item)) => items.extend(item),
Err(err) => {
self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes);
err.with_span_label(
open_brace_span,
"while parsing this item list starting here",
)
.with_span_label(self.prev_token.span, "the item list ends here")
.emit();
break;
}
}
}
Ok(items)
}
/// Recover on a doc comment before `}`.
fn recover_doc_comment_before_brace(&mut self) -> bool {
if let token::DocComment(..) = self.token.kind {
if self.look_ahead(1, |tok| tok == &token::CloseBrace) {
// FIXME: merge with `DocCommentDoesNotDocumentAnything` (E0585)
struct_span_code_err!(
self.dcx(),
self.token.span,
E0584,
"found a documentation comment that doesn't document anything",
)
.with_span_label(self.token.span, "this doc comment doesn't document anything")
.with_help(
"doc comments must come before what they document, if a comment was \
intended use `//`",
)
.emit();
self.bump();
return true;
}
}
false
}
/// Parses defaultness (i.e., `default` or nothing).
fn parse_defaultness(&mut self) -> Defaultness {
// We are interested in `default` followed by another identifier.
// However, we must avoid keywords that occur as binary operators.
// Currently, the only applicable keyword is `as` (`default as Ty`).
if self.check_keyword(exp!(Default))
&& self.look_ahead(1, |t| t.is_non_raw_ident_where(|i| i.name != kw::As))
{
self.psess.gated_spans.gate(sym::specialization, self.token.span);
self.bump(); // `default`
Defaultness::Default(self.prev_token_uninterpolated_span())
} else if self.eat_keyword(exp!(Final)) {
self.psess.gated_spans.gate(sym::final_associated_functions, self.prev_token.span);
Defaultness::Final(self.prev_token_uninterpolated_span())
} else {
Defaultness::Implicit
}
}
/// Is this an `[impl(in? path)]? const? unsafe? auto? trait` item?
fn check_trait_front_matter(&mut self) -> bool {
const SUFFIXES: &[&[Symbol]] = &[
&[kw::Trait],
&[kw::Auto, kw::Trait],
&[kw::Unsafe, kw::Trait],
&[kw::Unsafe, kw::Auto, kw::Trait],
&[kw::Const, kw::Trait],
&[kw::Const, kw::Auto, kw::Trait],
&[kw::Const, kw::Unsafe, kw::Trait],
&[kw::Const, kw::Unsafe, kw::Auto, kw::Trait],
];
// `impl(`
if self.check_keyword(exp!(Impl)) && self.look_ahead(1, |t| t == &token::OpenParen) {
// `impl(in` unambiguously introduces an `impl` restriction
if self.is_keyword_ahead(2, &[kw::In]) {
return true;
}
// `impl(crate | self | super)` + SUFFIX
if self.is_keyword_ahead(2, &[kw::Crate, kw::SelfLower, kw::Super])
&& self.look_ahead(3, |t| t == &token::CloseParen)
&& SUFFIXES.iter().any(|suffix| {
suffix.iter().enumerate().all(|(i, kw)| self.is_keyword_ahead(i + 4, &[*kw]))
})
{
return true;
}
// Recover cases like `impl(path::to::module)` + SUFFIX to suggest inserting `in`.
SUFFIXES.iter().any(|suffix| {
suffix.iter().enumerate().all(|(i, kw)| {
self.tree_look_ahead(i + 2, |t| {
if let TokenTree::Token(token, _) = t {
token.is_keyword(*kw)
} else {
false
}
})
.unwrap_or(false)
})
})
} else {
SUFFIXES.iter().any(|suffix| {
suffix.iter().enumerate().all(|(i, kw)| {
// We use `check_keyword` for the first token to include it in the expected tokens.
if i == 0 {
match *kw {
kw::Const => self.check_keyword(exp!(Const)),
kw::Unsafe => self.check_keyword(exp!(Unsafe)),
kw::Auto => self.check_keyword(exp!(Auto)),
kw::Trait => self.check_keyword(exp!(Trait)),
_ => unreachable!(),
}
} else {
self.is_keyword_ahead(i, &[*kw])
}
})
})
}
}
/// Parses `[impl(in? path)]? const? unsafe? auto? trait Foo { ... }` or `trait Foo = Bar;`.
fn parse_item_trait(&mut self, attrs: &mut AttrVec, lo: Span) -> PResult<'a, ItemKind> {
let impl_restriction = self.parse_impl_restriction()?;
let constness = self.parse_constness(Case::Sensitive);
if let Const::Yes(span) = constness {
self.psess.gated_spans.gate(sym::const_trait_impl, span);
}
let safety = self.parse_safety(Case::Sensitive);
// Parse optional `auto` prefix.
let is_auto = if self.eat_keyword(exp!(Auto)) {
self.psess.gated_spans.gate(sym::auto_traits, self.prev_token.span);
IsAuto::Yes
} else {
IsAuto::No
};
self.expect_keyword(exp!(Trait))?;
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
// Parse optional colon and supertrait bounds.
let had_colon = self.eat(exp!(Colon));
let span_at_colon = self.prev_token.span;
let bounds = if had_colon { self.parse_generic_bounds()? } else { ThinVec::new() };
let span_before_eq = self.prev_token.span;
if self.eat(exp!(Eq)) {
// It's a trait alias.
if had_colon {
let span = span_at_colon.to(span_before_eq);
self.dcx().emit_err(diagnostics::BoundsNotAllowedOnTraitAliases { span });
}
let bounds = self.parse_generic_bounds()?;
generics.where_clause = self.parse_where_clause()?;
self.expect_semi()?;
let whole_span = lo.to(self.prev_token.span);
if is_auto == IsAuto::Yes {
self.dcx().emit_err(diagnostics::TraitAliasCannotBeAuto { span: whole_span });
}
if let Safety::Unsafe(_) = safety {
self.dcx().emit_err(diagnostics::TraitAliasCannotBeUnsafe { span: whole_span });
}
if let RestrictionKind::Restricted { .. } = impl_restriction.kind {
self.dcx()
.emit_err(diagnostics::TraitAliasCannotBeImplRestricted { span: whole_span });
}
self.psess.gated_spans.gate(sym::trait_alias, whole_span);
Ok(ItemKind::TraitAlias(Box::new(TraitAlias { constness, ident, generics, bounds })))
} else {
// It's a normal trait.
generics.where_clause = self.parse_where_clause()?;
let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?;
Ok(ItemKind::Trait(Box::new(Trait {
impl_restriction,
constness,
is_auto,
safety,
ident,
generics,
bounds,
items,
})))
}
}
pub fn parse_impl_item(
&mut self,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<Box<AssocItem>>>> {
let fn_parse_mode =
FnParseMode { req_name: |_, _| true, context: FnContext::Impl, req_body: true };
self.parse_assoc_item(fn_parse_mode, force_collect)
}
pub fn parse_trait_item(
&mut self,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<Box<AssocItem>>>> {
let fn_parse_mode = FnParseMode {
req_name: |edition, _| edition >= Edition::Edition2018,
context: FnContext::Trait,
req_body: false,
};
self.parse_assoc_item(fn_parse_mode, force_collect)
}
/// Parses associated items.
fn parse_assoc_item(
&mut self,
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<Box<AssocItem>>>> {
Ok(self
.parse_item_(
fn_parse_mode,
force_collect,
AllowConstBlockItems::DoesNotMatter, // due to `AssocItemKind::try_from` below
)?
.map(|Item { attrs, id, span, vis, kind, tokens }| {
let kind = match AssocItemKind::try_from(kind) {
Ok(kind) => kind,
Err(kind) => match kind {
ItemKind::Static(StaticItem {
ident,
ty,
safety: _,
mutability: _,
expr,
define_opaque,
eii_impl: _,
}) => {
self.dcx()
.emit_err(diagnostics::AssociatedStaticItemNotAllowed { span });
AssocItemKind::Const(Box::new(ConstItem {
defaultness: Defaultness::Implicit,
ident,
generics: Generics::default(),
ty,
body: expr,
kind: ConstItemKind::Body,
define_opaque,
}))
}
_ => return self.error_bad_item_kind(span, &kind, "`trait`s or `impl`s"),
},
};
Some(Box::new(Item { attrs, id, span, vis, kind, tokens }))
}))
}
/// Parses a `type` alias with the following grammar:
/// ```ebnf
/// TypeAlias = "type" Ident Generics (":" GenericBounds)? WhereClause ("=" Ty)? WhereClause ";" ;
/// ```
/// The `"type"` has already been eaten.
fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemKind> {
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
// Parse optional colon and param bounds.
let bounds =
if self.eat(exp!(Colon)) { self.parse_generic_bounds()? } else { ThinVec::new() };
generics.where_clause = self.parse_where_clause()?;
let ty = if self.eat(exp!(Eq)) { Some(self.parse_ty()?) } else { None };
let after_where_clause = self.parse_where_clause()?;
self.expect_semi()?;
Ok(ItemKind::TyAlias(Box::new(TyAlias {
defaultness,
ident,
generics,
after_where_clause,
bounds,
ty,
})))
}
/// Parses a `UseTree`.
///
/// ```text
/// USE_TREE = [`::`] `*` |
/// [`::`] `{` USE_TREE_LIST `}` |
/// PATH `::` `*` |
/// PATH `::` `{` USE_TREE_LIST `}` |
/// PATH [`as` IDENT]
/// ```
fn parse_use_tree(&mut self) -> PResult<'a, UseTree> {
let lo = self.token.span;
let mut prefix = ast::Path { segments: ThinVec::new(), span: lo.shrink_to_lo() };
let kind =
if self.check(exp!(OpenBrace)) || self.check(exp!(Star)) || self.is_import_coupler() {
// `use *;` or `use ::*;` or `use {...};` or `use ::{...};`
let mod_sep_ctxt = self.token.span.ctxt();
if self.eat_path_sep() {
prefix
.segments
.push(PathSegment::path_root(lo.shrink_to_lo().with_ctxt(mod_sep_ctxt)));
}
self.parse_use_tree_glob_or_nested()?
} else {
// `use path::*;` or `use path::{...};` or `use path;` or `use path as bar;`
prefix = self.parse_path(PathStyle::Mod)?;
if self.eat_path_sep() {
self.parse_use_tree_glob_or_nested()?
} else {
// Recover from using a colon as path separator.
while self.eat_noexpect(&token::Colon) {
self.dcx().emit_err(diagnostics::SingleColonImportPath {
span: self.prev_token.span,
});
// We parse the rest of the path and append it to the original prefix.
self.parse_path_segments(&mut prefix.segments, PathStyle::Mod, None)?;
prefix.span = lo.to(self.prev_token.span);
}
UseTreeKind::Simple(self.parse_rename()?)
}
};
Ok(UseTree { prefix, kind })
}
/// Parses `*` or `{...}`.
fn parse_use_tree_glob_or_nested(&mut self) -> PResult<'a, UseTreeKind> {
Ok(if self.eat(exp!(Star)) {
UseTreeKind::Glob(self.prev_token.span)
} else {
let lo = self.token.span;
UseTreeKind::Nested {
items: self.parse_use_tree_list()?,
span: lo.to(self.prev_token.span),
}
})
}
/// Parses a `UseTreeKind::Nested(list)`.
///
/// ```text
/// USE_TREE_LIST = ∅ | (USE_TREE `,`)* USE_TREE [`,`]
/// ```
fn parse_use_tree_list(&mut self) -> PResult<'a, ThinVec<(UseTree, ast::NodeId)>> {
self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |p| {
p.recover_vcs_conflict_marker();
Ok((p.parse_use_tree()?, DUMMY_NODE_ID))
})
.map(|(r, _)| r)
}
fn parse_rename(&mut self) -> PResult<'a, Option<Ident>> {
if self.eat_keyword(exp!(As)) {
self.parse_ident_or_underscore().map(Some)
} else {
Ok(None)
}
}
fn parse_ident_or_underscore(&mut self) -> PResult<'a, Ident> {
match self.token.ident() {
Some((ident @ Ident { name: kw::Underscore, .. }, IdentIsRaw::No)) => {
self.bump();
Ok(ident)
}
_ => self.parse_ident(),
}
}
/// Parses `extern crate` links.
///
/// # Examples
///
/// ```ignore (illustrative)
/// extern crate foo;
/// extern crate bar as foo;
/// ```
fn parse_item_extern_crate(&mut self) -> PResult<'a, ItemKind> {
// Accept `extern crate name-like-this` for better diagnostics
let orig_ident = self.parse_crate_name_with_dashes()?;
let (orig_name, item_ident) = if let Some(rename) = self.parse_rename()? {
(Some(orig_ident.name), rename)
} else {
(None, orig_ident)
};
self.expect_semi()?;
Ok(ItemKind::ExternCrate(orig_name, item_ident))
}
fn parse_crate_name_with_dashes(&mut self) -> PResult<'a, Ident> {
let ident = if self.token.is_keyword(kw::SelfLower) {
self.parse_path_segment_ident()
} else {
self.parse_ident()
}?;
let dash = exp!(Minus);
if self.token != dash.tok {
return Ok(ident);
}
// Accept `extern crate name-like-this` for better diagnostics.
let mut dashes = vec![];
let mut idents = vec![];
while self.eat(dash) {
dashes.push(self.prev_token.span);
idents.push(self.parse_ident()?);
}
let fixed_name_sp = ident.span.to(idents.last().unwrap().span);
let mut fixed_name = ident.name.to_string();
for part in idents {
write!(fixed_name, "_{}", part.name).unwrap();
}
self.dcx().emit_err(diagnostics::ExternCrateNameWithDashes {
span: fixed_name_sp,
sugg: diagnostics::ExternCrateNameWithDashesSugg { dashes },
});
Ok(Ident::from_str_and_span(&fixed_name, fixed_name_sp))
}
/// Parses `extern` for foreign ABIs modules.
///
/// `extern` is expected to have been consumed before calling this method.
///
/// # Examples
///
/// ```ignore (only-for-syntax-highlight)
/// extern "C" {}
/// extern {}
/// ```
fn parse_item_foreign_mod(
&mut self,
attrs: &mut AttrVec,
mut safety: Safety,
) -> PResult<'a, ItemKind> {
let extern_span = self.prev_token_uninterpolated_span();
let abi = self.parse_abi(); // ABI?
// FIXME: This recovery should be tested better.
if safety == Safety::Default
&& self.token.is_keyword(kw::Unsafe)
&& self.look_ahead(1, |t| *t == token::OpenBrace)
{
self.expect(exp!(OpenBrace)).unwrap_err().emit();
safety = Safety::Unsafe(self.token.span);
let _ = self.eat_keyword(exp!(Unsafe));
}
Ok(ItemKind::ForeignMod(ast::ForeignMod {
extern_span,
safety,
abi,
items: self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?,
}))
}
/// Parses a foreign item (one in an `extern { ... }` block).
pub fn parse_foreign_item(
&mut self,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<Box<ForeignItem>>>> {
let fn_parse_mode = FnParseMode {
req_name: |_, is_dot_dot_dot| is_dot_dot_dot == IsDotDotDot::No,
context: FnContext::Free,
req_body: false,
};
Ok(self
.parse_item_(
fn_parse_mode,
force_collect,
AllowConstBlockItems::DoesNotMatter, // due to `ForeignItemKind::try_from` below
)?
.map(|Item { attrs, id, span, vis, kind, tokens }| {
let kind = match ForeignItemKind::try_from(kind) {
Ok(kind) => kind,
Err(kind) => match kind {
ItemKind::Const(ConstItem { ident, ty, body, .. }) => {
let const_span = Some(span.with_hi(ident.span.lo()))
.filter(|span| span.can_be_used_for_suggestions());
self.dcx().emit_err(diagnostics::ExternItemCannotBeConst {
ident_span: ident.span,
const_span,
});
ForeignItemKind::Static(Box::new(StaticItem {
ident,
ty,
mutability: Mutability::Not,
expr: body,
safety: Safety::Default,
define_opaque: None,
eii_impl: None,
}))
}
_ => return self.error_bad_item_kind(span, &kind, "`extern` blocks"),
},
};
Some(Box::new(Item { attrs, id, span, vis, kind, tokens }))
}))
}
fn error_bad_item_kind<T>(&self, span: Span, kind: &ItemKind, ctx: &'static str) -> Option<T> {
// FIXME(#100717): needs variant for each `ItemKind` (instead of using `ItemKind::descr()`)
let span = self.psess.source_map().guess_head_span(span);
let descr = kind.descr();
let help = match kind {
ItemKind::DelegationMac(DelegationMac {
suffixes: DelegationSuffixes::Glob(_),
..
}) => false,
_ => true,
};
self.dcx().emit_err(diagnostics::BadItemKind { span, descr, ctx, help });
None
}
fn is_use_closure(&self) -> bool {
if self.token.is_keyword(kw::Use) {
// Check if this could be a closure.
self.look_ahead(1, |token| {
// Move or Async here would be an error but still we're parsing a closure
let dist =
if token.is_keyword(kw::Move) || token.is_keyword(kw::Async) { 2 } else { 1 };
self.look_ahead(dist, |token| matches!(token.kind, token::Or | token::OrOr))
})
} else {
false
}
}
pub(super) fn is_unsafe_foreign_mod(&self) -> bool {
// Look for `unsafe`.
if !self.token.is_keyword(kw::Unsafe) {
return false;
}
// Look for `extern`.
if !self.is_keyword_ahead(1, &[kw::Extern]) {
return false;
}
// Look for the optional ABI string literal.
let n = if self.look_ahead(2, |t| t.can_begin_string_literal()) { 3 } else { 2 };
// Look for the `{`. Use `tree_look_ahead` because the ABI (if present)
// might be a metavariable i.e. an invisible-delimited sequence, and
// `tree_look_ahead` will consider that a single element when looking
// ahead.
self.tree_look_ahead(n, |t| matches!(t, TokenTree::Delimited(_, _, Delimiter::Brace, _)))
== Some(true)
}
fn parse_global_static_front_matter(&mut self, case: Case) -> Option<Safety> {
let is_global_static = if self.check_keyword_case(exp!(Static), case) {
// Check if this could be a closure.
!self.look_ahead(1, |token| {
if token.is_keyword_case(kw::Move, case) || token.is_keyword_case(kw::Use, case) {
return true;
}
matches!(token.kind, token::Or | token::OrOr)
})
} else {
// `$qual static`
(self.check_keyword_case(exp!(Unsafe), case)
|| self.check_keyword_case(exp!(Safe), case))
&& self.look_ahead(1, |t| t.is_keyword_case(kw::Static, case))
};
if is_global_static {
let safety = self.parse_safety(case);
let _ = self.eat_keyword_case(exp!(Static), case);
Some(safety)
} else {
None
}
}
/// Recover on `const mut` with `const` already eaten.
fn recover_const_mut(&mut self, const_span: Span) {
if self.eat_keyword(exp!(Mut)) {
let span = self.prev_token.span;
self.dcx()
.emit_err(diagnostics::ConstGlobalCannotBeMutable { ident_span: span, const_span });
} else if self.eat_keyword(exp!(Let)) {
let span = self.prev_token.span;
self.dcx()
.emit_err(diagnostics::ConstLetMutuallyExclusive { span: const_span.to(span) });
}
}
fn parse_const_block_item(&mut self) -> PResult<'a, ConstBlockItem> {
self.expect_keyword(exp!(Const))?;
let const_span = self.prev_token.span;
self.psess.gated_spans.gate(sym::const_block_items, const_span);
let block = self.parse_block()?;
Ok(ConstBlockItem { id: DUMMY_NODE_ID, span: const_span.to(block.span), block })
}
/// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in
/// `mutability`.
///
/// ```ebnf
/// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ;
/// ```
fn parse_static_item(
&mut self,
safety: Safety,
mutability: Mutability,
) -> PResult<'a, ItemKind> {
let ident = self.parse_ident()?;
if self.token == TokenKind::Lt && self.may_recover() {
let generics = self.parse_generics()?;
self.dcx().emit_err(diagnostics::StaticWithGenerics { span: generics.span });
}
// Parse the type of a static item. That is, the `":" $ty` fragment.
// FIXME: This could maybe benefit from `.may_recover()`?
let ty = match (self.eat(exp!(Colon)), self.check(exp!(Eq)) | self.check(exp!(Semi))) {
(true, false) => self.parse_ty()?,
// If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing
// type.
(colon, _) => self.recover_missing_global_item_type(colon, Some(mutability)),
};
let expr = if self.eat(exp!(Eq)) { Some(self.parse_expr()?) } else { None };
self.expect_semi()?;
let item =
StaticItem { ident, ty, safety, mutability, expr, define_opaque: None, eii_impl: None };
Ok(ItemKind::Static(Box::new(item)))
}
/// Parse a constant item with the prefix `"const"` already parsed.
///
/// If `const_arg` is true, any expression assigned to the const will be parsed
/// as a const_arg instead of a body expression.
///
/// ```ebnf
/// Const = "const" ($ident | "_") Generics ":" $ty (= $expr)? WhereClause ";" ;
/// ```
fn parse_const_item(
&mut self,
const_span: Span,
) -> PResult<'a, (Ident, Generics, Box<Ty>, Option<Box<Expr>>)> {
let ident = self.parse_ident_or_underscore()?;
let mut generics = self.parse_generics()?;
// Check the span for emptiness instead of the list of parameters in order to correctly
// recognize and subsequently flag empty parameter lists (`<>`) as unstable.
if !generics.span.is_empty() {
self.psess.gated_spans.gate(sym::generic_const_items, generics.span);
}
// Parse the type of a constant item. That is, the `":" $ty` fragment.
// FIXME: This could maybe benefit from `.may_recover()`?
let ty = match (
self.eat(exp!(Colon)),
self.check(exp!(Eq)) | self.check(exp!(Semi)) | self.check_keyword(exp!(Where)),
) {
(true, false) => self.parse_ty()?,
// If there wasn't a `:` or the colon was followed by a `=`, `;` or `where`, recover a missing type.
(colon, _) => self.recover_missing_global_item_type(colon, None),
};
// Proactively parse a where-clause to be able to provide a good error message in case we
// encounter the item body following it.
let before_where_clause =
if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() };
let rhs = if self.eat(exp!(Eq)) { Some(self.parse_expr()?) } else { None };
let after_where_clause = self.parse_where_clause()?;
// Provide a nice error message if the user placed a where-clause before the item body.
// Users may be tempted to write such code if they are still used to the deprecated
// where-clause location on type aliases and associated types. See also #89122.
if before_where_clause.has_where_token
&& let Some(rhs) = &rhs
{
self.dcx().emit_err(diagnostics::WhereClauseBeforeConstBody {
span: before_where_clause.span,
name: ident.span,
body: rhs.span,
sugg: if !after_where_clause.has_where_token {
self.psess.source_map().span_to_snippet(rhs.span).ok().map(|body_s| {
diagnostics::WhereClauseBeforeConstBodySugg {
left: before_where_clause.span.shrink_to_lo(),
snippet: body_s,
right: before_where_clause.span.shrink_to_hi().to(rhs.span),
}
})
} else {
// FIXME(generic_const_items): Provide a structured suggestion to merge the first
// where-clause into the second one.
None
},
});
}
// Merge the predicates of both where-clauses since either one can be relevant.
// If we didn't parse a body (which is valid for associated consts in traits) and we were
// allowed to recover, `before_where_clause` contains the predicates, otherwise they are
// in `after_where_clause`. Further, both of them might contain predicates iff two
// where-clauses were provided which is syntactically ill-formed but we want to recover from
// it and treat them as one large where-clause.
let mut predicates = before_where_clause.predicates;
predicates.extend(after_where_clause.predicates);
let where_clause = WhereClause {
has_where_token: before_where_clause.has_where_token
|| after_where_clause.has_where_token,
predicates,
span: if after_where_clause.has_where_token {
after_where_clause.span
} else {
before_where_clause.span
},
};
if where_clause.has_where_token {
self.psess.gated_spans.gate(sym::generic_const_items, where_clause.span);
}
generics.where_clause = where_clause;
if let Some(rhs) = self.try_recover_const_missing_semi(&rhs, const_span) {
return Ok((ident, generics, ty, Some(rhs)));
}
self.expect_semi()?;
Ok((ident, generics, ty, rhs))
}
/// We were supposed to parse `":" $ty` but the `:` or the type was missing.
/// This means that the type is missing.
fn recover_missing_global_item_type(
&mut self,
colon_present: bool,
m: Option<Mutability>,
) -> Box<Ty> {
// Construct the error and stash it away with the hope
// that typeck will later enrich the error with a type.
let kind = match m {
Some(Mutability::Mut) => "static mut",
Some(Mutability::Not) => "static",
None => "const",
};
let colon = match colon_present {
true => "",
false => ":",
};
let span = self.prev_token.span.shrink_to_hi();
let err = self.dcx().create_err(diagnostics::MissingConstType { span, colon, kind });
err.stash(span, StashKey::ItemNoType);
// The user intended that the type be inferred,
// so treat this as if the user wrote e.g. `const A: _ = expr;`.
Box::new(Ty { kind: TyKind::Infer, span, id: ast::DUMMY_NODE_ID })
}
/// Parses an enum declaration.
fn parse_item_enum(&mut self) -> PResult<'a, ItemKind> {
if self.token.is_keyword(kw::Struct) {
let span = self.prev_token.span.to(self.token.span);
let err = diagnostics::EnumStructMutuallyExclusive { span };
if self.look_ahead(1, |t| t.is_ident()) {
self.bump();
self.dcx().emit_err(err);
} else {
return Err(self.dcx().create_err(err));
}
}
let prev_span = self.prev_token.span;
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
generics.where_clause = self.parse_where_clause()?;
// Possibly recover `enum Foo;` instead of `enum Foo {}`
let (variants, _) = if self.token == TokenKind::Semi {
self.dcx().emit_err(diagnostics::UseEmptyBlockNotSemi { span: self.token.span });
self.bump();
(thin_vec![], Trailing::No)
} else {
self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), |p| {
p.parse_enum_variant(ident.span)
})
.map_err(|mut err| {
err.span_label(ident.span, "while parsing this enum");
// Try to recover `enum Foo { ident : Ty }`.
if self.prev_token.is_non_reserved_ident() && self.token == token::Colon {
let snapshot = self.create_snapshot_for_diagnostic();
self.bump();
match self.parse_ty() {
Ok(_) => {
err.span_suggestion_verbose(
prev_span,
"perhaps you meant to use `struct` here",
"struct",
Applicability::MaybeIncorrect,
);
}
Err(e) => {
e.cancel();
}
}
self.restore_snapshot(snapshot);
}
self.eat_to_tokens(&[exp!(CloseBrace)]);
self.bump(); // }
err
})?
};
let enum_definition = EnumDef { variants: variants.into_iter().flatten().collect() };
Ok(ItemKind::Enum(ident, generics, enum_definition))
}
fn parse_enum_variant(&mut self, span: Span) -> PResult<'a, Option<Variant>> {
self.recover_vcs_conflict_marker();
let variant_attrs = self.parse_outer_attributes()?;
self.recover_vcs_conflict_marker();
let help = "enum variants can be `Variant`, `Variant = <integer>`, \
`Variant(Type, ..., TypeN)` or `Variant { fields: Types }`";
self.collect_tokens(None, variant_attrs, ForceCollect::No, |this, variant_attrs| {
let vlo = this.token.span;
let vis = this.parse_visibility(FollowedByType::No)?;
if !this.recover_nested_adt_item(kw::Enum)? {
return Ok((None, Trailing::No, UsePreAttrPos::No));
}
let ident = this.parse_field_ident("enum", vlo)?;
if this.token == token::Bang {
if let Err(err) = this.unexpected() {
err.with_note(msg!("macros cannot expand to enum variants")).emit();
}
this.bump();
this.parse_delim_args()?;
return Ok((None, Trailing::from(this.token == token::Comma), UsePreAttrPos::No));
}
let struct_def = if this.check(exp!(OpenBrace)) {
// Parse a struct variant.
let (fields, recovered) =
match this.parse_record_struct_body("struct", ident.span, false) {
Ok((fields, recovered)) => (fields, recovered),
Err(mut err) => {
if this.token == token::Colon {
// We handle `enum` to `struct` suggestion in the caller.
return Err(err);
}
this.eat_to_tokens(&[exp!(CloseBrace)]);
this.bump(); // }
err.span_label(span, "while parsing this enum");
err.help(help);
let guar = err.emit();
(thin_vec![], Recovered::Yes(guar))
}
};
VariantData::Struct { fields, recovered }
} else if this.check(exp!(OpenParen)) {
let body = match this.parse_tuple_struct_body() {
Ok(body) => body,
Err(mut err) => {
if this.token == token::Colon {
// We handle `enum` to `struct` suggestion in the caller.
return Err(err);
}
this.eat_to_tokens(&[exp!(CloseParen)]);
this.bump(); // )
err.span_label(span, "while parsing this enum");
err.help(help);
err.emit();
thin_vec![]
}
};
VariantData::Tuple(body, DUMMY_NODE_ID)
} else {
VariantData::Unit(DUMMY_NODE_ID)
};
let disr_expr =
if this.eat(exp!(Eq)) { Some(this.parse_expr_anon_const()?) } else { None };
let span = vlo.to(this.prev_token.span);
if ident.name == kw::Underscore {
this.psess.gated_spans.gate(sym::unnamed_enum_variants, span);
}
let vr = ast::Variant {
ident,
vis,
id: DUMMY_NODE_ID,
attrs: variant_attrs,
data: struct_def,
disr_expr,
span,
is_placeholder: false,
};
Ok((Some(vr), Trailing::from(this.token == token::Comma), UsePreAttrPos::No))
})
.map_err(|mut err| {
err.help(help);
err
})
}
/// Parses `struct Foo { ... }`.
fn parse_item_struct(&mut self) -> PResult<'a, ItemKind> {
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
// There is a special case worth noting here, as reported in issue #17904.
// If we are parsing a tuple struct it is the case that the where clause
// should follow the field list. Like so:
//
// struct Foo<T>(T) where T: Copy;
//
// If we are parsing a normal record-style struct it is the case
// that the where clause comes before the body, and after the generics.
// So if we look ahead and see a brace or a where-clause we begin
// parsing a record style struct.
//
// Otherwise if we look ahead and see a paren we parse a tuple-style
// struct.
let vdata = if self.token.is_keyword(kw::Where) {
let tuple_struct_body;
(generics.where_clause, tuple_struct_body) =
self.parse_struct_where_clause(ident, generics.span)?;
if let Some(body) = tuple_struct_body {
// If we see a misplaced tuple struct body: `struct Foo<T> where T: Copy, (T);`
let body = VariantData::Tuple(body, DUMMY_NODE_ID);
self.expect_semi()?;
body
} else if self.eat(exp!(Semi)) {
// If we see a: `struct Foo<T> where T: Copy;` style decl.
VariantData::Unit(DUMMY_NODE_ID)
} else {
// If we see: `struct Foo<T> where T: Copy { ... }`
let (fields, recovered) = self.parse_record_struct_body(
"struct",
ident.span,
generics.where_clause.has_where_token,
)?;
VariantData::Struct { fields, recovered }
}
// No `where` so: `struct Foo<T>;`
} else if self.eat(exp!(Semi)) {
VariantData::Unit(DUMMY_NODE_ID)
// Record-style struct definition
} else if self.token == token::OpenBrace {
let (fields, recovered) = self.parse_record_struct_body(
"struct",
ident.span,
generics.where_clause.has_where_token,
)?;
VariantData::Struct { fields, recovered }
// Tuple-style struct definition with optional where-clause.
} else if self.token == token::OpenParen {
let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
generics.where_clause = self.parse_where_clause()?;
self.expect_semi()?;
body
} else {
let err = diagnostics::UnexpectedTokenAfterStructName::new(self.token.span, self.token);
return Err(self.dcx().create_err(err));
};
Ok(ItemKind::Struct(ident, generics, vdata))
}
/// Parses `union Foo { ... }`.
fn parse_item_union(&mut self) -> PResult<'a, ItemKind> {
let ident = self.parse_ident()?;
let mut generics = self.parse_generics()?;
let vdata = if self.token.is_keyword(kw::Where) {
generics.where_clause = self.parse_where_clause()?;
let (fields, recovered) = self.parse_record_struct_body(
"union",
ident.span,
generics.where_clause.has_where_token,
)?;
VariantData::Struct { fields, recovered }
} else if self.token == token::OpenBrace {
let (fields, recovered) = self.parse_record_struct_body(
"union",
ident.span,
generics.where_clause.has_where_token,
)?;
VariantData::Struct { fields, recovered }
} else {
let token_str = super::token_descr(&self.token);
let msg = format!("expected `where` or `{{` after union name, found {token_str}");
let mut err = self.dcx().struct_span_err(self.token.span, msg);
err.span_label(self.token.span, "expected `where` or `{` after union name");
return Err(err);
};
Ok(ItemKind::Union(ident, generics, vdata))
}
/// This function parses the fields of record structs:
///
/// - `struct S { ... }`
/// - `enum E { Variant { ... } }`
pub(crate) fn parse_record_struct_body(
&mut self,
adt_ty: &str,
ident_span: Span,
parsed_where: bool,
) -> PResult<'a, (ThinVec<FieldDef>, Recovered)> {
let mut fields = ThinVec::new();
let mut recovered = Recovered::No;
if self.eat(exp!(OpenBrace)) {
while self.token != token::CloseBrace {
match self.parse_field_def(adt_ty, ident_span) {
Ok(field) => {
fields.push(field);
}
Err(mut err) => {
self.consume_block(
exp!(OpenBrace),
exp!(CloseBrace),
ConsumeClosingDelim::No,
);
err.span_label(ident_span, format!("while parsing this {adt_ty}"));
let guar = err.emit();
recovered = Recovered::Yes(guar);
break;
}
}
}
self.expect(exp!(CloseBrace))?;
} else {
let token_str = super::token_descr(&self.token);
let where_str = if parsed_where { "" } else { "`where`, or " };
let msg = format!("expected {where_str}`{{` after struct name, found {token_str}");
let mut err = self.dcx().struct_span_err(self.token.span, msg);
err.span_label(self.token.span, format!("expected {where_str}`{{` after struct name",));
return Err(err);
}
Ok((fields, recovered))
}
fn parse_unsafe_field(&mut self) -> Safety {
// not using parse_safety as that also accepts `safe`.
if self.eat_keyword(exp!(Unsafe)) {
let span = self.prev_token.span;
self.psess.gated_spans.gate(sym::unsafe_fields, span);
Safety::Unsafe(span)
} else {
Safety::Default
}
}
/// This is the case where we find `struct Foo<T>(T) where T: Copy;`
/// Unit like structs are handled in parse_item_struct function
pub(super) fn parse_tuple_struct_body(&mut self) -> PResult<'a, ThinVec<FieldDef>> {
let openparen_span = self.token.span;
let mut encountered_colon = false;
self.parse_paren_comma_seq(|p| {
let attrs = p.parse_outer_attributes()?;
p.collect_tokens(None, attrs, ForceCollect::No, |p, attrs| {
let mut snapshot = None;
if p.is_vcs_conflict_marker(&TokenKind::Shl, &TokenKind::Lt) {
// Account for `<<<<<<<` diff markers. We can't proactively error here because
// that can be a valid type start, so we snapshot and reparse only we've
// encountered another parse error.
snapshot = Some(p.create_snapshot_for_diagnostic());
}
let lo = p.token.span;
let vis = match p.parse_visibility(FollowedByType::Yes) {
Ok(vis) => vis,
Err(err) => {
if let Some(ref mut snapshot) = snapshot {
snapshot.recover_vcs_conflict_marker();
}
return Err(err);
}
};
let mut_restriction = p.parse_mut_restriction()?;
encountered_colon |=
p.token.is_ident() && p.look_ahead(1, |tok| tok == &token::Colon);
// Unsafe fields are not supported in tuple structs, as doing so would result in a
// parsing ambiguity for `struct X(unsafe fn())`.
let ty = match p.parse_ty() {
Ok(ty) => ty,
Err(err) => {
if let Some(ref mut snapshot) = snapshot {
snapshot.recover_vcs_conflict_marker();
}
return Err(err);
}
};
let mut default = None;
if p.token == token::Eq {
let mut snapshot = p.create_snapshot_for_diagnostic();
snapshot.bump();
match snapshot.parse_expr_anon_const() {
Ok(const_expr) => {
let sp = ty.span.shrink_to_hi().to(const_expr.value.span);
p.psess.gated_spans.gate(sym::default_field_values, sp);
p.restore_snapshot(snapshot);
default = Some(const_expr);
}
Err(err) => {
err.cancel();
}
}
}
Ok((
FieldDef {
span: lo.to(ty.span),
vis,
extras: Self::field_def_extras(Safety::Default, mut_restriction, default),
ident: None,
id: DUMMY_NODE_ID,
ty,
attrs,
is_placeholder: false,
},
Trailing::from(p.token == token::Comma),
UsePreAttrPos::No,
))
})
})
.map(|(r, _)| r)
.map_err(|mut error| {
if self.token == token::Colon {
error.subdiagnostic(UseDoubleColonSuggestion { colon: self.token.span });
}
if encountered_colon {
self.eat_to_tokens(&[exp!(CloseParen)]);
self.bump();
error.subdiagnostic(UseRegularStructSuggestion {
open: openparen_span,
close: self.prev_token.span,
semicolon: if self.token == token::Semi { Some(self.token.span) } else { None },
});
}
error
})
}
fn field_def_extras(
safety: Safety,
mut_restriction: MutRestriction,
default: Option<AnonConst>,
) -> Option<Box<FieldDefExtras>> {
match (safety, mut_restriction, default) {
(
Safety::Default,
// We are throwing away the mut restriction span here.
// see the span field comment for more info
MutRestriction { kind: RestrictionKind::Unrestricted, span: _ },
None,
) => None,
(safety, mut_restriction, default) => {
Some(Box::new(FieldDefExtras { safety, mut_restriction, default }))
}
}
}
/// Parses an element of a struct declaration.
fn parse_field_def(&mut self, adt_ty: &str, ident_span: Span) -> PResult<'a, FieldDef> {
self.recover_vcs_conflict_marker();
let attrs = self.parse_outer_attributes()?;
self.recover_vcs_conflict_marker();
self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
let lo = this.token.span;
let vis = this.parse_visibility(FollowedByType::No)?;
let mut_restriction = this.parse_mut_restriction()?;
let safety = this.parse_unsafe_field();
this.parse_single_struct_field(
adt_ty,
lo,
vis,
mut_restriction,
safety,
attrs,
ident_span,
)
.map(|field| (field, Trailing::No, UsePreAttrPos::No))
})
}
/// Parses a structure field declaration.
fn parse_single_struct_field(
&mut self,
adt_ty: &str,
lo: Span,
vis: Visibility,
mut_restriction: MutRestriction,
safety: Safety,
attrs: AttrVec,
ident_span: Span,
) -> PResult<'a, FieldDef> {
let a_var = self.parse_name_and_ty(adt_ty, lo, vis, mut_restriction, safety, attrs)?;
match self.token.kind {
token::Comma => {
self.bump();
}
token::Semi => {
self.bump();
let sp = self.prev_token.span;
let mut err =
self.dcx().struct_span_err(sp, format!("{adt_ty} fields are separated by `,`"));
err.span_suggestion_short(
sp,
"replace `;` with `,`",
",",
Applicability::MachineApplicable,
);
err.span_label(ident_span, format!("while parsing this {adt_ty}"));
err.emit();
}
token::CloseBrace => {}
token::DocComment(..) => {
let previous_span = self.prev_token.span;
let mut err = diagnostics::DocCommentDoesNotDocumentAnything {
span: self.token.span,
missing_comma: None,
};
self.bump(); // consume the doc comment
if self.eat(exp!(Comma)) || self.token == token::CloseBrace {
self.dcx().emit_err(err);
} else {
let sp = previous_span.shrink_to_hi();
err.missing_comma = Some(sp);
return Err(self.dcx().create_err(err));
}
}
_ => {
let sp = self.prev_token.span.shrink_to_hi();
let msg =
format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token));
// Try to recover extra trailing angle brackets
if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind
&& let Some(last_segment) = segments.last()
{
let guar = self.check_trailing_angle_brackets(
last_segment,
&[exp!(Comma), exp!(CloseBrace)],
);
if let Some(_guar) = guar {
// Handle a case like `Vec<u8>>,` where we can continue parsing fields
// after the comma
let _ = self.eat(exp!(Comma));
// `check_trailing_angle_brackets` already emitted a nicer error, as
// proven by the presence of `_guar`. We can continue parsing.
return Ok(a_var);
}
}
let mut err = self.dcx().struct_span_err(sp, msg);
if self.token.is_ident()
|| (self.token == TokenKind::Pound
&& (self.look_ahead(1, |t| t == &token::OpenBracket)))
{
// This is likely another field, TokenKind::Pound is used for `#[..]`
// attribute for next field. Emit the diagnostic and continue parsing.
err.span_suggestion(
sp,
"try adding a comma",
",",
Applicability::MachineApplicable,
);
err.emit();
} else {
return Err(err);
}
}
}
Ok(a_var)
}
fn expect_field_ty_separator(&mut self) -> PResult<'a, ()> {
if let Err(err) = self.expect(exp!(Colon)) {
let sm = self.psess.source_map();
let eq_typo = self.token == token::Eq && self.look_ahead(1, |t| t.is_path_start());
let semi_typo = self.token == token::Semi
&& self.look_ahead(1, |t| {
t.is_path_start()
// We check that we are in a situation like `foo; bar` to avoid bad suggestions
// when there's no type and `;` was used instead of a comma.
&& match (sm.lookup_line(self.token.span.hi()), sm.lookup_line(t.span.lo())) {
(Ok(l), Ok(r)) => l.line == r.line,
_ => true,
}
});
if eq_typo || semi_typo {
self.bump();
// Gracefully handle small typos.
err.with_span_suggestion_short(
self.prev_token.span,
"field names and their types are separated with `:`",
":",
Applicability::MachineApplicable,
)
.emit();
} else {
return Err(err);
}
}
Ok(())
}
/// Parses a structure field.
fn parse_name_and_ty(
&mut self,
adt_ty: &str,
lo: Span,
vis: Visibility,
mut_restriction: MutRestriction,
safety: Safety,
attrs: AttrVec,
) -> PResult<'a, FieldDef> {
let name = self.parse_field_ident(adt_ty, lo)?;
if self.token == token::Bang {
if let Err(mut err) = self.unexpected() {
// Encounter the macro invocation
err.subdiagnostic(MacroExpandsToAdtField { adt_ty });
return Err(err);
}
}
self.expect_field_ty_separator()?;
let ty = self.parse_ty()?;
if self.token == token::Colon && self.look_ahead(1, |&t| t != token::Colon) {
return Err(self
.dcx()
.struct_span_err(self.token.span, "found single colon in a struct field type path")
.with_span_suggestion_verbose(
self.token.span,
"write a path separator here",
"::",
Applicability::MaybeIncorrect,
));
}
let default = if self.token == token::Eq {
self.bump();
let const_expr = self.parse_expr_anon_const()?;
let sp = ty.span.shrink_to_hi().to(const_expr.value.span);
self.psess.gated_spans.gate(sym::default_field_values, sp);
Some(const_expr)
} else {
None
};
Ok(FieldDef {
span: lo.to(self.prev_token.span),
ident: Some(name),
vis,
extras: Self::field_def_extras(safety, mut_restriction, default),
id: DUMMY_NODE_ID,
ty,
attrs,
is_placeholder: false,
})
}
/// Parses a field identifier. Specialized version of `parse_ident_common`
/// for better diagnostics and suggestions.
fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> {
let (ident, is_raw) = self.ident_or_err(true)?;
if is_raw == IdentIsRaw::No
&& ident.is_reserved()
&& !(ident.name == kw::Underscore && adt_ty == "enum")
{
let snapshot = self.create_snapshot_for_diagnostic();
let err = if self.check_fn_front_matter(false, Case::Sensitive) {
let inherited_vis = Visibility { span: DUMMY_SP, kind: VisibilityKind::Inherited };
// We use `parse_fn` to get a span for the function
let fn_parse_mode =
FnParseMode { req_name: |_, _| true, context: FnContext::Free, req_body: true };
match self.parse_fn(
&mut AttrVec::new(),
fn_parse_mode,
lo,
&inherited_vis,
Case::Insensitive,
) {
Ok(_) => self
.dcx()
.struct_span_err(
lo.to(self.prev_token.span),
format!("functions are not allowed in {adt_ty} definitions"),
)
.with_help(
"unlike in C++, Java, and C#, functions are declared in `impl` blocks",
)
.with_help(
"see https://doc.rust-lang.org/book/ch05-03-method-syntax.html \
for more information",
),
Err(err) => {
err.cancel();
self.restore_snapshot(snapshot);
self.expected_ident_found_err()
}
}
} else if self.eat_keyword(exp!(Struct)) {
match self.parse_item_struct() {
Ok(item) => {
let ItemKind::Struct(ident, ..) = item else { unreachable!() };
self.dcx()
.struct_span_err(
lo.with_hi(ident.span.hi()),
format!("structs are not allowed in {adt_ty} definitions"),
)
.with_help(
"consider creating a new `struct` definition instead of nesting",
)
}
Err(err) => {
err.cancel();
self.restore_snapshot(snapshot);
self.expected_ident_found_err()
}
}
} else {
let mut err = self.expected_ident_found_err();
if self.eat_keyword_noexpect(kw::Let)
&& let removal_span = self.prev_token.span.until(self.token.span)
&& let Ok(ident) = self
.parse_ident_common(false)
// Cancel this error, we don't need it.
.map_err(|err| err.cancel())
&& self.token == TokenKind::Colon
{
err.span_suggestion_verbose(
removal_span,
"remove the `let` keyword",
String::new(),
Applicability::MachineApplicable,
);
err.note("the `let` keyword is not allowed in `struct` fields");
err.note(
"see <https://doc.rust-lang.org/book/ch05-01-defining-structs.html> \
for more information",
);
err.emit();
return Ok(ident);
} else {
self.restore_snapshot(snapshot);
}
err
};
return Err(err);
}
self.bump();
Ok(ident)
}
/// Parses a declarative macro 2.0 definition.
/// The `macro` keyword has already been parsed.
/// ```ebnf
/// MacBody = "{" TOKEN_STREAM "}" ;
/// MacParams = "(" TOKEN_STREAM ")" ;
/// DeclMac = "macro" Ident MacParams? MacBody ;
/// ```
fn parse_item_decl_macro(&mut self, lo: Span) -> PResult<'a, ItemKind> {
let ident = self.parse_ident()?;
let body = if self.check(exp!(OpenBrace)) {
self.parse_delim_args()? // `MacBody`
} else if self.check(exp!(OpenParen)) {
let params = self.parse_token_tree(); // `MacParams`
let pspan = params.span();
if !self.check(exp!(OpenBrace)) {
self.unexpected()?;
}
let body = self.parse_token_tree(); // `MacBody`
// Convert `MacParams MacBody` into `{ MacParams => MacBody }`.
let bspan = body.span();
let arrow = TokenTree::token_alone(token::FatArrow, pspan.between(bspan)); // `=>`
let tokens = TokenStream::new(vec![params, arrow, body]);
let dspan = DelimSpan::from_pair(pspan.shrink_to_lo(), bspan.shrink_to_hi());
Box::new(DelimArgs { dspan, delim: Delimiter::Brace, tokens })
} else {
self.unexpected_any()?
};
self.psess.gated_spans.gate(sym::decl_macro, lo.to(self.prev_token.span));
Ok(ItemKind::MacroDef(
ident,
ast::MacroDef { body, macro_rules: false, eii_declaration: None },
))
}
/// Is this a possibly malformed start of a `macro_rules! foo` item definition?
fn is_macro_rules_item(&mut self) -> IsMacroRulesItem {
if self.check_keyword(exp!(MacroRules)) {
let macro_rules_span = self.token.span;
if self.look_ahead(1, |t| *t == token::Bang) && self.look_ahead(2, |t| t.is_ident()) {
return IsMacroRulesItem::Yes { has_bang: true };
} else if self.look_ahead(1, |t| t.is_ident()) {
// macro_rules foo
self.dcx().emit_err(diagnostics::MacroRulesMissingBang {
span: macro_rules_span,
hi: macro_rules_span.shrink_to_hi(),
});
return IsMacroRulesItem::Yes { has_bang: false };
}
}
IsMacroRulesItem::No
}
/// Parses a `macro_rules! foo { ... }` declarative macro.
fn parse_item_macro_rules(
&mut self,
vis: &Visibility,
has_bang: bool,
) -> PResult<'a, ItemKind> {
self.expect_keyword(exp!(MacroRules))?; // `macro_rules`
if has_bang {
self.expect(exp!(Bang))?; // `!`
}
let ident = self.parse_ident()?;
if self.eat(exp!(Bang)) {
// Handle macro_rules! foo!
let span = self.prev_token.span;
self.dcx().emit_err(diagnostics::MacroNameRemoveBang { span });
}
let body = self.parse_delim_args()?;
self.eat_semi_for_macro_if_needed(&body, None);
self.complain_if_pub_macro(vis, true);
Ok(ItemKind::MacroDef(
ident,
ast::MacroDef { body, macro_rules: true, eii_declaration: None },
))
}
/// Item macro invocations or `macro_rules!` definitions need inherited visibility.
/// If that's not the case, emit an error.
fn complain_if_pub_macro(&self, vis: &Visibility, macro_rules: bool) {
if let VisibilityKind::Inherited = vis.kind {
return;
}
let vstr = pprust::vis_to_string(vis);
let vstr = vstr.trim_end();
if macro_rules {
self.dcx().emit_err(diagnostics::MacroRulesVisibility { span: vis.span, vis: vstr });
} else {
self.dcx()
.emit_err(diagnostics::MacroInvocationVisibility { span: vis.span, vis: vstr });
}
}
fn eat_semi_for_macro_if_needed(&mut self, args: &DelimArgs, path: Option<&Path>) {
if args.need_semicolon() && !self.eat(exp!(Semi)) {
self.report_invalid_macro_expansion_item(args, path);
}
}
fn report_invalid_macro_expansion_item(&self, args: &DelimArgs, path: Option<&Path>) {
let span = args.dspan.entire();
let mut err = self.dcx().struct_span_err(
span,
"macros that expand to items must be delimited with braces or followed by a semicolon",
);
// FIXME: This will make us not emit the help even for declarative
// macros within the same crate (that we can fix), which is sad.
if !span.from_expansion() {
let DelimSpan { open, close } = args.dspan;
// Check if this looks like `macro_rules!(name) { ... }`
// a common mistake when trying to define a macro.
if let Some(path) = path
&& path.segments.first().is_some_and(|seg| seg.ident.name == sym::macro_rules)
&& args.delim == Delimiter::Parenthesis
{
let replace =
if path.span.hi() + rustc_span::BytePos(1) < open.lo() { "" } else { " " };
err.multipart_suggestion(
"to define a macro, remove the parentheses around the macro name",
vec![(open, replace.to_string()), (close, String::new())],
Applicability::MachineApplicable,
);
} else {
err.multipart_suggestion(
"change the delimiters to curly braces",
vec![(open, "{".to_string()), (close, '}'.to_string())],
Applicability::MaybeIncorrect,
);
err.span_suggestion_verbose(
span.with_neighbor(self.token.span).shrink_to_hi(),
"add a semicolon",
';',
Applicability::MaybeIncorrect,
);
}
}
err.emit();
}
/// Checks if current token is one of tokens which cannot be nested like `kw::Enum`. In case
/// it is, we try to parse the item and report error about nested types.
fn recover_nested_adt_item(&mut self, keyword: Symbol) -> PResult<'a, bool> {
if (self.token.is_keyword(kw::Enum)
|| self.token.is_keyword(kw::Struct)
|| self.token.is_keyword(kw::Union))
&& self.look_ahead(1, |t| t.is_ident())
{
let kw_token = self.token;
let kw_str = pprust::token_to_string(&kw_token);
let item = self.parse_item(
ForceCollect::No,
AllowConstBlockItems::DoesNotMatter, // self.token != kw::Const
)?;
let mut item = item.unwrap().span;
if self.token == token::Comma {
item = item.to(self.token.span);
}
self.dcx().emit_err(diagnostics::NestedAdt {
span: kw_token.span,
item,
kw_str,
keyword: keyword.as_str(),
});
// We successfully parsed the item but we must inform the caller about nested problem.
return Ok(false);
}
Ok(true)
}
fn check_impl_frontmatter(&mut self, look_ahead: usize) -> bool {
const ALL_QUALS: &[Symbol] = &[kw::Const, kw::Unsafe];
// In contrast to the loop below, this call inserts `impl` into the
// list of expected tokens shown in diagnostics.
if self.check_keyword(exp!(Impl)) {
return true;
}
let mut i = 0;
while i < ALL_QUALS.len() {
let action = self.look_ahead(i + look_ahead, |token| {
if token.is_keyword(kw::Impl) {
return Some(true);
}
if ALL_QUALS.iter().any(|&qual| token.is_keyword(qual)) {
// Ok, we found a legal keyword, keep looking for `impl`
return None;
}
Some(false)
});
if let Some(ret) = action {
return ret;
}
i += 1;
}
self.is_keyword_ahead(i, &[kw::Impl])
}
/// Try to recover from over-parsing in const item when a semicolon is missing.
///
/// This detects cases where we parsed too much because a semicolon was missing
/// and the next line started an expression that the parser treated as a continuation
/// (e.g., `foo() \n &bar` was parsed as `foo() & bar`).
///
/// Returns a corrected expression if recovery is successful.
fn try_recover_const_missing_semi(
&mut self,
rhs: &Option<Box<Expr>>,
const_span: Span,
) -> Option<Box<Expr>> {
if self.token == TokenKind::Semi {
return None;
}
let Some(rhs) = rhs else {
return None;
};
if !self.in_fn_body || !self.may_recover() || rhs.span.from_expansion() {
return None;
}
if let Some((span, guar)) =
self.missing_semi_from_binop("const", rhs, Some(const_span.shrink_to_lo()))
{
self.fn_body_missing_semi_guar = Some(guar);
Some(self.mk_expr(span, ExprKind::Err(guar)))
} else {
None
}
}
}
enum IsMacroRulesItem {
Yes { has_bang: bool },
No,
}