Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest adding { .. } around a const function call with arguments #94731

Merged
merged 8 commits into from
Mar 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ impl<'a> Parser<'a> {
span: Span,
attr_type: OuterAttributeType,
) -> Option<Span> {
let mut snapshot = self.clone();
let mut snapshot = self.create_snapshot_for_diagnostic();
let lo = span.lo()
+ BytePos(match attr_type {
OuterAttributeType::Attribute => 1,
Expand Down
87 changes: 65 additions & 22 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use super::{
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};
Expand All @@ -21,6 +22,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder, Handler, PResult};
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;

Expand Down Expand Up @@ -154,6 +156,28 @@ impl AttemptLocalParseRecovery {
}
}

// 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> {
TaKO8Ki marked this conversation as resolved.
Show resolved Hide resolved
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,
Expand All @@ -179,6 +203,25 @@ impl<'a> Parser<'a> {
&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)
}
Expand Down Expand Up @@ -438,7 +481,7 @@ impl<'a> Parser<'a> {
// 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);
Expand All @@ -464,7 +507,7 @@ impl<'a> Parser<'a> {
Applicability::MaybeIncorrect,
)
.emit();
*self = snapshot;
self.restore_snapshot(snapshot);
let mut tail = self.mk_block(
vec![self.mk_stmt_err(expr.span)],
s,
Expand Down Expand Up @@ -678,7 +721,7 @@ impl<'a> Parser<'a> {
/// 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) {
Expand Down Expand Up @@ -712,14 +755,14 @@ impl<'a> Parser<'a> {
.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);
}
}
}
Expand Down Expand Up @@ -825,7 +868,7 @@ impl<'a> Parser<'a> {
// `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
Expand All @@ -835,14 +878,14 @@ impl<'a> Parser<'a> {
}
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() {
Expand All @@ -852,7 +895,7 @@ impl<'a> Parser<'a> {
}
Err(expr_err) => {
expr_err.cancel();
*self = snapshot;
self.restore_snapshot(snapshot);
false
}
}
Expand Down Expand Up @@ -917,7 +960,7 @@ impl<'a> Parser<'a> {
|| 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 =
Expand All @@ -929,15 +972,15 @@ impl<'a> Parser<'a> {
{
// 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 {
// We have some certainty that this was a bad turbofish at this point.
// `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>`.
Expand All @@ -954,7 +997,7 @@ impl<'a> Parser<'a> {
expr_err.cancel();
// Not entirely sure now, but we bubble the error up with the
// suggestion.
*self = snapshot;
self.restore_snapshot(snapshot);
Err(err)
}
}
Expand Down Expand Up @@ -1008,7 +1051,7 @@ impl<'a> Parser<'a> {
}

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.
Expand All @@ -1018,7 +1061,7 @@ impl<'a> Parser<'a> {

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.
Expand Down Expand Up @@ -1959,12 +2002,12 @@ impl<'a> Parser<'a> {
}

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;
}
};
Expand Down Expand Up @@ -2056,7 +2099,7 @@ impl<'a> Parser<'a> {
// 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();
}
Expand Down Expand Up @@ -2088,7 +2131,7 @@ impl<'a> Parser<'a> {
err.cancel();
}
}
*self = snapshot;
self.restore_snapshot(snapshot);
Err(err)
}

