diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 403 |
1 files changed, 168 insertions, 235 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index ae77961b7bc..11301f03e48 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,27 +1,33 @@ -use super::diagnostics::{ - CatchAfterTry, CommaAfterBaseStruct, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, - ExpectedElseBlock, ExpectedExpressionFoundLet, FieldExpressionWithGeneric, - FloatLiteralRequiresIntegerPart, IfExpressionMissingCondition, IfExpressionMissingThenBlock, - IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator, - InvalidComparisonOperatorSub, InvalidLogicalOperator, InvalidLogicalOperatorSub, - LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, - MalformedLoopLabel, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, - NotAsNegationOperator, NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, - RequireColonAfterLabeledExpression, SnapshotParser, TildeAsUnaryOperator, - UnexpectedTokenAfterLabel, -}; +use super::diagnostics::SnapshotParser; use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken, }; -use crate::maybe_recover_from_interpolated_ty_qpath; -use crate::parser::diagnostics::{ - IntLiteralTooLarge, InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, - InvalidIntLiteralWidth, InvalidNumLiteralBasePrefix, InvalidNumLiteralSuffix, - MissingCommaAfterMatchArm, +use crate::errors::{ + ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncMoveOrderIncorrect, + BinaryFloatLiteralNotSupported, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct, + ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg, + DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedExpressionFoundLet, + FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt, + HexadecimalFloatLiteralNotSupported, IfExpressionMissingCondition, + IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub, IntLiteralTooLarge, + InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub, + InvalidFloatLiteralSuffix, InvalidFloatLiteralWidth, InvalidIntLiteralWidth, + InvalidInterpolatedExpression, InvalidLiteralSuffix, InvalidLiteralSuffixOnTupleIndex, + InvalidLogicalOperator, InvalidLogicalOperatorSub, InvalidNumLiteralBasePrefix, + InvalidNumLiteralSuffix, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator, + LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel, + MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm, + MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray, NoFieldsForFnCall, + NotAsNegationOperator, NotAsNegationOperatorSub, OctalFloatLiteralNotSupported, + OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields, + RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere, + StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedTokenAfterLabel, + UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses, }; +use crate::maybe_recover_from_interpolated_ty_qpath; use core::mem; use rustc_ast::ptr::P; @@ -38,6 +44,7 @@ use rustc_ast::{ClosureBinder, StmtKind}; use rustc_ast_pretty::pprust; use rustc_errors::IntoDiagnostic; use rustc_errors::{Applicability, Diagnostic, PResult}; +use rustc_session::errors::ExprParenthesesNeeded; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_span::source_map::{self, Span, Spanned}; @@ -421,13 +428,11 @@ impl<'a> Parser<'a> { /// but the next token implies this should be parsed as an expression. /// For example: `if let Some(x) = x { x } else { 0 } / 2`. fn error_found_expr_would_be_stmt(&self, lhs: &Expr) { - let mut err = self.struct_span_err( - self.token.span, - &format!("expected expression, found `{}`", pprust::token_to_string(&self.token),), - ); - err.span_label(self.token.span, "expected expression"); - self.sess.expr_parentheses_needed(&mut err, lhs.span); - err.emit(); + self.sess.emit_err(FoundExprWouldBeStmt { + span: self.token.span, + token: self.token.clone(), + suggestion: ExprParenthesesNeeded::surrounding(lhs.span), + }); } /// Possibly translate the current token to an associative operator. @@ -578,21 +583,16 @@ impl<'a> Parser<'a> { make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo)) } token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => { - let mut err = this.struct_span_err(lo, "leading `+` is not supported"); - err.span_label(lo, "unexpected `+`"); + let mut err = + LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None }; // a block on the LHS might have been intended to be an expression instead if let Some(sp) = this.sess.ambiguous_block_expr_parse.borrow().get(&lo) { - this.sess.expr_parentheses_needed(&mut err, *sp); + err.add_parentheses = Some(ExprParenthesesNeeded::surrounding(*sp)); } else { - err.span_suggestion_verbose( - lo, - "try removing the `+`", - "", - Applicability::MachineApplicable, - ); + err.remove_plus = Some(lo); } - err.emit(); + this.sess.emit_err(err); this.bump(); this.parse_prefix_expr(None) @@ -755,9 +755,34 @@ impl<'a> Parser<'a> { match self.parse_path(PathStyle::Expr) { Ok(path) => { - let (op_noun, op_verb) = match self.token.kind { - token::Lt => ("comparison", "comparing"), - token::BinOp(token::Shl) => ("shift", "shifting"), + let span_after_type = parser_snapshot_after_type.token.span; + let expr = mk_expr( + self, + lhs, + self.mk_ty(path.span, TyKind::Path(None, path.clone())), + ); + + let args_span = self.look_ahead(1, |t| t.span).to(span_after_type); + let suggestion = ComparisonOrShiftInterpretedAsGenericSugg { + left: expr.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }; + + match self.token.kind { + token::Lt => self.sess.emit_err(ComparisonInterpretedAsGeneric { + comparison: self.token.span, + r#type: path, + args: args_span, + suggestion, + }), + token::BinOp(token::Shl) => { + self.sess.emit_err(ShiftInterpretedAsGeneric { + shift: self.token.span, + r#type: path, + args: args_span, + suggestion, + }) + } _ => { // We can end up here even without `<` being the next token, for // example because `parse_ty_no_plus` returns `Err` on keywords, @@ -771,33 +796,7 @@ impl<'a> Parser<'a> { // Successfully parsed the type path leaving a `<` yet to parse. type_err.cancel(); - // Report non-fatal diagnostics, keep `x as usize` as an expression - // in AST and continue parsing. - let msg = format!( - "`<` is interpreted as a start of generic arguments for `{}`, not a {}", - pprust::path_to_string(&path), - op_noun, - ); - let span_after_type = parser_snapshot_after_type.token.span; - let expr = - mk_expr(self, lhs, self.mk_ty(path.span, TyKind::Path(None, path))); - - self.struct_span_err(self.token.span, &msg) - .span_label( - self.look_ahead(1, |t| t.span).to(span_after_type), - "interpreted as generic arguments", - ) - .span_label(self.token.span, format!("not interpreted as {op_noun}")) - .multipart_suggestion( - &format!("try {op_verb} the cast value"), - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ) - .emit(); - + // Keep `x as usize` as an expression in AST and continue parsing. expr } Err(path_err) => { @@ -1158,7 +1157,9 @@ impl<'a> Parser<'a> { } let span = self.prev_token.span; let field = ExprKind::Field(base, Ident::new(field, span)); - self.expect_no_suffix(span, "a tuple index", suffix); + if let Some(suffix) = suffix { + self.expect_no_tuple_index_suffix(span, suffix); + } self.mk_expr(lo.to(span), field) } @@ -1196,9 +1197,8 @@ impl<'a> Parser<'a> { ) -> Option<P<Expr>> { match (seq.as_mut(), snapshot) { (Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => { - let name = pprust::path_to_string(&path); snapshot.bump(); // `(` - match snapshot.parse_struct_fields(path, false, Delimiter::Parenthesis) { + match snapshot.parse_struct_fields(path.clone(), false, Delimiter::Parenthesis) { Ok((fields, ..)) if snapshot.eat(&token::CloseDelim(Delimiter::Parenthesis)) => { @@ -1208,29 +1208,25 @@ impl<'a> Parser<'a> { let close_paren = self.prev_token.span; let span = lo.to(self.prev_token.span); if !fields.is_empty() { - let replacement_err = self.struct_span_err( + let mut replacement_err = ParenthesesWithStructFields { span, - "invalid `struct` delimiters or `fn` call arguments", - ); - mem::replace(err, replacement_err).cancel(); - - err.multipart_suggestion( - &format!("if `{name}` is a struct, use braces as delimiters"), - vec![ - (open_paren, " { ".to_string()), - (close_paren, " }".to_string()), - ], - Applicability::MaybeIncorrect, - ); - err.multipart_suggestion( - &format!("if `{name}` is a function, use the arguments directly"), - fields - .into_iter() - .map(|field| (field.span.until(field.expr.span), String::new())) - .collect(), - Applicability::MaybeIncorrect, - ); - err.emit(); + r#type: path, + braces_for_struct: BracesForStructLiteral { + first: open_paren, + second: close_paren, + }, + no_fields_for_fn: NoFieldsForFnCall { + fields: fields + .into_iter() + .map(|field| field.span.until(field.expr.span)) + .collect(), + }, + } + .into_diagnostic(&self.sess.span_diagnostic); + replacement_err.emit(); + + let old_err = mem::replace(err, replacement_err); + old_err.cancel(); } else { err.emit(); } @@ -1314,7 +1310,7 @@ impl<'a> Parser<'a> { // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }` // then suggest parens around the lhs. if let Some(sp) = self.sess.ambiguous_block_expr_parse.borrow().get(&lo) { - self.sess.expr_parentheses_needed(&mut err, *sp); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } err }) @@ -1537,15 +1533,19 @@ impl<'a> Parser<'a> { && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) { // We're probably inside of a `Path<'a>` that needs a turbofish - self.sess.emit_err(UnexpectedTokenAfterLabel(self.token.span)); + self.sess.emit_err(UnexpectedTokenAfterLabel { + span: self.token.span, + remove_label: None, + enclose_in_block: None, + }); consume_colon = false; Ok(self.mk_expr_err(lo)) } else { - // FIXME: use UnexpectedTokenAfterLabel, needs multipart suggestions - let msg = "expected `while`, `for`, `loop` or `{` after a label"; - - let mut err = self.struct_span_err(self.token.span, msg); - err.span_label(self.token.span, msg); + let mut err = UnexpectedTokenAfterLabel { + span: self.token.span, + remove_label: None, + enclose_in_block: None, + }; // Continue as an expression in an effort to recover on `'label: non_block_expr`. let expr = self.parse_expr().map(|expr| { @@ -1572,28 +1572,15 @@ impl<'a> Parser<'a> { // If there are no breaks that may use this label, suggest removing the label and // recover to the unmodified expression. if !found_labeled_breaks { - let msg = "consider removing the label"; - err.span_suggestion_verbose( - lo.until(span), - msg, - "", - Applicability::MachineApplicable, - ); + err.remove_label = Some(lo.until(span)); return expr; } - let sugg_msg = "consider enclosing expression in a block"; - let suggestions = vec![ - (span.shrink_to_lo(), "{ ".to_owned()), - (span.shrink_to_hi(), " }".to_owned()), - ]; - - err.multipart_suggestion_verbose( - sugg_msg, - suggestions, - Applicability::MachineApplicable, - ); + err.enclose_in_block = Some(UnexpectedTokenAfterLabelSugg { + left: span.shrink_to_lo(), + right: span.shrink_to_hi(), + }); // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to suppress future errors about `break 'label`. let stmt = self.mk_stmt(span, StmtKind::Expr(expr)); @@ -1601,7 +1588,7 @@ impl<'a> Parser<'a> { self.mk_expr(span, ExprKind::Block(blk, label)) }); - err.emit(); + self.sess.emit_err(err); expr }?; @@ -1672,19 +1659,13 @@ impl<'a> Parser<'a> { // 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)?; - self.struct_span_err( - lexpr.span, - "parentheses are required around this expression to avoid confusion with a labeled break expression", - ) - .multipart_suggestion( - "wrap the expression in parentheses", - vec![ - (lexpr.span.shrink_to_lo(), "(".to_string()), - (lexpr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(LabeledLoopInBreak { + span: lexpr.span, + sub: WrapExpressionInParentheses { + left: lexpr.span.shrink_to_lo(), + right: lexpr.span.shrink_to_hi(), + }, + }); Some(lexpr) } else if self.token != token::OpenDelim(Delimiter::Brace) || !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) @@ -1756,9 +1737,8 @@ impl<'a> Parser<'a> { }; if let Some(expr) = expr { if matches!(expr.kind, ExprKind::Err) { - let mut err = self - .diagnostic() - .struct_span_err(self.token.span, "invalid interpolated expression"); + let mut err = InvalidInterpolatedExpression { span: self.token.span } + .into_diagnostic(&self.sess.span_diagnostic); err.downgrade_to_delayed_bug(); return err; } @@ -1790,7 +1770,10 @@ impl<'a> Parser<'a> { }); if let Some(token) = &recovered { self.bump(); - self.error_float_lits_must_have_int_part(&token); + self.sess.emit_err(FloatLiteralRequiresIntegerPart { + span: token.span, + correct: pprust::token_to_string(token).into_owned(), + }); } } @@ -1818,13 +1801,6 @@ impl<'a> Parser<'a> { } } - fn error_float_lits_must_have_int_part(&self, token: &Token) { - self.sess.emit_err(FloatLiteralRequiresIntegerPart { - span: token.span, - correct: pprust::token_to_string(token).into_owned(), - }); - } - 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 { @@ -1853,11 +1829,13 @@ impl<'a> Parser<'a> { // by lexer, so here we don't report it the second time. LitError::LexerError => {} LitError::InvalidSuffix => { - self.expect_no_suffix( - span, - &format!("{} {} literal", kind.article(), kind.descr()), - suffix, - ); + 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"); @@ -1883,15 +1861,12 @@ impl<'a> Parser<'a> { } } LitError::NonDecimalFloat(base) => { - let descr = match base { - 16 => "hexadecimal", - 8 => "octal", - 2 => "binary", + match base { + 16 => self.sess.emit_err(HexadecimalFloatLiteralNotSupported { span }), + 8 => self.sess.emit_err(OctalFloatLiteralNotSupported { span }), + 2 => self.sess.emit_err(BinaryFloatLiteralNotSupported { span }), _ => unreachable!(), }; - self.struct_span_err(span, &format!("{descr} float literal is not supported")) - .span_label(span, "not supported") - .emit(); } LitError::IntTooLarge => { self.sess.emit_err(IntLiteralTooLarge { span }); @@ -1899,38 +1874,17 @@ impl<'a> Parser<'a> { } } - pub(super) fn expect_no_suffix(&self, sp: Span, kind: &str, suffix: Option<Symbol>) { - if let Some(suf) = suffix { - let mut err = if kind == "a tuple index" - && [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suf) - { - // #59553: warn instead of reject out of hand to allow the fix to percolate - // through the ecosystem when people fix their macros - let mut err = self - .sess - .span_diagnostic - .struct_span_warn(sp, &format!("suffixes on {kind} are invalid")); - err.note(&format!( - "`{}` is *temporarily* accepted on tuple index fields as it was \ - incorrectly accepted on stable for a few releases", - suf, - )); - err.help( - "on proc macros, you'll want to use `syn::Index::from` or \ - `proc_macro::Literal::*_unsuffixed` for code that will desugar \ - to tuple field access", - ); - err.note( - "see issue #60210 <https://github.com/rust-lang/rust/issues/60210> \ - for more information", - ); - err - } else { - self.struct_span_err(sp, &format!("suffixes on {kind} are invalid")) - .forget_guarantee() - }; - err.span_label(sp, format!("invalid suffix `{suf}`")); - err.emit(); + pub(super) fn expect_no_tuple_index_suffix(&self, span: Span, suffix: Symbol) { + if [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suffix) { + // #59553: warn instead of reject out of hand to allow the fix to percolate + // through the ecosystem when people fix their macros + self.sess.emit_warning(InvalidLiteralSuffixOnTupleIndex { + span, + suffix, + exception: Some(()), + }); + } else { + self.sess.emit_err(InvalidLiteralSuffixOnTupleIndex { span, suffix, exception: None }); } } @@ -1964,14 +1918,13 @@ impl<'a> Parser<'a> { let mut snapshot = self.create_snapshot_for_diagnostic(); match snapshot.parse_array_or_repeat_expr(Delimiter::Brace) { Ok(arr) => { - let hi = snapshot.prev_token.span; - self.struct_span_err(arr.span, "this is a block expression, not an array") - .multipart_suggestion( - "to make an array, use square brackets instead of curly braces", - vec![(lo, "[".to_owned()), (hi, "]".to_owned())], - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(ArrayBracketsInsteadOfSpaces { + span: arr.span, + sub: ArrayBracketsInsteadOfSpacesSugg { + left: lo, + right: snapshot.prev_token.span, + }, + }); self.restore_snapshot(snapshot); Some(self.mk_expr_err(arr.span)) @@ -2134,7 +2087,8 @@ impl<'a> Parser<'a> { // Check for `move async` and recover if self.check_keyword(kw::Async) { let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo); - Err(self.incorrect_move_async_order_found(move_async_span)) + Err(AsyncMoveOrderIncorrect { span: move_async_span } + .into_diagnostic(&self.sess.span_diagnostic)) } else { Ok(CaptureBy::Value) } @@ -2515,39 +2469,22 @@ impl<'a> Parser<'a> { self.bump(); // `;` let mut stmts = vec![self.mk_stmt(first_expr.span, ast::StmtKind::Expr(first_expr.clone()))]; - let err = |this: &mut Parser<'_>, stmts: Vec<ast::Stmt>| { + let err = |this: &Parser<'_>, stmts: Vec<ast::Stmt>| { let span = stmts[0].span.to(stmts[stmts.len() - 1].span); - let mut err = this.struct_span_err(span, "`match` arm body without braces"); - let (these, s, are) = - if stmts.len() > 1 { ("these", "s", "are") } else { ("this", "", "is") }; - err.span_label( - span, - &format!( - "{these} statement{s} {are} not surrounded by a body", - these = these, - s = s, - are = are - ), - ); - err.span_label(arrow_span, "while parsing the `match` arm starting here"); - if stmts.len() > 1 { - err.multipart_suggestion( - &format!("surround the statement{s} with a body"), - vec![ - (span.shrink_to_lo(), "{ ".to_string()), - (span.shrink_to_hi(), " }".to_string()), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion( - semi_sp, - "use a comma to end a `match` arm expression", - ",", - Applicability::MachineApplicable, - ); - } - err.emit(); + + this.sess.emit_err(MatchArmBodyWithoutBraces { + statements: span, + arrow: arrow_span, + num_statements: stmts.len(), + sub: if stmts.len() > 1 { + MatchArmBodyWithoutBracesSugg::AddBraces { + left: span.shrink_to_lo(), + right: span.shrink_to_hi(), + } + } else { + MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp } + }, + }); this.mk_expr_err(span) }; // We might have either a `,` -> `;` typo, or a block without braces. We need @@ -2836,23 +2773,19 @@ impl<'a> Parser<'a> { let expr = self.parse_struct_expr(qself.cloned(), 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.error_struct_lit_not_allowed_here(path.span, expr.span); + self.sess.emit_err(StructLiteralNotAllowedHere { + span: expr.span, + sub: StructLiteralNotAllowedHereSugg { + left: path.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, + }); } return Some(expr); } None } - fn error_struct_lit_not_allowed_here(&self, lo: Span, sp: Span) { - self.struct_span_err(sp, "struct literals are not allowed here") - .multipart_suggestion( - "surround the struct literal with parentheses", - vec![(lo.shrink_to_lo(), "(".to_string()), (sp.shrink_to_hi(), ")".to_string())], - Applicability::MachineApplicable, - ) - .emit(); - } - pub(super) fn parse_struct_fields( &mut self, pth: ast::Path, |
