blob: 1523aba4be9ab443e499cabf3150759deb208bd3 [file]
use ast::token::IdentIsRaw;
use rustc_ast as ast;
use rustc_ast::ast::*;
use rustc_ast::token::{self, InvisibleOrigin, MetaVarKind, TokenKind};
use rustc_ast::tokenstream::TokenTree;
use rustc_ast::util::case::Case;
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, PResult};
use rustc_session::lint::builtin::VARARGS_WITHOUT_PATTERN;
use rustc_span::edition::Edition;
use rustc_span::{ErrorGuaranteed, Ident, Span, kw, respan, sym};
use thin_vec::ThinVec;
use tracing::debug;
use super::diagnostics::dummy_arg;
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{
ExpKeywordPair, FollowedByType, ForceCollect, Parser, Recovered, Trailing, UsePreAttrPos,
};
use crate::diagnostics::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst};
use crate::exp;
/// The parsing configuration used to parse a parameter list (see `parse_fn_params`).
///
/// The function decides if, per-parameter `p`, `p` must have a pattern or just a type.
///
/// This function pointer accepts an edition, because in edition 2015, trait declarations
/// were allowed to omit parameter names. In 2018, they became required. It also accepts an
/// `IsDotDotDot` parameter, as `extern` function declarations and function pointer types are
/// allowed to omit the name of the `...` but regular function items are not.
type ReqName = fn(Edition, IsDotDotDot) -> bool;
#[derive(Copy, Clone, PartialEq)]
pub(crate) enum IsDotDotDot {
Yes,
No,
}
/// Parsing configuration for functions.
///
/// The syntax of function items is slightly different within trait definitions,
/// impl blocks, and modules. It is still parsed using the same code, just with
/// different flags set, so that even when the input is wrong and produces a parse
/// error, it still gets into the AST and the rest of the parser and
/// type checker can run.
#[derive(Clone, Copy)]
pub(crate) struct FnParseMode {
/// A function pointer that decides if, per-parameter `p`, `p` must have a
/// pattern or just a type. This field affects parsing of the parameters list.
///
/// ```text
/// fn foo(alef: A) -> X { X::new() }
/// -----^^ affects parsing this part of the function signature
/// |
/// if req_name returns false, then this name is optional
///
/// fn bar(A) -> X;
/// ^
/// |
/// if req_name returns true, this is an error
/// ```
///
/// Calling this function pointer should only return false if:
///
/// * The item is being parsed inside of a trait definition.
/// Within an impl block or a module, it should always evaluate
/// to true.
/// * The span is from Edition 2015. In particular, you can get a
/// 2015 span inside a 2021 crate using macros.
///
/// Or if `IsDotDotDot::Yes`, this function will also return `false` if the item being parsed
/// is inside an `extern` block.
pub(super) req_name: ReqName,
/// The context in which this function is parsed, used for diagnostics.
/// This indicates the fn is a free function or method and so on.
pub(super) context: FnContext,
/// If this flag is set to `true`, then plain, semicolon-terminated function
/// prototypes are not allowed here.
///
/// ```text
/// fn foo(alef: A) -> X { X::new() }
/// ^^^^^^^^^^^^
/// |
/// this is always allowed
///
/// fn bar(alef: A, bet: B) -> X;
/// ^
/// |
/// if req_body is set to true, this is an error
/// ```
///
/// This field should only be set to false if the item is inside of a trait
/// definition or extern block. Within an impl block or a module, it should
/// always be set to true.
pub(super) req_body: bool,
}
/// The context in which a function is parsed.
/// FIXME(estebank, xizheyin): Use more variants.
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum FnContext {
/// Free context.
Free,
/// A Function Pointer Type `fn(..)`.
FunctionPtrType,
/// A Trait context.
Trait,
/// An Impl block.
Impl,
}
/// Parsing of functions and methods.
impl<'a> Parser<'a> {
/// Parse a function starting from the front matter (`const ...`) to the body `{ ... }` or `;`.
pub(super) fn parse_fn(
&mut self,
attrs: &mut AttrVec,
fn_parse_mode: FnParseMode,
sig_lo: Span,
vis: &Visibility,
case: Case,
) -> PResult<'a, (Ident, FnSig, Generics, Option<Box<FnContract>>, Option<Box<Block>>)> {
let fn_span = self.token.span;
let header = self.parse_fn_front_matter(vis, case, FrontMatterParsingMode::Function)?; // `const ... fn`
let ident = self.parse_ident()?; // `foo`
let mut generics = self.parse_generics()?; // `<'a, T, ...>`
let decl = match self.parse_fn_decl(&fn_parse_mode, AllowPlus::Yes, RecoverReturnSign::Yes)
{
Ok(decl) => decl,
Err(old_err) => {
// If we see `for Ty ...` then user probably meant `impl` item.
if self.token.is_keyword(kw::For) {
old_err.cancel();
return Err(self.dcx().create_err(diagnostics::FnTypoWithImpl { fn_span }));
} else {
return Err(old_err);
}
}
};
// Store the end of function parameters to give better diagnostics
// inside `parse_fn_body()`.
let fn_params_end = self.prev_token.span.shrink_to_hi();
let contract = self.parse_contract()?;
generics.where_clause = self.parse_where_clause()?; // `where T: Ord`
// `fn_params_end` is needed only when it's followed by a where clause.
let fn_params_end =
if generics.where_clause.has_where_token { Some(fn_params_end) } else { None };
let mut sig_hi = self.prev_token.span;
// Either `;` or `{ ... }`.
let body =
self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?;
let fn_sig_span = sig_lo.to(sig_hi);
Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, contract, body))
}
/// Provide diagnostics when function body is not found
fn error_fn_body_not_found(
&mut self,
ident_span: Span,
req_body: bool,
fn_params_end: Option<Span>,
) -> PResult<'a, ErrorGuaranteed> {
let expected: &[_] =
if req_body { &[exp!(OpenBrace)] } else { &[exp!(Semi), exp!(OpenBrace)] };
match self.expected_one_of_not_found(&[], expected) {
Ok(error_guaranteed) => Ok(error_guaranteed),
Err(mut err) => {
if self.token == token::CloseBrace {
// The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
// the AST for typechecking.
err.span_label(ident_span, "while parsing this `fn`");
Ok(err.emit())
} else if self.token == token::RArrow
&& let Some(fn_params_end) = fn_params_end
{
// Instead of a function body, the parser has encountered a right arrow
// preceded by a where clause.
// Find whether token behind the right arrow is a function trait and
// store its span.
let fn_trait_span =
[sym::FnOnce, sym::FnMut, sym::Fn].into_iter().find_map(|symbol| {
if self.prev_token.is_ident_named(symbol) {
Some(self.prev_token.span)
} else {
None
}
});
// Parse the return type (along with the right arrow) and store its span.
// If there's a parse error, cancel it and return the existing error
// as we are primarily concerned with the
// expected-function-body-but-found-something-else error here.
let arrow_span = self.token.span;
let ty_span = match self.parse_ret_ty(
AllowPlus::Yes,
RecoverQPath::Yes,
RecoverReturnSign::Yes,
) {
Ok(ty_span) => ty_span.span().shrink_to_hi(),
Err(parse_error) => {
parse_error.cancel();
return Err(err);
}
};
let ret_ty_span = arrow_span.to(ty_span);
if let Some(fn_trait_span) = fn_trait_span {
// Typo'd Fn* trait bounds such as
// fn foo<F>() where F: FnOnce -> () {}
err.subdiagnostic(diagnostics::FnTraitMissingParen { span: fn_trait_span });
} else if let Ok(snippet) = self.psess.source_map().span_to_snippet(ret_ty_span)
{
// If token behind right arrow is not a Fn* trait, the programmer
// probably misplaced the return type after the where clause like
// `fn foo<T>() where T: Default -> u8 {}`
err.primary_message(
"return type should be specified after the function parameters",
);
err.subdiagnostic(diagnostics::MisplacedReturnType {
fn_params_end,
snippet,
ret_ty_span,
});
}
Err(err)
} else {
Err(err)
}
}
}
}
/// Parse the "body" of a function.
/// This can either be `;` when there's no body,
/// or e.g. a block when the function is a provided one.
fn parse_fn_body(
&mut self,
attrs: &mut AttrVec,
ident: &Ident,
sig_hi: &mut Span,
req_body: bool,
fn_params_end: Option<Span>,
) -> PResult<'a, Option<Box<Block>>> {
let has_semi = if req_body {
self.token == TokenKind::Semi
} else {
// Only include `;` in list of expected tokens if body is not required
self.check(exp!(Semi))
};
let (inner_attrs, body) = if has_semi {
// Include the trailing semicolon in the span of the signature
self.expect_semi()?;
*sig_hi = self.prev_token.span;
(AttrVec::new(), None)
} else if self.check(exp!(OpenBrace)) || self.token.is_metavar_block() {
let prev_in_fn_body = self.in_fn_body;
self.in_fn_body = true;
let res = self.parse_block_common(self.token.span, BlockCheckMode::Default, None).map(
|(attrs, mut body)| {
if let Some(guar) = self.fn_body_missing_semi_guar.take() {
body.stmts.push(self.mk_stmt(
body.span,
StmtKind::Expr(self.mk_expr(body.span, ExprKind::Err(guar))),
));
}
(attrs, Some(body))
},
);
self.in_fn_body = prev_in_fn_body;
res?
} else if self.token == token::Eq {
// Recover `fn foo() = $expr;`.
self.bump(); // `=`
let eq_sp = self.prev_token.span;
let _ = self.parse_expr()?;
self.expect_semi()?; // `;`
let span = eq_sp.to(self.prev_token.span);
let guar = self.dcx().emit_err(diagnostics::FunctionBodyEqualsExpr {
span,
sugg: diagnostics::FunctionBodyEqualsExprSugg {
eq: eq_sp,
semi: self.prev_token.span,
},
});
(AttrVec::new(), Some(self.mk_block_err(span, guar)))
} else {
self.error_fn_body_not_found(ident.span, req_body, fn_params_end)?;
(AttrVec::new(), None)
};
attrs.extend(inner_attrs);
Ok(body)
}
/// Is the current token the start of an `FnHeader` / not a valid parse?
///
/// `check_pub` adds additional `pub` to the checks in case users place it
/// wrongly, can be used to ensure `pub` never comes after `default`.
pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool {
const ALL_QUALS: &[ExpKeywordPair] = &[
exp!(Pub),
exp!(Gen),
exp!(Const),
exp!(Async),
exp!(Unsafe),
exp!(Safe),
exp!(Extern),
];
// We use an over-approximation here.
// `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`,
// only if it wasn't preceded by `default` as `default pub` is invalid.
let quals: &[_] = if check_pub {
ALL_QUALS
} else {
&[exp!(Gen), exp!(Const), exp!(Async), exp!(Unsafe), exp!(Safe), exp!(Extern)]
};
self.check_keyword_case(exp!(Fn), case) // Definitely an `fn`.
// `$qual fn` or `$qual $qual`:
|| quals.iter().any(|&exp| self.check_keyword_case(exp, case))
&& self.look_ahead(1, |t| {
// `$qual fn`, e.g. `const fn` or `async fn`.
t.is_keyword_case(kw::Fn, case)
// Two qualifiers `$qual $qual` is enough, e.g. `async unsafe`.
|| (
(
t.is_non_raw_ident_where(|i|
quals.iter().any(|exp| exp.kw == 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(|exp| {
exp.kw.as_str() == i.name.as_str().to_lowercase()
}))
)
// Rule out `unsafe extern {`.
&& !self.is_unsafe_foreign_mod()
// Rule out `async gen {` and `async gen move {`
&& !self.is_async_gen_block()
// Rule out `const unsafe auto` and `const unsafe trait` and `const unsafe impl`
&& !self.is_keyword_ahead(2, &[kw::Auto, kw::Trait, kw::Impl])
)
})
// `extern ABI fn`
|| self.check_keyword_case(exp!(Extern), case)
// Use `tree_look_ahead` because `ABI` might be a metavariable,
// i.e. an invisible-delimited sequence, and `tree_look_ahead`
// will consider that a single element when looking ahead.
&& self.look_ahead(1, |t| t.can_begin_string_literal())
&& (self.tree_look_ahead(2, |tt| {
match tt {
TokenTree::Token(t, _) => t.is_keyword_case(kw::Fn, case),
TokenTree::Delimited(..) => false,
}
}) == Some(true) ||
// This branch is only for better diagnostics; `pub`, `unsafe`, etc. are not
// allowed here.
(self.may_recover()
&& self.tree_look_ahead(2, |tt| {
match tt {
TokenTree::Token(t, _) =>
ALL_QUALS.iter().any(|exp| {
t.is_keyword(exp.kw)
}),
TokenTree::Delimited(..) => false,
}
}) == Some(true)
&& self.tree_look_ahead(3, |tt| {
match tt {
TokenTree::Token(t, _) => t.is_keyword_case(kw::Fn, case),
TokenTree::Delimited(..) => false,
}
}) == Some(true)
)
)
}
/// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,
/// up to and including the `fn` keyword. The formal grammar is:
///
/// ```text
/// Extern = "extern" StringLit? ;
/// FnQual = "const"? "async"? "unsafe"? Extern? ;
/// FnFrontMatter = FnQual "fn" ;
/// ```
///
/// `vis` represents the visibility that was already parsed, if any. Use
/// `Visibility::Inherited` when no visibility is known.
///
/// If `parsing_mode` is `FrontMatterParsingMode::FunctionPtrType`, we error on `const` and `async` qualifiers,
/// which are not allowed in function pointer types.
pub(super) fn parse_fn_front_matter(
&mut self,
orig_vis: &Visibility,
case: Case,
parsing_mode: FrontMatterParsingMode,
) -> PResult<'a, FnHeader> {
let sp_start = self.token.span;
let constness = self.parse_constness(case);
if parsing_mode == FrontMatterParsingMode::FunctionPtrType
&& let Const::Yes(const_span) = constness
{
self.dcx().emit_err(FnPointerCannotBeConst {
span: const_span,
suggestion: const_span.until(self.token.span),
});
}
let async_start_sp = self.token.span;
let coroutine_kind = self.parse_coroutine_kind(case);
if parsing_mode == FrontMatterParsingMode::FunctionPtrType
&& let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind
{
self.dcx().emit_err(FnPointerCannotBeAsync {
span: async_span,
suggestion: async_span.until(self.token.span),
});
}
// FIXME(gen_blocks): emit a similar error for `gen fn()`
let unsafe_start_sp = self.token.span;
let safety = self.parse_safety(case);
let ext_start_sp = self.token.span;
let ext = self.parse_extern(case);
if let Some(CoroutineKind::Async { span, .. }) = coroutine_kind {
if span.is_rust_2015() {
self.dcx().emit_err(diagnostics::AsyncFnIn2015 {
span,
help: diagnostics::HelpUseLatestEdition::new(),
});
}
}
match coroutine_kind {
Some(CoroutineKind::Gen { span, .. }) | Some(CoroutineKind::AsyncGen { span, .. }) => {
self.psess.gated_spans.gate(sym::gen_blocks, span);
}
Some(CoroutineKind::Async { .. }) | None => {}
}
if !self.eat_keyword_case(exp!(Fn), case) {
// It is possible for `expect_one_of` to recover given the contents of
// `self.expected_token_types`, therefore, do not use `self.unexpected()` which doesn't
// account for this.
match self.expect_one_of(&[], &[]) {
Ok(Recovered::Yes(_)) => {}
Ok(Recovered::No) => unreachable!(),
Err(mut err) => {
// Qualifier keywords ordering check
enum WrongKw {
Duplicated(Span),
Misplaced(Span),
/// `MisplacedDisallowedQualifier` is only used instead of `Misplaced`,
/// when the misplaced keyword is disallowed by the current `FrontMatterParsingMode`.
/// In this case, we avoid generating the suggestion to swap around the keywords,
/// as we already generated a suggestion to remove the keyword earlier.
MisplacedDisallowedQualifier,
}
// We may be able to recover
let mut recover_constness = constness;
let mut recover_coroutine_kind = coroutine_kind;
let mut recover_safety = safety;
// This will allow the machine fix to directly place the keyword in the correct place or to indicate
// that the keyword is already present and the second instance should be removed.
let wrong_kw = if self.check_keyword(exp!(Const)) {
match constness {
Const::Yes(sp) => Some(WrongKw::Duplicated(sp)),
Const::No => {
recover_constness = Const::Yes(self.token.span);
match parsing_mode {
FrontMatterParsingMode::Function => {
Some(WrongKw::Misplaced(async_start_sp))
}
FrontMatterParsingMode::FunctionPtrType => {
self.dcx().emit_err(FnPointerCannotBeConst {
span: self.token.span,
suggestion: self
.token
.span
.with_lo(self.prev_token.span.hi()),
});
Some(WrongKw::MisplacedDisallowedQualifier)
}
}
}
}
} else if self.check_keyword(exp!(Async)) {
match coroutine_kind {
Some(CoroutineKind::Async { span, .. }) => {
Some(WrongKw::Duplicated(span))
}
Some(CoroutineKind::AsyncGen { span, .. }) => {
Some(WrongKw::Duplicated(span))
}
Some(CoroutineKind::Gen { .. }) => {
recover_coroutine_kind = Some(CoroutineKind::AsyncGen {
span: self.token.span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
});
// FIXME(gen_blocks): This span is wrong, didn't want to think about it.
Some(WrongKw::Misplaced(unsafe_start_sp))
}
None => {
recover_coroutine_kind = Some(CoroutineKind::Async {
span: self.token.span,
closure_id: DUMMY_NODE_ID,
return_impl_trait_id: DUMMY_NODE_ID,
});
match parsing_mode {
FrontMatterParsingMode::Function => {
Some(WrongKw::Misplaced(async_start_sp))
}
FrontMatterParsingMode::FunctionPtrType => {
self.dcx().emit_err(FnPointerCannotBeAsync {
span: self.token.span,
suggestion: self
.token
.span
.with_lo(self.prev_token.span.hi()),
});
Some(WrongKw::MisplacedDisallowedQualifier)
}
}
}
}
} else if self.check_keyword(exp!(Unsafe)) {
match safety {
Safety::Unsafe(sp) => Some(WrongKw::Duplicated(sp)),
Safety::Safe(sp) => {
recover_safety = Safety::Unsafe(self.token.span);
Some(WrongKw::Misplaced(sp))
}
Safety::Default => {
recover_safety = Safety::Unsafe(self.token.span);
Some(WrongKw::Misplaced(ext_start_sp))
}
}
} else if self.check_keyword(exp!(Safe)) {
match safety {
Safety::Safe(sp) => Some(WrongKw::Duplicated(sp)),
Safety::Unsafe(sp) => {
recover_safety = Safety::Safe(self.token.span);
Some(WrongKw::Misplaced(sp))
}
Safety::Default => {
recover_safety = Safety::Safe(self.token.span);
Some(WrongKw::Misplaced(ext_start_sp))
}
}
} else {
None
};
// The keyword is already present, suggest removal of the second instance
if let Some(WrongKw::Duplicated(original_sp)) = wrong_kw {
let original_kw = self
.span_to_snippet(original_sp)
.expect("Span extracted directly from keyword should always work");
err.span_suggestion_verbose(
self.token_uninterpolated_span(),
format!("`{original_kw}` already used earlier, remove this one"),
"",
Applicability::MachineApplicable,
)
.span_note(original_sp, format!("`{original_kw}` first seen here"));
}
// The keyword has not been seen yet, suggest correct placement in the function front matter
else if let Some(WrongKw::Misplaced(correct_pos_sp)) = wrong_kw {
let correct_pos_sp = correct_pos_sp.to(self.prev_token.span);
if let Ok(current_qual) = self.span_to_snippet(correct_pos_sp) {
let misplaced_qual_sp = self.token_uninterpolated_span();
let misplaced_qual = self.span_to_snippet(misplaced_qual_sp).unwrap();
err.span_suggestion_verbose(
correct_pos_sp.to(misplaced_qual_sp),
format!("`{misplaced_qual}` must come before `{current_qual}`"),
format!("{misplaced_qual} {current_qual}"),
Applicability::MachineApplicable,
).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`");
}
}
// Recover incorrect visibility order such as `async pub`
else if self.check_keyword(exp!(Pub)) {
let sp = sp_start.to(self.prev_token.span);
if let Ok(snippet) = self.span_to_snippet(sp) {
let current_vis = match self.parse_visibility(FollowedByType::No) {
Ok(v) => v,
Err(d) => {
d.cancel();
return Err(err);
}
};
let vs = pprust::vis_to_string(&current_vis);
let vs = vs.trim_end();
// There was no explicit visibility
if matches!(orig_vis.kind, VisibilityKind::Inherited) {
err.span_suggestion_verbose(
sp_start.to(self.prev_token.span),
format!("visibility `{vs}` must come before `{snippet}`"),
format!("{vs} {snippet}"),
Applicability::MachineApplicable,
);
}
// There was an explicit visibility
else {
err.span_suggestion_verbose(
current_vis.span,
"there is already a visibility modifier, remove one",
"",
Applicability::MachineApplicable,
)
.span_note(orig_vis.span, "explicit visibility first seen here");
}
}
}
// FIXME(gen_blocks): add keyword recovery logic for genness
if let Some(wrong_kw) = wrong_kw
&& self.may_recover()
&& self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case))
{
// Advance past the misplaced keyword and `fn`
self.bump();
self.bump();
// When we recover from a `MisplacedDisallowedQualifier`, we already emitted an error for the disallowed qualifier
// So we don't emit another error that the qualifier is unexpected.
if matches!(wrong_kw, WrongKw::MisplacedDisallowedQualifier) {
err.cancel();
} else {
err.emit();
}
return Ok(FnHeader {
constness: recover_constness,
safety: recover_safety,
coroutine_kind: recover_coroutine_kind,
ext,
});
}
return Err(err);
}
}
}
Ok(FnHeader { constness, safety, coroutine_kind, ext })
}
/// Parses the parameter list and result type of a function declaration.
pub(super) fn parse_fn_decl(
&mut self,
fn_parse_mode: &FnParseMode,
ret_allow_plus: AllowPlus,
recover_return_sign: RecoverReturnSign,
) -> PResult<'a, Box<FnDecl>> {
Ok(Box::new(FnDecl {
inputs: self.parse_fn_params(fn_parse_mode)?,
output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes, recover_return_sign)?,
}))
}
/// Parses the parameter list of a function, including the `(` and `)` delimiters.
pub(super) fn parse_fn_params(
&mut self,
fn_parse_mode: &FnParseMode,
) -> PResult<'a, ThinVec<Param>> {
let mut first_param = true;
// Parse the arguments, starting out with `self` being allowed...
if self.token != TokenKind::OpenParen
// might be typo'd trait impl, handled elsewhere
&& !self.token.is_keyword(kw::For)
{
// recover from missing argument list, e.g. `fn main -> () {}`
self.dcx().emit_err(diagnostics::MissingFnParams {
span: self.prev_token.span.shrink_to_hi(),
});
return Ok(ThinVec::new());
}
let (mut params, _) = self.parse_paren_comma_seq(|p| {
p.recover_vcs_conflict_marker();
let snapshot = p.create_snapshot_for_diagnostic();
let param = p.parse_param_general(fn_parse_mode, first_param, true).or_else(|e| {
let guar = e.emit();
// When parsing a param failed, we should check to make the span of the param
// not contain '(' before it.
// For example when parsing `*mut Self` in function `fn oof(*mut Self)`.
let lo = if let TokenKind::OpenParen = p.prev_token.kind {
p.prev_token.span.shrink_to_hi()
} else {
p.prev_token.span
};
p.restore_snapshot(snapshot);
// Skip every token until next possible arg or end.
p.eat_to_tokens(&[exp!(Comma), exp!(CloseParen)]);
// Create a placeholder argument for proper arg count (issue #34264).
Ok(dummy_arg(Ident::new(sym::dummy, lo.to(p.prev_token.span)), guar))
});
// ...now that we've parsed the first argument, `self` is no longer allowed.
first_param = false;
param
})?;
// Replace duplicated recovered params with `_` pattern to avoid unnecessary errors.
self.deduplicate_recovered_params_names(&mut params);
Ok(params)
}
/// Parses a single function parameter.
///
/// - `self` is syntactically allowed when `first_param` holds.
/// - `recover_arg_parse` is used to recover from a failed argument parse.
pub(super) fn parse_param_general(
&mut self,
fn_parse_mode: &FnParseMode,
first_param: bool,
recover_arg_parse: bool,
) -> PResult<'a, Param> {
let lo = self.token.span;
let attrs = self.parse_outer_attributes()?;
self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
// Possibly parse `self`. Recover if we parsed it and it wasn't allowed here.
if let Some(mut param) = this.parse_self_param()? {
param.attrs = attrs;
let res = if first_param { Ok(param) } else { this.recover_bad_self_param(param) };
return Ok((res?, Trailing::No, UsePreAttrPos::No));
}
let is_dot_dot_dot = if this.token.kind == token::DotDotDot {
IsDotDotDot::Yes
} else {
IsDotDotDot::No
};
let is_name_required = (fn_parse_mode.req_name)(
this.token.span.with_neighbor(this.prev_token.span).edition(),
is_dot_dot_dot,
);
let is_name_required = if is_name_required && is_dot_dot_dot == IsDotDotDot::Yes {
this.psess.buffer_lint(
VARARGS_WITHOUT_PATTERN,
this.token.span,
ast::CRATE_NODE_ID,
diagnostics::VarargsWithoutPattern { span: this.token.span },
);
false
} else {
is_name_required
};
let (pat, ty) = if is_name_required || this.is_named_param() {
debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required);
let (pat, colon) = this.parse_fn_param_pat_colon()?;
if !colon {
let mut err = this.unexpected().unwrap_err();
let pat_span = pat.span;
return if let Some(ident) = this.parameter_without_type(
&mut err,
pat,
is_name_required,
first_param,
fn_parse_mode,
) {
let guar = err.emit();
let mut arg = dummy_arg(ident, guar);
arg.span = pat_span;
Ok((arg, Trailing::No, UsePreAttrPos::No))
} else {
Err(err)
};
}
this.eat_incorrect_doc_comment_for_param_type();
(pat, this.parse_ty_for_param()?)
} else {
debug!("parse_param_general ident_to_pat");
let parser_snapshot_before_ty = this.create_snapshot_for_diagnostic();
this.eat_incorrect_doc_comment_for_param_type();
let mut ty = this.parse_ty_for_param();
if let Ok(t) = &ty {
// Check for trailing angle brackets
if let TyKind::Path(_, Path { segments, .. }) = &t.kind
&& let Some(segment) = segments.last()
&& let Some(guar) =
this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)])
{
return Ok((
dummy_arg(segment.ident, guar),
Trailing::No,
UsePreAttrPos::No,
));
}
if this.token != token::Comma && this.token != token::CloseParen {
// This wasn't actually a type, but a pattern looking like a type,
// so we are going to rollback and re-parse for recovery.
ty = this.unexpected_any();
}
}
match ty {
Ok(ty) => {
let pat = this.mk_pat(ty.span, PatKind::Missing);
(Box::new(pat), ty)
}
// If this is a C-variadic argument and we hit an error, return the error.
Err(err) if this.token == token::DotDotDot => return Err(err),
Err(err) if this.unmatched_angle_bracket_count > 0 => return Err(err),
Err(err) if recover_arg_parse => {
// Recover from attempting to parse the argument as a type without pattern.
err.cancel();
this.restore_snapshot(parser_snapshot_before_ty);
this.recover_arg_parse(fn_parse_mode.context)?
}
Err(err) => return Err(err),
}
};
let span = lo.to(this.prev_token.span);
Ok((
Param { attrs, id: ast::DUMMY_NODE_ID, is_placeholder: false, pat, span, ty },
Trailing::No,
UsePreAttrPos::No,
))
})
}
/// Returns the parsed optional self parameter and whether a self shortcut was used.
fn parse_self_param(&mut self) -> PResult<'a, Option<Param>> {
// Extract an identifier *after* having confirmed that the token is one.
let expect_self_ident = |this: &mut Self| match this.token.ident() {
Some((ident, IdentIsRaw::No)) => {
this.bump();
ident
}
_ => unreachable!(),
};
// is lifetime `n` tokens ahead?
let is_lifetime = |this: &Self, n| this.look_ahead(n, |t| t.is_lifetime());
// Is `self` `n` tokens ahead?
let is_isolated_self = |this: &Self, n| {
this.is_keyword_ahead(n, &[kw::SelfLower])
&& this.look_ahead(n + 1, |t| t != &token::PathSep)
};
// Is `pin const self` `n` tokens ahead?
let is_isolated_pin_const_self = |this: &Self, n| {
this.look_ahead(n, |token| token.is_ident_named(sym::pin))
&& this.is_keyword_ahead(n + 1, &[kw::Const])
&& is_isolated_self(this, n + 2)
};
// Is `mut self` `n` tokens ahead?
let is_isolated_mut_self =
|this: &Self, n| this.is_keyword_ahead(n, &[kw::Mut]) && is_isolated_self(this, n + 1);
// Is `pin mut self` `n` tokens ahead?
let is_isolated_pin_mut_self = |this: &Self, n| {
this.look_ahead(n, |token| token.is_ident_named(sym::pin))
&& is_isolated_mut_self(this, n + 1)
};
// Parse `self` or `self: TYPE`. We already know the current token is `self`.
let parse_self_possibly_typed = |this: &mut Self, m| {
let eself_ident = expect_self_ident(this);
let eself_hi = this.prev_token.span;
let eself = if this.eat(exp!(Colon)) {
SelfKind::Explicit(this.parse_ty()?, m)
} else {
SelfKind::Value(m)
};
Ok((eself, eself_ident, eself_hi))
};
let expect_self_ident_not_typed =
|this: &mut Self, modifier: &SelfKind, modifier_span: Span| {
let eself_ident = expect_self_ident(this);
// Recover `: Type` after a qualified self
if this.may_recover() && this.eat_noexpect(&token::Colon) {
let snap = this.create_snapshot_for_diagnostic();
match this.parse_ty() {
Ok(ty) => {
this.dcx().emit_err(diagnostics::IncorrectTypeOnSelf {
span: ty.span,
move_self_modifier: diagnostics::MoveSelfModifier {
removal_span: modifier_span,
insertion_span: ty.span.shrink_to_lo(),
modifier: modifier.to_ref_suggestion(),
},
});
}
Err(diag) => {
diag.cancel();
this.restore_snapshot(snap);
}
}
}
eself_ident
};
// Recover for the grammar `*self`, `*const self`, and `*mut self`.
let recover_self_ptr = |this: &mut Self| {
this.dcx().emit_err(diagnostics::SelfArgumentPointer { span: this.token.span });
Ok((SelfKind::Value(Mutability::Not), expect_self_ident(this), this.prev_token.span))
};
// Parse optional `self` parameter of a method.
// Only a limited set of initial token sequences is considered `self` parameters; anything
// else is parsed as a normal function parameter list, so some lookahead is required.
let eself_lo = self.token.span;
let (eself, eself_ident, eself_hi) = match self.token.uninterpolate().kind {
token::And => {
let has_lifetime = is_lifetime(self, 1);
let skip_lifetime_count = has_lifetime as usize;
let eself = if is_isolated_self(self, skip_lifetime_count + 1) {
// `&{'lt} self`
self.bump(); // &
let lifetime = has_lifetime.then(|| self.expect_lifetime());
SelfKind::Region(lifetime, Mutability::Not)
} else if is_isolated_mut_self(self, skip_lifetime_count + 1) {
// `&{'lt} mut self`
self.bump(); // &
let lifetime = has_lifetime.then(|| self.expect_lifetime());
self.bump(); // mut
SelfKind::Region(lifetime, Mutability::Mut)
} else if is_isolated_pin_const_self(self, skip_lifetime_count + 1) {
// `&{'lt} pin const self`
self.bump(); // &
let lifetime = has_lifetime.then(|| self.expect_lifetime());
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
self.bump(); // pin
self.bump(); // const
SelfKind::Pinned(lifetime, Mutability::Not)
} else if is_isolated_pin_mut_self(self, skip_lifetime_count + 1) {
// `&{'lt} pin mut self`
self.bump(); // &
let lifetime = has_lifetime.then(|| self.expect_lifetime());
self.psess.gated_spans.gate(sym::pin_ergonomics, self.token.span);
self.bump(); // pin
self.bump(); // mut
SelfKind::Pinned(lifetime, Mutability::Mut)
} else {
// `&not_self`
return Ok(None);
};
let hi = self.token.span;
let self_ident = expect_self_ident_not_typed(self, &eself, eself_lo.until(hi));
(eself, self_ident, hi)
}
// `*self`
token::Star if is_isolated_self(self, 1) => {
self.bump();
recover_self_ptr(self)?
}
// `*mut self` and `*const self`
token::Star
if self.look_ahead(1, |t| t.is_mutability()) && is_isolated_self(self, 2) =>
{
self.bump();
self.bump();
recover_self_ptr(self)?
}
// `self` and `self: TYPE`
token::Ident(..) if is_isolated_self(self, 0) => {
parse_self_possibly_typed(self, Mutability::Not)?
}
// `mut self` and `mut self: TYPE`
token::Ident(..) if is_isolated_mut_self(self, 0) => {
self.bump();
parse_self_possibly_typed(self, Mutability::Mut)?
}
_ => return Ok(None),
};
let eself = respan(eself_lo.to(eself_hi), eself);
Ok(Some(Param::from_self(AttrVec::default(), eself, eself_ident)))
}
fn is_named_param(&self) -> bool {
let offset = match &self.token.kind {
token::OpenInvisible(origin) => match origin {
InvisibleOrigin::MetaVar(MetaVarKind::Pat(_)) => {
return self.check_noexpect_past_close_delim(&token::Colon);
}
_ => 0,
},
token::And | token::AndAnd => 1,
_ if self.token.is_keyword(kw::Mut) => 1,
_ => 0,
};
self.look_ahead(offset, |t| t.is_ident())
&& self.look_ahead(offset + 1, |t| t == &token::Colon)
}
pub(super) fn recover_self_param(&mut self) -> bool {
matches!(
self.parse_outer_attributes()
.and_then(|_| self.parse_self_param())
.map_err(|e| e.cancel()),
Ok(Some(_))
)
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub(crate) enum FrontMatterParsingMode {
/// Parse the front matter of a function declaration
Function,
/// Parse the front matter of a function pointet type.
/// For function pointer types, the `const` and `async` keywords are not permitted.
FunctionPtrType,
}