Skip to content

parser: Tweak function parameter parsing to avoid rollback on succesfull path #54415

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

Merged
merged 1 commit into from
Sep 22, 2018
Merged
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
62 changes: 26 additions & 36 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1780,27 +1780,32 @@ impl<'a> Parser<'a> {
(pat, self.parse_ty()?)
} else {
debug!("parse_arg_general ident_to_pat");
let parser_snapshot_before_ty = self.clone();
let mut ty = self.parse_ty();
if ty.is_ok() && self.token == token::Colon {
// This wasn't actually a type, but a pattern looking like a type,
// so we are going to rollback and re-parse for recovery.
ty = self.unexpected();
}
match ty {
Ok(ty) => {
let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
let pat = P(Pat {
id: ast::DUMMY_NODE_ID,
node: PatKind::Ident(
BindingMode::ByValue(Mutability::Immutable), ident, None),
span: ty.span,
});
(pat, ty)
}
Err(mut err) => {
// Recover from attempting to parse the argument as a type without pattern.
err.cancel();
mem::replace(self, parser_snapshot_before_ty);
let pat = self.parse_pat()?;
self.expect(&token::Colon)?;
let ty = self.parse_ty()?;

let parser_snapshot_before_pat = self.clone();

// Once we can use edition 2018 in the compiler,
// replace this with real try blocks.
macro_rules! try_block {
($($inside:tt)*) => (
(||{ ::std::ops::Try::from_ok({ $($inside)* }) })()
)
}

// We're going to try parsing the argument as a pattern (even though it's not
// allowed). This way we can provide better errors to the user.
let pat_arg: PResult<'a, _> = try_block! {
let pat = self.parse_pat()?;
self.expect(&token::Colon)?;
(pat, self.parse_ty()?)
};

match pat_arg {
Ok((pat, ty)) => {
let mut err = self.diagnostic().struct_span_err_with_code(
pat.span,
"patterns aren't allowed in methods without bodies",
Expand All @@ -1813,6 +1818,7 @@ impl<'a> Parser<'a> {
Applicability::MachineApplicable,
);
err.emit();

// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
let pat = P(Pat {
node: PatKind::Wild,
Expand All @@ -1821,22 +1827,6 @@ impl<'a> Parser<'a> {
});
(pat, ty)
}
Err(mut err) => {
err.cancel();
// Recover from attempting to parse the argument as a pattern. This means
// the type is alone, with no name, e.g. `fn foo(u32)`.
mem::replace(self, parser_snapshot_before_pat);
debug!("parse_arg_general ident_to_pat");
let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
let ty = self.parse_ty()?;
let pat = P(Pat {
id: ast::DUMMY_NODE_ID,
node: PatKind::Ident(
BindingMode::ByValue(Mutability::Immutable), ident, None),
span: ty.span,
});
(pat, ty)
}
}
};

Expand Down