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};
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;
}
}
+/// 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<UnmatchedBrace>,
+}
+
+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<S: Into<MultiSpan>>(
&self,
&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<String, SpanSnippetError> {
self.sess.source_map().span_to_snippet(span)
}
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() {
(
)
} 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);
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,
// 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);
Applicability::MaybeIncorrect,
)
.emit();
- *self = snapshot;
+ self.restore_snapshot(snapshot);
let mut tail = self.mk_block(
vec![self.mk_stmt_err(expr.span)],
s,
/// 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) {
.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);
}
}
}
// `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
}
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() {
}
Err(expr_err) => {
expr_err.cancel();
- *self = snapshot;
+ self.restore_snapshot(snapshot);
false
}
}
|| 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<bar<`, consume the rest of the type args.
let modifiers =
{
// We don't have `foo< bar >(` 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 {
// `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<bar>::new()` or return at `foo<bar>`.
expr_err.cancel();
// Not entirely sure now, but we bubble the error up with the
// suggestion.
- *self = snapshot;
+ self.restore_snapshot(snapshot);
Err(err)
}
}
}
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.
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.
Ok(())
}
+ pub(super) fn maybe_recover_from_prefix_increment(
+ &mut self,
+ operand_expr: P<Expr>,
+ op_span: Span,
+ prev_is_semi: bool,
+ ) -> PResult<'a, P<Expr>> {
+ 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<Expr>,
+ op_span: Span,
+ prev_is_semi: bool,
+ ) -> PResult<'a, P<Expr>> {
+ 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<Expr>,
+ kind: IncDecRecovery,
+ op_span: Span,
+ ) -> PResult<'a, P<Expr>> {
+ 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<Expr>> {
+ 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<Expr>> {
+ 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<Expr>> {
+ 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 `<Ty>::AssocItem` expression/pattern/type.
_ => 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();
"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) => {
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 {
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,
);
(
ident,
"self: ".to_string(),
- format!("{}: &{}TypeName", ident, mutab),
+ format!("{ident}: &{mutab}TypeName"),
"_: ".to_string(),
pat.span.shrink_to_lo(),
pat.span,
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,
}
fn recover_const_param_decl(&mut self, ty_generics: Option<&Generics>) -> Option<GenericArg> {
- 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;
}
};
(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`",
// 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();
}
err.cancel();
}
}
- *self = snapshot;
+ self.restore_snapshot(snapshot);
Err(err)
}
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(&[], &[]) {
// reasonable error.
inner_err.cancel();
err.cancel();
- *self = snapshot;
+ self.restore_snapshot(snapshot);
}
Ok(mut pat) => {
// We've parsed the rest of the pattern.
}
_ => {
// Carry on as if we had not done anything. This should be unreachable.
- *self = snapshot;
+ self.restore_snapshot(snapshot);
}
};
first_pat