Expand Down Expand Up @@ -2148,7 +2191,7 @@ impl<'a> Parser<'a> {
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(&[], &[]) {
Expand All @@ -2160,7 +2203,7 @@ impl<'a> Parser<'a> {
// reasonable error.
inner_err.cancel();
err.cancel();
*self = snapshot;
self.restore_snapshot(snapshot);
}
Ok(mut pat) => {
// We've parsed the rest of the pattern.
Expand Down Expand Up @@ -2239,7 +2282,7 @@ impl<'a> Parser<'a> {
}
_ => {
// Carry on as if we had not done anything. This should be unreachable.
*self = snapshot;
self.restore_snapshot(snapshot);
}
};
first_pat
Expand Down
22 changes: 11 additions & 11 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ impl<'a> Parser<'a> {
ExprKind::Path(None, ast::Path { segments, .. }),
TokenKind::Ident(kw::For | kw::Loop | kw::While, false),
) if segments.len() == 1 => {
let snapshot = self.clone();
let snapshot = self.create_snapshot_for_diagnostic();
let label = Label {
ident: Ident::from_str_and_span(
&format!("'{}", segments[0].ident),
Expand All @@ -726,7 +726,7 @@ impl<'a> Parser<'a> {
}
Err(err) => {
err.cancel();
*self = snapshot;
self.restore_snapshot(snapshot);
}
}
}
Expand Down Expand Up @@ -1886,7 +1886,7 @@ impl<'a> Parser<'a> {
lo: Span,
attrs: AttrVec,
) -> Option<P<Expr>> {
let mut snapshot = self.clone();
let mut snapshot = self.create_snapshot_for_diagnostic();
match snapshot.parse_array_or_repeat_expr(attrs, token::Brace) {
Ok(arr) => {
let hi = snapshot.prev_token.span;
Expand All @@ -1902,7 +1902,7 @@ impl<'a> Parser<'a> {
.note("to define an array, one would use square brackets instead of curly braces")
.emit();

*self = snapshot;
self.restore_snapshot(snapshot);
Some(self.mk_expr_err(arr.span))
}
Err(e) => {
Expand Down Expand Up @@ -2370,7 +2370,7 @@ impl<'a> Parser<'a> {
if self.token.kind != token::Semi {
return None;
}
let start_snapshot = self.clone();
let start_snapshot = self.create_snapshot_for_diagnostic();
let semi_sp = self.token.span;
self.bump(); // `;`
let mut stmts =
Expand Down Expand Up @@ -2418,15 +2418,15 @@ impl<'a> Parser<'a> {
return Some(err(self, stmts));
}
if self.token.kind == token::Comma {
*self = start_snapshot;
self.restore_snapshot(start_snapshot);
return None;
}
let pre_pat_snapshot = self.clone();
let pre_pat_snapshot = self.create_snapshot_for_diagnostic();
match self.parse_pat_no_top_alt(None) {
Ok(_pat) => {
if self.token.kind == token::FatArrow {
// Reached arm end.
*self = pre_pat_snapshot;
self.restore_snapshot(pre_pat_snapshot);
return Some(err(self, stmts));
}
}
Expand All @@ -2435,21 +2435,21 @@ impl<'a> Parser<'a> {
}
}

*self = pre_pat_snapshot;
self.restore_snapshot(pre_pat_snapshot);
match self.parse_stmt_without_recovery(true, ForceCollect::No) {
// Consume statements for as long as possible.
Ok(Some(stmt)) => {
stmts.push(stmt);
}
Ok(None) => {
*self = start_snapshot;
self.restore_snapshot(start_snapshot);
break;
}
// We couldn't parse either yet another statement missing it's
// enclosing block nor the next arm's pattern or closing brace.
Err(stmt_err) => {
stmt_err.cancel();
*self = start_snapshot;
self.restore_snapshot(start_snapshot);
break;
}
}
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_parse/src/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -624,9 +624,18 @@ impl<'a> Parser<'a> {
GenericArg::Const(self.parse_const_arg()?)
} else if self.check_type() {
// Parse type argument.
let is_const_fn = self.look_ahead(1, |t| t.kind == token::OpenDelim(token::Paren));
let mut snapshot = self.create_snapshot_for_diagnostic();
match self.parse_ty() {
Ok(ty) => GenericArg::Type(ty),
Err(err) => {
if is_const_fn {
if let Ok(expr) = (*snapshot).parse_expr_res(Restrictions::CONST_EXPR, None)
{
self.restore_snapshot(snapshot);
return Ok(Some(self.dummy_const_arg_needs_braces(err, expr.span)));
}
}
// Try to recover from possible `const` arg without braces.
return self.recover_const_arg(start, err).map(Some);
}
Expand All @@ -636,7 +645,7 @@ impl<'a> Parser<'a> {
} else {
// Fall back by trying to parse a const-expr expression. If we successfully do so,
// then we should report an error that it needs to be wrapped in braces.
let snapshot = self.clone();
let snapshot = self.create_snapshot_for_diagnostic();
match self.parse_expr_res(Restrictions::CONST_EXPR, None) {
Ok(expr) => {
return Ok(Some(self.dummy_const_arg_needs_braces(
Expand All @@ -645,7 +654,7 @@ impl<'a> Parser<'a> {
)));
}
Err(err) => {
*self = snapshot;
self.restore_snapshot(snapshot);
err.cancel();
return Ok(None);
}
Expand Down
Loading