X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;ds=sidebyside;f=compiler%2Frustc_parse%2Fsrc%2Fparser%2Fdiagnostics.rs;h=a8a1842f3771dc802b07eede0a120644551c2df0;hb=7287f929b9c4a684be6ea8980970de7693eef841;hp=21d5bec65f03a8f57d2a19d087eb17f08836d315;hpb=774655da5fabdef01f862c50d1796abbe59efb7d;p=rust.git diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 21d5bec65f0..a8a1842f377 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -5,6 +5,7 @@ SemiColonMode, SeqSep, TokenExpectType, TokenType, }; +use crate::lexer::UnmatchedBrace; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; @@ -21,6 +22,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, Ident}; use rustc_span::{MultiSpan, Span, SpanSnippetError, DUMMY_SP}; +use std::ops::{Deref, DerefMut}; use std::mem::take; @@ -154,6 +156,79 @@ pub fn no(&self) -> bool { } } +/// Information for emitting suggestions and recovering from +/// C-style `i++`, `--i`, etc. +#[derive(Debug, Copy, Clone)] +struct IncDecRecovery { + /// This increment/decrement is not a subexpression. + standalone: bool, + /// Is this an increment or decrement? + op: IncOrDec, + /// Is this pre- or postfix? + fixity: UnaryFixity, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum IncOrDec { + Inc, + // FIXME: `i--` recovery isn't implemented yet + #[allow(dead_code)] + Dec, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +enum UnaryFixity { + Pre, + Post, +} + +impl IncOrDec { + fn chr(&self) -> char { + match self { + Self::Inc => '+', + Self::Dec => '-', + } + } + + fn name(&self) -> &'static str { + match self { + Self::Inc => "increment", + Self::Dec => "decrement", + } + } +} + +impl std::fmt::Display for UnaryFixity { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Pre => write!(f, "prefix"), + Self::Post => write!(f, "postfix"), + } + } +} + +// SnapshotParser is used to create a snapshot of the parser +// without causing duplicate errors being emitted when the `Parser` +// is dropped. +pub(super) struct SnapshotParser<'a> { + parser: Parser<'a>, + unclosed_delims: Vec, +} + +impl<'a> Deref for SnapshotParser<'a> { + type Target = Parser<'a>; + + fn deref(&self) -> &Self::Target { + &self.parser + } +} + +impl<'a> DerefMut for SnapshotParser<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.parser + } +} + impl<'a> Parser<'a> { pub(super) fn span_err>( &self, @@ -179,6 +254,25 @@ pub(super) fn diagnostic(&self) -> &'a Handler { &self.sess.span_diagnostic } + /// Relace `self` with `snapshot.parser` and extend `unclosed_delims` with `snapshot.unclosed_delims`. + /// This is to avoid losing unclosed delims errors `create_snapshot_for_diagnostic` clears. + pub(super) fn restore_snapshot(&mut self, snapshot: SnapshotParser<'a>) { + *self = snapshot.parser; + self.unclosed_delims.extend(snapshot.unclosed_delims.clone()); + } + + /// Create a snapshot of the `Parser`. + pub(super) fn create_snapshot_for_diagnostic(&self) -> SnapshotParser<'a> { + let mut snapshot = self.clone(); + let unclosed_delims = self.unclosed_delims.clone(); + // Clear `unclosed_delims` in snapshot to avoid + // duplicate errors being emitted when the `Parser` + // is dropped (which may or may not happen, depending + // if the parsing the snapshot is created for is successful) + snapshot.unclosed_delims.clear(); + SnapshotParser { parser: snapshot, unclosed_delims } + } + pub(super) fn span_to_snippet(&self, span: Span) -> Result { self.sess.source_map().span_to_snippet(span) } @@ -327,8 +421,8 @@ fn tokens_to_string(tokens: &[TokenType]) -> String { expect.clone() }; ( - format!("expected one of {}, found {}", expect, actual), - (self.prev_token.span.shrink_to_hi(), format!("expected one of {}", short_expect)), + format!("expected one of {expect}, found {actual}"), + (self.prev_token.span.shrink_to_hi(), format!("expected one of {short_expect}")), ) } else if expected.is_empty() { ( @@ -337,8 +431,8 @@ fn tokens_to_string(tokens: &[TokenType]) -> String { ) } else { ( - format!("expected {}, found {}", expect, actual), - (self.prev_token.span.shrink_to_hi(), format!("expected {}", expect)), + format!("expected {expect}, found {actual}"), + (self.prev_token.span.shrink_to_hi(), format!("expected {expect}")), ) }; self.last_unexpected_token_span = Some(self.token.span); @@ -421,7 +515,7 @@ fn check_too_many_raw_str_terminators(&mut self, err: &mut Diagnostic) -> bool { String::new(), Applicability::MachineApplicable, ); - err.note(&format!("the raw string started with {} `#`s", n_hashes)); + err.note(&format!("the raw string started with {n_hashes} `#`s")); true } _ => false, @@ -438,7 +532,7 @@ pub fn maybe_suggest_struct_literal( // fn foo() -> Foo { // field: value, // } - let mut snapshot = self.clone(); + let mut snapshot = self.create_snapshot_for_diagnostic(); let path = Path { segments: vec![], span: self.prev_token.span.shrink_to_lo(), tokens: None }; let struct_expr = snapshot.parse_struct_expr(None, path, AttrVec::new(), false); @@ -464,7 +558,7 @@ pub fn maybe_suggest_struct_literal( Applicability::MaybeIncorrect, ) .emit(); - *self = snapshot; + self.restore_snapshot(snapshot); let mut tail = self.mk_block( vec![self.mk_stmt_err(expr.span)], s, @@ -678,7 +772,7 @@ pub(super) fn check_trailing_angle_brackets( /// angle brackets. pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut PathSegment) { if token::ModSep == self.token.kind && segment.args.is_none() { - let snapshot = self.clone(); + let snapshot = self.create_snapshot_for_diagnostic(); self.bump(); let lo = self.token.span; match self.parse_angle_args(None) { @@ -712,14 +806,14 @@ pub(super) fn check_turbofish_missing_angle_brackets(&mut self, segment: &mut Pa .emit(); } else { // This doesn't look like an invalid turbofish, can't recover parse state. - *self = snapshot; + self.restore_snapshot(snapshot); } } Err(err) => { // We couldn't parse generic parameters, unlikely to be a turbofish. Rely on // generic parse error instead. err.cancel(); - *self = snapshot; + self.restore_snapshot(snapshot); } } } @@ -825,7 +919,7 @@ fn attempt_chained_comparison_suggestion( // `x == y < z` (BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => { // Consume `z`/outer-op-rhs. - let snapshot = self.clone(); + let snapshot = self.create_snapshot_for_diagnostic(); match self.parse_expr() { Ok(r2) => { // We are sure that outer-op-rhs could be consumed, the suggestion is @@ -835,14 +929,14 @@ fn attempt_chained_comparison_suggestion( } Err(expr_err) => { expr_err.cancel(); - *self = snapshot; + self.restore_snapshot(snapshot); false } } } // `x > y == z` (BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => { - let snapshot = self.clone(); + let snapshot = self.create_snapshot_for_diagnostic(); // At this point it is always valid to enclose the lhs in parentheses, no // further checks are necessary. match self.parse_expr() { @@ -852,7 +946,7 @@ fn attempt_chained_comparison_suggestion( } Err(expr_err) => { expr_err.cancel(); - *self = snapshot; + self.restore_snapshot(snapshot); false } } @@ -917,7 +1011,7 @@ pub(super) fn check_no_chained_comparison( || outer_op.node == AssocOp::Greater { if outer_op.node == AssocOp::Less { - let snapshot = self.clone(); + let snapshot = self.create_snapshot_for_diagnostic(); self.bump(); // So far we have parsed `foo(` or `foo< bar >::`, so we rewind the // parser and bail out. - *self = snapshot.clone(); + self.restore_snapshot(snapshot); } } return if token::ModSep == self.token.kind { @@ -937,7 +1031,7 @@ pub(super) fn check_no_chained_comparison( // `foo< bar >::` suggest(&mut err); - let snapshot = self.clone(); + let snapshot = self.create_snapshot_for_diagnostic(); self.bump(); // `::` // Consume the rest of the likely `foo::new()` or return at `foo`. @@ -954,7 +1048,7 @@ pub(super) fn check_no_chained_comparison( expr_err.cancel(); // Not entirely sure now, but we bubble the error up with the // suggestion. - *self = snapshot; + self.restore_snapshot(snapshot); Err(err) } } @@ -1008,7 +1102,7 @@ pub(super) fn check_no_chained_comparison( } fn consume_fn_args(&mut self) -> Result<(), ()> { - let snapshot = self.clone(); + let snapshot = self.create_snapshot_for_diagnostic(); self.bump(); // `(` // Consume the fn call arguments. @@ -1018,7 +1112,7 @@ fn consume_fn_args(&mut self) -> Result<(), ()> { if self.token.kind == token::Eof { // Not entirely sure that what we consumed were fn arguments, rollback. - *self = snapshot; + self.restore_snapshot(snapshot); Err(()) } else { // 99% certain that the suggestion is correct, continue parsing. @@ -1124,6 +1218,122 @@ pub(super) fn maybe_recover_from_bad_type_plus( Ok(()) } + pub(super) fn maybe_recover_from_prefix_increment( + &mut self, + operand_expr: P, + op_span: Span, + prev_is_semi: bool, + ) -> PResult<'a, P> { + let kind = IncDecRecovery { + standalone: prev_is_semi, + op: IncOrDec::Inc, + fixity: UnaryFixity::Pre, + }; + + self.recover_from_inc_dec(operand_expr, kind, op_span) + } + + pub(super) fn maybe_recover_from_postfix_increment( + &mut self, + operand_expr: P, + op_span: Span, + prev_is_semi: bool, + ) -> PResult<'a, P> { + let kind = IncDecRecovery { + standalone: prev_is_semi, + op: IncOrDec::Inc, + fixity: UnaryFixity::Post, + }; + + self.recover_from_inc_dec(operand_expr, kind, op_span) + } + + fn recover_from_inc_dec( + &mut self, + base: P, + kind: IncDecRecovery, + op_span: Span, + ) -> PResult<'a, P> { + let mut err = self.struct_span_err( + op_span, + &format!("Rust has no {} {} operator", kind.fixity, kind.op.name()), + ); + err.span_label(op_span, &format!("not a valid {} operator", kind.fixity)); + + let help_base_case = |mut err: DiagnosticBuilder<'_>, base| { + err.help(&format!("use `{}= 1` instead", kind.op.chr())); + err.emit(); + Ok(base) + }; + + // (pre, post) + let spans = match kind.fixity { + UnaryFixity::Pre => (op_span, base.span.shrink_to_hi()), + UnaryFixity::Post => (base.span.shrink_to_lo(), op_span), + }; + + if kind.standalone { + self.inc_dec_standalone_recovery(err, kind, spans) + } else { + let Ok(base_src) = self.span_to_snippet(base.span) + else { return help_base_case(err, base) }; + match kind.fixity { + UnaryFixity::Pre => self.prefix_inc_dec_suggest(base_src, err, kind, spans), + UnaryFixity::Post => self.postfix_inc_dec_suggest(base_src, err, kind, spans), + } + } + } + + fn prefix_inc_dec_suggest( + &mut self, + base_src: String, + mut err: DiagnosticBuilder<'a>, + kind: IncDecRecovery, + (pre_span, post_span): (Span, Span), + ) -> PResult<'a, P> { + err.multipart_suggestion( + &format!("use `{}= 1` instead", kind.op.chr()), + vec![ + (pre_span, "{ ".to_string()), + (post_span, format!(" {}= 1; {} }}", kind.op.chr(), base_src)), + ], + Applicability::MachineApplicable, + ); + Err(err) + } + + fn postfix_inc_dec_suggest( + &mut self, + base_src: String, + mut err: DiagnosticBuilder<'a>, + kind: IncDecRecovery, + (pre_span, post_span): (Span, Span), + ) -> PResult<'a, P> { + err.multipart_suggestion( + &format!("use `{}= 1` instead", kind.op.chr()), + vec![ + (pre_span, "{ let tmp = ".to_string()), + (post_span, format!("; {} {}= 1; tmp }}", base_src, kind.op.chr())), + ], + Applicability::MachineApplicable, + ); + Err(err) + } + + fn inc_dec_standalone_recovery( + &mut self, + mut err: DiagnosticBuilder<'a>, + kind: IncDecRecovery, + (pre_span, post_span): (Span, Span), + ) -> PResult<'a, P> { + err.multipart_suggestion( + &format!("use `{}= 1` instead", kind.op.chr()), + vec![(pre_span, String::new()), (post_span, format!(" {}= 1", kind.op.chr()))], + Applicability::MachineApplicable, + ); + Err(err) + } + /// Tries to recover from associated item paths like `[T]::AssocItem` / `(T, U)::AssocItem`. /// Attempts to convert the base expression/pattern/type into a type, parses the `::AssocItem` /// tail, and combines them into a `::AssocItem` expression/pattern/type. @@ -1191,7 +1401,7 @@ pub fn maybe_consume_incorrect_semicolon(&mut self, items: &[P]) -> bool { _ => None, }; if let Some(name) = previous_item_kind_name { - err.help(&format!("{} declarations are not followed by a semicolon", name)); + err.help(&format!("{name} declarations are not followed by a semicolon")); } } err.emit(); @@ -1226,12 +1436,12 @@ pub(super) fn unexpected_try_recover( "expected `{}`, found {}", token_str, match (&self.token.kind, self.subparser_name) { - (token::Eof, Some(origin)) => format!("end of {}", origin), + (token::Eof, Some(origin)) => format!("end of {origin}"), _ => this_token_str, }, ); let mut err = self.struct_span_err(sp, &msg); - let label_exp = format!("expected `{}`", token_str); + let label_exp = format!("expected `{token_str}`"); match self.recover_closing_delimiter(&[t.clone()], err) { Err(e) => err = e, Ok(recovered) => { @@ -1368,7 +1578,7 @@ pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P> { Applicability::MachineApplicable, ); } - err.span_suggestion(lo.shrink_to_lo(), &format!("{}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax", prefix), "r#".to_string(), Applicability::MachineApplicable); + err.span_suggestion(lo.shrink_to_lo(), &format!("{prefix}you can still access the deprecated `try!()` macro using the \"raw identifier\" syntax"), "r#".to_string(), Applicability::MachineApplicable); err.emit(); Ok(self.mk_expr_err(lo.to(hi))) } else { @@ -1504,7 +1714,7 @@ pub(super) fn recover_closing_delimiter( delim.retain(|c| c != '`'); err.span_suggestion_short( self.prev_token.span.shrink_to_hi(), - &format!("`{}` may belong here", delim), + &format!("`{delim}` may belong here"), delim, Applicability::MaybeIncorrect, ); @@ -1698,7 +1908,7 @@ pub(super) fn parameter_without_type( ( ident, "self: ".to_string(), - format!("{}: &{}TypeName", ident, mutab), + format!("{ident}: &{mutab}TypeName"), "_: ".to_string(), pat.span.shrink_to_lo(), pat.span, @@ -1826,7 +2036,7 @@ pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a, ErrorGua let (span, msg) = match (&self.token.kind, self.subparser_name) { (&token::Eof, Some(origin)) => { let sp = self.sess.source_map().next_point(self.prev_token.span); - (sp, format!("expected expression, found end of {}", origin)) + (sp, format!("expected expression, found end of {origin}")) } _ => ( self.token.span, @@ -1959,12 +2169,12 @@ pub fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P> } fn recover_const_param_decl(&mut self, ty_generics: Option<&Generics>) -> Option { - let snapshot = self.clone(); + let snapshot = self.create_snapshot_for_diagnostic(); let param = match self.parse_const_param(vec![]) { Ok(param) => param, Err(err) => { err.cancel(); - *self = snapshot; + self.restore_snapshot(snapshot); return None; } }; @@ -1975,8 +2185,8 @@ fn recover_const_param_decl(&mut self, ty_generics: Option<&Generics>) -> Option (ty_generics, self.sess.source_map().span_to_snippet(param.span())) { let (span, sugg) = match &generics.params[..] { - [] => (generics.span, format!("<{}>", snippet)), - [.., generic] => (generic.span().shrink_to_hi(), format!(", {}", snippet)), + [] => (generics.span, format!("<{snippet}>")), + [.., generic] => (generic.span().shrink_to_hi(), format!(", {snippet}")), }; err.multipart_suggestion( "`const` parameters must be declared for the `impl`", @@ -2056,7 +2266,7 @@ pub fn recover_const_arg( // We perform these checks and early return to avoid taking a snapshot unnecessarily. return Err(err); } - let snapshot = self.clone(); + let snapshot = self.create_snapshot_for_diagnostic(); if is_op_or_dot { self.bump(); } @@ -2101,7 +2311,7 @@ pub fn recover_const_arg( err.cancel(); } } - *self = snapshot; + self.restore_snapshot(snapshot); Err(err) } @@ -2161,7 +2371,7 @@ pub(super) fn incorrect_move_async_order_found( let span = self.token.span; // We only emit "unexpected `:`" error here if we can successfully parse the // whole pattern correctly in that case. - let snapshot = self.clone(); + let snapshot = self.create_snapshot_for_diagnostic(); // Create error for "unexpected `:`". match self.expected_one_of_not_found(&[], &[]) { @@ -2173,7 +2383,7 @@ pub(super) fn incorrect_move_async_order_found( // reasonable error. inner_err.cancel(); err.cancel(); - *self = snapshot; + self.restore_snapshot(snapshot); } Ok(mut pat) => { // We've parsed the rest of the pattern. @@ -2252,7 +2462,7 @@ pub(super) fn incorrect_move_async_order_found( } _ => { // Carry on as if we had not done anything. This should be unreachable. - *self = snapshot; + self.restore_snapshot(snapshot); } }; first_pat