| use rustc_ast::util::parser::AssocOp; |
| use rustc_ast::{BinOpKind, Expr, ExprKind, token}; |
| use rustc_errors::{Applicability, Diag, PResult}; |
| use rustc_span::{Span, Spanned, respan, sym}; |
| |
| use crate::parser::Parser; |
| use crate::{diagnostics, exp}; |
| |
| impl<'a> Parser<'a> { |
| /// Recover from alphabetic logic operators `and` and `or` as found in e.g., Python and PHP. |
| pub(super) fn recover_from_alpha_logic_op(&self) -> Option<Spanned<AssocOp>> { |
| if self.may_recover() |
| && let Some(ident) = self.token.non_raw_ident() |
| { |
| let (op, sub): (_, fn(_) -> _) = match ident.name { |
| sym::and => (BinOpKind::And, diagnostics::InvalidLogicalOperatorSub::Conjunction), |
| sym::or => (BinOpKind::Or, diagnostics::InvalidLogicalOperatorSub::Disjunction), |
| _ => return None, |
| }; |
| |
| self.dcx().emit_err(diagnostics::InvalidLogicalOperator { |
| span: self.token.span, |
| incorrect: ident.name, |
| sub: sub(self.token.span), |
| }); |
| |
| Some(respan(self.token.span, AssocOp::Binary(op))) |
| } else { |
| None |
| } |
| } |
| |
| /// Reject `...` being used as an expression operator. |
| pub(super) fn reject_dotdotdot_expr_op(&self) { |
| if self.token == token::DotDotDot { |
| self.dcx().emit_err(diagnostics::DotDotDotExprOp { span: self.token.span }); |
| } |
| } |
| |
| /// Reject `<-` being used as an expression operator. |
| pub(super) fn reject_larrow_expr_op(&self) { |
| if self.token == token::LArrow { |
| self.dcx().emit_err(diagnostics::LArrowExprOp { span: self.token.span }); |
| } |
| } |
| |
| /// Recover from strict equality operators `===` and `!==` as found in e.g., JS and PHP. |
| pub(super) fn recover_from_strict_eq_op(&mut self, op: Spanned<AssocOp>) { |
| if let AssocOp::Binary(bop @ BinOpKind::Eq | bop @ BinOpKind::Ne) = op.node |
| && self.token == token::Eq |
| && self.prev_token.span.hi() == self.token.span.lo() |
| { |
| let sp = op.span.to(self.token.span); |
| let sugg = bop.as_str().into(); |
| let invalid = format!("{sugg}="); |
| self.dcx().emit_err(diagnostics::InvalidComparisonOperator { |
| span: sp, |
| invalid: invalid.clone(), |
| sub: diagnostics::InvalidComparisonOperatorSub::Correctable { |
| span: sp, |
| invalid, |
| correct: sugg, |
| }, |
| }); |
| self.bump(); |
| } |
| } |
| |
| /// Recover from inequality operator `<>` ("diamond") as found in e.g., PHP. |
| pub(super) fn recover_from_diamond_ne_op(&mut self) { |
| if let (token::Lt, token::Gt) = (self.prev_token.kind, self.token.kind) |
| && self.prev_token.span.hi() == self.token.span.lo() |
| { |
| let sp = self.prev_token.span.to(self.token.span); |
| self.dcx().emit_err(diagnostics::InvalidComparisonOperator { |
| span: sp, |
| invalid: "<>".into(), |
| sub: diagnostics::InvalidComparisonOperatorSub::Correctable { |
| span: sp, |
| invalid: "<>".into(), |
| correct: "!=".into(), |
| }, |
| }); |
| self.bump(); |
| } |
| } |
| |
| /// Recover from comparison operator `<=>` ("spaceship") as found in e.g., C++. |
| pub(super) fn recover_from_spaceship_cmp_op(&mut self) { |
| if let (token::Le, token::Gt) = (self.prev_token.kind, self.token.kind) |
| && self.prev_token.span.hi() == self.token.span.lo() |
| { |
| let sp = self.prev_token.span.to(self.token.span); |
| self.dcx().emit_err(diagnostics::InvalidComparisonOperator { |
| span: sp, |
| invalid: "<=>".into(), |
| sub: diagnostics::InvalidComparisonOperatorSub::Spaceship(sp), |
| }); |
| self.bump(); |
| } |
| } |
| |
| /// Recover from postfix increment operator `++` as found in many C-style languages. |
| pub(super) fn recover_from_postfix_inc_op( |
| &mut self, |
| lhs: &Expr, |
| starts_stmt: bool, |
| ) -> PResult<'a, ()> { |
| if let (token::Plus, token::Plus) = (self.prev_token.kind, self.token.kind) |
| && self.prev_token.span.hi() == self.token.span.lo() |
| { |
| let op_span = self.prev_token.span.to(self.token.span); |
| self.bump(); // eat the second `+` |
| Err(self.report_inc_dec_op(lhs, starts_stmt, IncOrDec::Inc, UnaryFixity::Post, op_span)) |
| } else { |
| Ok(()) |
| } |
| } |
| |
| /// Recover from postfix decrement operator `--` as found in many C-style languages. |
| pub(super) fn recover_from_postfix_dec_op( |
| &mut self, |
| lhs: &Expr, |
| starts_stmt: bool, |
| ) -> PResult<'a, ()> { |
| if let (token::Minus, token::Minus) = (self.prev_token.kind, self.token.kind) |
| && self.prev_token.span.hi() == self.token.span.lo() |
| && !self.look_ahead(1, |tok| tok.can_begin_expr()) |
| { |
| let op_span = self.prev_token.span.to(self.token.span); |
| self.bump(); // eat the second `-` |
| Err(self.report_inc_dec_op(lhs, starts_stmt, IncOrDec::Dec, UnaryFixity::Post, op_span)) |
| } else { |
| Ok(()) |
| } |
| } |
| |
| /// Report increment operator `++` & decrement operator `--` as found in many C-style languages. |
| pub(super) fn report_inc_dec_op( |
| &mut self, |
| base: &Expr, |
| starts_stmt: bool, |
| op: IncOrDec, |
| fixity: UnaryFixity, |
| op_span: Span, |
| ) -> Diag<'a> { |
| // FIXME: Don't return an error diag, emit the diag here *and* return a new expr of the form |
| // `$base += 1` / `$base -= 1` (taking `base: Expr` by value) for *proper* recovery. |
| // (Just emitting the diag would be insufficient since callers would most likely just |
| // use `$base` as the recovered AST node which would lead to annoying follow-up diags |
| // like "variable doesn't need to be mutable" getting emitted in some cases.) |
| |
| let mut err = { |
| let fixity = match fixity { |
| UnaryFixity::Pre => "prefix", |
| UnaryFixity::Post => "postfix", |
| }; |
| let op = match op { |
| IncOrDec::Inc => "increment", |
| IncOrDec::Dec => "decrement", |
| }; |
| self.dcx() |
| .struct_span_err(op_span, format!("Rust has no {fixity} {op} operator")) |
| .with_span_label(op_span, format!("not a valid {fixity} operator")) |
| }; |
| |
| let op = match op { |
| IncOrDec::Inc => "+= 1", |
| IncOrDec::Dec => "-= 1", |
| }; |
| let (pre_span, post_span) = match fixity { |
| UnaryFixity::Pre => (op_span, base.span.shrink_to_hi()), |
| UnaryFixity::Post => (base.span.shrink_to_lo(), op_span), |
| }; |
| |
| if starts_stmt { |
| let mut patches = Vec::new(); |
| if !pre_span.is_empty() { |
| patches.push((pre_span, String::new())); |
| } |
| patches.push((post_span, format!(" {op}"))); |
| err.multipart_suggestion( |
| format!("use `{op}` instead"), |
| patches, |
| Applicability::MachineApplicable, |
| ); |
| } else { |
| let Ok(base_src) = self.span_to_snippet(base.span) else { |
| err.help(format!("use `{op}` instead")); |
| return err; |
| }; |
| match fixity { |
| UnaryFixity::Pre => { |
| err.multipart_suggestion( |
| format!("use `{op}` instead"), |
| vec![(pre_span, "{ ".into()), (post_span, format!(" {op}; {base_src} }}"))], |
| Applicability::MachineApplicable, |
| ); |
| } |
| UnaryFixity::Post => { |
| // won't suggest since we can not handle the precedences |
| // for example: `a + b++` has been parsed (a + b)++ and we can not suggest here |
| if !matches!(base.kind, ExprKind::Binary(..)) { |
| let tmp_var = if base_src.trim() == "tmp" { "tmp_" } else { "tmp" }; |
| err.multipart_suggestion( |
| format!("use `{op}` instead"), |
| vec![ |
| (pre_span, format!("{{ let {tmp_var} = ")), |
| (post_span, format!("; {base_src} {op}; {tmp_var} }}")), |
| ], |
| Applicability::HasPlaceholders, |
| ); |
| } |
| } |
| } |
| } |
| err |
| } |
| |
| /// Recover from array expressions as found in C like `{0, 1, 2, 3}`. |
| pub(super) fn recover_from_c_array(&mut self, lo: Span) -> Option<Box<Expr>> { |
| if !self.may_recover() |
| || self.token.kind != token::OpenBrace |
| || self.look_ahead(1, |t| !matches!(t.kind, token::Literal(_))) |
| || self.look_ahead(2, |t| t != &token::Comma) |
| || self.look_ahead(3, |t| !t.can_begin_expr()) |
| { |
| return None; |
| } |
| |
| let mut snapshot = self.create_snapshot_for_diagnostic(); |
| match snapshot.parse_expr_array_or_repeat(exp!(CloseBrace)) { |
| Ok(arr) => { |
| let guar = self.dcx().emit_err(diagnostics::ArrayBracketsInsteadOfBraces { |
| span: arr.span, |
| sub: diagnostics::ArrayBracketsInsteadOfBracesSugg { |
| left: lo, |
| right: snapshot.prev_token.span, |
| }, |
| }); |
| |
| self.restore_snapshot(snapshot); |
| Some(self.mk_expr_err(arr.span, guar)) |
| } |
| Err(e) => { |
| e.cancel(); |
| None |
| } |
| } |
| } |
| } |
| |
| #[derive(Copy, Clone)] |
| pub(super) enum IncOrDec { |
| Inc, |
| Dec, |
| } |
| |
| #[derive(Copy, Clone)] |
| pub(super) enum UnaryFixity { |
| Pre, |
| Post, |
| } |