summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_parse/src/parser
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_parse/src/parser')
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs1
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs1
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs174
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs199
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs118
-rw-r--r--compiler/rustc_parse/src/parser/item.rs96
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs28
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs42
-rw-r--r--compiler/rustc_parse/src/parser/path.rs3
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs31
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs205
11 files changed, 712 insertions, 186 deletions
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index c7d239b64..686454a8f 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -6,7 +6,6 @@ use rustc_ast::attr;
use rustc_ast::token::{self, Delimiter, Nonterminal};
use rustc_errors::{error_code, fluent, Diagnostic, IntoDiagnostic, PResult};
use rustc_span::{sym, BytePos, Span};
-use std::convert::TryInto;
// Public for rustfmt usage
#[derive(Debug)]
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index a084a7010..b97f22417 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -8,7 +8,6 @@ use rustc_errors::PResult;
use rustc_session::parse::ParseSess;
use rustc_span::{sym, Span, DUMMY_SP};
-use std::convert::TryInto;
use std::ops::Range;
/// A wrapper type to ensure that the parser handles outer attributes correctly.
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index c316a4dd6..4c918c670 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -12,9 +12,10 @@ use crate::errors::{
IncorrectAwait, IncorrectSemicolon, IncorrectUseOfAwait, ParenthesesInForHead,
ParenthesesInForHeadSugg, PatternMethodParamWithoutBody, QuestionMarkInType,
QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
- StructLiteralBodyWithoutPathSugg, SuggEscapeToUseAsIdentifier, SuggRemoveComma,
- UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
- UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead,
+ StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg,
+ SuggEscapeToUseAsIdentifier, SuggRemoveComma, UnexpectedConstInGenericParam,
+ UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
+ UseEqInstead,
};
use crate::lexer::UnmatchedBrace;
@@ -31,7 +32,8 @@ use rustc_ast::{
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{
- fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult,
+ fluent, Applicability, DiagnosticBuilder, DiagnosticMessage, FatalError, Handler, MultiSpan,
+ PResult,
};
use rustc_errors::{pluralize, Diagnostic, ErrorGuaranteed, IntoDiagnostic};
use rustc_session::errors::ExprParenthesesNeeded;
@@ -41,7 +43,6 @@ use rustc_span::{Span, SpanSnippetError, DUMMY_SP};
use std::mem::take;
use std::ops::{Deref, DerefMut};
use thin_vec::{thin_vec, ThinVec};
-use tracing::{debug, trace};
/// Creates a placeholder argument.
pub(super) fn dummy_arg(ident: Ident) -> Param {
@@ -159,8 +160,6 @@ enum IsStandalone {
Standalone,
/// It's a subexpression, i.e., *not* standalone.
Subexpr,
- /// It's maybe standalone; we're not sure.
- Maybe,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -213,14 +212,8 @@ impl MultiSugg {
err.multipart_suggestion(&self.msg, self.patches, self.applicability);
}
- /// Overrides individual messages and applicabilities.
- fn emit_many(
- err: &mut Diagnostic,
- msg: &str,
- applicability: Applicability,
- suggestions: impl Iterator<Item = Self>,
- ) {
- err.multipart_suggestions(msg, suggestions.map(|s| s.patches), applicability);
+ fn emit_verbose(self, err: &mut Diagnostic) {
+ err.multipart_suggestion_verbose(&self.msg, self.patches, self.applicability);
}
}
@@ -631,12 +624,15 @@ impl<'a> Parser<'a> {
&mut self,
lo: Span,
s: BlockCheckMode,
+ maybe_struct_name: token::Token,
+ can_be_struct_literal: bool,
) -> Option<PResult<'a, P<Block>>> {
if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {
// We might be having a struct literal where people forgot to include the path:
// fn foo() -> Foo {
// field: value,
// }
+ info!(?maybe_struct_name, ?self.token);
let mut snapshot = self.create_snapshot_for_diagnostic();
let path = Path {
segments: ThinVec::new(),
@@ -656,13 +652,6 @@ impl<'a> Parser<'a> {
// field: value,
// } }
err.delay_as_bug();
- self.sess.emit_err(StructLiteralBodyWithoutPath {
- span: expr.span,
- sugg: StructLiteralBodyWithoutPathSugg {
- before: expr.span.shrink_to_lo(),
- after: expr.span.shrink_to_hi(),
- },
- });
self.restore_snapshot(snapshot);
let mut tail = self.mk_block(
vec![self.mk_stmt_err(expr.span)],
@@ -670,7 +659,25 @@ impl<'a> Parser<'a> {
lo.to(self.prev_token.span),
);
tail.could_be_bare_literal = true;
- Ok(tail)
+ if maybe_struct_name.is_ident() && can_be_struct_literal {
+ // Account for `if Example { a: one(), }.is_pos() {}`.
+ Err(self.sess.create_err(StructLiteralNeedingParens {
+ span: maybe_struct_name.span.to(expr.span),
+ sugg: StructLiteralNeedingParensSugg {
+ before: maybe_struct_name.span.shrink_to_lo(),
+ after: expr.span.shrink_to_hi(),
+ },
+ }))
+ } else {
+ self.sess.emit_err(StructLiteralBodyWithoutPath {
+ span: expr.span,
+ sugg: StructLiteralBodyWithoutPathSugg {
+ before: expr.span.shrink_to_lo(),
+ after: expr.span.shrink_to_hi(),
+ },
+ });
+ Ok(tail)
+ }
}
(Err(err), Ok(tail)) => {
// We have a block tail that contains a somehow valid type ascription expr.
@@ -1112,7 +1119,11 @@ impl<'a> Parser<'a> {
return if token::ModSep == self.token.kind {
// We have some certainty that this was a bad turbofish at this point.
// `foo< bar >::`
- err.suggest_turbofish = Some(op.span.shrink_to_lo());
+ if let ExprKind::Binary(o, ..) = inner_op.kind && o.node == BinOpKind::Lt {
+ err.suggest_turbofish = Some(op.span.shrink_to_lo());
+ } else {
+ err.help_turbofish = Some(());
+ }
let snapshot = self.create_snapshot_for_diagnostic();
self.bump(); // `::`
@@ -1138,7 +1149,11 @@ impl<'a> Parser<'a> {
} else if token::OpenDelim(Delimiter::Parenthesis) == self.token.kind {
// We have high certainty that this was a bad turbofish at this point.
// `foo< bar >(`
- err.suggest_turbofish = Some(op.span.shrink_to_lo());
+ if let ExprKind::Binary(o, ..) = inner_op.kind && o.node == BinOpKind::Lt {
+ err.suggest_turbofish = Some(op.span.shrink_to_lo());
+ } else {
+ err.help_turbofish = Some(());
+ }
// Consume the fn call arguments.
match self.consume_fn_args() {
Err(()) => Err(err.into_diagnostic(&self.sess.span_diagnostic)),
@@ -1238,7 +1253,7 @@ impl<'a> Parser<'a> {
let sum_span = ty.span.to(self.prev_token.span);
let sub = match &ty.kind {
- TyKind::Rptr(lifetime, mut_ty) => {
+ TyKind::Ref(lifetime, mut_ty) => {
let sum_with_parens = pprust::to_string(|s| {
s.s.word("&");
s.print_opt_lifetime(lifetime);
@@ -1267,12 +1282,10 @@ impl<'a> Parser<'a> {
&mut self,
operand_expr: P<Expr>,
op_span: Span,
- prev_is_semi: bool,
+ start_stmt: bool,
) -> PResult<'a, P<Expr>> {
- let standalone =
- if prev_is_semi { IsStandalone::Standalone } else { IsStandalone::Subexpr };
+ let standalone = if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr };
let kind = IncDecRecovery { standalone, op: IncOrDec::Inc, fixity: UnaryFixity::Pre };
-
self.recover_from_inc_dec(operand_expr, kind, op_span)
}
@@ -1280,13 +1293,13 @@ impl<'a> Parser<'a> {
&mut self,
operand_expr: P<Expr>,
op_span: Span,
+ start_stmt: bool,
) -> PResult<'a, P<Expr>> {
let kind = IncDecRecovery {
- standalone: IsStandalone::Maybe,
+ standalone: if start_stmt { IsStandalone::Standalone } else { IsStandalone::Subexpr },
op: IncOrDec::Inc,
fixity: UnaryFixity::Post,
};
-
self.recover_from_inc_dec(operand_expr, kind, op_span)
}
@@ -1315,34 +1328,25 @@ impl<'a> Parser<'a> {
};
match kind.standalone {
- IsStandalone::Standalone => self.inc_dec_standalone_suggest(kind, spans).emit(&mut err),
+ IsStandalone::Standalone => {
+ self.inc_dec_standalone_suggest(kind, spans).emit_verbose(&mut err)
+ }
IsStandalone::Subexpr => {
let Ok(base_src) = self.span_to_snippet(base.span)
- else { return help_base_case(err, base) };
+ else { return help_base_case(err, base) };
match kind.fixity {
UnaryFixity::Pre => {
self.prefix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)
}
UnaryFixity::Post => {
- self.postfix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)
+ // won't suggest since we can not handle the precedences
+ // for example: `a + b++` has been parsed (a + b)++ and we can not suggest here
+ if !matches!(base.kind, ExprKind::Binary(_, _, _)) {
+ self.postfix_inc_dec_suggest(base_src, kind, spans).emit(&mut err)
+ }
}
}
}
- IsStandalone::Maybe => {
- let Ok(base_src) = self.span_to_snippet(base.span)
- else { return help_base_case(err, base) };
- let sugg1 = match kind.fixity {
- UnaryFixity::Pre => self.prefix_inc_dec_suggest(base_src, kind, spans),
- UnaryFixity::Post => self.postfix_inc_dec_suggest(base_src, kind, spans),
- };
- let sugg2 = self.inc_dec_standalone_suggest(kind, spans);
- MultiSugg::emit_many(
- &mut err,
- "use `+= 1` instead",
- Applicability::Unspecified,
- [sugg1, sugg2].into_iter(),
- )
- }
}
Err(err)
}
@@ -1392,7 +1396,6 @@ impl<'a> Parser<'a> {
}
patches.push((post_span, format!(" {}= 1", kind.op.chr())));
-
MultiSugg {
msg: format!("use `{}= 1` instead", kind.op.chr()),
patches,
@@ -2577,6 +2580,75 @@ impl<'a> Parser<'a> {
Ok(())
}
+ pub fn is_diff_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> bool {
+ (0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind))
+ && self.look_ahead(3, |tok| tok == short_kind)
+ }
+
+ fn diff_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> Option<Span> {
+ if self.is_diff_marker(long_kind, short_kind) {
+ let lo = self.token.span;
+ for _ in 0..4 {
+ self.bump();
+ }
+ return Some(lo.to(self.prev_token.span));
+ }
+ None
+ }
+
+ pub fn recover_diff_marker(&mut self) {
+ let Some(start) = self.diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) else {
+ return;
+ };
+ let mut spans = Vec::with_capacity(3);
+ spans.push(start);
+ let mut middlediff3 = None;
+ let mut middle = None;
+ let mut end = None;
+ loop {
+ if self.token.kind == TokenKind::Eof {
+ break;
+ }
+ if let Some(span) = self.diff_marker(&TokenKind::OrOr, &TokenKind::BinOp(token::Or)) {
+ middlediff3 = Some(span);
+ }
+ if let Some(span) = self.diff_marker(&TokenKind::EqEq, &TokenKind::Eq) {
+ middle = Some(span);
+ }
+ if let Some(span) = self.diff_marker(&TokenKind::BinOp(token::Shr), &TokenKind::Gt) {
+ spans.push(span);
+ end = Some(span);
+ break;
+ }
+ self.bump();
+ }
+ let mut err = self.struct_span_err(spans, "encountered diff marker");
+ err.span_label(start, "after this is the code before the merge");
+ if let Some(middle) = middlediff3 {
+ err.span_label(middle, "");
+ }
+ if let Some(middle) = middle {
+ err.span_label(middle, "");
+ }
+ if let Some(end) = end {
+ err.span_label(end, "above this are the incoming code changes");
+ }
+ err.help(
+ "if you're having merge conflicts after pulling new code, the top section is the code \
+ you already had and the bottom section is the remote code",
+ );
+ err.help(
+ "if you're in the middle of a rebase, the top section is the code being rebased onto \
+ and the bottom section is the code coming from the current commit being rebased",
+ );
+ err.note(
+ "for an explanation on these markers from the `git` documentation, visit \
+ <https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_checking_out_conflicts>",
+ );
+ err.emit();
+ FatalError.raise()
+ }
+
/// Parse and throw away a parenthesized comma separated
/// sequence of patterns until `)` is reached.
fn skip_pat_list(&mut self) -> PResult<'a, ()> {
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index f6a6ed379..bf93a89f0 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -83,7 +83,7 @@ macro_rules! maybe_whole_expr {
pub(super) enum LhsExpr {
NotYetParsed,
AttributesParsed(AttrWrapper),
- AlreadyParsed(P<Expr>),
+ AlreadyParsed { expr: P<Expr>, starts_statement: bool },
}
impl From<Option<AttrWrapper>> for LhsExpr {
@@ -97,11 +97,11 @@ impl From<Option<AttrWrapper>> for LhsExpr {
}
impl From<P<Expr>> for LhsExpr {
- /// Converts the `expr: P<Expr>` into `LhsExpr::AlreadyParsed(expr)`.
+ /// Converts the `expr: P<Expr>` into `LhsExpr::AlreadyParsed { expr, starts_statement: false }`.
///
/// This conversion does not allocate.
fn from(expr: P<Expr>) -> Self {
- LhsExpr::AlreadyParsed(expr)
+ LhsExpr::AlreadyParsed { expr, starts_statement: false }
}
}
@@ -173,14 +173,16 @@ impl<'a> Parser<'a> {
min_prec: usize,
lhs: LhsExpr,
) -> PResult<'a, P<Expr>> {
- let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs {
+ let mut starts_stmt = false;
+ let mut lhs = if let LhsExpr::AlreadyParsed { expr, starts_statement } = lhs {
+ starts_stmt = starts_statement;
expr
} else {
let attrs = match lhs {
LhsExpr::AttributesParsed(attrs) => Some(attrs),
_ => None,
};
- if [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token.kind) {
+ if self.token.is_range_separator() {
return self.parse_prefix_range_expr(attrs);
} else {
self.parse_prefix_expr(attrs)?
@@ -292,7 +294,7 @@ impl<'a> Parser<'a> {
let op_span = self.prev_token.span.to(self.token.span);
// Eat the second `+`
self.bump();
- lhs = self.recover_from_postfix_increment(lhs, op_span)?;
+ lhs = self.recover_from_postfix_increment(lhs, op_span, starts_stmt)?;
continue;
}
@@ -512,7 +514,7 @@ impl<'a> Parser<'a> {
}
debug_assert!(
- [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token.kind),
+ self.token.is_range_separator(),
"parse_prefix_range_expr: token {:?} is not DotDot/DotDotEq",
self.token
);
@@ -560,17 +562,23 @@ impl<'a> Parser<'a> {
// Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr()
match this.token.uninterpolate().kind {
- token::Not => make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Not)), // `!expr`
- token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)), // `~expr`
+ // `!expr`
+ token::Not => make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Not)),
+ // `~expr`
+ token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)),
+ // `-expr`
token::BinOp(token::Minus) => {
make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Neg))
- } // `-expr`
+ }
+ // `*expr`
token::BinOp(token::Star) => {
make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Deref))
- } // `*expr`
+ }
+ // `&expr` and `&&expr`
token::BinOp(token::And) | token::AndAnd => {
make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo))
}
+ // `+lit`
token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => {
let mut err =
LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None };
@@ -585,19 +593,20 @@ impl<'a> Parser<'a> {
this.bump();
this.parse_prefix_expr(None)
- } // `+expr`
+ }
// Recover from `++x`:
token::BinOp(token::Plus)
if this.look_ahead(1, |t| *t == token::BinOp(token::Plus)) =>
{
- let prev_is_semi = this.prev_token == token::Semi;
+ let starts_stmt = this.prev_token == token::Semi
+ || this.prev_token == token::CloseDelim(Delimiter::Brace);
let pre_span = this.token.span.to(this.look_ahead(1, |t| t.span));
// Eat both `+`s.
this.bump();
this.bump();
let operand_expr = this.parse_dot_or_call_expr(Default::default())?;
- this.recover_from_prefix_increment(operand_expr, pre_span, prev_is_semi)
+ this.recover_from_prefix_increment(operand_expr, pre_span, starts_stmt)
}
token::Ident(..) if this.token.is_keyword(kw::Box) => {
make_it!(this, attrs, |this, _| this.parse_box_expr(lo))
@@ -621,7 +630,7 @@ impl<'a> Parser<'a> {
Ok((span, self.mk_unary(op, expr)))
}
- // Recover on `!` suggesting for bitwise negation instead.
+ /// Recover on `~expr` in favor of `!expr`.
fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
self.sess.emit_err(TildeAsUnaryOperator(lo));
@@ -648,7 +657,6 @@ impl<'a> Parser<'a> {
/// Recover on `not expr` in favor of `!expr`.
fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
- // Emit the error...
let negated_token = self.look_ahead(1, |t| t.clone());
let sub_diag = if negated_token.is_numeric_lit() {
@@ -669,7 +677,6 @@ impl<'a> Parser<'a> {
),
});
- // ...and recover!
self.parse_unary_expr(lo, UnOp::Not)
}
@@ -896,7 +903,11 @@ impl<'a> Parser<'a> {
let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon);
let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below.
let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo);
- let expr = self.parse_prefix_expr(None);
+ let expr = if self.token.is_range_separator() {
+ self.parse_prefix_range_expr(None)
+ } else {
+ self.parse_prefix_expr(None)
+ };
let (hi, expr) = self.interpolated_or_expr_span(expr)?;
let span = lo.to(hi);
if let Some(lt) = lifetime {
@@ -1318,7 +1329,10 @@ impl<'a> Parser<'a> {
self.parse_array_or_repeat_expr(Delimiter::Bracket)
} else if self.check_path() {
self.parse_path_start_expr()
- } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) {
+ } else if self.check_keyword(kw::Move)
+ || self.check_keyword(kw::Static)
+ || self.check_const_closure()
+ {
self.parse_closure_expr()
} else if self.eat_keyword(kw::If) {
self.parse_if_expr()
@@ -1339,9 +1353,6 @@ impl<'a> Parser<'a> {
err.span_label(sp, "while parsing this `loop` expression");
err
})
- } else if self.eat_keyword(kw::Continue) {
- let kind = ExprKind::Continue(self.eat_label());
- Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
} else if self.eat_keyword(kw::Match) {
let match_sp = self.prev_token.span;
self.parse_match_expr().map_err(|mut err| {
@@ -1365,6 +1376,8 @@ impl<'a> Parser<'a> {
self.parse_try_block(lo)
} else if self.eat_keyword(kw::Return) {
self.parse_return_expr()
+ } else if self.eat_keyword(kw::Continue) {
+ self.parse_continue_expr(lo)
} else if self.eat_keyword(kw::Break) {
self.parse_break_expr()
} else if self.eat_keyword(kw::Yield) {
@@ -1461,9 +1474,8 @@ impl<'a> Parser<'a> {
} else if self.eat(&token::Comma) {
// Vector with two or more elements.
let sep = SeqSep::trailing_allowed(token::Comma);
- let (remaining_exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?;
- let mut exprs = vec![first_expr];
- exprs.extend(remaining_exprs);
+ let (mut exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?;
+ exprs.insert(0, first_expr);
ExprKind::Array(exprs)
} else {
// Vector with one element
@@ -1496,12 +1508,13 @@ impl<'a> Parser<'a> {
prior_type_ascription: self.last_type_ascription,
});
(lo.to(self.prev_token.span), ExprKind::MacCall(mac))
- } else if self.check(&token::OpenDelim(Delimiter::Brace)) &&
- let Some(expr) = self.maybe_parse_struct_expr(&qself, &path) {
- if qself.is_some() {
- self.sess.gated_spans.gate(sym::more_qualified_paths, path.span);
- }
- return expr;
+ } else if self.check(&token::OpenDelim(Delimiter::Brace))
+ && let Some(expr) = self.maybe_parse_struct_expr(&qself, &path)
+ {
+ if qself.is_some() {
+ self.sess.gated_spans.gate(sym::more_qualified_paths, path.span);
+ }
+ return expr;
} else {
(path.span, ExprKind::Path(qself, path))
};
@@ -1534,15 +1547,16 @@ impl<'a> Parser<'a> {
&& (matches!(self.token.kind, token::CloseDelim(_) | token::Comma)
|| self.token.is_op())
{
- let lit = self.recover_unclosed_char(label_.ident, |self_| {
- self_.sess.create_err(UnexpectedTokenAfterLabel {
- span: self_.token.span,
- remove_label: None,
- enclose_in_block: None,
- })
- });
+ let (lit, _) =
+ self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| {
+ self_.sess.create_err(UnexpectedTokenAfterLabel {
+ span: self_.token.span,
+ remove_label: None,
+ enclose_in_block: None,
+ })
+ });
consume_colon = false;
- Ok(self.mk_expr(lo, ExprKind::Lit(lit.token_lit)))
+ Ok(self.mk_expr(lo, ExprKind::Lit(lit)))
} else if !ate_colon
&& (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))
{
@@ -1581,7 +1595,7 @@ impl<'a> Parser<'a> {
vis.0
};
- // Suggestion involves adding a (as of time of writing this, unstable) labeled block.
+ // Suggestion involves adding a labeled block.
//
// If there are no breaks that may use this label, suggest removing the label and
// recover to the unmodified expression.
@@ -1617,12 +1631,13 @@ impl<'a> Parser<'a> {
Ok(expr)
}
- /// Emit an error when a char is parsed as a lifetime because of a missing quote
- pub(super) fn recover_unclosed_char(
+ /// Emit an error when a char is parsed as a lifetime because of a missing quote.
+ pub(super) fn recover_unclosed_char<L>(
&self,
lifetime: Ident,
+ mk_lit_char: impl FnOnce(Symbol, Span) -> L,
err: impl FnOnce(&Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>,
- ) -> ast::MetaItemLit {
+ ) -> L {
if let Some(mut diag) =
self.sess.span_diagnostic.steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar)
{
@@ -1644,11 +1659,7 @@ impl<'a> Parser<'a> {
.emit();
}
let name = lifetime.without_first_quote().name;
- ast::MetaItemLit {
- token_lit: token::Lit::new(token::LitKind::Char, name, None),
- kind: ast::LitKind::Char(name.as_str().chars().next().unwrap_or('_')),
- span: lifetime.span,
- }
+ mk_lit_char(name, lifetime.span)
}
/// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead.
@@ -1703,10 +1714,10 @@ impl<'a> Parser<'a> {
fn parse_break_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
let mut label = self.eat_label();
- let kind = if label.is_some() && self.token == token::Colon {
+ let kind = if self.token == token::Colon && let Some(label) = label.take() {
// The value expression can be a labeled loop, see issue #86948, e.g.:
// `loop { break 'label: loop { break 'label 42; }; }`
- let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?;
+ let lexpr = self.parse_labeled_expr(label, true)?;
self.sess.emit_err(LabeledLoopInBreak {
span: lexpr.span,
sub: WrapExpressionInParentheses {
@@ -1718,8 +1729,8 @@ impl<'a> Parser<'a> {
} else if self.token != token::OpenDelim(Delimiter::Brace)
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
{
- let expr = self.parse_expr_opt()?;
- if let Some(expr) = &expr {
+ let mut expr = self.parse_expr_opt()?;
+ if let Some(expr) = &mut expr {
if label.is_some()
&& matches!(
expr.kind,
@@ -1737,7 +1748,19 @@ impl<'a> Parser<'a> {
BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span),
);
}
+
+ // Recover `break label aaaaa`
+ if self.may_recover()
+ && let ExprKind::Path(None, p) = &expr.kind
+ && let [segment] = &*p.segments
+ && let &ast::PathSegment { ident, args: None, .. } = segment
+ && let Some(next) = self.parse_expr_opt()?
+ {
+ label = Some(self.recover_ident_into_label(ident));
+ *expr = next;
+ }
}
+
expr
} else {
None
@@ -1746,6 +1769,23 @@ impl<'a> Parser<'a> {
self.maybe_recover_from_bad_qpath(expr)
}
+ /// Parse `"continue" label?`.
+ fn parse_continue_expr(&mut self, lo: Span) -> PResult<'a, P<Expr>> {
+ let mut label = self.eat_label();
+
+ // Recover `continue label` -> `continue 'label`
+ if self.may_recover()
+ && label.is_none()
+ && let Some((ident, _)) = self.token.ident()
+ {
+ self.bump();
+ label = Some(self.recover_ident_into_label(ident));
+ }
+
+ let kind = ExprKind::Continue(label);
+ Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
+ }
+
/// Parse `"yield" expr?`.
fn parse_yield_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
@@ -1764,8 +1804,8 @@ impl<'a> Parser<'a> {
Some(lit) => match lit.kind {
ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit {
style,
- symbol: lit.token_lit.symbol,
- suffix: lit.token_lit.suffix,
+ symbol: lit.symbol,
+ suffix: lit.suffix,
span: lit.span,
symbol_unescaped,
}),
@@ -1775,7 +1815,23 @@ impl<'a> Parser<'a> {
}
}
- fn handle_missing_lit(&mut self) -> PResult<'a, MetaItemLit> {
+ pub(crate) fn mk_token_lit_char(name: Symbol, span: Span) -> (token::Lit, Span) {
+ (token::Lit { symbol: name, suffix: None, kind: token::Char }, span)
+ }
+
+ fn mk_meta_item_lit_char(name: Symbol, span: Span) -> MetaItemLit {
+ ast::MetaItemLit {
+ symbol: name,
+ suffix: None,
+ kind: ast::LitKind::Char(name.as_str().chars().next().unwrap_or('_')),
+ span,
+ }
+ }
+
+ fn handle_missing_lit<L>(
+ &mut self,
+ mk_lit_char: impl FnOnce(Symbol, Span) -> L,
+ ) -> PResult<'a, L> {
if let token::Interpolated(inner) = &self.token.kind {
let expr = match inner.as_ref() {
token::NtExpr(expr) => Some(expr),
@@ -1799,7 +1855,7 @@ impl<'a> Parser<'a> {
// On an error path, eagerly consider a lifetime to be an unclosed character lit
if self.token.is_lifetime() {
let lt = self.expect_lifetime();
- Ok(self.recover_unclosed_char(lt.ident, err))
+ Ok(self.recover_unclosed_char(lt.ident, mk_lit_char, err))
} else {
Err(err(self))
}
@@ -1808,11 +1864,13 @@ impl<'a> Parser<'a> {
pub(super) fn parse_token_lit(&mut self) -> PResult<'a, (token::Lit, Span)> {
self.parse_opt_token_lit()
.ok_or(())
- .or_else(|()| self.handle_missing_lit().map(|lit| (lit.token_lit, lit.span)))
+ .or_else(|()| self.handle_missing_lit(Parser::mk_token_lit_char))
}
pub(super) fn parse_meta_item_lit(&mut self) -> PResult<'a, MetaItemLit> {
- self.parse_opt_meta_item_lit().ok_or(()).or_else(|()| self.handle_missing_lit())
+ self.parse_opt_meta_item_lit()
+ .ok_or(())
+ .or_else(|()| self.handle_missing_lit(Parser::mk_meta_item_lit_char))
}
fn recover_after_dot(&mut self) -> Option<Token> {
@@ -2015,7 +2073,7 @@ impl<'a> Parser<'a> {
});
}
- let (attrs, blk) = self.parse_block_common(lo, blk_mode)?;
+ let (attrs, blk) = self.parse_block_common(lo, blk_mode, true)?;
Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs))
}
@@ -2041,6 +2099,8 @@ impl<'a> Parser<'a> {
ClosureBinder::NotPresent
};
+ let constness = self.parse_constness(Case::Sensitive);
+
let movability =
if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
@@ -2087,6 +2147,7 @@ impl<'a> Parser<'a> {
ExprKind::Closure(Box::new(ast::Closure {
binder,
capture_clause,
+ constness,
asyncness,
movability,
fn_decl,
@@ -3013,9 +3074,29 @@ impl<'a> Parser<'a> {
false
}
+ /// Converts an ident into 'label and emits an "expected a label, found an identifier" error.
+ fn recover_ident_into_label(&mut self, ident: Ident) -> Label {
+ // Convert `label` -> `'label`,
+ // so that nameres doesn't complain about non-existing label
+ let label = format!("'{}", ident.name);
+ let ident = Ident { name: Symbol::intern(&label), span: ident.span };
+
+ self.struct_span_err(ident.span, "expected a label, found an identifier")
+ .span_suggestion(
+ ident.span,
+ "labels start with a tick",
+ label,
+ Applicability::MachineApplicable,
+ )
+ .emit();
+
+ Label { ident }
+ }
+
/// Parses `ident (COLON expr)?`.
fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
let attrs = self.parse_outer_attributes()?;
+ self.recover_diff_marker();
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
let lo = this.token.span;
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index fa75670b2..8ba811715 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -1,11 +1,20 @@
+use crate::errors::{WhereClauseBeforeTupleStructBody, WhereClauseBeforeTupleStructBodySugg};
+
use super::{ForceCollect, Parser, TrailingToken};
+use ast::token::Delimiter;
use rustc_ast::token;
use rustc_ast::{
self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, TyKind, WhereClause,
};
use rustc_errors::{Applicability, PResult};
-use rustc_span::symbol::kw;
+use rustc_span::symbol::{kw, Ident};
+use rustc_span::Span;
+
+enum PredicateOrStructBody {
+ Predicate(ast::WherePredicate),
+ StructBody(Vec<ast::FieldDef>),
+}
impl<'a> Parser<'a> {
/// Parses bounds of a lifetime parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`.
@@ -240,23 +249,39 @@ impl<'a> Parser<'a> {
})
}
- /// Parses an optional where-clause and places it in `generics`.
+ /// Parses an optional where-clause.
///
/// ```ignore (only-for-syntax-highlight)
/// where T : Trait<U, V> + 'b, 'a : 'b
/// ```
pub(super) fn parse_where_clause(&mut self) -> PResult<'a, WhereClause> {
+ self.parse_where_clause_common(None).map(|(clause, _)| clause)
+ }
+
+ pub(super) fn parse_struct_where_clause(
+ &mut self,
+ struct_name: Ident,
+ body_insertion_point: Span,
+ ) -> PResult<'a, (WhereClause, Option<Vec<ast::FieldDef>>)> {
+ self.parse_where_clause_common(Some((struct_name, body_insertion_point)))
+ }
+
+ fn parse_where_clause_common(
+ &mut self,
+ struct_: Option<(Ident, Span)>,
+ ) -> PResult<'a, (WhereClause, Option<Vec<ast::FieldDef>>)> {
let mut where_clause = WhereClause {
has_where_token: false,
predicates: Vec::new(),
span: self.prev_token.span.shrink_to_hi(),
};
+ let mut tuple_struct_body = None;
if !self.eat_keyword(kw::Where) {
- return Ok(where_clause);
+ return Ok((where_clause, None));
}
where_clause.has_where_token = true;
- let lo = self.prev_token.span;
+ let where_lo = self.prev_token.span;
// We are considering adding generics to the `where` keyword as an alternative higher-rank
// parameter syntax (as in `where<'a>` or `where<T>`. To avoid that being a breaking
@@ -272,7 +297,8 @@ impl<'a> Parser<'a> {
}
loop {
- let lo = self.token.span;
+ let where_sp = where_lo.to(self.prev_token.span);
+ let pred_lo = self.token.span;
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
let lifetime = self.expect_lifetime();
// Bounds starting with a colon are mandatory, but possibly empty.
@@ -280,13 +306,21 @@ impl<'a> Parser<'a> {
let bounds = self.parse_lt_param_bounds();
where_clause.predicates.push(ast::WherePredicate::RegionPredicate(
ast::WhereRegionPredicate {
- span: lo.to(self.prev_token.span),
+ span: pred_lo.to(self.prev_token.span),
lifetime,
bounds,
},
));
} else if self.check_type() {
- where_clause.predicates.push(self.parse_ty_where_predicate()?);
+ match self.parse_ty_where_predicate_or_recover_tuple_struct_body(
+ struct_, pred_lo, where_sp,
+ )? {
+ PredicateOrStructBody::Predicate(pred) => where_clause.predicates.push(pred),
+ PredicateOrStructBody::StructBody(body) => {
+ tuple_struct_body = Some(body);
+ break;
+ }
+ }
} else {
break;
}
@@ -297,7 +331,7 @@ impl<'a> Parser<'a> {
if self.eat_keyword_noexpect(kw::Where) {
let msg = "cannot define duplicate `where` clauses on an item";
let mut err = self.struct_span_err(self.token.span, msg);
- err.span_label(lo, "previous `where` clause starts here");
+ err.span_label(pred_lo, "previous `where` clause starts here");
err.span_suggestion_verbose(
prev_token.shrink_to_hi().to(self.prev_token.span),
"consider joining the two `where` clauses into one",
@@ -310,8 +344,72 @@ impl<'a> Parser<'a> {
}
}
- where_clause.span = lo.to(self.prev_token.span);
- Ok(where_clause)
+ where_clause.span = where_lo.to(self.prev_token.span);
+ Ok((where_clause, tuple_struct_body))
+ }
+
+ fn parse_ty_where_predicate_or_recover_tuple_struct_body(
+ &mut self,
+ struct_: Option<(Ident, Span)>,
+ pred_lo: Span,
+ where_sp: Span,
+ ) -> PResult<'a, PredicateOrStructBody> {
+ let mut snapshot = None;
+
+ if let Some(struct_) = struct_
+ && self.may_recover()
+ && self.token.kind == token::OpenDelim(Delimiter::Parenthesis)
+ {
+ snapshot = Some((struct_, self.create_snapshot_for_diagnostic()));
+ };
+
+ match self.parse_ty_where_predicate() {
+ Ok(pred) => Ok(PredicateOrStructBody::Predicate(pred)),
+ Err(type_err) => {
+ let Some(((struct_name, body_insertion_point), mut snapshot)) = snapshot else {
+ return Err(type_err);
+ };
+
+ // Check if we might have encountered an out of place tuple struct body.
+ match snapshot.parse_tuple_struct_body() {
+ // Since we don't know the exact reason why we failed to parse the
+ // predicate (we might have stumbled upon something bogus like `(T): ?`),
+ // employ a simple heuristic to weed out some pathological cases:
+ // Look for a semicolon (strong indicator) or anything that might mark
+ // the end of the item (weak indicator) following the body.
+ Ok(body)
+ if matches!(snapshot.token.kind, token::Semi | token::Eof)
+ || snapshot.token.can_begin_item() =>
+ {
+ type_err.cancel();
+
+ let body_sp = pred_lo.to(snapshot.prev_token.span);
+ let map = self.sess.source_map();
+
+ self.sess.emit_err(WhereClauseBeforeTupleStructBody {
+ span: where_sp,
+ name: struct_name.span,
+ body: body_sp,
+ sugg: map.span_to_snippet(body_sp).ok().map(|body| {
+ WhereClauseBeforeTupleStructBodySugg {
+ left: body_insertion_point.shrink_to_hi(),
+ snippet: body,
+ right: map.end_point(where_sp).to(body_sp),
+ }
+ }),
+ });
+
+ self.restore_snapshot(snapshot);
+ Ok(PredicateOrStructBody::StructBody(body))
+ }
+ Ok(_) => Err(type_err),
+ Err(body_err) => {
+ body_err.cancel();
+ Err(type_err)
+ }
+ }
+ }
+ }
}
fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 03f25392a..53680a82b 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -3,6 +3,7 @@ use crate::errors::{DocCommentDoesNotDocumentAnything, UseEmptyBlockNotSemi};
use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken};
+use crate::errors::FnTypoWithImpl;
use rustc_ast::ast::*;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, TokenKind};
@@ -21,10 +22,8 @@ use rustc_span::lev_distance::lev_distance;
use rustc_span::source_map::{self, Span};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::DUMMY_SP;
-use std::convert::TryFrom;
use std::mem;
use thin_vec::ThinVec;
-use tracing::debug;
impl<'a> Parser<'a> {
/// Parses a source module as a crate. This is the main entry point for the parser.
@@ -99,7 +98,9 @@ impl<'a> Parser<'a> {
fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
) -> PResult<'a, Option<Item>> {
+ self.recover_diff_marker();
let attrs = self.parse_outer_attributes()?;
+ self.recover_diff_marker();
self.parse_item_common(attrs, true, false, fn_parse_mode, force_collect)
}
@@ -705,11 +706,12 @@ impl<'a> Parser<'a> {
if self.recover_doc_comment_before_brace() {
continue;
}
+ self.recover_diff_marker();
match parse_item(self) {
Ok(None) => {
- let is_unnecessary_semicolon = !items.is_empty()
+ let mut is_unnecessary_semicolon = !items.is_empty()
// When the close delim is `)` in a case like the following, `token.kind` is expected to be `token::CloseDelim(Delimiter::Parenthesis)`,
- // but the actual `token.kind` is `token::CloseDelim(Delimiter::Bracket)`.
+ // but the actual `token.kind` is `token::CloseDelim(Delimiter::Brace)`.
// 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.
@@ -728,7 +730,13 @@ impl<'a> Parser<'a> {
.span_to_snippet(self.prev_token.span)
.map_or(false, |snippet| snippet == "}")
&& self.token.kind == token::Semi;
- let semicolon_span = self.token.span;
+ let mut semicolon_span = self.token.span;
+ if !is_unnecessary_semicolon {
+ // #105369, Detect spurious `;` before assoc fn body
+ is_unnecessary_semicolon = self.token == token::OpenDelim(Delimiter::Brace)
+ && self.prev_token.kind == 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);
@@ -1034,8 +1042,11 @@ impl<'a> Parser<'a> {
/// USE_TREE_LIST = Ø | (USE_TREE `,`)* USE_TREE [`,`]
/// ```
fn parse_use_tree_list(&mut self) -> PResult<'a, Vec<(UseTree, ast::NodeId)>> {
- self.parse_delim_comma_seq(Delimiter::Brace, |p| Ok((p.parse_use_tree()?, DUMMY_NODE_ID)))
- .map(|(r, _)| r)
+ self.parse_delim_comma_seq(Delimiter::Brace, |p| {
+ p.recover_diff_marker();
+ Ok((p.parse_use_tree()?, DUMMY_NODE_ID))
+ })
+ .map(|(r, _)| r)
}
fn parse_rename(&mut self) -> PResult<'a, Option<Ident>> {
@@ -1374,7 +1385,9 @@ impl<'a> Parser<'a> {
}
fn parse_enum_variant(&mut self) -> PResult<'a, Option<Variant>> {
+ self.recover_diff_marker();
let variant_attrs = self.parse_outer_attributes()?;
+ self.recover_diff_marker();
self.collect_tokens_trailing_token(
variant_attrs,
ForceCollect::No,
@@ -1441,8 +1454,16 @@ impl<'a> Parser<'a> {
// struct.
let vdata = if self.token.is_keyword(kw::Where) {
- generics.where_clause = self.parse_where_clause()?;
- if self.eat(&token::Semi) {
+ let tuple_struct_body;
+ (generics.where_clause, tuple_struct_body) =
+ self.parse_struct_where_clause(class_name, 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(&token::Semi) {
// If we see a: `struct Foo<T> where T: Copy;` style decl.
VariantData::Unit(DUMMY_NODE_ID)
} else {
@@ -1562,15 +1583,38 @@ impl<'a> Parser<'a> {
Ok((fields, recovered))
}
- fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<FieldDef>> {
+ pub(super) fn parse_tuple_struct_body(&mut self) -> PResult<'a, Vec<FieldDef>> {
// This is the case where we find `struct Foo<T>(T) where T: Copy;`
// Unit like structs are handled in parse_item_struct function
self.parse_paren_comma_seq(|p| {
let attrs = p.parse_outer_attributes()?;
p.collect_tokens_trailing_token(attrs, ForceCollect::No, |p, attrs| {
+ let mut snapshot = None;
+ if p.is_diff_marker(&TokenKind::BinOp(token::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 = p.parse_visibility(FollowedByType::Yes)?;
- let ty = p.parse_ty()?;
+ let vis = match p.parse_visibility(FollowedByType::Yes) {
+ Ok(vis) => vis,
+ Err(err) => {
+ if let Some(ref mut snapshot) = snapshot {
+ snapshot.recover_diff_marker();
+ }
+ return Err(err);
+ }
+ };
+ let ty = match p.parse_ty() {
+ Ok(ty) => ty,
+ Err(err) => {
+ if let Some(ref mut snapshot) = snapshot {
+ snapshot.recover_diff_marker();
+ }
+ return Err(err);
+ }
+ };
Ok((
FieldDef {
@@ -1591,7 +1635,9 @@ impl<'a> Parser<'a> {
/// Parses an element of a struct declaration.
fn parse_field_def(&mut self, adt_ty: &str) -> PResult<'a, FieldDef> {
+ self.recover_diff_marker();
let attrs = self.parse_outer_attributes()?;
+ self.recover_diff_marker();
self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| {
let lo = this.token.span;
let vis = this.parse_visibility(FollowedByType::No)?;
@@ -2126,11 +2172,26 @@ impl<'a> Parser<'a> {
vis: &Visibility,
case: Case,
) -> PResult<'a, (Ident, FnSig, Generics, Option<P<Block>>)> {
+ let fn_span = self.token.span;
let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn`
let ident = self.parse_ident()?; // `foo`
let mut generics = self.parse_generics()?; // `<'a, T, ...>`
- let decl =
- self.parse_fn_decl(fn_parse_mode.req_name, AllowPlus::Yes, RecoverReturnSign::Yes)?; // `(p: u8, ...)`
+ let decl = match self.parse_fn_decl(
+ fn_parse_mode.req_name,
+ 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.sess.create_err(FnTypoWithImpl { fn_span }));
+ } else {
+ return Err(old_err);
+ }
+ }
+ };
generics.where_clause = self.parse_where_clause()?; // `where T: Ord`
let mut sig_hi = self.prev_token.span;
@@ -2161,7 +2222,8 @@ impl<'a> Parser<'a> {
*sig_hi = self.prev_token.span;
(AttrVec::new(), None)
} else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
- self.parse_inner_attrs_and_block().map(|(attrs, body)| (attrs, Some(body)))?
+ self.parse_block_common(self.token.span, BlockCheckMode::Default, false)
+ .map(|(attrs, body)| (attrs, Some(body)))?
} else if self.token.kind == token::Eq {
// Recover `fn foo() = $expr;`.
self.bump(); // `=`
@@ -2403,10 +2465,11 @@ impl<'a> Parser<'a> {
}
/// Parses the parameter list of a function, including the `(` and `)` delimiters.
- fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, Vec<Param>> {
+ pub(super) fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, Vec<Param>> {
let mut first_param = true;
// Parse the arguments, starting out with `self` being allowed...
let (mut params, _) = self.parse_paren_comma_seq(|p| {
+ p.recover_diff_marker();
let param = p.parse_param_general(req_name, first_param).or_else(|mut e| {
e.emit();
let lo = p.prev_token.span;
@@ -2444,7 +2507,6 @@ impl<'a> Parser<'a> {
};
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();
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index bebb01266..ffb23b50a 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -25,7 +25,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::util::case::Case;
use rustc_ast::AttrId;
use rustc_ast::DUMMY_NODE_ID;
-use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, DelimArgs, Extern};
+use rustc_ast::{self as ast, AnonConst, AttrStyle, Const, DelimArgs, Extern};
use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, MacDelimiter, Mutability, StrLit};
use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
use rustc_ast_pretty::pprust;
@@ -317,7 +317,7 @@ impl TokenCursor {
// required to wrap the text. E.g.
// - `abc d` is wrapped as `r"abc d"` (num_of_hashes = 0)
// - `abc "d"` is wrapped as `r#"abc "d""#` (num_of_hashes = 1)
- // - `abc "##d##"` is wrapped as `r###"abc "d""###` (num_of_hashes = 3)
+ // - `abc "##d##"` is wrapped as `r###"abc ##"d"##"###` (num_of_hashes = 3)
let mut num_of_hashes = 0;
let mut count = 0;
for ch in data.as_str().chars() {
@@ -542,9 +542,9 @@ impl<'a> Parser<'a> {
}
}
- /// Expect next token to be edible or inedible token. If edible,
+ /// Expect next token to be edible or inedible token. If edible,
/// then consume it; if inedible, then return without consuming
- /// anything. Signal a fatal error if next token is unexpected.
+ /// anything. Signal a fatal error if next token is unexpected.
pub fn expect_one_of(
&mut self,
edible: &[TokenKind],
@@ -736,6 +736,16 @@ impl<'a> Parser<'a> {
self.check_or_expected(self.token.can_begin_const_arg(), TokenType::Const)
}
+ fn check_const_closure(&self) -> bool {
+ self.is_keyword_ahead(0, &[kw::Const])
+ && self.look_ahead(1, |t| match &t.kind {
+ token::Ident(kw::Move | kw::Static | kw::Async, _)
+ | token::OrOr
+ | token::BinOp(token::Or) => true,
+ _ => false,
+ })
+ }
+
fn check_inline_const(&self, dist: usize) -> bool {
self.is_keyword_ahead(dist, &[kw::Const])
&& self.look_ahead(dist + 1, |t| match &t.kind {
@@ -1217,11 +1227,7 @@ impl<'a> Parser<'a> {
value: self.mk_expr(blk.span, ExprKind::Block(blk, None)),
};
let blk_span = anon_const.value.span;
- Ok(self.mk_expr_with_attrs(
- span.to(blk_span),
- ExprKind::ConstBlock(anon_const),
- AttrVec::from(attrs),
- ))
+ Ok(self.mk_expr_with_attrs(span.to(blk_span), ExprKind::ConstBlock(anon_const), attrs))
}
/// Parses mutability (`mut` or nothing).
@@ -1503,6 +1509,10 @@ impl<'a> Parser<'a> {
pub fn clear_expected_tokens(&mut self) {
self.expected_tokens.clear();
}
+
+ pub fn approx_token_stream_pos(&self) -> usize {
+ self.token_cursor.num_next_calls
+ }
}
pub(crate) fn make_unclosed_delims_error(
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index cbeec951e..e73a17ced 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -411,16 +411,20 @@ impl<'a> Parser<'a> {
{
// Recover a `'a` as a `'a'` literal
let lt = self.expect_lifetime();
- let lit = self.recover_unclosed_char(lt.ident, |self_| {
- let expected = expected.unwrap_or("pattern");
- let msg =
- format!("expected {}, found {}", expected, super::token_descr(&self_.token));
+ let (lit, _) =
+ self.recover_unclosed_char(lt.ident, Parser::mk_token_lit_char, |self_| {
+ let expected = expected.unwrap_or("pattern");
+ let msg = format!(
+ "expected {}, found {}",
+ expected,
+ super::token_descr(&self_.token)
+ );
- let mut err = self_.struct_span_err(self_.token.span, &msg);
- err.span_label(self_.token.span, format!("expected {}", expected));
- err
- });
- PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit.token_lit)))
+ let mut err = self_.struct_span_err(self_.token.span, &msg);
+ err.span_label(self_.token.span, format!("expected {}", expected));
+ err
+ });
+ PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit)))
} else {
// Try to parse everything else as literal with optional minus
match self.parse_literal_maybe_minus() {
@@ -465,7 +469,7 @@ impl<'a> Parser<'a> {
/// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`.
///
/// Allowed binding patterns generated by `binding ::= ref? mut? $ident @ $pat_rhs`
- /// should already have been parsed by now at this point,
+ /// should already have been parsed by now at this point,
/// if the next token is `@` then we can try to parse the more general form.
///
/// Consult `parse_pat_ident` for the `binding` grammar.
@@ -487,17 +491,6 @@ impl<'a> Parser<'a> {
if let PatKind::Ident(_, _, sub @ None) = &mut rhs.kind {
// The user inverted the order, so help them fix that.
- let mut applicability = Applicability::MachineApplicable;
- // FIXME(bindings_after_at): Remove this code when stabilizing the feature.
- lhs.walk(&mut |p| match p.kind {
- // `check_match` is unhappy if the subpattern has a binding anywhere.
- PatKind::Ident(..) => {
- applicability = Applicability::MaybeIncorrect;
- false // Short-circuit.
- }
- _ => true,
- });
-
let lhs_span = lhs.span;
// Move the LHS into the RHS as a subpattern.
// The RHS is now the full pattern.
@@ -506,7 +499,12 @@ impl<'a> Parser<'a> {
self.struct_span_err(sp, "pattern on wrong side of `@`")
.span_label(lhs_span, "pattern on the left, should be on the right")
.span_label(rhs.span, "binding on the right, should be on the left")
- .span_suggestion(sp, "switch the order", pprust::pat_to_string(&rhs), applicability)
+ .span_suggestion(
+ sp,
+ "switch the order",
+ pprust::pat_to_string(&rhs),
+ Applicability::MachineApplicable,
+ )
.emit();
} else {
// The special case above doesn't apply so we may have e.g. `A(x) @ B(y)`.
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 2d432e3f5..5333d3b85 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -277,8 +277,7 @@ impl<'a> Parser<'a> {
if let Some(arg) = args
.iter()
.rev()
- .skip_while(|arg| matches!(arg, AngleBracketedArg::Constraint(_)))
- .next()
+ .find(|arg| !matches!(arg, AngleBracketedArg::Constraint(_)))
{
err.span_suggestion_verbose(
arg.span().shrink_to_hi(),
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 42197e637..4ff9927aa 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -164,7 +164,10 @@ impl<'a> Parser<'a> {
// Perform this outside of the `collect_tokens_trailing_token` closure,
// since our outer attributes do not apply to this part of the expression
let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
- this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr))
+ this.parse_assoc_expr_with(
+ 0,
+ LhsExpr::AlreadyParsed { expr, starts_statement: true },
+ )
})?;
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
} else {
@@ -198,7 +201,10 @@ impl<'a> Parser<'a> {
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
let e = self.maybe_recover_from_bad_qpath(e)?;
let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?;
- let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
+ let e = self.parse_assoc_expr_with(
+ 0,
+ LhsExpr::AlreadyParsed { expr: e, starts_statement: false },
+ )?;
StmtKind::Expr(e)
};
Ok(self.mk_stmt(lo.to(hi), kind))
@@ -498,7 +504,7 @@ impl<'a> Parser<'a> {
/// Parses a block. Inner attributes are allowed.
pub(super) fn parse_inner_attrs_and_block(&mut self) -> PResult<'a, (AttrVec, P<Block>)> {
- self.parse_block_common(self.token.span, BlockCheckMode::Default)
+ self.parse_block_common(self.token.span, BlockCheckMode::Default, true)
}
/// Parses a block. Inner attributes are allowed.
@@ -506,16 +512,23 @@ impl<'a> Parser<'a> {
&mut self,
lo: Span,
blk_mode: BlockCheckMode,
+ can_be_struct_literal: bool,
) -> PResult<'a, (AttrVec, P<Block>)> {
maybe_whole!(self, NtBlock, |x| (AttrVec::new(), x));
+ let maybe_ident = self.prev_token.clone();
self.maybe_recover_unexpected_block_label();
if !self.eat(&token::OpenDelim(Delimiter::Brace)) {
return self.error_block_no_opening_brace();
}
let attrs = self.parse_inner_attributes()?;
- let tail = match self.maybe_suggest_struct_literal(lo, blk_mode) {
+ let tail = match self.maybe_suggest_struct_literal(
+ lo,
+ blk_mode,
+ maybe_ident,
+ can_be_struct_literal,
+ ) {
Some(tail) => tail?,
None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
};
@@ -531,13 +544,23 @@ impl<'a> Parser<'a> {
recover: AttemptLocalParseRecovery,
) -> PResult<'a, P<Block>> {
let mut stmts = vec![];
+ let mut snapshot = None;
while !self.eat(&token::CloseDelim(Delimiter::Brace)) {
if self.token == token::Eof {
break;
}
+ if self.is_diff_marker(&TokenKind::BinOp(token::Shl), &TokenKind::Lt) {
+ // Account for `<<<<<<<` diff markers. We can't proactively error here because
+ // that can be a valid path start, so we snapshot and reparse only we've
+ // encountered another parse error.
+ snapshot = Some(self.create_snapshot_for_diagnostic());
+ }
let stmt = match self.parse_full_stmt(recover) {
Err(mut err) if recover.yes() => {
self.maybe_annotate_with_ascription(&mut err, false);
+ if let Some(ref mut snapshot) = snapshot {
+ snapshot.recover_diff_marker();
+ }
err.emit();
self.recover_stmt_(SemiColonMode::Ignore, BlockMode::Ignore);
Some(self.mk_stmt_err(self.token.span))
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index b7206b576..867974672 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -1,8 +1,9 @@
use super::{Parser, PathStyle, TokenType};
-use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg};
+use crate::errors::{ExpectedFnPathFoundFnKeyword, FnPtrWithGenerics, FnPtrWithGenericsSugg};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
+use ast::DUMMY_NODE_ID;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::util::case::Case;
@@ -12,7 +13,9 @@ use rustc_ast::{
};
use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
use rustc_span::source_map::Span;
-use rustc_span::symbol::{kw, sym};
+use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::Symbol;
+use thin_vec::thin_vec;
/// Any `?` or `~const` modifiers that appear at the start of a bound.
struct BoundModifiers {
@@ -501,7 +504,7 @@ impl<'a> Parser<'a> {
self.bump_with((dyn_tok, dyn_tok_sp));
}
let ty = self.parse_ty_no_plus()?;
- Ok(TyKind::Rptr(opt_lifetime, MutTy { ty, mutbl }))
+ Ok(TyKind::Ref(opt_lifetime, MutTy { ty, mutbl }))
}
// Parses the `typeof(EXPR)`.
@@ -613,6 +616,25 @@ impl<'a> Parser<'a> {
/// Parses an `impl B0 + ... + Bn` type.
fn parse_impl_ty(&mut self, impl_dyn_multi: &mut bool) -> PResult<'a, TyKind> {
// Always parse bounds greedily for better error recovery.
+ if self.token.is_lifetime() {
+ self.look_ahead(1, |t| {
+ if let token::Ident(symname, _) = t.kind {
+ // parse pattern with "'a Sized" we're supposed to give suggestion like
+ // "'a + Sized"
+ self.struct_span_err(
+ self.token.span,
+ &format!("expected `+` between lifetime and {}", symname),
+ )
+ .span_suggestion_verbose(
+ self.token.span.shrink_to_hi(),
+ "add `+`",
+ " +",
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ }
+ })
+ }
let bounds = self.parse_generic_bounds(None)?;
*impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus);
Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds))
@@ -705,12 +727,15 @@ impl<'a> Parser<'a> {
let mut bounds = Vec::new();
let mut negative_bounds = Vec::new();
+ // In addition to looping while we find generic bounds:
+ // We continue even if we find a keyword. This is necessary for error recovery on,
+ // for example, `impl fn()`. The only keyword that can go after generic bounds is
+ // `where`, so stop if it's it.
+ // We also continue if we find types (not traits), again for error recovery.
while self.can_begin_bound()
- // Continue even if we find a keyword.
- // This is necessary for error recover on, for example, `impl fn()`.
- //
- // The only keyword that can go after generic bounds is `where`, so stop if it's it.
- || (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where))
+ || (self.may_recover()
+ && (self.token.can_begin_type()
+ || (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where))))
{
if self.token.is_keyword(kw::Dyn) {
// Account for `&dyn Trait + dyn Other`.
@@ -911,8 +936,50 @@ impl<'a> Parser<'a> {
has_parens: bool,
modifiers: BoundModifiers,
) -> PResult<'a, GenericBound> {
- let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
- let path = self.parse_path(PathStyle::Type)?;
+ let mut lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+ let mut path = if self.token.is_keyword(kw::Fn)
+ && self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
+ && let Some(path) = self.recover_path_from_fn()
+ {
+ path
+ } else if !self.token.is_path_start() && self.token.can_begin_type() {
+ let ty = self.parse_ty_no_plus()?;
+ // Instead of finding a path (a trait), we found a type.
+ let mut err = self.struct_span_err(ty.span, "expected a trait, found type");
+
+ // If we can recover, try to extract a path from the type. Note
+ // that we do not use the try operator when parsing the type because
+ // if it fails then we get a parser error which we don't want (we're trying
+ // to recover from errors, not make more).
+ let path = if self.may_recover()
+ && matches!(ty.kind, TyKind::Ptr(..) | TyKind::Ref(..))
+ && let TyKind::Path(_, path) = &ty.peel_refs().kind {
+ // Just get the indirection part of the type.
+ let span = ty.span.until(path.span);
+
+ err.span_suggestion_verbose(
+ span,
+ "consider removing the indirection",
+ "",
+ Applicability::MaybeIncorrect,
+ );
+
+ path.clone()
+ } else {
+ return Err(err);
+ };
+
+ err.emit();
+
+ path
+ } else {
+ self.parse_path(PathStyle::Type)?
+ };
+
+ if self.may_recover() && self.token == TokenKind::OpenDelim(Delimiter::Parenthesis) {
+ self.recover_fn_trait_with_lifetime_params(&mut path, &mut lifetime_defs)?;
+ }
+
if has_parens {
if self.token.is_like_plus() {
// Someone has written something like `&dyn (Trait + Other)`. The correct code
@@ -941,6 +1008,38 @@ impl<'a> Parser<'a> {
Ok(GenericBound::Trait(poly_trait, modifier))
}
+ // recovers a `Fn(..)` parenthesized-style path from `fn(..)`
+ fn recover_path_from_fn(&mut self) -> Option<ast::Path> {
+ let fn_token_span = self.token.span;
+ self.bump();
+ let args_lo = self.token.span;
+ let snapshot = self.create_snapshot_for_diagnostic();
+ match self.parse_fn_decl(|_| false, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) {
+ Ok(decl) => {
+ self.sess.emit_err(ExpectedFnPathFoundFnKeyword { fn_token_span });
+ Some(ast::Path {
+ span: fn_token_span.to(self.prev_token.span),
+ segments: thin_vec![ast::PathSegment {
+ ident: Ident::new(Symbol::intern("Fn"), fn_token_span),
+ id: DUMMY_NODE_ID,
+ args: Some(P(ast::GenericArgs::Parenthesized(ast::ParenthesizedArgs {
+ span: args_lo.to(self.prev_token.span),
+ inputs: decl.inputs.iter().map(|a| a.ty.clone()).collect(),
+ inputs_span: args_lo.until(decl.output.span()),
+ output: decl.output.clone(),
+ }))),
+ }],
+ tokens: None,
+ })
+ }
+ Err(diag) => {
+ diag.cancel();
+ self.restore_snapshot(snapshot);
+ None
+ }
+ }
+ }
+
/// Optionally parses `for<$generic_params>`.
pub(super) fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<GenericParam>> {
if self.eat_keyword(kw::For) {
@@ -955,6 +1054,92 @@ impl<'a> Parser<'a> {
}
}
+ /// Recover from `Fn`-family traits (Fn, FnMut, FnOnce) with lifetime arguments
+ /// (e.g. `FnOnce<'a>(&'a str) -> bool`). Up to generic arguments have already
+ /// been eaten.
+ fn recover_fn_trait_with_lifetime_params(
+ &mut self,
+ fn_path: &mut ast::Path,
+ lifetime_defs: &mut Vec<GenericParam>,
+ ) -> PResult<'a, ()> {
+ let fn_path_segment = fn_path.segments.last_mut().unwrap();
+ let generic_args = if let Some(p_args) = &fn_path_segment.args {
+ p_args.clone().into_inner()
+ } else {
+ // Normally it wouldn't come here because the upstream should have parsed
+ // generic parameters (otherwise it's impossible to call this function).
+ return Ok(());
+ };
+ let lifetimes =
+ if let ast::GenericArgs::AngleBracketed(ast::AngleBracketedArgs { span: _, args }) =
+ &generic_args
+ {
+ args.into_iter()
+ .filter_map(|arg| {
+ if let ast::AngleBracketedArg::Arg(generic_arg) = arg
+ && let ast::GenericArg::Lifetime(lifetime) = generic_arg {
+ Some(lifetime)
+ } else {
+ None
+ }
+ })
+ .collect()
+ } else {
+ Vec::new()
+ };
+ // Only try to recover if the trait has lifetime params.
+ if lifetimes.is_empty() {
+ return Ok(());
+ }
+
+ // Parse `(T, U) -> R`.
+ let inputs_lo = self.token.span;
+ let inputs: Vec<_> =
+ self.parse_fn_params(|_| false)?.into_iter().map(|input| input.ty).collect();
+ let inputs_span = inputs_lo.to(self.prev_token.span);
+ let output = self.parse_ret_ty(AllowPlus::No, RecoverQPath::No, RecoverReturnSign::No)?;
+ let args = ast::ParenthesizedArgs {
+ span: fn_path_segment.span().to(self.prev_token.span),
+ inputs,
+ inputs_span,
+ output,
+ }
+ .into();
+ *fn_path_segment =
+ ast::PathSegment { ident: fn_path_segment.ident, args, id: ast::DUMMY_NODE_ID };
+
+ // Convert parsed `<'a>` in `Fn<'a>` into `for<'a>`.
+ let mut generic_params = lifetimes
+ .iter()
+ .map(|lt| GenericParam {
+ id: lt.id,
+ ident: lt.ident,
+ attrs: ast::AttrVec::new(),
+ bounds: Vec::new(),
+ is_placeholder: false,
+ kind: ast::GenericParamKind::Lifetime,
+ colon_span: None,
+ })
+ .collect::<Vec<GenericParam>>();
+ lifetime_defs.append(&mut generic_params);
+
+ let generic_args_span = generic_args.span();
+ let mut err =
+ self.struct_span_err(generic_args_span, "`Fn` traits cannot take lifetime parameters");
+ let snippet = format!(
+ "for<{}> ",
+ lifetimes.iter().map(|lt| lt.ident.as_str()).intersperse(", ").collect::<String>(),
+ );
+ let before_fn_path = fn_path.span.shrink_to_lo();
+ err.multipart_suggestion(
+ "consider using a higher-ranked trait bound instead",
+ vec![(generic_args_span, "".to_owned()), (before_fn_path, snippet)],
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ Ok(())
+ }
+
pub(super) fn check_lifetime(&mut self) -> bool {
self.expected_tokens.push(TokenType::Lifetime);
self.token.is_lifetime()