Skip to content

Commit 7c34cf7

Browse files
authored
Rollup merge of #54415 - petrochenkov:norollback, r=estebank
parser: Tweak function parameter parsing to avoid rollback on succesfull path Since rollback is not perfect and may e.g. leave non-fatal errors after it, we need to make sure compilation fails if it happens. So in particular case of `fn parse_arg_general` we need to parse the "good" `TYPE` first and only then rollback and recover erroneous `PAT: TYPE` if necessary. Found when working on rust-lang/rfcs#2544 (comment). r? @ghost
2 parents 0bf52b3 + 9784d35 commit 7c34cf7

File tree

1 file changed

+26
-36
lines changed

1 file changed

+26
-36
lines changed

src/libsyntax/parse/parser.rs

+26-36
Original file line numberDiff line numberDiff line change
@@ -1781,27 +1781,32 @@ impl<'a> Parser<'a> {
17811781
(pat, self.parse_ty()?)
17821782
} else {
17831783
debug!("parse_arg_general ident_to_pat");
1784+
let parser_snapshot_before_ty = self.clone();
1785+
let mut ty = self.parse_ty();
1786+
if ty.is_ok() && self.token == token::Colon {
1787+
// This wasn't actually a type, but a pattern looking like a type,
1788+
// so we are going to rollback and re-parse for recovery.
1789+
ty = self.unexpected();
1790+
}
1791+
match ty {
1792+
Ok(ty) => {
1793+
let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
1794+
let pat = P(Pat {
1795+
id: ast::DUMMY_NODE_ID,
1796+
node: PatKind::Ident(
1797+
BindingMode::ByValue(Mutability::Immutable), ident, None),
1798+
span: ty.span,
1799+
});
1800+
(pat, ty)
1801+
}
1802+
Err(mut err) => {
1803+
// Recover from attempting to parse the argument as a type without pattern.
1804+
err.cancel();
1805+
mem::replace(self, parser_snapshot_before_ty);
1806+
let pat = self.parse_pat()?;
1807+
self.expect(&token::Colon)?;
1808+
let ty = self.parse_ty()?;
17841809

1785-
let parser_snapshot_before_pat = self.clone();
1786-
1787-
// Once we can use edition 2018 in the compiler,
1788-
// replace this with real try blocks.
1789-
macro_rules! try_block {
1790-
($($inside:tt)*) => (
1791-
(||{ ::std::ops::Try::from_ok({ $($inside)* }) })()
1792-
)
1793-
}
1794-
1795-
// We're going to try parsing the argument as a pattern (even though it's not
1796-
// allowed). This way we can provide better errors to the user.
1797-
let pat_arg: PResult<'a, _> = try_block! {
1798-
let pat = self.parse_pat()?;
1799-
self.expect(&token::Colon)?;
1800-
(pat, self.parse_ty()?)
1801-
};
1802-
1803-
match pat_arg {
1804-
Ok((pat, ty)) => {
18051810
let mut err = self.diagnostic().struct_span_err_with_code(
18061811
pat.span,
18071812
"patterns aren't allowed in methods without bodies",
@@ -1814,6 +1819,7 @@ impl<'a> Parser<'a> {
18141819
Applicability::MachineApplicable,
18151820
);
18161821
err.emit();
1822+
18171823
// Pretend the pattern is `_`, to avoid duplicate errors from AST validation.
18181824
let pat = P(Pat {
18191825
node: PatKind::Wild,
@@ -1822,22 +1828,6 @@ impl<'a> Parser<'a> {
18221828
});
18231829
(pat, ty)
18241830
}
1825-
Err(mut err) => {
1826-
err.cancel();
1827-
// Recover from attempting to parse the argument as a pattern. This means
1828-
// the type is alone, with no name, e.g. `fn foo(u32)`.
1829-
mem::replace(self, parser_snapshot_before_pat);
1830-
debug!("parse_arg_general ident_to_pat");
1831-
let ident = Ident::new(keywords::Invalid.name(), self.prev_span);
1832-
let ty = self.parse_ty()?;
1833-
let pat = P(Pat {
1834-
id: ast::DUMMY_NODE_ID,
1835-
node: PatKind::Ident(
1836-
BindingMode::ByValue(Mutability::Immutable), ident, None),
1837-
span: ty.span,
1838-
});
1839-
(pat, ty)
1840-
}
18411831
}
18421832
};
18431833

0 commit comments

Comments
 (0)