diff options
Diffstat (limited to 'compiler/rustc_parse/src/parser/expr.rs')
| -rw-r--r-- | compiler/rustc_parse/src/parser/expr.rs | 787 |
1 files changed, 296 insertions, 491 deletions
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 33bebfc2c9a..18aa109d7a6 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1,4 +1,14 @@ -use super::diagnostics::SnapshotParser; +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, OuterAttributeNotAllowedOnIfElse, RequireColonAfterLabeledExpression, + SnapshotParser, TildeAsUnaryOperator, UnexpectedTokenAfterLabel, +}; use super::pat::{CommaRecoveryMode, RecoverColon, RecoverComma, PARAM_EXPECTED}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{ @@ -6,6 +16,11 @@ use super::{ 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 core::mem; use rustc_ast::ptr::P; @@ -20,10 +35,10 @@ use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits}; use rustc_ast::{ClosureBinder, StmtKind}; use rustc_ast_pretty::pprust; -use rustc_data_structures::thin_vec::ThinVec; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult}; +use rustc_errors::{Applicability, Diagnostic, PResult}; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_session::lint::BuiltinLintDiagnostics; +use rustc_session::SessionDiagnostic; use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Pos}; @@ -45,20 +60,12 @@ macro_rules! maybe_whole_expr { token::NtPath(path) => { let path = (**path).clone(); $p.bump(); - return Ok($p.mk_expr( - $p.prev_token.span, - ExprKind::Path(None, path), - AttrVec::new(), - )); + return Ok($p.mk_expr($p.prev_token.span, ExprKind::Path(None, path))); } token::NtBlock(block) => { let block = block.clone(); $p.bump(); - return Ok($p.mk_expr( - $p.prev_token.span, - ExprKind::Block(block, None), - AttrVec::new(), - )); + return Ok($p.mk_expr($p.prev_token.span, ExprKind::Block(block, None))); } _ => {} }; @@ -120,7 +127,7 @@ impl<'a> Parser<'a> { // Special-case handling of `foo(_, _, _)` err.emit(); self.bump(); - Ok(self.mk_expr(self.prev_token.span, ExprKind::Err, AttrVec::new())) + Ok(self.mk_expr(self.prev_token.span, ExprKind::Err)) } _ => Err(err), }, @@ -225,15 +232,18 @@ impl<'a> Parser<'a> { AssocOp::Equal => "==", AssocOp::NotEqual => "!=", _ => unreachable!(), - }; - self.struct_span_err(sp, &format!("invalid comparison operator `{sugg}=`")) - .span_suggestion_short( - sp, - &format!("`{s}=` is not a valid comparison operator, use `{s}`", s = sugg), - sugg, - Applicability::MachineApplicable, - ) - .emit(); + } + .into(); + let invalid = format!("{}=", &sugg); + self.sess.emit_err(InvalidComparisonOperator { + span: sp, + invalid: invalid.clone(), + sub: InvalidComparisonOperatorSub::Correctable { + span: sp, + invalid, + correct: sugg, + }, + }); self.bump(); } @@ -243,14 +253,15 @@ impl<'a> Parser<'a> { && self.prev_token.span.hi() == self.token.span.lo() { let sp = op.span.to(self.token.span); - self.struct_span_err(sp, "invalid comparison operator `<>`") - .span_suggestion_short( - sp, - "`<>` is not a valid comparison operator, use `!=`", - "!=", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(InvalidComparisonOperator { + span: sp, + invalid: "<>".into(), + sub: InvalidComparisonOperatorSub::Correctable { + span: sp, + invalid: "<>".into(), + correct: "!=".into(), + }, + }); self.bump(); } @@ -260,12 +271,11 @@ impl<'a> Parser<'a> { && self.prev_token.span.hi() == self.token.span.lo() { let sp = op.span.to(self.token.span); - self.struct_span_err(sp, "invalid comparison operator `<=>`") - .span_label( - sp, - "`<=>` is not a valid comparison operator, use `std::cmp::Ordering`", - ) - .emit(); + self.sess.emit_err(InvalidComparisonOperator { + span: sp, + invalid: "<=>".into(), + sub: InvalidComparisonOperatorSub::Spaceship(sp), + }); self.bump(); } @@ -329,11 +339,9 @@ impl<'a> Parser<'a> { | AssocOp::GreaterEqual => { let ast_op = op.to_ast_binop().unwrap(); let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs); - self.mk_expr(span, binary, AttrVec::new()) - } - AssocOp::Assign => { - self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span), AttrVec::new()) + self.mk_expr(span, binary) } + AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs, cur_op_span)), AssocOp::AssignOp(k) => { let aop = match k { token::Plus => BinOpKind::Add, @@ -348,7 +356,7 @@ impl<'a> Parser<'a> { token::Shr => BinOpKind::Shr, }; let aopexpr = self.mk_assign_op(source_map::respan(cur_op_span, aop), lhs, rhs); - self.mk_expr(span, aopexpr, AttrVec::new()) + self.mk_expr(span, aopexpr) } AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotEq => { self.span_bug(span, "AssocOp should have been handled by special case") @@ -441,11 +449,19 @@ impl<'a> Parser<'a> { } (Some(op), _) => (op, self.token.span), (None, Some((Ident { name: sym::and, span }, false))) => { - self.error_bad_logical_op("and", "&&", "conjunction"); + self.sess.emit_err(InvalidLogicalOperator { + span: self.token.span, + incorrect: "and".into(), + sub: InvalidLogicalOperatorSub::Conjunction(self.token.span), + }); (AssocOp::LAnd, span) } (None, Some((Ident { name: sym::or, span }, false))) => { - self.error_bad_logical_op("or", "||", "disjunction"); + self.sess.emit_err(InvalidLogicalOperator { + span: self.token.span, + incorrect: "or".into(), + sub: InvalidLogicalOperatorSub::Disjunction(self.token.span), + }); (AssocOp::LOr, span) } _ => return None, @@ -453,19 +469,6 @@ impl<'a> Parser<'a> { Some(source_map::respan(span, op)) } - /// Error on `and` and `or` suggesting `&&` and `||` respectively. - fn error_bad_logical_op(&self, bad: &str, good: &str, english: &str) { - self.struct_span_err(self.token.span, &format!("`{bad}` is not a logical operator")) - .span_suggestion_short( - self.token.span, - &format!("use `{good}` to perform logical {english}"), - good, - Applicability::MachineApplicable, - ) - .note("unlike in e.g., python and PHP, `&&` and `||` are used for logical operators") - .emit(); - } - /// Checks if this expression is a successfully parsed statement. fn expr_is_complete(&self, e: &Expr) -> bool { self.restrictions.contains(Restrictions::STMT_EXPR) @@ -491,7 +494,7 @@ impl<'a> Parser<'a> { let limits = if op == AssocOp::DotDot { RangeLimits::HalfOpen } else { RangeLimits::Closed }; let range = self.mk_range(Some(lhs), rhs, limits); - Ok(self.mk_expr(span, range, AttrVec::new())) + Ok(self.mk_expr(span, range)) } fn is_at_start_of_range_notation_rhs(&self) -> bool { @@ -540,7 +543,7 @@ impl<'a> Parser<'a> { (lo, None) }; let range = this.mk_range(None, opt_end, limits); - Ok(this.mk_expr(span, range, attrs.into())) + Ok(this.mk_expr_with_attrs(span, range, attrs)) }) } @@ -553,7 +556,7 @@ impl<'a> Parser<'a> { ($this:ident, $attrs:expr, |this, _| $body:expr) => { $this.collect_tokens_for_expr($attrs, |$this, attrs| { let (hi, ex) = $body?; - Ok($this.mk_expr(lo.to(hi), ex, attrs.into())) + Ok($this.mk_expr_with_attrs(lo.to(hi), ex, attrs)) }) }; } @@ -630,14 +633,7 @@ impl<'a> Parser<'a> { // Recover on `!` suggesting for bitwise negation instead. fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { - self.struct_span_err(lo, "`~` cannot be used as a unary operator") - .span_suggestion_short( - lo, - "use `!` to perform bitwise not", - "!", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(TildeAsUnaryOperator(lo)); self.parse_unary_expr(lo, UnOp::Not) } @@ -663,20 +659,14 @@ 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 not_token = self.look_ahead(1, |t| t.clone()); - self.struct_span_err( - not_token.span, - &format!("unexpected {} after identifier", super::token_descr(¬_token)), - ) - .span_suggestion_short( + let negated_token = self.look_ahead(1, |t| t.clone()); + self.sess.emit_err(NotAsNegationOperator { + negated: negated_token.span, + negated_desc: super::token_descr(&negated_token), // Span the `not` plus trailing whitespace to avoid // trailing whitespace after the `!` in our suggestion - self.sess.source_map().span_until_non_whitespace(lo.to(not_token.span)), - "use `!` to perform logical negation", - "!", - Applicability::MachineApplicable, - ) - .emit(); + not: self.sess.source_map().span_until_non_whitespace(lo.to(negated_token.span)), + }); // ...and recover! self.parse_unary_expr(lo, UnOp::Not) @@ -705,11 +695,7 @@ impl<'a> Parser<'a> { expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind, ) -> PResult<'a, P<Expr>> { let mk_expr = |this: &mut Self, lhs: P<Expr>, rhs: P<Ty>| { - this.mk_expr( - this.mk_expr_sp(&lhs, lhs_span, rhs.span), - expr_kind(lhs, rhs), - AttrVec::new(), - ) + this.mk_expr(this.mk_expr_sp(&lhs, lhs_span, rhs.span), expr_kind(lhs, rhs)) }; // Save the state of the parser before parsing type normally, in case there is a @@ -737,17 +723,13 @@ impl<'a> Parser<'a> { segments[0].ident.span, ), }; - match self.parse_labeled_expr(label, AttrVec::new(), false) { + match self.parse_labeled_expr(label, false) { Ok(expr) => { type_err.cancel(); - self.struct_span_err(label.ident.span, "malformed loop label") - .span_suggestion( - label.ident.span, - "use the correct loop label format", - label.ident, - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(MalformedLoopLabel { + span: label.ident.span, + correct_label: label.ident, + }); return Ok(expr); } Err(err) => { @@ -925,15 +907,7 @@ impl<'a> Parser<'a> { } fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) { - self.struct_span_err(span, "borrow expressions cannot be annotated with lifetimes") - .span_label(lt_span, "annotated with lifetime here") - .span_suggestion( - lt_span, - "remove the lifetime annotation", - "", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(LifetimeInBorrowExpression { span, lifetime_span: lt_span }); } /// Parse `mut?` or `raw [ const | mut ]`. @@ -965,15 +939,15 @@ impl<'a> Parser<'a> { &mut self, e0: P<Expr>, lo: Span, - mut attrs: Vec<ast::Attribute>, + mut attrs: ast::AttrVec, ) -> PResult<'a, P<Expr>> { // Stitch the list of outer attributes onto the return value. // A little bit ugly, but the best way given the current code // structure self.parse_dot_or_call_expr_with_(e0, lo).map(|expr| { expr.map(|mut expr| { - attrs.extend::<Vec<_>>(expr.attrs.into()); - expr.attrs = attrs.into(); + attrs.extend(expr.attrs); + expr.attrs = attrs; expr }) }) @@ -990,7 +964,7 @@ impl<'a> Parser<'a> { }; if has_question { // `expr?` - e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e), AttrVec::new()); + e = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Try(e)); continue; } let has_dot = if self.prev_token.kind == TokenKind::Ident(kw::Return, false) { @@ -1168,7 +1142,7 @@ 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); - self.mk_expr(lo.to(span), field, AttrVec::new()) + self.mk_expr(lo.to(span), field) } /// Parse a function call expression, `expr(...)`. @@ -1182,9 +1156,9 @@ impl<'a> Parser<'a> { }; let open_paren = self.token.span; - let mut seq = self.parse_paren_expr_seq().map(|args| { - self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args), AttrVec::new()) - }); + let mut seq = self + .parse_paren_expr_seq() + .map(|args| self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))); if let Some(expr) = self.maybe_recover_struct_lit_bad_delims(lo, open_paren, &mut seq, snapshot) { @@ -1264,7 +1238,7 @@ impl<'a> Parser<'a> { let index = self.parse_expr()?; self.suggest_missing_semicolon_before_array(prev_span, open_delim_span)?; self.expect(&token::CloseDelim(Delimiter::Bracket))?; - Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index), AttrVec::new())) + Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index))) } /// Assuming we have just parsed `.`, continue parsing into an expression. @@ -1283,23 +1257,15 @@ impl<'a> Parser<'a> { 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), - AttrVec::new(), - )) + Ok(self.mk_expr(span, ExprKind::MethodCall(segment, self_arg, args, fn_span))) } else { // Field access `expr.f` if let Some(args) = segment.args { - self.struct_span_err( - args.span(), - "field expressions cannot have generic arguments", - ) - .emit(); + 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), AttrVec::new())) + Ok(self.mk_expr(span, ExprKind::Field(self_arg, segment.ident))) } } @@ -1314,10 +1280,6 @@ impl<'a> Parser<'a> { // Outer attributes are already parsed and will be // added to the return value after the fact. - // - // Therefore, prevent sub-parser from parsing - // attributes by giving them an empty "already-parsed" list. - let attrs = AttrVec::new(); // Note: when adding new syntax here, don't forget to adjust `TokenKind::can_begin_expr()`. let lo = self.token.span; @@ -1325,13 +1287,13 @@ impl<'a> Parser<'a> { // This match arm is a special-case of the `_` match arm below and // could be removed without changing functionality, but it's faster // to have it here, especially for programs with large constants. - self.parse_lit_expr(attrs) + self.parse_lit_expr() } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) { - self.parse_tuple_parens_expr(attrs) + self.parse_tuple_parens_expr() } else if self.check(&token::OpenDelim(Delimiter::Brace)) { - self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs) + self.parse_block_expr(None, lo, BlockCheckMode::Default) } else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) { - self.parse_closure_expr(attrs).map_err(|mut err| { + self.parse_closure_expr().map_err(|mut err| { // 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) { @@ -1340,65 +1302,66 @@ impl<'a> Parser<'a> { err }) } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { - self.parse_array_or_repeat_expr(attrs, Delimiter::Bracket) + self.parse_array_or_repeat_expr(Delimiter::Bracket) } else if self.check_path() { - self.parse_path_start_expr(attrs) + self.parse_path_start_expr() } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { - self.parse_closure_expr(attrs) + self.parse_closure_expr() } else if self.eat_keyword(kw::If) { - self.parse_if_expr(attrs) + self.parse_if_expr() } else if self.check_keyword(kw::For) { if self.choose_generics_over_qpath(1) { - self.parse_closure_expr(attrs) + self.parse_closure_expr() } else { assert!(self.eat_keyword(kw::For)); - self.parse_for_expr(None, self.prev_token.span, attrs) + self.parse_for_expr(None, self.prev_token.span) } } else if self.eat_keyword(kw::While) { - self.parse_while_expr(None, self.prev_token.span, attrs) + self.parse_while_expr(None, self.prev_token.span) } else if let Some(label) = self.eat_label() { - self.parse_labeled_expr(label, attrs, true) + self.parse_labeled_expr(label, true) } else if self.eat_keyword(kw::Loop) { let sp = self.prev_token.span; - self.parse_loop_expr(None, self.prev_token.span, attrs).map_err(|mut err| { + self.parse_loop_expr(None, self.prev_token.span).map_err(|mut err| { 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, attrs)) + 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(attrs).map_err(|mut err| { + self.parse_match_expr().map_err(|mut err| { err.span_label(match_sp, "while parsing this `match` expression"); err }) } else if self.eat_keyword(kw::Unsafe) { let sp = self.prev_token.span; - self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided), attrs) - .map_err(|mut err| { + self.parse_block_expr(None, lo, BlockCheckMode::Unsafe(ast::UserProvided)).map_err( + |mut err| { err.span_label(sp, "while parsing this `unsafe` expression"); err - }) + }, + ) } else if self.check_inline_const(0) { self.parse_const_block(lo.to(self.token.span), false) } else if self.is_do_catch_block() { - self.recover_do_catch(attrs) + self.recover_do_catch() } else if self.is_try_block() { self.expect_keyword(kw::Try)?; - self.parse_try_block(lo, attrs) + self.parse_try_block(lo) } else if self.eat_keyword(kw::Return) { - self.parse_return_expr(attrs) + self.parse_return_expr() } else if self.eat_keyword(kw::Break) { - self.parse_break_expr(attrs) + self.parse_break_expr() } else if self.eat_keyword(kw::Yield) { - self.parse_yield_expr(attrs) + self.parse_yield_expr() } else if self.is_do_yeet() { - self.parse_yeet_expr(attrs) + self.parse_yeet_expr() } else if self.check_keyword(kw::Let) { - self.parse_let_expr(attrs) + self.parse_let_expr() } else if self.eat_keyword(kw::Underscore) { - Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs)) + Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore)) } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) { // Don't complain about bare semicolons after unclosed braces // recovery in order to keep the error count down. Fixing the @@ -1417,32 +1380,32 @@ impl<'a> Parser<'a> { if self.check_keyword(kw::Async) { if self.is_async_block() { // Check for `async {` and `async move {`. - self.parse_async_block(attrs) + self.parse_async_block() } else { - self.parse_closure_expr(attrs) + self.parse_closure_expr() } } else if self.eat_keyword(kw::Await) { - self.recover_incorrect_await_syntax(lo, self.prev_token.span, attrs) + self.recover_incorrect_await_syntax(lo, self.prev_token.span) } else { - self.parse_lit_expr(attrs) + self.parse_lit_expr() } } else { - self.parse_lit_expr(attrs) + self.parse_lit_expr() } } - fn parse_lit_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + 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), attrs); + let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Lit(literal)); self.maybe_recover_from_bad_qpath(expr) } None => self.try_macro_suggestion(), } } - fn parse_tuple_parens_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_tuple_parens_expr(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; self.expect(&token::OpenDelim(Delimiter::Parenthesis))?; let (es, trailing_comma) = match self.parse_seq_to_end( @@ -1462,15 +1425,11 @@ impl<'a> Parser<'a> { // `(e,)` is a tuple with only one field, `e`. ExprKind::Tup(es) }; - let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs); + let expr = self.mk_expr(lo.to(self.prev_token.span), kind); self.maybe_recover_from_bad_qpath(expr) } - fn parse_array_or_repeat_expr( - &mut self, - attrs: AttrVec, - close_delim: Delimiter, - ) -> PResult<'a, P<Expr>> { + fn parse_array_or_repeat_expr(&mut self, close_delim: Delimiter) -> PResult<'a, P<Expr>> { let lo = self.token.span; self.bump(); // `[` or other open delim @@ -1499,45 +1458,42 @@ impl<'a> Parser<'a> { ExprKind::Array(vec![first_expr]) } }; - let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs); + let expr = self.mk_expr(lo.to(self.prev_token.span), kind); self.maybe_recover_from_bad_qpath(expr) } - fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_path_start_expr(&mut self) -> PResult<'a, P<Expr>> { let (qself, path) = if self.eat_lt() { let (qself, path) = self.parse_qpath(PathStyle::Expr)?; (Some(qself), path) } else { (None, self.parse_path(PathStyle::Expr)?) }; - let lo = path.span; // `!`, as an operator, is prefix, so we know this isn't that. - let (hi, kind) = if self.eat(&token::Not) { + let (span, kind) = if self.eat(&token::Not) { // MACRO INVOCATION expression if qself.is_some() { - self.struct_span_err(path.span, "macros cannot use qualified paths").emit(); + self.sess.emit_err(MacroInvocationWithQualifiedPath(path.span)); } - let mac = MacCall { + let lo = path.span; + let mac = P(MacCall { path, args: self.parse_mac_args()?, prior_type_ascription: self.last_type_ascription, - }; - (self.prev_token.span, ExprKind::MacCall(mac)) - } else if self.check(&token::OpenDelim(Delimiter::Brace)) { - if let Some(expr) = self.maybe_parse_struct_expr(qself.as_ref(), &path, &attrs) { + }); + (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) { if qself.is_some() { self.sess.gated_spans.gate(sym::more_qualified_paths, path.span); } return expr; - } else { - (path.span, ExprKind::Path(qself, path)) - } } else { (path.span, ExprKind::Path(qself, path)) }; - let expr = self.mk_expr(lo.to(hi), kind, attrs); + let expr = self.mk_expr(span, kind); self.maybe_recover_from_bad_qpath(expr) } @@ -1545,31 +1501,30 @@ impl<'a> Parser<'a> { fn parse_labeled_expr( &mut self, label: Label, - attrs: AttrVec, mut consume_colon: bool, ) -> PResult<'a, P<Expr>> { let lo = label.ident.span; let label = Some(label); let ate_colon = self.eat(&token::Colon); let expr = if self.eat_keyword(kw::While) { - self.parse_while_expr(label, lo, attrs) + self.parse_while_expr(label, lo) } else if self.eat_keyword(kw::For) { - self.parse_for_expr(label, lo, attrs) + self.parse_for_expr(label, lo) } else if self.eat_keyword(kw::Loop) { - self.parse_loop_expr(label, lo, attrs) + self.parse_loop_expr(label, lo) } else if self.check_noexpect(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() { - self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs) + self.parse_block_expr(label, lo, BlockCheckMode::Default) } else if !ate_colon && (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt)) { // We're probably inside of a `Path<'a>` that needs a turbofish - let msg = "expected `while`, `for`, `loop` or `{` after a label"; - self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); + self.sess.emit_err(UnexpectedTokenAfterLabel(self.token.span)); 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); @@ -1626,7 +1581,7 @@ impl<'a> Parser<'a> { // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to supress future errors about `break 'label`. let stmt = self.mk_stmt(span, StmtKind::Expr(expr)); let blk = self.mk_block(vec![stmt], BlockCheckMode::Default, span); - self.mk_expr(span, ExprKind::Block(blk, label), ThinVec::new()) + self.mk_expr(span, ExprKind::Block(blk, label)) }); err.emit(); @@ -1634,44 +1589,27 @@ impl<'a> Parser<'a> { }?; if !ate_colon && consume_colon { - self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span); + self.sess.emit_err(RequireColonAfterLabeledExpression { + span: expr.span, + label: lo, + label_end: lo.shrink_to_hi(), + }); } Ok(expr) } - fn error_labeled_expr_must_be_followed_by_colon(&self, lo: Span, span: Span) { - self.struct_span_err(span, "labeled expression must be followed by `:`") - .span_label(lo, "the label") - .span_suggestion_short( - lo.shrink_to_hi(), - "add `:` after the label", - ": ", - Applicability::MachineApplicable, - ) - .note("labels are used before loops and blocks, allowing e.g., `break 'label` to them") - .emit(); - } - /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead. - fn recover_do_catch(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn recover_do_catch(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; self.bump(); // `do` self.bump(); // `catch` - let span_dc = lo.to(self.prev_token.span); - self.struct_span_err(span_dc, "found removed `do catch` syntax") - .span_suggestion( - span_dc, - "replace with the new syntax", - "try", - Applicability::MachineApplicable, - ) - .note("following RFC #2388, the new non-placeholder syntax is `try`") - .emit(); + let span = lo.to(self.prev_token.span); + self.sess.emit_err(DoCatchSyntaxRemoved { span }); - self.parse_try_block(lo, attrs) + self.parse_try_block(lo) } /// Parse an expression if the token can begin one. @@ -1680,15 +1618,15 @@ impl<'a> Parser<'a> { } /// Parse `"return" expr?`. - fn parse_return_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_return_expr(&mut self) -> PResult<'a, P<Expr>> { let lo = self.prev_token.span; let kind = ExprKind::Ret(self.parse_expr_opt()?); - let expr = self.mk_expr(lo.to(self.prev_token.span), kind, attrs); + let expr = self.mk_expr(lo.to(self.prev_token.span), kind); self.maybe_recover_from_bad_qpath(expr) } /// Parse `"do" "yeet" expr?`. - fn parse_yeet_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_yeet_expr(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; self.bump(); // `do` @@ -1698,7 +1636,7 @@ impl<'a> Parser<'a> { let span = lo.to(self.prev_token.span); self.sess.gated_spans.gate(sym::yeet_expr, span); - let expr = self.mk_expr(span, kind, attrs); + let expr = self.mk_expr(span, kind); self.maybe_recover_from_bad_qpath(expr) } @@ -1710,13 +1648,13 @@ impl<'a> Parser<'a> { /// `break 'lbl: loop {}`); a labeled break with an unlabeled loop as its value /// expression only gets a warning for compatibility reasons; and a labeled break /// with a labeled loop does not even get a warning because there is no ambiguity. - fn parse_break_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + 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 { // 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(), AttrVec::new(), true)?; + 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", @@ -1758,17 +1696,17 @@ impl<'a> Parser<'a> { } else { None }; - let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Break(label, kind), attrs); + let expr = self.mk_expr(lo.to(self.prev_token.span), ExprKind::Break(label, kind)); self.maybe_recover_from_bad_qpath(expr) } /// Parse `"yield" expr?`. - fn parse_yield_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_yield_expr(&mut self) -> PResult<'a, P<Expr>> { let lo = self.prev_token.span; let kind = ExprKind::Yield(self.parse_expr_opt()?); let span = lo.to(self.prev_token.span); self.sess.gated_spans.gate(sym::generators, span); - let expr = self.mk_expr(span, kind, attrs); + let expr = self.mk_expr(span, kind); self.maybe_recover_from_bad_qpath(expr) } @@ -1780,8 +1718,8 @@ impl<'a> Parser<'a> { Some(lit) => match lit.kind { ast::LitKind::Str(symbol_unescaped, style) => Ok(ast::StrLit { style, - symbol: lit.token.symbol, - suffix: lit.token.suffix, + symbol: lit.token_lit.symbol, + suffix: lit.token_lit.suffix, span: lit.span, symbol_unescaped, }), @@ -1858,20 +1796,16 @@ impl<'a> Parser<'a> { 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_lit_token(lit, span).unwrap_or_else(|_| unreachable!())) + Some(Lit::from_token_lit(lit, span).unwrap_or_else(|_| unreachable!())) } } } fn error_float_lits_must_have_int_part(&self, token: &Token) { - self.struct_span_err(token.span, "float literals must have an integer part") - .span_suggestion( - token.span, - "must have an integer part", - pprust::token_to_string(token), - Applicability::MachineApplicable, - ) - .emit(); + 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) { @@ -1913,28 +1847,11 @@ impl<'a> Parser<'a> { let suf = suf.as_str(); if looks_like_width_suffix(&['i', 'u'], &suf) { // If it looks like a width, try to be helpful. - let msg = format!("invalid width `{}` for integer literal", &suf[1..]); - self.struct_span_err(span, &msg) - .help("valid widths are 8, 16, 32, 64 and 128") - .emit(); + self.sess.emit_err(InvalidIntLiteralWidth { span, width: suf[1..].into() }); } else if let Some(fixed) = fix_base_capitalisation(suf) { - let msg = "invalid base prefix for number literal"; - - self.struct_span_err(span, msg) - .note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase") - .span_suggestion( - span, - "try making the prefix lowercase", - fixed, - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(InvalidNumLiteralBasePrefix { span, fixed }); } else { - let msg = format!("invalid suffix `{suf}` for number literal"); - self.struct_span_err(span, &msg) - .span_label(span, format!("invalid suffix `{suf}`")) - .help("the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)") - .emit(); + self.sess.emit_err(InvalidNumLiteralSuffix { span, suffix: suf.to_string() }); } } LitError::InvalidFloatSuffix => { @@ -1942,14 +1859,10 @@ impl<'a> Parser<'a> { let suf = suf.as_str(); if looks_like_width_suffix(&['f'], suf) { // If it looks like a width, try to be helpful. - let msg = format!("invalid width `{}` for float literal", &suf[1..]); - self.struct_span_err(span, &msg).help("valid widths are 32 and 64").emit(); + self.sess + .emit_err(InvalidFloatLiteralWidth { span, width: suf[1..].to_string() }); } else { - let msg = format!("invalid suffix `{suf}` for float literal"); - self.struct_span_err(span, &msg) - .span_label(span, format!("invalid suffix `{suf}`")) - .help("valid suffixes are `f32` and `f64`") - .emit(); + self.sess.emit_err(InvalidFloatLiteralSuffix { span, suffix: suf.to_string() }); } } LitError::NonDecimalFloat(base) => { @@ -1964,7 +1877,7 @@ impl<'a> Parser<'a> { .emit(); } LitError::IntTooLarge => { - self.struct_span_err(span, "integer literal is too large").emit(); + self.sess.emit_err(IntLiteralTooLarge { span }); } } } @@ -2012,14 +1925,10 @@ 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), AttrVec::new()); + let expr = self.mk_expr(lit.span, ExprKind::Lit(lit)); if minus_present { - Ok(self.mk_expr( - lo.to(self.prev_token.span), - self.mk_unary(UnOp::Neg, expr), - AttrVec::new(), - )) + Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_unary(UnOp::Neg, expr))) } else { Ok(expr) } @@ -2034,13 +1943,9 @@ impl<'a> Parser<'a> { /// Emits a suggestion if it looks like the user meant an array but /// accidentally used braces, causing the code to be interpreted as a block /// expression. - fn maybe_suggest_brackets_instead_of_braces( - &mut self, - lo: Span, - attrs: AttrVec, - ) -> Option<P<Expr>> { + fn maybe_suggest_brackets_instead_of_braces(&mut self, lo: Span) -> Option<P<Expr>> { let mut snapshot = self.create_snapshot_for_diagnostic(); - match snapshot.parse_array_or_repeat_expr(attrs, Delimiter::Brace) { + 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") @@ -2084,14 +1989,10 @@ impl<'a> Parser<'a> { .span_to_snippet(snapshot.token.span) .map_or(false, |snippet| snippet == "]") => { - let mut err = self.struct_span_err(open_delim_span, "expected `;`, found `[`"); - err.span_suggestion_verbose( - prev_span.shrink_to_hi(), - "consider adding `;` here", - ';', - Applicability::MaybeIncorrect, - ); - return Err(err); + return Err(MissingSemicolonBeforeArray { + open_delim: open_delim_span, + semicolon: prev_span.shrink_to_hi(), + }.into_diagnostic(self.sess)); } Ok(_) => (), Err(err) => err.cancel(), @@ -2106,37 +2007,32 @@ impl<'a> Parser<'a> { opt_label: Option<Label>, lo: Span, blk_mode: BlockCheckMode, - mut attrs: AttrVec, ) -> PResult<'a, P<Expr>> { if self.is_array_like_block() { - if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo, attrs.clone()) { + if let Some(arr) = self.maybe_suggest_brackets_instead_of_braces(lo) { return Ok(arr); } } - if let Some(label) = opt_label { - self.sess.gated_spans.gate(sym::label_break_value, label.ident.span); - } - if self.token.is_whole_block() { - self.struct_span_err(self.token.span, "cannot use a `block` macro fragment here") - .span_label(lo.to(self.token.span), "the `block` fragment is within this context") - .emit(); + self.sess.emit_err(InvalidBlockMacroSegment { + span: self.token.span, + context: lo.to(self.token.span), + }); } - let (inner_attrs, blk) = self.parse_block_common(lo, blk_mode)?; - attrs.extend(inner_attrs); - Ok(self.mk_expr(blk.span, ExprKind::Block(blk, opt_label), attrs)) + let (attrs, blk) = self.parse_block_common(lo, blk_mode)?; + Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs)) } /// Parse a block which takes no attributes and has no label fn parse_simple_block(&mut self) -> PResult<'a, P<Expr>> { let blk = self.parse_block()?; - Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None), AttrVec::new())) + Ok(self.mk_expr(blk.span, ExprKind::Block(blk, None))) } /// Parses a closure expression (e.g., `move |args| expr`). - fn parse_closure_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_closure_expr(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; let binder = if self.check_keyword(kw::For) { @@ -2171,7 +2067,7 @@ impl<'a> Parser<'a> { _ => { // If an explicit return type is given, require a block to appear (RFC 968). let body_lo = self.token.span; - self.parse_block_expr(None, body_lo, BlockCheckMode::Default, AttrVec::new())? + self.parse_block_expr(None, body_lo, BlockCheckMode::Default)? } }; @@ -2202,7 +2098,6 @@ impl<'a> Parser<'a> { body, lo.to(decl_hi), ), - attrs, ); // Disable recovery for closure body @@ -2265,7 +2160,7 @@ impl<'a> Parser<'a> { Ok(( Param { - attrs: attrs.into(), + attrs, ty, pat, span: lo.to(this.prev_token.span), @@ -2278,19 +2173,13 @@ impl<'a> Parser<'a> { } /// Parses an `if` expression (`if` token already eaten). - fn parse_if_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_if_expr(&mut self) -> PResult<'a, P<Expr>> { let lo = self.prev_token.span; let cond = self.parse_cond_expr()?; - - self.parse_if_after_cond(attrs, lo, cond) + self.parse_if_after_cond(lo, cond) } - fn parse_if_after_cond( - &mut self, - attrs: AttrVec, - lo: Span, - mut cond: P<Expr>, - ) -> PResult<'a, P<Expr>> { + fn parse_if_after_cond(&mut self, lo: Span, mut cond: P<Expr>) -> PResult<'a, P<Expr>> { let cond_span = cond.span; // Tries to interpret `cond` as either a missing expression if it's a block, // or as an unfinished expression if it's a binop and the RHS is a block. @@ -2299,11 +2188,19 @@ impl<'a> Parser<'a> { let block = match &mut cond.kind { ExprKind::Binary(Spanned { span: binop_span, .. }, _, right) if let ExprKind::Block(_, None) = right.kind => { - this.error_missing_if_then_block(lo, cond_span.shrink_to_lo().to(*binop_span), true).emit(); + self.sess.emit_err(IfExpressionMissingThenBlock { + if_span: lo, + sub: IfExpressionMissingThenBlockSub::UnfinishedCondition( + cond_span.shrink_to_lo().to(*binop_span) + ), + }); std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi())) }, ExprKind::Block(_, None) => { - this.error_missing_if_cond(lo, cond_span).emit(); + self.sess.emit_err(IfExpressionMissingCondition { + if_span: self.sess.source_map().next_point(lo), + block_span: self.sess.source_map().start_point(cond_span), + }); std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi())) } _ => { @@ -2321,7 +2218,10 @@ impl<'a> Parser<'a> { if let Some(block) = recover_block_from_condition(self) { block } else { - self.error_missing_if_then_block(lo, cond_span, false).emit(); + self.sess.emit_err(IfExpressionMissingThenBlock { + if_span: lo, + sub: IfExpressionMissingThenBlockSub::AddThenBlock(cond_span.shrink_to_hi()), + }); self.mk_block_err(cond_span.shrink_to_hi()) } } else { @@ -2346,40 +2246,7 @@ impl<'a> Parser<'a> { block }; let els = if self.eat_keyword(kw::Else) { Some(self.parse_else_expr()?) } else { None }; - Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els), attrs)) - } - - fn error_missing_if_then_block( - &self, - if_span: Span, - cond_span: Span, - is_unfinished: bool, - ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - let mut err = self.struct_span_err( - if_span, - "this `if` expression is missing a block after the condition", - ); - if is_unfinished { - err.span_help(cond_span, "this binary operation is possibly unfinished"); - } else { - err.span_help(cond_span.shrink_to_hi(), "add a block here"); - } - err - } - - fn error_missing_if_cond( - &self, - lo: Span, - span: Span, - ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - let next_span = self.sess.source_map().next_point(lo); - let mut err = self.struct_span_err(next_span, "missing condition for `if` expression"); - err.span_label(next_span, "expected condition here"); - err.span_label( - self.sess.source_map().start_point(span), - "if this block is the condition of the `if` expression, then it must be followed by another block" - ); - err + Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::If(cond, thn, els))) } /// Parses the condition of a `if` or `while` expression. @@ -2388,7 +2255,7 @@ impl<'a> Parser<'a> { } /// Parses a `let $pat = $expr` pseudo-expression. - fn parse_let_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_let_expr(&mut self) -> PResult<'a, P<Expr>> { // This is a *approximate* heuristic that detects if `let` chains are // being parsed in the right position. It's approximate because it // doesn't deny all invalid `let` expressions, just completely wrong usages. @@ -2397,8 +2264,7 @@ impl<'a> Parser<'a> { TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _) ); if !self.restrictions.contains(Restrictions::ALLOW_LET) || not_in_chain { - self.struct_span_err(self.token.span, "expected expression, found `let` statement") - .emit(); + self.sess.emit_err(ExpectedExpressionFoundLet { span: self.token.span }); } self.bump(); // Eat `let` token @@ -2414,7 +2280,7 @@ impl<'a> Parser<'a> { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) })?; let span = lo.to(expr.span); - Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span), attrs)) + Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span))) } /// Parses an `else { ... }` expression (`else` token already eaten). @@ -2422,7 +2288,7 @@ impl<'a> Parser<'a> { let else_span = self.prev_token.span; // `else` let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery. let expr = if self.eat_keyword(kw::If) { - self.parse_if_expr(AttrVec::new())? + self.parse_if_expr()? } else if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) { self.parse_simple_block()? } else { @@ -2436,16 +2302,13 @@ impl<'a> Parser<'a> { if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) && classify::expr_requires_semi_to_be_stmt(&cond) => { - self.struct_span_err(first_tok_span, format!("expected `{{`, found {first_tok}")) - .span_label(else_span, "expected an `if` or a block after this `else`") - .span_suggestion( - cond.span.shrink_to_lo(), - "add an `if` if this is the condition of a chained `else if` statement", - "if ", - Applicability::MaybeIncorrect, - ) - .emit(); - self.parse_if_after_cond(AttrVec::new(), cond.span.shrink_to_lo(), cond)? + self.sess.emit_err(ExpectedElseBlock { + first_tok_span, + first_tok, + else_span, + condition_start: cond.span.shrink_to_lo(), + }); + self.parse_if_after_cond(cond.span.shrink_to_lo(), cond)? } Err(e) => { e.cancel(); @@ -2469,25 +2332,22 @@ impl<'a> Parser<'a> { branch_span: Span, attrs: &[ast::Attribute], ) { - let (span, last) = match attrs { + let (attributes, last) = match attrs { [] => return, [x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span), }; let ctx = if is_ctx_else { "else" } else { "if" }; - self.struct_span_err(last, "outer attributes are not allowed on `if` and `else` branches") - .span_label(branch_span, "the attributes are attached to this branch") - .span_label(ctx_span, format!("the branch belongs to this `{ctx}`")) - .span_suggestion(span, "remove the attributes", "", Applicability::MachineApplicable) - .emit(); + self.sess.emit_err(OuterAttributeNotAllowedOnIfElse { + last, + branch_span, + ctx_span, + ctx: ctx.to_string(), + attributes, + }); } /// 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, - mut attrs: AttrVec, - ) -> PResult<'a, P<Expr>> { + fn parse_for_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> { // Record whether we are about to parse `for (`. // This is used below for recovery in case of `for ( $stuff ) $block` // in which case we will suggest `for $stuff $block`. @@ -2510,63 +2370,51 @@ impl<'a> Parser<'a> { let pat = self.recover_parens_around_for_head(pat, begin_paren); - let (iattrs, loop_block) = self.parse_inner_attrs_and_block()?; - attrs.extend(iattrs); + let (attrs, loop_block) = self.parse_inner_attrs_and_block()?; let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label); - Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs)) + Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs)) } fn error_missing_in_for_loop(&mut self) { - let (span, msg, sugg) = if self.token.is_ident_named(sym::of) { + let (span, sub): (_, fn(_) -> _) = if self.token.is_ident_named(sym::of) { // Possibly using JS syntax (#75311). let span = self.token.span; self.bump(); - (span, "try using `in` here instead", "in") + (span, MissingInInForLoopSub::InNotOf) } else { - (self.prev_token.span.between(self.token.span), "try adding `in` here", " in ") + (self.prev_token.span.between(self.token.span), MissingInInForLoopSub::AddIn) }; - self.struct_span_err(span, "missing `in` in `for` loop") - .span_suggestion_short( - span, - msg, - sugg, - // Has been misleading, at least in the past (closed Issue #48492). - Applicability::MaybeIncorrect, - ) - .emit(); + + self.sess.emit_err(MissingInInForLoop { span, sub: sub(span) }); } /// Parses a `while` or `while let` expression (`while` token already eaten). - fn parse_while_expr( - &mut self, - opt_label: Option<Label>, - lo: Span, - mut attrs: AttrVec, - ) -> PResult<'a, P<Expr>> { + fn parse_while_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> { let cond = self.parse_cond_expr().map_err(|mut err| { err.span_label(lo, "while parsing the condition of this `while` expression"); err })?; - let (iattrs, body) = self.parse_inner_attrs_and_block().map_err(|mut err| { + let (attrs, body) = self.parse_inner_attrs_and_block().map_err(|mut err| { err.span_label(lo, "while parsing the body of this `while` expression"); err.span_label(cond.span, "this `while` condition successfully parsed"); err })?; - attrs.extend(iattrs); - Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::While(cond, body, opt_label), attrs)) + Ok(self.mk_expr_with_attrs( + lo.to(self.prev_token.span), + ExprKind::While(cond, body, opt_label), + attrs, + )) } /// Parses `loop { ... }` (`loop` token already eaten). - fn parse_loop_expr( - &mut self, - opt_label: Option<Label>, - lo: Span, - mut attrs: AttrVec, - ) -> PResult<'a, P<Expr>> { - let (iattrs, body) = self.parse_inner_attrs_and_block()?; - attrs.extend(iattrs); - Ok(self.mk_expr(lo.to(self.prev_token.span), ExprKind::Loop(body, opt_label), attrs)) + fn parse_loop_expr(&mut self, opt_label: Option<Label>, lo: Span) -> PResult<'a, P<Expr>> { + 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), + attrs, + )) } pub(crate) fn eat_label(&mut self) -> Option<Label> { @@ -2577,7 +2425,7 @@ impl<'a> Parser<'a> { } /// Parses a `match ... { ... }` expression (`match` token already eaten). - fn parse_match_expr(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_match_expr(&mut self) -> PResult<'a, P<Expr>> { let match_span = self.prev_token.span; let lo = self.prev_token.span; let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; @@ -2597,7 +2445,7 @@ impl<'a> Parser<'a> { return Err(e); } } - attrs.extend(self.parse_inner_attributes()?); + let attrs = self.parse_inner_attributes()?; let mut arms: Vec<Arm> = Vec::new(); while self.token != token::CloseDelim(Delimiter::Brace) { @@ -2611,13 +2459,17 @@ impl<'a> Parser<'a> { if self.token == token::CloseDelim(Delimiter::Brace) { self.bump(); } - return Ok(self.mk_expr(span, ExprKind::Match(scrutinee, arms), attrs)); + return Ok(self.mk_expr_with_attrs( + span, + ExprKind::Match(scrutinee, arms), + attrs, + )); } } } let hi = self.token.span; self.bump(); - Ok(self.mk_expr(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs)) + Ok(self.mk_expr_with_attrs(lo.to(hi), ExprKind::Match(scrutinee, arms), attrs)) } /// Attempt to recover from match arm body with statements and no surrounding braces. @@ -2785,7 +2637,7 @@ impl<'a> Parser<'a> { let span = body.span; return Ok(( ast::Arm { - attrs: attrs.into(), + attrs, pat, guard, body, @@ -2840,17 +2692,9 @@ impl<'a> Parser<'a> { .is_ok(); if pattern_follows && snapshot.check(&TokenKind::FatArrow) { err.cancel(); - this.struct_span_err( - hi.shrink_to_hi(), - "expected `,` following `match` arm", - ) - .span_suggestion( - hi.shrink_to_hi(), - "missing a comma here to end this `match` arm", - ",", - Applicability::MachineApplicable, - ) - .emit(); + this.sess.emit_err(MissingCommaAfterMatchArm { + span: hi.shrink_to_hi(), + }); return Ok(true); } } @@ -2863,7 +2707,7 @@ impl<'a> Parser<'a> { Ok(( ast::Arm { - attrs: attrs.into(), + attrs, pat, guard, body: expr, @@ -2877,21 +2721,14 @@ impl<'a> Parser<'a> { } /// Parses a `try {...}` expression (`try` token already eaten). - fn parse_try_block(&mut self, span_lo: Span, mut attrs: AttrVec) -> PResult<'a, P<Expr>> { - let (iattrs, body) = self.parse_inner_attrs_and_block()?; - attrs.extend(iattrs); + fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> { + let (attrs, body) = self.parse_inner_attrs_and_block()?; if self.eat_keyword(kw::Catch) { - let mut error = self.struct_span_err( - self.prev_token.span, - "keyword `catch` cannot follow a `try` block", - ); - error.help("try using `match` on the result of the `try` block instead"); - error.emit(); - Err(error) + Err(CatchAfterTry { span: self.prev_token.span }.into_diagnostic(self.sess)) } else { let span = span_lo.to(body.span); self.sess.gated_spans.gate(sym::try_blocks, span); - Ok(self.mk_expr(span, ExprKind::TryBlock(body), attrs)) + Ok(self.mk_expr_with_attrs(span, ExprKind::TryBlock(body), attrs)) } } @@ -2913,14 +2750,13 @@ impl<'a> Parser<'a> { } /// Parses an `async move? {...}` expression. - fn parse_async_block(&mut self, mut attrs: AttrVec) -> PResult<'a, P<Expr>> { + fn parse_async_block(&mut self) -> PResult<'a, P<Expr>> { let lo = self.token.span; self.expect_keyword(kw::Async)?; let capture_clause = self.parse_capture_clause()?; - let (iattrs, body) = self.parse_inner_attrs_and_block()?; - attrs.extend(iattrs); + let (attrs, body) = self.parse_inner_attrs_and_block()?; let kind = ExprKind::Async(capture_clause, DUMMY_NODE_ID, body); - Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs)) + Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs)) } fn is_async_block(&self) -> bool { @@ -2954,14 +2790,13 @@ impl<'a> Parser<'a> { &mut self, qself: Option<&ast::QSelf>, path: &ast::Path, - attrs: &AttrVec, ) -> Option<PResult<'a, P<Expr>>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); if struct_allowed || self.is_certainly_not_a_block() { if let Err(err) = self.expect(&token::OpenDelim(Delimiter::Brace)) { return Some(Err(err)); } - let expr = self.parse_struct_expr(qself.cloned(), path.clone(), attrs.clone(), true); + 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); @@ -3098,7 +2933,6 @@ impl<'a> Parser<'a> { &mut self, qself: Option<ast::QSelf>, pth: ast::Path, - attrs: AttrVec, recover: bool, ) -> PResult<'a, P<Expr>> { let lo = pth.span; @@ -3111,7 +2945,7 @@ impl<'a> Parser<'a> { } else { ExprKind::Struct(P(ast::StructExpr { qself, path: pth, fields, rest: base })) }; - Ok(self.mk_expr(span, expr, attrs)) + Ok(self.mk_expr(span, expr)) } /// Use in case of error after field-looking code: `S { foo: () with a }`. @@ -3139,18 +2973,10 @@ impl<'a> Parser<'a> { if self.token != token::Comma { return; } - self.struct_span_err( - span.to(self.prev_token.span), - "cannot use a comma after the base struct", - ) - .span_suggestion_short( - self.token.span, - "remove this comma", - "", - Applicability::MachineApplicable, - ) - .note("the base struct must always be the last field") - .emit(); + self.sess.emit_err(CommaAfterBaseStruct { + span: span.to(self.prev_token.span), + comma: self.token.span, + }); self.recover_stmt(); } @@ -3166,7 +2992,7 @@ impl<'a> Parser<'a> { // Mimic `x: x` for the `x` field shorthand. let ident = this.parse_ident_common(false)?; let path = ast::Path::from_ident(ident); - (ident, this.mk_expr(ident.span, ExprKind::Path(None, path), AttrVec::new())) + (ident, this.mk_expr(ident.span, ExprKind::Path(None, path))) } else { let ident = this.parse_field_name()?; this.error_on_eq_field_init(ident); @@ -3180,7 +3006,7 @@ impl<'a> Parser<'a> { span: lo.to(expr.span), expr, is_shorthand, - attrs: attrs.into(), + attrs, id: DUMMY_NODE_ID, is_placeholder: false, }, @@ -3196,43 +3022,18 @@ impl<'a> Parser<'a> { return; } - self.struct_span_err(self.token.span, "expected `:`, found `=`") - .span_suggestion( - field_name.span.shrink_to_hi().to(self.token.span), - "replace equals symbol with a colon", - ":", - Applicability::MachineApplicable, - ) - .emit(); + self.sess.emit_err(EqFieldInit { + span: self.token.span, + eq: field_name.span.shrink_to_hi().to(self.token.span), + }); } fn err_dotdotdot_syntax(&self, span: Span) { - self.struct_span_err(span, "unexpected token: `...`") - .span_suggestion( - span, - "use `..` for an exclusive range", - "..", - Applicability::MaybeIncorrect, - ) - .span_suggestion( - span, - "or `..=` for an inclusive range", - "..=", - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(DotDotDot { span }); } fn err_larrow_operator(&self, span: Span) { - self.struct_span_err(span, "unexpected token: `<-`") - .span_suggestion( - span, - "if you meant to write a comparison against a negative value, add a \ - space in between `<` and `-`", - "< -", - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(LeftArrowOperator { span }); } fn mk_assign_op(&self, binop: BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind { @@ -3271,17 +3072,21 @@ impl<'a> Parser<'a> { fn mk_await_expr(&mut self, self_arg: P<Expr>, lo: Span) -> P<Expr> { let span = lo.to(self.prev_token.span); - let await_expr = self.mk_expr(span, ExprKind::Await(self_arg), AttrVec::new()); + let await_expr = self.mk_expr(span, ExprKind::Await(self_arg)); self.recover_from_await_method_call(); await_expr } - pub(crate) fn mk_expr(&self, span: Span, kind: ExprKind, attrs: AttrVec) -> P<Expr> { + pub(crate) fn mk_expr_with_attrs(&self, span: Span, kind: ExprKind, attrs: AttrVec) -> P<Expr> { P(Expr { kind, span, attrs, id: DUMMY_NODE_ID, tokens: None }) } + pub(crate) fn mk_expr(&self, span: Span, kind: ExprKind) -> P<Expr> { + P(Expr { kind, span, attrs: AttrVec::new(), id: DUMMY_NODE_ID, tokens: None }) + } + pub(super) fn mk_expr_err(&self, span: Span) -> P<Expr> { - self.mk_expr(span, ExprKind::Err, AttrVec::new()) + self.mk_expr(span, ExprKind::Err) } /// Create expression span ensuring the span of the parent node @@ -3297,7 +3102,7 @@ impl<'a> Parser<'a> { fn collect_tokens_for_expr( &mut self, attrs: AttrWrapper, - f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, P<Expr>>, + f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, P<Expr>>, ) -> PResult<'a, P<Expr>> { self.collect_tokens_trailing_token(attrs, ForceCollect::No, |this, attrs| { let res = f(this, attrs)?; |
