summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_parse/src/parser/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs435
1 files changed, 217 insertions, 218 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index a781748ef..f6a6ed379 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -7,46 +7,42 @@ use super::{
};
use crate::errors::{
ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect,
- BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct,
- ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg,
- DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedExpressionFoundLet,
+ BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, ComparisonInterpretedAsGeneric,
+ ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit,
+ ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet,
FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt,
- HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition,
- IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge,
+ IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub,
InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
- InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth,
- InvalidInterpolatedExpression, InvalidLiteralSuffix, InvalidLiteralSuffixOnTupleIndex,
- InvalidLogicalOperator, InvalidLogicalOperatorSub, InvalidNumLiteralBasePrefix,
- InvalidNumLiteralSuffix, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator,
+ InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator,
+ InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator,
LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel,
MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm,
- MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, NoFieldsForFnCall,
- NotAsNegationOperator, NotAsNegationOperatorSub, OctalFloatLiteralNotSupported,
+ MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray,
+ NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub,
OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere,
- StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedTokenAfterLabel,
- UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
+ StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf,
+ UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
};
use crate::maybe_recover_from_interpolated_ty_qpath;
-
use core::mem;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::Spacing;
+use rustc_ast::util::case::Case;
use rustc_ast::util::classify;
-use rustc_ast::util::literal::LitError;
use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
use rustc_ast::visit::Visitor;
-use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID};
+use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID};
use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
-use rustc_ast::{ClosureBinder, StmtKind};
+use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
use rustc_ast_pretty::pprust;
use rustc_errors::{
Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult,
StashKey,
};
-use rustc_session::errors::ExprParenthesesNeeded;
+use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded};
use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::source_map::{self, Span, Spanned};
@@ -132,7 +128,7 @@ impl<'a> Parser<'a> {
Ok(expr) => Ok(expr),
Err(mut err) => match self.token.ident() {
Some((Ident { name: kw::Underscore, .. }, false))
- if self.look_ahead(1, |t| t == &token::Comma) =>
+ if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>
{
// Special-case handling of `foo(_, _, _)`
err.emit();
@@ -394,20 +390,11 @@ impl<'a> Parser<'a> {
// want to keep their span info to improve diagnostics in these cases in a later stage.
(true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3`
(true, Some(AssocOp::Subtract)) | // `{ 42 } -5`
- (true, Some(AssocOp::Add)) // `{ 42 } + 42
- // If the next token is a keyword, then the tokens above *are* unambiguously incorrect:
- // `if x { a } else { b } && if y { c } else { d }`
- if !self.look_ahead(1, |t| t.is_used_keyword()) => {
- // These cases are ambiguous and can't be identified in the parser alone.
- let sp = self.sess.source_map().start_point(self.token.span);
- self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);
- false
- }
- (true, Some(AssocOp::LAnd)) |
- (true, Some(AssocOp::LOr)) |
- (true, Some(AssocOp::BitOr)) => {
- // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`. Separated from the
- // above due to #74233.
+ (true, Some(AssocOp::Add)) | // `{ 42 } + 42` (unary plus)
+ (true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
+ (true, Some(AssocOp::LOr)) | // `{ 42 } || 42` ("logical or" or closure)
+ (true, Some(AssocOp::BitOr)) // `{ 42 } | 42` or `{ 42 } |x| 42`
+ => {
// These cases are ambiguous and can't be identified in the parser alone.
//
// Bitwise AND is left out because guessing intent is hard. We can make
@@ -418,7 +405,7 @@ impl<'a> Parser<'a> {
self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span);
false
}
- (true, Some(ref op)) if !op.can_continue_expr_unambiguously() => false,
+ (true, Some(op)) if !op.can_continue_expr_unambiguously() => false,
(true, Some(_)) => {
self.error_found_expr_would_be_stmt(lhs);
true
@@ -456,7 +443,7 @@ impl<'a> Parser<'a> {
return None;
}
(Some(op), _) => (op, self.token.span),
- (None, Some((Ident { name: sym::and, span }, false))) => {
+ (None, Some((Ident { name: sym::and, span }, false))) if self.may_recover() => {
self.sess.emit_err(InvalidLogicalOperator {
span: self.token.span,
incorrect: "and".into(),
@@ -464,7 +451,7 @@ impl<'a> Parser<'a> {
});
(AssocOp::LAnd, span)
}
- (None, Some((Ident { name: sym::or, span }, false))) => {
+ (None, Some((Ident { name: sym::or, span }, false))) if self.may_recover() => {
self.sess.emit_err(InvalidLogicalOperator {
span: self.token.span,
incorrect: "or".into(),
@@ -615,7 +602,7 @@ impl<'a> Parser<'a> {
token::Ident(..) if this.token.is_keyword(kw::Box) => {
make_it!(this, attrs, |this, _| this.parse_box_expr(lo))
}
- token::Ident(..) if this.is_mistaken_not_ident_negation() => {
+ token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => {
make_it!(this, attrs, |this, _| this.recover_not_expr(lo))
}
_ => return this.parse_dot_or_call_expr(Some(attrs)),
@@ -718,6 +705,10 @@ impl<'a> Parser<'a> {
let cast_expr = match self.parse_as_cast_ty() {
Ok(rhs) => mk_expr(self, lhs, rhs),
Err(type_err) => {
+ if !self.may_recover() {
+ return Err(type_err);
+ }
+
// Rewind to before attempting to parse the type with generics, to recover
// from situations like `x as usize < y` in which we first tried to parse
// `usize < y` as a type with generic arguments.
@@ -829,23 +820,18 @@ impl<'a> Parser<'a> {
("cast", None)
};
- // Save the memory location of expr before parsing any following postfix operators.
- // This will be compared with the memory location of the output expression.
- // If they different we can assume we parsed another expression because the existing expression is not reallocated.
- let addr_before = &*cast_expr as *const _ as usize;
let with_postfix = self.parse_dot_or_call_expr_with_(cast_expr, span)?;
- let changed = addr_before != &*with_postfix as *const _ as usize;
// Check if an illegal postfix operator has been added after the cast.
- // If the resulting expression is not a cast, or has a different memory location, it is an illegal postfix operator.
- if !matches!(with_postfix.kind, ExprKind::Cast(_, _) | ExprKind::Type(_, _)) || changed {
+ // If the resulting expression is not a cast, it is an illegal postfix operator.
+ if !matches!(with_postfix.kind, ExprKind::Cast(_, _) | ExprKind::Type(_, _)) {
let msg = format!(
"{cast_kind} cannot be followed by {}",
match with_postfix.kind {
ExprKind::Index(_, _) => "indexing",
ExprKind::Try(_) => "`?`",
ExprKind::Field(_, _) => "a field access",
- ExprKind::MethodCall(_, _, _, _) => "a method call",
+ ExprKind::MethodCall(_) => "a method call",
ExprKind::Call(_, _) => "a function call",
ExprKind::Await(_) => "`.await`",
ExprKind::Err => return Ok(with_postfix),
@@ -1197,6 +1183,10 @@ impl<'a> Parser<'a> {
seq: &mut PResult<'a, P<Expr>>,
snapshot: Option<(SnapshotParser<'a>, ExprKind)>,
) -> Option<P<Expr>> {
+ if !self.may_recover() {
+ return None;
+ }
+
match (seq.as_mut(), snapshot) {
(Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {
snapshot.bump(); // `(`
@@ -1263,24 +1253,32 @@ impl<'a> Parser<'a> {
}
let fn_span_lo = self.token.span;
- let mut segment = self.parse_path_segment(PathStyle::Expr, None)?;
- self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(Delimiter::Parenthesis)]);
- self.check_turbofish_missing_angle_brackets(&mut segment);
+ let mut seg = self.parse_path_segment(PathStyle::Expr, None)?;
+ self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]);
+ self.check_turbofish_missing_angle_brackets(&mut seg);
if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
// Method call `expr.f()`
let args = self.parse_paren_expr_seq()?;
let fn_span = fn_span_lo.to(self.prev_token.span);
let span = lo.to(self.prev_token.span);
- Ok(self.mk_expr(span, ExprKind::MethodCall(segment, self_arg, args, fn_span)))
+ Ok(self.mk_expr(
+ span,
+ ExprKind::MethodCall(Box::new(ast::MethodCall {
+ seg,
+ receiver: self_arg,
+ args,
+ span: fn_span,
+ })),
+ ))
} else {
// Field access `expr.f`
- if let Some(args) = segment.args {
+ if let Some(args) = seg.args {
self.sess.emit_err(FieldExpressionWithGeneric(args.span()));
}
let span = lo.to(self.prev_token.span);
- Ok(self.mk_expr(span, ExprKind::Field(self_arg, segment.ident)))
+ Ok(self.mk_expr(span, ExprKind::Field(self_arg, seg.ident)))
}
}
@@ -1360,7 +1358,7 @@ impl<'a> Parser<'a> {
)
} else if self.check_inline_const(0) {
self.parse_const_block(lo.to(self.token.span), false)
- } else if self.is_do_catch_block() {
+ } else if self.may_recover() && self.is_do_catch_block() {
self.recover_do_catch()
} else if self.is_try_block() {
self.expect_keyword(kw::Try)?;
@@ -1411,9 +1409,9 @@ impl<'a> Parser<'a> {
fn parse_lit_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
- match self.parse_opt_lit() {
- Some(literal) => {
- let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(literal));
+ match self.parse_opt_token_lit() {
+ Some((token_lit, _)) => {
+ let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(token_lit));
self.maybe_recover_from_bad_qpath(expr)
}
None => self.try_macro_suggestion(),
@@ -1494,12 +1492,12 @@ impl<'a> Parser<'a> {
let lo = path.span;
let mac = P(MacCall {
path,
- args: self.parse_mac_args()?,
+ args: self.parse_delim_args()?,
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.as_ref(), &path) {
+ 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);
}
@@ -1532,6 +1530,7 @@ impl<'a> Parser<'a> {
{
self.parse_block_expr(label, lo, BlockCheckMode::Default)
} else if !ate_colon
+ && self.may_recover()
&& (matches!(self.token.kind, token::CloseDelim(_) | token::Comma)
|| self.token.is_op())
{
@@ -1543,7 +1542,7 @@ impl<'a> Parser<'a> {
})
});
consume_colon = false;
- Ok(self.mk_expr(lo, ExprKind::Lit(lit)))
+ Ok(self.mk_expr(lo, ExprKind::Lit(lit.token_lit)))
} else if !ate_colon
&& (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))
{
@@ -1620,10 +1619,10 @@ impl<'a> Parser<'a> {
/// Emit an error when a char is parsed as a lifetime because of a missing quote
pub(super) fn recover_unclosed_char(
- &mut self,
+ &self,
lifetime: Ident,
- err: impl FnOnce(&mut Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>,
- ) -> ast::Lit {
+ err: impl FnOnce(&Self) -> DiagnosticBuilder<'a, ErrorGuaranteed>,
+ ) -> ast::MetaItemLit {
if let Some(mut diag) =
self.sess.span_diagnostic.steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar)
{
@@ -1644,9 +1643,10 @@ impl<'a> Parser<'a> {
)
.emit();
}
- ast::Lit {
- token_lit: token::Lit::new(token::LitKind::Char, lifetime.name, None),
- kind: ast::LitKind::Char(lifetime.name.as_str().chars().next().unwrap_or('_')),
+ 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,
}
}
@@ -1719,13 +1719,13 @@ impl<'a> Parser<'a> {
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
{
let expr = self.parse_expr_opt()?;
- if let Some(ref expr) = expr {
+ if let Some(expr) = &expr {
if label.is_some()
&& matches!(
expr.kind,
ExprKind::While(_, _, None)
| ExprKind::ForLoop(_, _, _, None)
- | ExprKind::Loop(_, None)
+ | ExprKind::Loop(_, None, _)
| ExprKind::Block(_, None)
)
{
@@ -1759,8 +1759,8 @@ impl<'a> Parser<'a> {
/// Returns a string literal if the next token is a string literal.
/// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind,
/// and returns `None` if the next token is not literal at all.
- pub fn parse_str_lit(&mut self) -> Result<ast::StrLit, Option<Lit>> {
- match self.parse_opt_lit() {
+ pub fn parse_str_lit(&mut self) -> Result<ast::StrLit, Option<MetaItemLit>> {
+ match self.parse_opt_meta_item_lit() {
Some(lit) => match lit.kind {
ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit {
style,
@@ -1775,41 +1775,47 @@ impl<'a> Parser<'a> {
}
}
- pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> {
- self.parse_opt_lit().ok_or(()).or_else(|()| {
- if let token::Interpolated(inner) = &self.token.kind {
- let expr = match inner.as_ref() {
- token::NtExpr(expr) => Some(expr),
- token::NtLiteral(expr) => Some(expr),
- _ => None,
- };
- if let Some(expr) = expr {
- if matches!(expr.kind, ExprKind::Err) {
- let mut err = InvalidInterpolatedExpression { span: self.token.span }
- .into_diagnostic(&self.sess.span_diagnostic);
- err.downgrade_to_delayed_bug();
- return Err(err);
- }
- }
- }
- let token = self.token.clone();
- let err = |self_: &mut Self| {
- let msg = format!("unexpected token: {}", super::token_descr(&token));
- self_.struct_span_err(token.span, &msg)
+ fn handle_missing_lit(&mut self) -> PResult<'a, MetaItemLit> {
+ if let token::Interpolated(inner) = &self.token.kind {
+ let expr = match inner.as_ref() {
+ token::NtExpr(expr) => Some(expr),
+ token::NtLiteral(expr) => Some(expr),
+ _ => None,
};
- // 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))
- } else {
- Err(err(self))
+ if let Some(expr) = expr {
+ if matches!(expr.kind, ExprKind::Err) {
+ let mut err = InvalidInterpolatedExpression { span: self.token.span }
+ .into_diagnostic(&self.sess.span_diagnostic);
+ err.downgrade_to_delayed_bug();
+ return Err(err);
+ }
}
- })
+ }
+ let token = self.token.clone();
+ let err = |self_: &Self| {
+ let msg = format!("unexpected token: {}", super::token_descr(&token));
+ self_.struct_span_err(token.span, &msg)
+ };
+ // 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))
+ } else {
+ Err(err(self))
+ }
}
- /// Matches `lit = true | false | token_lit`.
- /// Returns `None` if the next token is not a literal.
- pub(super) fn parse_opt_lit(&mut self) -> Option<Lit> {
+ 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)))
+ }
+
+ 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())
+ }
+
+ fn recover_after_dot(&mut self) -> Option<Token> {
let mut recovered = None;
if self.token == token::Dot {
// Attempt to recover `.4` as `0.4`. We don't currently have any syntax where
@@ -1835,100 +1841,53 @@ impl<'a> Parser<'a> {
}
}
- let token = recovered.as_ref().unwrap_or(&self.token);
- match Lit::from_token(token) {
- Ok(lit) => {
- self.bump();
- Some(lit)
- }
- Err(LitError::NotLiteral) => None,
- Err(err) => {
- let span = token.span;
- let token::Literal(lit) = token.kind else {
- unreachable!();
- };
- self.bump();
- self.report_lit_error(err, lit, span);
- // Pack possible quotes and prefixes from the original literal into
- // the error literal's symbol so they can be pretty-printed faithfully.
- let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None);
- let symbol = Symbol::intern(&suffixless_lit.to_string());
- let lit = token::Lit::new(token::Err, symbol, lit.suffix);
- Some(Lit::from_token_lit(lit, span).unwrap_or_else(|_| unreachable!()))
- }
- }
+ recovered
}
- fn report_lit_error(&self, err: LitError, lit: token::Lit, span: Span) {
- // Checks if `s` looks like i32 or u1234 etc.
- fn looks_like_width_suffix(first_chars: &[char], s: &str) -> bool {
- s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit())
- }
-
- // Try to lowercase the prefix if it's a valid base prefix.
- fn fix_base_capitalisation(s: &str) -> Option<String> {
- if let Some(stripped) = s.strip_prefix('B') {
- Some(format!("0b{stripped}"))
- } else if let Some(stripped) = s.strip_prefix('O') {
- Some(format!("0o{stripped}"))
- } else if let Some(stripped) = s.strip_prefix('X') {
- Some(format!("0x{stripped}"))
- } else {
- None
- }
- }
+ /// Matches `lit = true | false | token_lit`.
+ /// Returns `None` if the next token is not a literal.
+ pub(super) fn parse_opt_token_lit(&mut self) -> Option<(token::Lit, Span)> {
+ let recovered = self.recover_after_dot();
+ let token = recovered.as_ref().unwrap_or(&self.token);
+ let span = token.span;
+ token::Lit::from_token(token).map(|token_lit| {
+ self.bump();
+ (token_lit, span)
+ })
+ }
- let token::Lit { kind, suffix, .. } = lit;
- match err {
- // `NotLiteral` is not an error by itself, so we don't report
- // it and give the parser opportunity to try something else.
- LitError::NotLiteral => {}
- // `LexerError` *is* an error, but it was already reported
- // by lexer, so here we don't report it the second time.
- LitError::LexerError => {}
- LitError::InvalidSuffix => {
- if let Some(suffix) = suffix {
- self.sess.emit_err(InvalidLiteralSuffix {
- span,
- kind: format!("{}", kind.descr()),
- suffix,
- });
- }
- }
- LitError::InvalidIntSuffix => {
- let suf = suffix.expect("suffix error with no suffix");
- let suf = suf.as_str();
- if looks_like_width_suffix(&['i', 'u'], &suf) {
- // If it looks like a width, try to be helpful.
- self.sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() });
- } else if let Some(fixed) = fix_base_capitalisation(suf) {
- self.sess.emit_err(InvalidNumLiteralBasePrefix { span, fixed });
- } else {
- self.sess.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() });
- }
- }
- LitError::InvalidFloatSuffix => {
- let suf = suffix.expect("suffix error with no suffix");
- let suf = suf.as_str();
- if looks_like_width_suffix(&['f'], suf) {
- // If it looks like a width, try to be helpful.
- self.sess
- .emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() });
- } else {
- self.sess.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() });
+ /// Matches `lit = true | false | token_lit`.
+ /// Returns `None` if the next token is not a literal.
+ pub(super) fn parse_opt_meta_item_lit(&mut self) -> Option<MetaItemLit> {
+ let recovered = self.recover_after_dot();
+ let token = recovered.as_ref().unwrap_or(&self.token);
+ match token::Lit::from_token(token) {
+ Some(token_lit) => {
+ match MetaItemLit::from_token_lit(token_lit, token.span) {
+ Ok(lit) => {
+ self.bump();
+ Some(lit)
+ }
+ Err(err) => {
+ let span = token.span;
+ let token::Literal(lit) = token.kind else {
+ unreachable!();
+ };
+ self.bump();
+ report_lit_error(&self.sess, err, lit, span);
+ // Pack possible quotes and prefixes from the original literal into
+ // the error literal's symbol so they can be pretty-printed faithfully.
+ let suffixless_lit = token::Lit::new(lit.kind, lit.symbol, None);
+ let symbol = Symbol::intern(&suffixless_lit.to_string());
+ let lit = token::Lit::new(token::Err, symbol, lit.suffix);
+ Some(
+ MetaItemLit::from_token_lit(lit, span)
+ .unwrap_or_else(|_| unreachable!()),
+ )
+ }
}
}
- LitError::NonDecimalFloat(base) => {
- match base {
- 16 => self.sess.emit_err(HexadecimalFloatLiteralNotSupported { span }),
- 8 => self.sess.emit_err(OctalFloatLiteralNotSupported { span }),
- 2 => self.sess.emit_err(BinaryFloatLiteralNotSupported { span }),
- _ => unreachable!(),
- };
- }
- LitError::IntTooLarge => {
- self.sess.emit_err(IntLiteralTooLarge { span });
- }
+ None => None,
}
}
@@ -1953,8 +1912,8 @@ impl<'a> Parser<'a> {
let lo = self.token.span;
let minus_present = self.eat(&token::BinOp(token::Minus));
- let lit = self.parse_lit()?;
- let expr = self.mk_expr(lit.span, ExprKind::Lit(lit));
+ let (token_lit, span) = self.parse_token_lit()?;
+ let expr = self.mk_expr(span, ExprKind::Lit(token_lit));
if minus_present {
Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_unary(UnOp::Neg, expr)))
@@ -1999,6 +1958,10 @@ impl<'a> Parser<'a> {
prev_span: Span,
open_delim_span: Span,
) -> PResult<'a, ()> {
+ if !self.may_recover() {
+ return Ok(());
+ }
+
if self.token.kind == token::Comma {
if !self.sess.source_map().is_multiline(prev_span.until(self.token.span)) {
return Ok(());
@@ -2039,7 +2002,7 @@ impl<'a> Parser<'a> {
lo: Span,
blk_mode: BlockCheckMode,
) -> PResult<'a, P<Expr>> {
- if self.is_array_like_block() {
+ if self.may_recover() && self.is_array_like_block() {
if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo) {
return Ok(arr);
}
@@ -2082,15 +2045,15 @@ impl<'a> Parser<'a> {
if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
let asyncness = if self.token.uninterpolated_span().rust_2018() {
- self.parse_asyncness()
+ self.parse_asyncness(Case::Sensitive)
} else {
Async::No
};
let capture_clause = self.parse_capture_clause()?;
- let decl = self.parse_fn_block_decl()?;
+ let (fn_decl, fn_arg_span) = self.parse_fn_block_decl()?;
let decl_hi = self.prev_token.span;
- let mut body = match decl.output {
+ let mut body = match fn_decl.output {
FnRetTy::Default(_) => {
let restrictions = self.restrictions - Restrictions::STMT_EXPR;
self.parse_expr_res(restrictions, None)?
@@ -2109,12 +2072,7 @@ impl<'a> Parser<'a> {
if self.token.kind == TokenKind::Semi
&& matches!(self.token_cursor.frame.delim_sp, Some((Delimiter::Parenthesis, _)))
- // HACK: This is needed so we can detect whether we're inside a macro,
- // where regular assumptions about what tokens can follow other tokens
- // don't necessarily apply.
&& self.may_recover()
- // FIXME(Nilstrieb): Remove this check once `may_recover` actually stops recovery
- && self.subparser_name.is_none()
{
// It is likely that the closure body is a block but where the
// braces have been removed. We will recover and eat the next
@@ -2126,15 +2084,16 @@ impl<'a> Parser<'a> {
let closure = self.mk_expr(
lo.to(body.span),
- ExprKind::Closure(
+ ExprKind::Closure(Box::new(ast::Closure {
binder,
capture_clause,
asyncness,
movability,
- decl,
+ fn_decl,
body,
- lo.to(decl_hi),
- ),
+ fn_decl_span: lo.to(decl_hi),
+ fn_arg_span,
+ })),
);
// Disable recovery for closure body
@@ -2162,7 +2121,9 @@ impl<'a> Parser<'a> {
}
/// Parses the `|arg, arg|` header of a closure.
- fn parse_fn_block_decl(&mut self) -> PResult<'a, P<FnDecl>> {
+ fn parse_fn_block_decl(&mut self) -> PResult<'a, (P<FnDecl>, Span)> {
+ let arg_start = self.token.span.lo();
+
let inputs = if self.eat(&token::OrOr) {
Vec::new()
} else {
@@ -2178,10 +2139,11 @@ impl<'a> Parser<'a> {
self.expect_or()?;
args
};
+ let arg_span = self.prev_token.span.with_lo(arg_start);
let output =
self.parse_ret_ty(AllowPlus::Yes, RecoverQPath::Yes, RecoverReturnSign::Yes)?;
- Ok(P(FnDecl { inputs, output }))
+ Ok((P(FnDecl { inputs, output }), arg_span))
}
/// Parses a parameter in a closure header (e.g., `|arg, arg|`).
@@ -2263,13 +2225,14 @@ impl<'a> Parser<'a> {
self.mk_block_err(cond_span.shrink_to_hi())
}
} else {
- let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery.
+ let attrs = self.parse_outer_attributes()?; // For recovery.
let block = if self.check(&token::OpenDelim(Delimiter::Brace)) {
self.parse_block()?
} else {
if let Some(block) = recover_block_from_condition(self) {
block
} else {
+ self.error_on_extra_if(&cond)?;
// Parse block, which will always fail, but we can add a nice note to the error
self.parse_block().map_err(|mut err| {
err.span_note(
@@ -2280,7 +2243,7 @@ impl<'a> Parser<'a> {
})?
}
};
- self.error_on_if_block_attrs(lo, false, block.span, &attrs);
+ self.error_on_if_block_attrs(lo, false, block.span, attrs);
block
};
let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None };
@@ -2321,7 +2284,15 @@ impl<'a> Parser<'a> {
RecoverColon::Yes,
CommaRecoveryMode::LikelyTuple,
)?;
- self.expect(&token::Eq)?;
+ if self.token == token::EqEq {
+ self.sess.emit_err(ExpectedEqForLetExpr {
+ span: self.token.span,
+ sugg_span: self.token.span,
+ });
+ self.bump();
+ } else {
+ self.expect(&token::Eq)?;
+ }
let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| {
this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
})?;
@@ -2333,7 +2304,7 @@ impl<'a> Parser<'a> {
/// Parses an `else { ... }` expression (`else` token already eaten).
fn parse_else_expr(&mut self) -> PResult<'a, P<Expr>> {
let else_span = self.prev_token.span; // `else`
- let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery.
+ let attrs = self.parse_outer_attributes()?; // For recovery.
let expr = if self.eat_keyword(kw::If) {
self.parse_if_expr()?
} else if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) {
@@ -2368,7 +2339,7 @@ impl<'a> Parser<'a> {
},
}
};
- self.error_on_if_block_attrs(else_span, true, expr.span, &attrs);
+ self.error_on_if_block_attrs(else_span, true, expr.span, attrs);
Ok(expr)
}
@@ -2377,8 +2348,13 @@ impl<'a> Parser<'a> {
ctx_span: Span,
is_ctx_else: bool,
branch_span: Span,
- attrs: &[ast::Attribute],
+ attrs: AttrWrapper,
) {
+ if attrs.is_empty() {
+ return;
+ }
+
+ let attrs: &[ast::Attribute] = &attrs.take_for_recovery(self.sess);
let (attributes, last) = match attrs {
[] => return,
[x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span),
@@ -2393,6 +2369,16 @@ impl<'a> Parser<'a> {
});
}
+ fn error_on_extra_if(&mut self, cond: &P<Expr>) -> PResult<'a, ()> {
+ if let ExprKind::Binary(Spanned { span: binop_span, node: binop}, _, right) = &cond.kind &&
+ let BinOpKind::And = binop &&
+ let ExprKind::If(cond, ..) = &right.kind {
+ Err(self.sess.create_err(UnexpectedIfWithIf(binop_span.shrink_to_hi().to(cond.span.shrink_to_lo()))))
+ } else {
+ Ok(())
+ }
+ }
+
/// Parses `for <src_pat> in <src_expr> <src_loop_block>` (`for` token already eaten).
fn parse_for_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
// Record whether we are about to parse `for (`.
@@ -2456,10 +2442,11 @@ impl<'a> Parser<'a> {
/// Parses `loop { ... }` (`loop` token already eaten).
fn parse_loop_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> {
+ let loop_span = self.prev_token.span;
let (attrs, body) = self.parse_inner_attrs_and_block()?;
Ok(self.mk_expr_with_attrs(
lo.to(self.prev_token.span),
- ExprKind::Loop(body, opt_label),
+ ExprKind::Loop(body, opt_label, loop_span),
attrs,
))
}
@@ -2602,8 +2589,8 @@ impl<'a> Parser<'a> {
// Used to check the `let_chains` and `if_let_guard` features mostly by scanning
// `&&` tokens.
fn check_let_expr(expr: &Expr) -> (bool, bool) {
- match expr.kind {
- ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, ref lhs, ref rhs) => {
+ match &expr.kind {
+ ExprKind::Binary(BinOp { node: BinOpKind::And, .. }, lhs, rhs) => {
let lhs_rslt = check_let_expr(lhs);
let rhs_rslt = check_let_expr(rhs);
(lhs_rslt.0 || rhs_rslt.0, false)
@@ -2826,7 +2813,7 @@ impl<'a> Parser<'a> {
fn maybe_parse_struct_expr(
&mut self,
- qself: Option<&ast::QSelf>,
+ qself: &Option<P<ast::QSelf>>,
path: &ast::Path,
) -> Option<PResult<'a, P<Expr>>> {
let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
@@ -2834,7 +2821,7 @@ impl<'a> Parser<'a> {
if let Err(err) = self.expect(&token::OpenDelim(Delimiter::Brace)) {
return Some(Err(err));
}
- let expr = self.parse_struct_expr(qself.cloned(), path.clone(), true);
+ let expr = self.parse_struct_expr(qself.clone(), path.clone(), true);
if let (Ok(expr), false) = (&expr, struct_allowed) {
// This is a struct literal, but we don't can't accept them here.
self.sess.emit_err(StructLiteralNotAllowedHere {
@@ -2867,7 +2854,7 @@ impl<'a> Parser<'a> {
};
while self.token != token::CloseDelim(close_delim) {
- if self.eat(&token::DotDot) {
+ if self.eat(&token::DotDot) || self.recover_struct_field_dots(close_delim) {
let exp_span = self.prev_token.span;
// We permit `.. }` on the left-hand side of a destructuring assignment.
if self.check(&token::CloseDelim(close_delim)) {
@@ -2965,7 +2952,7 @@ impl<'a> Parser<'a> {
/// Precondition: already parsed the '{'.
pub(super) fn parse_struct_expr(
&mut self,
- qself: Option<ast::QSelf>,
+ qself: Option<P<ast::QSelf>>,
pth: ast::Path,
recover: bool,
) -> PResult<'a, P<Expr>> {
@@ -3014,6 +3001,18 @@ impl<'a> Parser<'a> {
self.recover_stmt();
}
+ fn recover_struct_field_dots(&mut self, close_delim: Delimiter) -> bool {
+ if !self.look_ahead(1, |t| *t == token::CloseDelim(close_delim))
+ && self.eat(&token::DotDotDot)
+ {
+ // recover from typo of `...`, suggest `..`
+ let span = self.prev_token.span;
+ self.sess.emit_err(MissingDotDot { token_span: span, sugg_span: span });
+ return true;
+ }
+ false
+ }
+
/// Parses `ident (COLON expr)?`.
fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
let attrs = self.parse_outer_attributes()?;